diff mbox

[3/4] mach-ux500: export System-on-Chip information ux500 via sysfs

Message ID 1312981422-13294-3-git-send-email-lee.jones@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Lee Jones Aug. 10, 2011, 1:03 p.m. UTC
Here we make use of the new drivers/base/soc driver to export
vital SoC information out to userspace via sysfs. This patch
provides a data structure of strings to populate the base
nodes found in:

/sys/devices/soc/[1|2|3|...]/[family|machine|revision|soc_id].

It also adds one more node as requested by ST-Ericsson.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/mach-ux500/Kconfig              |    1 +
 arch/arm/mach-ux500/id.c                 |  115 ++++++++++++++++++++++++++++++
 arch/arm/mach-ux500/include/mach/setup.h |    1 +
 3 files changed, 117 insertions(+), 0 deletions(-)

Comments

Jamie Iles Aug. 10, 2011, 1:34 p.m. UTC | #1
Hi Lee,

A few nits inline.

Jamie

On Wed, Aug 10, 2011 at 02:03:41PM +0100, Lee Jones wrote:
> Here we make use of the new drivers/base/soc driver to export
> vital SoC information out to userspace via sysfs. This patch
> provides a data structure of strings to populate the base
> nodes found in:
> 
> /sys/devices/soc/[1|2|3|...]/[family|machine|revision|soc_id].
> 
> It also adds one more node as requested by ST-Ericsson.
> 
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>  arch/arm/mach-ux500/Kconfig              |    1 +
>  arch/arm/mach-ux500/id.c                 |  115 ++++++++++++++++++++++++++++++
>  arch/arm/mach-ux500/include/mach/setup.h |    1 +
>  3 files changed, 117 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
> index 4210cb4..4d2f2c2 100644
> --- a/arch/arm/mach-ux500/Kconfig
> +++ b/arch/arm/mach-ux500/Kconfig
> @@ -26,6 +26,7 @@ config MACH_U8500
>  	bool "U8500 Development platform"
>  	depends on UX500_SOC_DB8500
>  	select TPS6105X
> +	select SYS_SOC
>  	help
>  	  Include support for the mop500 development platform.
>  
> diff --git a/arch/arm/mach-ux500/id.c b/arch/arm/mach-ux500/id.c
> index d35122e..917d222 100644
> --- a/arch/arm/mach-ux500/id.c
> +++ b/arch/arm/mach-ux500/id.c
> @@ -2,12 +2,17 @@
>   * Copyright (C) ST-Ericsson SA 2010
>   *
>   * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
> + * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson
>   * License terms: GNU General Public License (GPL) version 2
>   */
>  
>  #include <linux/kernel.h>
>  #include <linux/init.h>
>  #include <linux/io.h>
> +#include <linux/sys_soc.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/platform_device.h>
>  
>  #include <asm/cputype.h>
>  #include <asm/tlbflush.h>
> @@ -105,3 +110,113 @@ void __init ux500_map_io(void)
>  
>  	ux500_print_soc_info(asicid);
>  }
> +
> +#define U8500_BB_UID_BASE (U8500_BACKUPRAM1_BASE + 0xFC0)
> +#define U8500_BB_UID_LENGTH 5
> +
> +struct device *soc_parent;
> +
> +static void ux500_get_machine(char *buf)
> +{
> +	sprintf(buf, "DB%4x", dbx500_partnumber());
> +}
> +
> +static void ux500_get_family(char *buf)
> +{
> +	sprintf(buf, "Ux500");
> +}
> +
> +static void ux500_get_soc_id(char *buf)
> +{
> +	void __iomem *uid_base;
> +	int i;
> +	ssize_t sz = 0;
> +
> +	if (dbx500_partnumber() == 0x8500) {
> +		uid_base = __io_address(U8500_BB_UID_BASE);
> +		for (i = 0; i < U8500_BB_UID_LENGTH; i++) {
> +			sz += sprintf(buf + sz, "%08x", readl(uid_base + i * sizeof(u32)));
> +		}
> +		return;
> +	} else {
> +		/* Don't know where it is located for U5500 */
> +		sprintf(buf, "N/A");
> +		return;
> +	}
> +}
> +
> +static void ux500_get_revision(char *buf)
> +{
> +	unsigned int rev = dbx500_revision();
> +
> +	if (rev == 0x01) {
> +		sprintf(buf, "%s", "ED");
> +		return;
> +	}
> +	else if (rev >= 0xA0) {
> +		sprintf(buf, "%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf);
> +		return;
> +	}
> +
> +	sprintf(buf, "%s", "Unknown");
> +}
> +
> +static ssize_t ux500_get_process(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	if (dbx500_id.process == 0x00)
> +		return sprintf(buf, "Standard\n");
> +
> +	return sprintf(buf, "%02xnm\n", dbx500_id.process);
> +}
> +
> +static void soc_info_put(struct soc_device *soc_dev)
> +{
> +	char buf[1024];
> +
> +	ux500_get_machine(buf);
> +	soc_dev->machine = kzalloc(strlen(buf), GFP_KERNEL);
> +	strcpy((char *)soc_dev->machine, buf);
> +
> +	ux500_get_family(buf);
> +	soc_dev->family = kzalloc(strlen(buf), GFP_KERNEL);
> +	strcpy((char *)soc_dev->family, buf);
> +
> +	ux500_get_soc_id(buf);
> +	soc_dev->soc_id = kzalloc(strlen(buf), GFP_KERNEL);
> +	strcpy((char *)soc_dev->soc_id, buf);
> +
> +	ux500_get_revision(buf);
> +	soc_dev->revision = kzalloc(strlen(buf), GFP_KERNEL);
> +	strcpy((char *)soc_dev->revision, buf);

The returns of kzalloc() aren't checked here, but this can be simplified 
if you have the helpers do the allocation e.g.

	static const char *ux500_get_revision(void)
	{
		return kasprintf(GFP_KERNEL, "%s", ...);
	}

> +}
> +
> +struct device_attribute ux500_soc_attrs[] = {
> +	__ATTR(process,  S_IRUGO, ux500_get_process,  NULL),
> +	__ATTR_NULL,
> +};
> +
> +static int __init ux500_soc_sysfs_init(void)
> +{
> +	int ret;
> +	int i = 0;
> +	struct soc_device *soc_dev = kzalloc(sizeof(struct soc_device), GFP_KERNEL);
> +
> +	soc_parent = kzalloc(sizeof(struct device), GFP_KERNEL);

Should check the return values of kzalloc() here.  
kzalloc(sizeof(*soc_parent), GFP_KERNEL) might be a bit safer too.

> +	soc_info_put(soc_dev);

soc_info_put() sounds like a reference counting thing to me, so 
soc_info_populate() would be nicer :-)

> +
> +	ret = soc_device_register(soc_parent, soc_dev);
> +
> +	if (ret >= 0) {
> +		while (ux500_soc_attrs[i].attr.name != NULL) {
> +			ret = device_create_file(soc_parent, &ux500_soc_attrs[i++]);
> +			if (ret)
> +				goto out;
> +		}
> +	}
> +out:
> +	return ret;
> +}
> +postcore_initcall(ux500_soc_sysfs_init);
> diff --git a/arch/arm/mach-ux500/include/mach/setup.h b/arch/arm/mach-ux500/include/mach/setup.h
> index a7d363f..a24093f 100644
> --- a/arch/arm/mach-ux500/include/mach/setup.h
> +++ b/arch/arm/mach-ux500/include/mach/setup.h
> @@ -35,6 +35,7 @@ extern void __init amba_add_devices(struct amba_device *devs[], int num);
>  
>  struct sys_timer;
>  extern struct sys_timer ux500_timer;
> +extern struct device *soc_parent;
>  
>  #define __IO_DEV_DESC(x, sz)	{		\
>  	.virtual	= IO_ADDRESS(x),	\
> -- 
> 1.7.4.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Greg Kroah-Hartman Aug. 10, 2011, 3:03 p.m. UTC | #2
On Wed, Aug 10, 2011 at 02:03:41PM +0100, Lee Jones wrote:
> Here we make use of the new drivers/base/soc driver to export
> vital SoC information out to userspace via sysfs. This patch
> provides a data structure of strings to populate the base
> nodes found in:
> 
> /sys/devices/soc/[1|2|3|...]/[family|machine|revision|soc_id].
> 
> It also adds one more node as requested by ST-Ericsson.

What is that node, and why was it requested and where is it documented?
Arnd Bergmann Aug. 24, 2011, 4:10 p.m. UTC | #3
On Wednesday 10 August 2011, Lee Jones wrote:
> +
> +static void ux500_get_soc_id(char *buf)
> +{
> +       void __iomem *uid_base;
> +       int i;
> +       ssize_t sz = 0;
> +
> +       if (dbx500_partnumber() == 0x8500) {
> +               uid_base = __io_address(U8500_BB_UID_BASE);
> +               for (i = 0; i < U8500_BB_UID_LENGTH; i++) {
> +                       sz += sprintf(buf + sz, "%08x", readl(uid_base + i * sizeof(u32)));
> +               }
> +               return;
> +       } else {
> +               /* Don't know where it is located for U5500 */
> +               sprintf(buf, "N/A");
> +               return;
> +       }
> +}
> +

This still feels like it's hanging upside-down. You add an SOC node before you know
what it is, and then attach other device to it.

Similarly, having a function named 'ux500_soc_sysfs_init' is plain wrong.

You don't initialize sysfs here, but you should be probing a device with a
driver that happens to have a sysfs interface.

All probing of devices in general should start at the root and then trickle
down as you discover the child devices:
Each board has its own init_machine() callback that knows the main devices,
most importantly the SoC and registers them. When the driver for the SoC
gets loaded, that driver knows what devices are present in the device and
registers those recursively.

When you get this right, you can also eliminate the ugly machine_is_* checks
in the board file.

	Arnd
Lee Jones Aug. 25, 2011, 9:20 a.m. UTC | #4
On 24/08/11 17:10, Arnd Bergmann wrote:
> On Wednesday 10 August 2011, Lee Jones wrote:
>> +
>> +static void ux500_get_soc_id(char *buf)
>> +{
>> +       void __iomem *uid_base;
>> +       int i;
>> +       ssize_t sz = 0;
>> +
>> +       if (dbx500_partnumber() == 0x8500) {
>> +               uid_base = __io_address(U8500_BB_UID_BASE);
>> +               for (i = 0; i < U8500_BB_UID_LENGTH; i++) {
>> +                       sz += sprintf(buf + sz, "%08x", readl(uid_base + i * sizeof(u32)));
>> +               }
>> +               return;
>> +       } else {
>> +               /* Don't know where it is located for U5500 */
>> +               sprintf(buf, "N/A");
>> +               return;
>> +       }
>> +}
>> +
> 
> This still feels like it's hanging upside-down. You add an SOC node before you know
> what it is, and then attach other device to it.

Does this comment have anything to do with the code above, or was it
quoted just to illustrate that we do find out what the SoC is eventually?

If I am understanding you correctly, you mean that we're registering a
'/sys/devices/soc/NODE', before we know what kind of SoC we're dealing
with and which devices it contains?

If that is the case, I don't believe that this is an issue. If this code
has been reached, we know that we're dealing with an SoC, of any type
and we decided on a naming convention of 1, 2, 3, ..., thus making prior
knowledge of the type of SoC irrelevant.

IMO, as soon as we know we're dealing with an SoC, then
'/sys/devices/soc' along with the first node '1' should be registered.
Then as we have devices appear, they should be allowed to register
themselves as children of that node.

Again, please correct me if I misunderstand you.

> Similarly, having a function named 'ux500_soc_sysfs_init' is plain wrong.
> 
> You don't initialize sysfs here, but you should be probing a device with a
> driver that happens to have a sysfs interface.

Understood. Would something like 'ux500_export_soc_info_init' be more
suitable, or maybe even drop the init? It seems a little long (although
fully describes what we're trying to achieve) to me. Perhaps you can
provide a more succinct suggestion.

> All probing of devices in general should start at the root and then trickle
> down as you discover the child devices:
> Each board has its own init_machine() callback that knows the main devices,
> most importantly the SoC and registers them. When the driver for the SoC
> gets loaded, that driver knows what devices are present in the device and
> registers those recursively.

I completely agree with you. So how does that differ to what's happening
here?

> When you get this right, you can also eliminate the ugly machine_is_* checks
> in the board file.

We can?

Kind regards,
Lee
Arnd Bergmann Aug. 25, 2011, 2:56 p.m. UTC | #5
On Thursday 25 August 2011, Lee Jones wrote:
> On 24/08/11 17:10, Arnd Bergmann wrote:
> > On Wednesday 10 August 2011, Lee Jones wrote:
> >> +
> >> +static void ux500_get_soc_id(char *buf)
> >> +{
> >> +       void __iomem *uid_base;
> >> +       int i;
> >> +       ssize_t sz = 0;
> >> +
> >> +       if (dbx500_partnumber() == 0x8500) {
> >> +               uid_base = __io_address(U8500_BB_UID_BASE);
> >> +               for (i = 0; i < U8500_BB_UID_LENGTH; i++) {
> >> +                       sz += sprintf(buf + sz, "%08x", readl(uid_base + i * sizeof(u32)));
> >> +               }
> >> +               return;
> >> +       } else {
> >> +               /* Don't know where it is located for U5500 */
> >> +               sprintf(buf, "N/A");
> >> +               return;
> >> +       }
> >> +}
> >> +
> > 
> > This still feels like it's hanging upside-down. You add an SOC node before you know
> > what it is, and then attach other device to it.
> 
> Does this comment have anything to do with the code above, or was it
> quoted just to illustrate that we do find out what the SoC is eventually?

I should have been more elaborate here. The problem is that you try to provide
a generic *soc*id* function for the entire ux500 platform, which already
supports multiple SoCs and will gain support for further ones. Obviously,
the ID is a central part of the SoC that will be different for every one,
making it totally pointless to share the function across multiple SoCs.

Then you go further and inside that function check which soc you actually
have and *directly* access the registers from some magic address constant.
We spend a lot of work right now trying to get rid of those constants,
but a lot of the time we still need them to set up platform_devices on
platforms that don't yet use device tree probing. However, under no
circumstances should a random function just take a hardcoded base
address and do I/O on that.

> If I am understanding you correctly, you mean that we're registering a
> '/sys/devices/soc/NODE', before we know what kind of SoC we're dealing
> with and which devices it contains?
> 
> If that is the case, I don't believe that this is an issue. If this code
> has been reached, we know that we're dealing with an SoC, of any type
> and we decided on a naming convention of 1, 2, 3, ..., thus making prior
> knowledge of the type of SoC irrelevant.
> 
> IMO, as soon as we know we're dealing with an SoC, then
> '/sys/devices/soc' along with the first node '1' should be registered.
> Then as we have devices appear, they should be allowed to register
> themselves as children of that node.
> 
> Again, please correct me if I misunderstand you.

The name is indeed irrelevant, although a name such as 'db8500' would
arguably be more useful than a name of '1'.

Why can't you just put all db8500 specific code into the cpu-db8500.c
file along with the other code that knows about what a db8500 looks like?

> > Similarly, having a function named 'ux500_soc_sysfs_init' is plain wrong.
> > 
> > You don't initialize sysfs here, but you should be probing a device with a
> > driver that happens to have a sysfs interface.
> 
> Understood. Would something like 'ux500_export_soc_info_init' be more
> suitable, or maybe even drop the init? It seems a little long (although
> fully describes what we're trying to achieve) to me. Perhaps you can
> provide a more succinct suggestion.

The problem is the idea that you separate the "info" part from the actual
"soc" part. What I want to see is a *driver* for the soc that handles all
aspects of the soc that are unique to the soc but not to specific
devices inside of the soc.

> > All probing of devices in general should start at the root and then trickle
> > down as you discover the child devices:
> > Each board has its own init_machine() callback that knows the main devices,
> > most importantly the SoC and registers them. When the driver for the SoC
> > gets loaded, that driver knows what devices are present in the device and
> > registers those recursively.
> 
> I completely agree with you. So how does that differ to what's happening
> here?

You currently have a single mop500_init_machine() function that tries to handle
multiple very different boards, instead of having one init_machine function
for each one that is actually different.

> > When you get this right, you can also eliminate the ugly machine_is_* checks
> > in the board file.
> 
> We can?

What you should have here is instead of 

>static void __init mop500_init_machine(void)
>{
>        int i2c0_devs;
>
>        /*
>         * The HREFv60 board removed a GPIO expander and routed
>         * all these GPIO pins to the internal GPIO controller
>         * instead.
>         */
>        if (!machine_is_snowball()) {
>                if (machine_is_hrefv60())
>                        mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
>                else
>                        mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
>        }
>
>        u8500_init_devices();
>
>        mop500_pins_init();
>        if (machine_is_snowball())
>                platform_add_devices(snowball_platform_devs,
>                                        ARRAY_SIZE(snowball_platform_devs));
>        else
>                platform_add_devices(mop500_platform_devs,
>                                        ARRAY_SIZE(mop500_platform_devs));

just do

static void snowball_init_machine(void)
{
	u8500_init_devices();
	snowball_pins_init();
	platform_add_devices(snowball_platform_devs,
                             ARRAY_SIZE(snowball_platform_devs));
	...
}

static void hrefv60_init_machine(void)
{
	u8500_init_devices();
	hrefv60_pins_init();
	platform_add_devices(mop500_platform_devs,
                             ARRAY_SIZE(mop500_platform_devs));
	...
}

Everything related to the soc node should then go into the u8500_init_devices()
function that already knows how to take care of that soc. The remaining parts
of the init_machine just deal with the board-specific components, which can
of course be very similar on related boards, e.g. each of the boards would
add an ab8500 device to the external bus, if I interpret the source correctly.

	Arnd
Lee Jones Aug. 25, 2011, 3:16 p.m. UTC | #6
Whoa, what a brilliant response. Thanks Arnd.

I think I'm going to have to take this offline with Linus.

On 25/08/11 15:56, Arnd Bergmann wrote:
> On Thursday 25 August 2011, Lee Jones wrote:
>> On 24/08/11 17:10, Arnd Bergmann wrote:
>>> On Wednesday 10 August 2011, Lee Jones wrote:
>>>> +
>>>> +static void ux500_get_soc_id(char *buf)
>>>> +{
>>>> +       void __iomem *uid_base;
>>>> +       int i;
>>>> +       ssize_t sz = 0;
>>>> +
>>>> +       if (dbx500_partnumber() == 0x8500) {
>>>> +               uid_base = __io_address(U8500_BB_UID_BASE);
>>>> +               for (i = 0; i < U8500_BB_UID_LENGTH; i++) {
>>>> +                       sz += sprintf(buf + sz, "%08x", readl(uid_base + i * sizeof(u32)));
>>>> +               }
>>>> +               return;
>>>> +       } else {
>>>> +               /* Don't know where it is located for U5500 */
>>>> +               sprintf(buf, "N/A");
>>>> +               return;
>>>> +       }
>>>> +}
>>>> +
>>>
>>> This still feels like it's hanging upside-down. You add an SOC node before you know
>>> what it is, and then attach other device to it.
>>
>> Does this comment have anything to do with the code above, or was it
>> quoted just to illustrate that we do find out what the SoC is eventually?
> 
> I should have been more elaborate here. The problem is that you try to provide
> a generic *soc*id* function for the entire ux500 platform, which already
> supports multiple SoCs and will gain support for further ones. Obviously,
> the ID is a central part of the SoC that will be different for every one,
> making it totally pointless to share the function across multiple SoCs.
> 
> Then you go further and inside that function check which soc you actually
> have and *directly* access the registers from some magic address constant.
> We spend a lot of work right now trying to get rid of those constants,
> but a lot of the time we still need them to set up platform_devices on
> platforms that don't yet use device tree probing. However, under no
> circumstances should a random function just take a hardcoded base
> address and do I/O on that.
> 
>> If I am understanding you correctly, you mean that we're registering a
>> '/sys/devices/soc/NODE', before we know what kind of SoC we're dealing
>> with and which devices it contains?
>>
>> If that is the case, I don't believe that this is an issue. If this code
>> has been reached, we know that we're dealing with an SoC, of any type
>> and we decided on a naming convention of 1, 2, 3, ..., thus making prior
>> knowledge of the type of SoC irrelevant.
>>
>> IMO, as soon as we know we're dealing with an SoC, then
>> '/sys/devices/soc' along with the first node '1' should be registered.
>> Then as we have devices appear, they should be allowed to register
>> themselves as children of that node.
>>
>> Again, please correct me if I misunderstand you.
> 
> The name is indeed irrelevant, although a name such as 'db8500' would
> arguably be more useful than a name of '1'.
> 
> Why can't you just put all db8500 specific code into the cpu-db8500.c
> file along with the other code that knows about what a db8500 looks like?
> 
>>> Similarly, having a function named 'ux500_soc_sysfs_init' is plain wrong.
>>>
>>> You don't initialize sysfs here, but you should be probing a device with a
>>> driver that happens to have a sysfs interface.
>>
>> Understood. Would something like 'ux500_export_soc_info_init' be more
>> suitable, or maybe even drop the init? It seems a little long (although
>> fully describes what we're trying to achieve) to me. Perhaps you can
>> provide a more succinct suggestion.
> 
> The problem is the idea that you separate the "info" part from the actual
> "soc" part. What I want to see is a *driver* for the soc that handles all
> aspects of the soc that are unique to the soc but not to specific
> devices inside of the soc.
> 
>>> All probing of devices in general should start at the root and then trickle
>>> down as you discover the child devices:
>>> Each board has its own init_machine() callback that knows the main devices,
>>> most importantly the SoC and registers them. When the driver for the SoC
>>> gets loaded, that driver knows what devices are present in the device and
>>> registers those recursively.
>>
>> I completely agree with you. So how does that differ to what's happening
>> here?
> 
> You currently have a single mop500_init_machine() function that tries to handle
> multiple very different boards, instead of having one init_machine function
> for each one that is actually different.
> 
>>> When you get this right, you can also eliminate the ugly machine_is_* checks
>>> in the board file.
>>
>> We can?
> 
> What you should have here is instead of 
> 
>> static void __init mop500_init_machine(void)
>> {
>>        int i2c0_devs;
>>
>>        /*
>>         * The HREFv60 board removed a GPIO expander and routed
>>         * all these GPIO pins to the internal GPIO controller
>>         * instead.
>>         */
>>        if (!machine_is_snowball()) {
>>                if (machine_is_hrefv60())
>>                        mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
>>                else
>>                        mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
>>        }
>>
>>        u8500_init_devices();
>>
>>        mop500_pins_init();
>>        if (machine_is_snowball())
>>                platform_add_devices(snowball_platform_devs,
>>                                        ARRAY_SIZE(snowball_platform_devs));
>>        else
>>                platform_add_devices(mop500_platform_devs,
>>                                        ARRAY_SIZE(mop500_platform_devs));
> 
> just do
> 
> static void snowball_init_machine(void)
> {
> 	u8500_init_devices();
> 	snowball_pins_init();
> 	platform_add_devices(snowball_platform_devs,
>                              ARRAY_SIZE(snowball_platform_devs));
> 	...
> }
> 
> static void hrefv60_init_machine(void)
> {
> 	u8500_init_devices();
> 	hrefv60_pins_init();
> 	platform_add_devices(mop500_platform_devs,
>                              ARRAY_SIZE(mop500_platform_devs));
> 	...
> }
> 
> Everything related to the soc node should then go into the u8500_init_devices()
> function that already knows how to take care of that soc. The remaining parts
> of the init_machine just deal with the board-specific components, which can
> of course be very similar on related boards, e.g. each of the boards would
> add an ab8500 device to the external bus, if I interpret the source correctly.
> 
> 	Arnd
Lee Jones Sept. 1, 2011, 6:58 a.m. UTC | #7
On 10/08/11 16:03, Greg KH wrote:
> On Wed, Aug 10, 2011 at 02:03:41PM +0100, Lee Jones wrote:
>> Here we make use of the new drivers/base/soc driver to export
>> vital SoC information out to userspace via sysfs. This patch
>> provides a data structure of strings to populate the base
>> nodes found in:
>>
>> /sys/devices/soc/[1|2|3|...]/[family|machine|revision|soc_id].
>>
>> It also adds one more node as requested by ST-Ericsson.
> 
> What is that node, and why was it requested and where is it documented?

I can answer the first two questions, but where would you like it
documented? Is the commit message suitable, or would you like another
Document text file created/appended to?
Greg Kroah-Hartman Sept. 1, 2011, 2:24 p.m. UTC | #8
On Thu, Sep 01, 2011 at 07:58:15AM +0100, Lee Jones wrote:
> On 10/08/11 16:03, Greg KH wrote:
> > On Wed, Aug 10, 2011 at 02:03:41PM +0100, Lee Jones wrote:
> >> Here we make use of the new drivers/base/soc driver to export
> >> vital SoC information out to userspace via sysfs. This patch
> >> provides a data structure of strings to populate the base
> >> nodes found in:
> >>
> >> /sys/devices/soc/[1|2|3|...]/[family|machine|revision|soc_id].
> >>
> >> It also adds one more node as requested by ST-Ericsson.
> > 
> > What is that node, and why was it requested and where is it documented?
> 
> I can answer the first two questions, but where would you like it
> documented? Is the commit message suitable, or would you like another
> Document text file created/appended to?

All user apis are documented in Documentation/ABI/, so files need to be
added there if you are adding/changing/deleting sysfs files.

thanks,

greg k-h
diff mbox

Patch

diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index 4210cb4..4d2f2c2 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -26,6 +26,7 @@  config MACH_U8500
 	bool "U8500 Development platform"
 	depends on UX500_SOC_DB8500
 	select TPS6105X
+	select SYS_SOC
 	help
 	  Include support for the mop500 development platform.
 
diff --git a/arch/arm/mach-ux500/id.c b/arch/arm/mach-ux500/id.c
index d35122e..917d222 100644
--- a/arch/arm/mach-ux500/id.c
+++ b/arch/arm/mach-ux500/id.c
@@ -2,12 +2,17 @@ 
  * Copyright (C) ST-Ericsson SA 2010
  *
  * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson
  * License terms: GNU General Public License (GPL) version 2
  */
 
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/sys_soc.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/platform_device.h>
 
 #include <asm/cputype.h>
 #include <asm/tlbflush.h>
@@ -105,3 +110,113 @@  void __init ux500_map_io(void)
 
 	ux500_print_soc_info(asicid);
 }
+
+#define U8500_BB_UID_BASE (U8500_BACKUPRAM1_BASE + 0xFC0)
+#define U8500_BB_UID_LENGTH 5
+
+struct device *soc_parent;
+
+static void ux500_get_machine(char *buf)
+{
+	sprintf(buf, "DB%4x", dbx500_partnumber());
+}
+
+static void ux500_get_family(char *buf)
+{
+	sprintf(buf, "Ux500");
+}
+
+static void ux500_get_soc_id(char *buf)
+{
+	void __iomem *uid_base;
+	int i;
+	ssize_t sz = 0;
+
+	if (dbx500_partnumber() == 0x8500) {
+		uid_base = __io_address(U8500_BB_UID_BASE);
+		for (i = 0; i < U8500_BB_UID_LENGTH; i++) {
+			sz += sprintf(buf + sz, "%08x", readl(uid_base + i * sizeof(u32)));
+		}
+		return;
+	} else {
+		/* Don't know where it is located for U5500 */
+		sprintf(buf, "N/A");
+		return;
+	}
+}
+
+static void ux500_get_revision(char *buf)
+{
+	unsigned int rev = dbx500_revision();
+
+	if (rev == 0x01) {
+		sprintf(buf, "%s", "ED");
+		return;
+	}
+	else if (rev >= 0xA0) {
+		sprintf(buf, "%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf);
+		return;
+	}
+
+	sprintf(buf, "%s", "Unknown");
+}
+
+static ssize_t ux500_get_process(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	if (dbx500_id.process == 0x00)
+		return sprintf(buf, "Standard\n");
+
+	return sprintf(buf, "%02xnm\n", dbx500_id.process);
+}
+
+static void soc_info_put(struct soc_device *soc_dev)
+{
+	char buf[1024];
+
+	ux500_get_machine(buf);
+	soc_dev->machine = kzalloc(strlen(buf), GFP_KERNEL);
+	strcpy((char *)soc_dev->machine, buf);
+
+	ux500_get_family(buf);
+	soc_dev->family = kzalloc(strlen(buf), GFP_KERNEL);
+	strcpy((char *)soc_dev->family, buf);
+
+	ux500_get_soc_id(buf);
+	soc_dev->soc_id = kzalloc(strlen(buf), GFP_KERNEL);
+	strcpy((char *)soc_dev->soc_id, buf);
+
+	ux500_get_revision(buf);
+	soc_dev->revision = kzalloc(strlen(buf), GFP_KERNEL);
+	strcpy((char *)soc_dev->revision, buf);
+}
+
+struct device_attribute ux500_soc_attrs[] = {
+	__ATTR(process,  S_IRUGO, ux500_get_process,  NULL),
+	__ATTR_NULL,
+};
+
+static int __init ux500_soc_sysfs_init(void)
+{
+	int ret;
+	int i = 0;
+	struct soc_device *soc_dev = kzalloc(sizeof(struct soc_device), GFP_KERNEL);
+
+	soc_parent = kzalloc(sizeof(struct device), GFP_KERNEL);
+
+	soc_info_put(soc_dev);
+
+	ret = soc_device_register(soc_parent, soc_dev);
+
+	if (ret >= 0) {
+		while (ux500_soc_attrs[i].attr.name != NULL) {
+			ret = device_create_file(soc_parent, &ux500_soc_attrs[i++]);
+			if (ret)
+				goto out;
+		}
+	}
+out:
+	return ret;
+}
+postcore_initcall(ux500_soc_sysfs_init);
diff --git a/arch/arm/mach-ux500/include/mach/setup.h b/arch/arm/mach-ux500/include/mach/setup.h
index a7d363f..a24093f 100644
--- a/arch/arm/mach-ux500/include/mach/setup.h
+++ b/arch/arm/mach-ux500/include/mach/setup.h
@@ -35,6 +35,7 @@  extern void __init amba_add_devices(struct amba_device *devs[], int num);
 
 struct sys_timer;
 extern struct sys_timer ux500_timer;
+extern struct device *soc_parent;
 
 #define __IO_DEV_DESC(x, sz)	{		\
 	.virtual	= IO_ADDRESS(x),	\