diff mbox

[1/4] Framework for exporting System-on-Chip information via sysfs

Message ID 1312981422-13294-1-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
This patch introduces a new driver to drivers/base. The
driver provides a means to export vital SoC data out to
userspace via sysfs. Standard information applicable to all
SoCs are exported to:

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

It is possible to create SoC specific items via the
soc_parent, which is returned post-registration, although
this should be discouraged.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/base/Kconfig    |    3 +
 drivers/base/Makefile   |    1 +
 drivers/base/soc.c      |  104 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/sys_soc.h |   41 ++++++++++++++++++
 4 files changed, 149 insertions(+), 0 deletions(-)
 create mode 100644 drivers/base/soc.c
 create mode 100644 include/linux/sys_soc.h

Comments

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

A few minor comments inline.

Jamie

On Wed, Aug 10, 2011 at 02:03:39PM +0100, Lee Jones wrote:
> This patch introduces a new driver to drivers/base. The
> driver provides a means to export vital SoC data out to
> userspace via sysfs. Standard information applicable to all
> SoCs are exported to:
> 
> /sys/devices/soc/[1|2|3|...]/[family|machine|revision|soc_id].
> 
> It is possible to create SoC specific items via the
> soc_parent, which is returned post-registration, although
> this should be discouraged.
> 
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>  drivers/base/Kconfig    |    3 +
>  drivers/base/Makefile   |    1 +
>  drivers/base/soc.c      |  104 +++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/sys_soc.h |   41 ++++++++++++++++++
>  4 files changed, 149 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/base/soc.c
>  create mode 100644 include/linux/sys_soc.h
> 
> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index d57e8d0..372ef3a 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -168,4 +168,7 @@ config SYS_HYPERVISOR
>  	bool
>  	default n
>  
> +config SYS_SOC
> +	bool
> +
>  endmenu
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index 4c5701c..a0d246d 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -18,6 +18,7 @@ ifeq ($(CONFIG_SYSFS),y)
>  obj-$(CONFIG_MODULES)	+= module.o
>  endif
>  obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
> +obj-$(CONFIG_SYS_SOC) += soc.o
>  
>  ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
>  
> diff --git a/drivers/base/soc.c b/drivers/base/soc.c
> new file mode 100644
> index 0000000..05944ef
> --- /dev/null
> +++ b/drivers/base/soc.c
> @@ -0,0 +1,104 @@
> +/*
> + * Copyright (C) ST-Ericsson SA 2011
> + *
> + * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/sysfs.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/stat.h>
> +#include <linux/slab.h>
> +#include <linux/sys_soc.h>
> +
> +static DEFINE_SPINLOCK(register_lock);
> +static int soc_count = 0;
> +
> +static struct device soc_grandparent = {
> +	.init_name = "soc",
> +};
> +
> +static ssize_t soc_info_get(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	struct soc_device *soc_dev = dev->platform_data;
> +
> +	if (!strncmp(attr->attr.name, "machine", 7))
> +		return sprintf(buf, "%s\n", soc_dev->machine);
> +	if (!strncmp(attr->attr.name, "family", 6))
> +		return sprintf(buf, "%s\n", soc_dev->family);
> +	if (!strncmp(attr->attr.name, "soc_id", 6))
> +		return sprintf(buf, "%s\n", soc_dev->soc_id);
> +	if (!strncmp(attr->attr.name, "revision", 8))
> +		return sprintf(buf, "%s\n", soc_dev->revision);

I think strcmp() would be better here rather than strncmp() otherwise 
there could be problems if someone adds an attribute that has a previous 
one as a starting sub-string.

> +
> +	return sprintf(buf, "Unknown attribute name - %s\n", attr->attr.name);

Just return -EINVAL or similar here to propogate the error to the user.

> +}
> +
> +struct device_attribute soc_attrs[] = {
> +	__ATTR(machine,  S_IRUGO, soc_info_get,  NULL),
> +	__ATTR(family,   S_IRUGO, soc_info_get,  NULL),
> +	__ATTR(soc_id,   S_IRUGO, soc_info_get,  NULL),
> +	__ATTR(revision, S_IRUGO, soc_info_get,  NULL),
> +	__ATTR_NULL,
> +};
> +
> +static void __exit soc_device_remove_files(struct device *soc)
> +{
> +	int i = 0;
> +
> +	while (soc_attrs[i++].attr.name != NULL)
> +		device_remove_file(soc, &soc_attrs[i]);
> +}
> +
> +static int __init soc_device_create_files(struct device *soc)
> +{
> +	int ret = 0;
> +	int i   = 0;
> +
> +	while (soc_attrs[i].attr.name != NULL) {
> +		ret = device_create_file(soc, &soc_attrs[i++]);
> +		if (ret)
> +			goto out;
> +	}
> +	return ret;
> +
> +out:
> +	soc_device_remove_files(soc);

This should count back from i and unregister the attributes as 
soc_device_remove_files may try and remove files that didn't get 
registered.

> +	return ret;
> +}
> +
> +int __init soc_device_register(struct device *soc_parent,
> +			struct soc_device *soc_dev)
> +{
> +	int ret;
> +
> +	spin_lock_irq(&register_lock);
> +
> +	if (!soc_count) {
> +		/* Register top-level SoC device '/sys/devices/soc' */
> +		ret = device_register(&soc_grandparent);
> +		if (ret)
> +		{
> +			spin_unlock_irq(&register_lock);
> +			return ret;
> +		}
> +	}
> +
> +	soc_count++;
> +	soc_parent->parent = &soc_grandparent;
> +	dev_set_name(soc_parent, "%i", soc_count);
> +	soc_parent->platform_data = soc_dev;

I don't think platform_data is the right place for this.  It's not clear 
what soc_parent and soc_dev do here as soc_dev never gets registered.

Should this be:

	soc_dev->parent = &soc_grandparent;
	dev_set_name(soc_dev, "%i", soc_count);
	device_register(soc_dev);

and drop soc_parent and add the files to soc_device?  In soc_info_get(), 
you could then do:

	struct soc_device *soc = container_of(dev, struct soc_device, dev);

> +	spin_unlock_irq(&register_lock);
> +
> +	ret = device_register(soc_parent);
> +	if (ret)
> +		return ret;
> +
> +	soc_device_create_files(soc_parent);
> +
> +	return ret;
> +}
> diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h
> new file mode 100644
> index 0000000..3de6405
> --- /dev/null
> +++ b/include/linux/sys_soc.h
> @@ -0,0 +1,41 @@
> +/*
> + * Copyright (C) ST-Ericsson SA 2011
> + * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +#ifndef __SYS_SOC_H
> +#define __SYS_SOC_H
> +
> +#include <linux/kobject.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +
> +#define MAX_SOCS 8
> +#define MACHINE  0
> +#define FAMILY   1
> +#define SOCID    2
> +#define REVISION 3

These defines don't appear to be used anywhere afaict.

> +
> +struct soc_device {
> +	struct device dev;
> +	const char *machine;
> +	const char *family;
> +	const char *soc_id;
> +	const char *revision;
> +};
> +
> +/**
> + * soc_device_register - register SoC as a device
> + * @soc_parent: Parent node '/sys/devices/soc/X'
> + * @soc_dev: SoC device specific information
> + */
> +int soc_device_register(struct device *soc_parent,
> +			struct soc_device *soc_dev);
> +
> +/**
> + *  soc_device_unregister - unregister SoC as a device
> + * @soc_parent: Parent node '/sys/devices/soc/X'
> + */
> +void soc_device_unregister(struct device *soc_parent);

This doesn't appear to exist, and probably doesn't need to.
Greg Kroah-Hartman Aug. 10, 2011, 3:02 p.m. UTC | #2
On Wed, Aug 10, 2011 at 02:03:39PM +0100, Lee Jones wrote:
> This patch introduces a new driver to drivers/base. The
> driver provides a means to export vital SoC data out to
> userspace via sysfs. Standard information applicable to all
> SoCs are exported to:
> 
> /sys/devices/soc/[1|2|3|...]/[family|machine|revision|soc_id].
> 
> It is possible to create SoC specific items via the
> soc_parent, which is returned post-registration, although
> this should be discouraged.

Then don't return the pointer, if you don't want it to be used.

> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>  drivers/base/Kconfig    |    3 +
>  drivers/base/Makefile   |    1 +
>  drivers/base/soc.c      |  104 +++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/sys_soc.h |   41 ++++++++++++++++++
>  4 files changed, 149 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/base/soc.c
>  create mode 100644 include/linux/sys_soc.h
> 
> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index d57e8d0..372ef3a 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -168,4 +168,7 @@ config SYS_HYPERVISOR
>  	bool
>  	default n
>  
> +config SYS_SOC
> +	bool
> +
>  endmenu
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index 4c5701c..a0d246d 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -18,6 +18,7 @@ ifeq ($(CONFIG_SYSFS),y)
>  obj-$(CONFIG_MODULES)	+= module.o
>  endif
>  obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
> +obj-$(CONFIG_SYS_SOC) += soc.o
>  
>  ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
>  
> diff --git a/drivers/base/soc.c b/drivers/base/soc.c
> new file mode 100644
> index 0000000..05944ef
> --- /dev/null
> +++ b/drivers/base/soc.c
> @@ -0,0 +1,104 @@
> +/*
> + * Copyright (C) ST-Ericsson SA 2011
> + *
> + * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/sysfs.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/stat.h>
> +#include <linux/slab.h>
> +#include <linux/sys_soc.h>
> +
> +static DEFINE_SPINLOCK(register_lock);
> +static int soc_count = 0;

Please just use the in-kernel api for stuff like this, and don't roll
your own.

> +
> +static struct device soc_grandparent = {
> +	.init_name = "soc",
> +};

No, please NEVER create a static struct device.  Again, use the properly
kernel functions that are provided to you to create such a thing.

> +
> +static ssize_t soc_info_get(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	struct soc_device *soc_dev = dev->platform_data;
> +
> +	if (!strncmp(attr->attr.name, "machine", 7))
> +		return sprintf(buf, "%s\n", soc_dev->machine);
> +	if (!strncmp(attr->attr.name, "family", 6))
> +		return sprintf(buf, "%s\n", soc_dev->family);
> +	if (!strncmp(attr->attr.name, "soc_id", 6))
> +		return sprintf(buf, "%s\n", soc_dev->soc_id);
> +	if (!strncmp(attr->attr.name, "revision", 8))
> +		return sprintf(buf, "%s\n", soc_dev->revision);
> +
> +	return sprintf(buf, "Unknown attribute name - %s\n", attr->attr.name);

No need to return the unknown attribute name, that's what userspace just
asked for, so it knows it.  Return an error instead (-EINVAL);

> +}
> +
> +struct device_attribute soc_attrs[] = {
> +	__ATTR(machine,  S_IRUGO, soc_info_get,  NULL),
> +	__ATTR(family,   S_IRUGO, soc_info_get,  NULL),
> +	__ATTR(soc_id,   S_IRUGO, soc_info_get,  NULL),
> +	__ATTR(revision, S_IRUGO, soc_info_get,  NULL),
> +	__ATTR_NULL,
> +};
> +
> +static void __exit soc_device_remove_files(struct device *soc)
> +{
> +	int i = 0;
> +
> +	while (soc_attrs[i++].attr.name != NULL)
> +		device_remove_file(soc, &soc_attrs[i]);
> +}

We do have this ability to add and remove whole attribute groups at
once.

Your ability to reinvent core code is admirable though :)

You really want to make this whole thing a class, and use it that way
instead of what you are trying to do here (roll your own class by
reinventing all of the same code again.)

Please rewrite it that way, it will be much simpler, and smaller, and
work properly (hint, you have races here with userspace tools...)

> +#include <linux/platform_device.h>

Why is platform device needed?

> +
> +#define MAX_SOCS 8

Why have a max?

> +#define MACHINE  0
> +#define FAMILY   1
> +#define SOCID    2
> +#define REVISION 3

Why does the whole kernel need these, very generic, defines?
> +
> +struct soc_device {
> +	struct device dev;
> +	const char *machine;
> +	const char *family;
> +	const char *soc_id;
> +	const char *revision;
> +};
> +
> +/**
> + * soc_device_register - register SoC as a device
> + * @soc_parent: Parent node '/sys/devices/soc/X'
> + * @soc_dev: SoC device specific information
> + */
> +int soc_device_register(struct device *soc_parent,
> +			struct soc_device *soc_dev);

No, you should just pass in the information for the soc device, and have
the core create everything for you (like the struct device and the
rest.)  So, soc_device should really just have everything above, minus
the struct device, as that would be better to pass into the function
from callers.

greg k-h
Lee Jones Aug. 24, 2011, 2:08 p.m. UTC | #3
Hi Jamie,

Thanks for reviewing this for me.

Most of your comments I've adhered to, but I do have a question inline.

>> +	return ret;
>> +}
>> +
>> +int __init soc_device_register(struct device *soc_parent,
>> +			struct soc_device *soc_dev)
>> +{
>> +	int ret;
>> +
>> +	spin_lock_irq(&register_lock);
>> +
>> +	if (!soc_count) {
>> +		/* Register top-level SoC device '/sys/devices/soc' */
>> +		ret = device_register(&soc_grandparent);
>> +		if (ret)
>> +		{
>> +			spin_unlock_irq(&register_lock);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	soc_count++;
>> +	soc_parent->parent = &soc_grandparent;
>> +	dev_set_name(soc_parent, "%i", soc_count);
>> +	soc_parent->platform_data = soc_dev;
> 
> I don't think platform_data is the right place for this.  

I agree with you, but I was unsure how else to get the data back.

> It's not clear
> what soc_parent and soc_dev do here as soc_dev never gets registered.
>
> Should this be:
> 
> 	soc_dev->parent = &soc_grandparent;
> 	dev_set_name(soc_dev, "%i", soc_count);
> 	device_register(soc_dev);

AFAIK soc_dev can't be registered. It's just a container which holds
each SoW's information to be exported in the way of const char *'s.

struct soc_device {
	struct device dev;
	const char *machine;
	const char *family;
	const char *soc_id;
	const char *revision;
};

I don't believe that the "struct device dev;" is required either, but
this is something I was advised to put in.

Can you think of a better way to store these values other than in
platform_data then?

> and drop soc_parent and add the files to soc_device?  In soc_info_get(), 
> you could then do:
> 
> 	struct soc_device *soc = container_of(dev, struct soc_device, dev);
> 
>> +	spin_unlock_irq(&register_lock);
>> +
>> +	ret = device_register(soc_parent);
>> +	if (ret)
>> +		return ret;
>> +
>> +	soc_device_create_files(soc_parent);
>> +
>> +	return ret;
>> +}

Kind regards,
Lee
Jamie Iles Aug. 24, 2011, 2:19 p.m. UTC | #4
Hi Lee,

On Wed, Aug 24, 2011 at 03:08:42PM +0100, Lee Jones wrote:
[...]
> >> +	return ret;
> >> +}
> >> +
> >> +int __init soc_device_register(struct device *soc_parent,
> >> +			struct soc_device *soc_dev)
> >> +{
> >> +	int ret;
> >> +
> >> +	spin_lock_irq(&register_lock);
> >> +
> >> +	if (!soc_count) {
> >> +		/* Register top-level SoC device '/sys/devices/soc' */
> >> +		ret = device_register(&soc_grandparent);
> >> +		if (ret)
> >> +		{
> >> +			spin_unlock_irq(&register_lock);
> >> +			return ret;
> >> +		}
> >> +	}
> >> +
> >> +	soc_count++;
> >> +	soc_parent->parent = &soc_grandparent;
> >> +	dev_set_name(soc_parent, "%i", soc_count);
> >> +	soc_parent->platform_data = soc_dev;
> > 
> > I don't think platform_data is the right place for this.  
> 
> I agree with you, but I was unsure how else to get the data back.
> 
> > It's not clear
> > what soc_parent and soc_dev do here as soc_dev never gets registered.
> >
> > Should this be:
> > 
> > 	soc_dev->parent = &soc_grandparent;
> > 	dev_set_name(soc_dev, "%i", soc_count);
> > 	device_register(soc_dev);
> 
> AFAIK soc_dev can't be registered. It's just a container which holds
> each SoW's information to be exported in the way of const char *'s.

Sorry, that should have been:

	dev_set_name(&soc_dev->dev, "%i", soc_count);
	device_register(&soc_dev->dev);

You should probably also set soc_dev->dev->release to something that'll 
kfree() the device on removal (even though they are unlikely to be 
removed).

> struct soc_device {
> 	struct device dev;
> 	const char *machine;
> 	const char *family;
> 	const char *soc_id;
> 	const char *revision;
> };
> 
> I don't believe that the "struct device dev;" is required either, but
> this is something I was advised to put in.
> 
> Can you think of a better way to store these values other than in
> platform_data then?

You can get the struct soc_device from the struct device with:

	#define to_soc_device(__dev) \
		container_of((__dev), struct soc_device, dev)

Then in the code:

	static ssize_t soc_info_get(struct device *dev,                                                                                                                                                                                                                                  
				    struct device_attribute *attr,                                                                                                                                                                                                                            
				    char *buf)                                                                                                                                                                                                                                                
	{                                                                                                                                                                                                                                                                                
		struct soc_device *soc_dev = to_soc_device(dev);
		...

struct platform_device is probably a good reference for this.

Jamie
Jamie Iles Aug. 24, 2011, 2:22 p.m. UTC | #5
On Wed, Aug 24, 2011 at 03:19:35PM +0100, Jamie Iles wrote:
> Hi Lee,
> 
> On Wed, Aug 24, 2011 at 03:08:42PM +0100, Lee Jones wrote:
> [...]
> > >> +	return ret;
> > >> +}
> > >> +
> > >> +int __init soc_device_register(struct device *soc_parent,
> > >> +			struct soc_device *soc_dev)
> > >> +{
> > >> +	int ret;
> > >> +
> > >> +	spin_lock_irq(&register_lock);
> > >> +
> > >> +	if (!soc_count) {
> > >> +		/* Register top-level SoC device '/sys/devices/soc' */
> > >> +		ret = device_register(&soc_grandparent);
> > >> +		if (ret)
> > >> +		{
> > >> +			spin_unlock_irq(&register_lock);
> > >> +			return ret;
> > >> +		}
> > >> +	}
> > >> +
> > >> +	soc_count++;
> > >> +	soc_parent->parent = &soc_grandparent;
> > >> +	dev_set_name(soc_parent, "%i", soc_count);
> > >> +	soc_parent->platform_data = soc_dev;
> > > 
> > > I don't think platform_data is the right place for this.  
> > 
> > I agree with you, but I was unsure how else to get the data back.
> > 
> > > It's not clear
> > > what soc_parent and soc_dev do here as soc_dev never gets registered.
> > >
> > > Should this be:
> > > 
> > > 	soc_dev->parent = &soc_grandparent;
> > > 	dev_set_name(soc_dev, "%i", soc_count);
> > > 	device_register(soc_dev);
> > 
> > AFAIK soc_dev can't be registered. It's just a container which holds
> > each SoW's information to be exported in the way of const char *'s.
> 
> Sorry, that should have been:
> 
> 	dev_set_name(&soc_dev->dev, "%i", soc_count);
> 	device_register(&soc_dev->dev);
> 
> You should probably also set soc_dev->dev->release to something that'll 
> kfree() the device on removal (even though they are unlikely to be 
> removed).

Also you could take advantage struct device::type and its groups member 
for adding the common attributes.  This means that the files get added 
automatically at device_register() so there is no race between the 
device appearing and the attributes being added.

Jamie
diff mbox

Patch

diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index d57e8d0..372ef3a 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -168,4 +168,7 @@  config SYS_HYPERVISOR
 	bool
 	default n
 
+config SYS_SOC
+	bool
+
 endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 4c5701c..a0d246d 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -18,6 +18,7 @@  ifeq ($(CONFIG_SYSFS),y)
 obj-$(CONFIG_MODULES)	+= module.o
 endif
 obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
+obj-$(CONFIG_SYS_SOC) += soc.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
 
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
new file mode 100644
index 0000000..05944ef
--- /dev/null
+++ b/drivers/base/soc.c
@@ -0,0 +1,104 @@ 
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+static DEFINE_SPINLOCK(register_lock);
+static int soc_count = 0;
+
+static struct device soc_grandparent = {
+	.init_name = "soc",
+};
+
+static ssize_t soc_info_get(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct soc_device *soc_dev = dev->platform_data;
+
+	if (!strncmp(attr->attr.name, "machine", 7))
+		return sprintf(buf, "%s\n", soc_dev->machine);
+	if (!strncmp(attr->attr.name, "family", 6))
+		return sprintf(buf, "%s\n", soc_dev->family);
+	if (!strncmp(attr->attr.name, "soc_id", 6))
+		return sprintf(buf, "%s\n", soc_dev->soc_id);
+	if (!strncmp(attr->attr.name, "revision", 8))
+		return sprintf(buf, "%s\n", soc_dev->revision);
+
+	return sprintf(buf, "Unknown attribute name - %s\n", attr->attr.name);
+}
+
+struct device_attribute soc_attrs[] = {
+	__ATTR(machine,  S_IRUGO, soc_info_get,  NULL),
+	__ATTR(family,   S_IRUGO, soc_info_get,  NULL),
+	__ATTR(soc_id,   S_IRUGO, soc_info_get,  NULL),
+	__ATTR(revision, S_IRUGO, soc_info_get,  NULL),
+	__ATTR_NULL,
+};
+
+static void __exit soc_device_remove_files(struct device *soc)
+{
+	int i = 0;
+
+	while (soc_attrs[i++].attr.name != NULL)
+		device_remove_file(soc, &soc_attrs[i]);
+}
+
+static int __init soc_device_create_files(struct device *soc)
+{
+	int ret = 0;
+	int i   = 0;
+
+	while (soc_attrs[i].attr.name != NULL) {
+		ret = device_create_file(soc, &soc_attrs[i++]);
+		if (ret)
+			goto out;
+	}
+	return ret;
+
+out:
+	soc_device_remove_files(soc);
+	return ret;
+}
+
+int __init soc_device_register(struct device *soc_parent,
+			struct soc_device *soc_dev)
+{
+	int ret;
+
+	spin_lock_irq(&register_lock);
+
+	if (!soc_count) {
+		/* Register top-level SoC device '/sys/devices/soc' */
+		ret = device_register(&soc_grandparent);
+		if (ret)
+		{
+			spin_unlock_irq(&register_lock);
+			return ret;
+		}
+	}
+
+	soc_count++;
+	soc_parent->parent = &soc_grandparent;
+	dev_set_name(soc_parent, "%i", soc_count);
+	soc_parent->platform_data = soc_dev;
+
+	spin_unlock_irq(&register_lock);
+
+	ret = device_register(soc_parent);
+	if (ret)
+		return ret;
+
+	soc_device_create_files(soc_parent);
+
+	return ret;
+}
diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h
new file mode 100644
index 0000000..3de6405
--- /dev/null
+++ b/include/linux/sys_soc.h
@@ -0,0 +1,41 @@ 
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+#ifndef __SYS_SOC_H
+#define __SYS_SOC_H
+
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#define MAX_SOCS 8
+#define MACHINE  0
+#define FAMILY   1
+#define SOCID    2
+#define REVISION 3
+
+struct soc_device {
+	struct device dev;
+	const char *machine;
+	const char *family;
+	const char *soc_id;
+	const char *revision;
+};
+
+/**
+ * soc_device_register - register SoC as a device
+ * @soc_parent: Parent node '/sys/devices/soc/X'
+ * @soc_dev: SoC device specific information
+ */
+int soc_device_register(struct device *soc_parent,
+			struct soc_device *soc_dev);
+
+/**
+ *  soc_device_unregister - unregister SoC as a device
+ * @soc_parent: Parent node '/sys/devices/soc/X'
+ */
+void soc_device_unregister(struct device *soc_parent);
+
+#endif /* __SYS_SOC_H */