diff options
author | Lee Jones <lee.jones@linaro.org> | 2012-02-06 14:22:22 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-02-10 14:42:25 -0500 |
commit | 74d1d82cdaaec727f5072eb1c9f49b7e920e076f (patch) | |
tree | a1bee0010203df0465df33fb337ae66bb6b59fee /drivers/base/soc.c | |
parent | 956563362be8ac7ce084b00825168be1adfb29ee (diff) |
drivers/base: add bus for System-on-Chip devices
Traditionally, any System-on-Chip based platform creates a flat list
of platform_devices directly under /sys/devices/platform.
In order to give these some better structure, this introduces a new
bus type for soc_devices that are registered with the new
soc_device_register() function. All devices that are on the same
chip should then be registered as child devices of the soc device.
The soc bus also exports a few standardised device attributes which
allow user space to query the specific type of soc.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/soc.c')
-rw-r--r-- | drivers/base/soc.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/drivers/base/soc.c b/drivers/base/soc.c new file mode 100644 index 000000000000..05f150382da8 --- /dev/null +++ b/drivers/base/soc.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * Copyright (C) ST-Ericsson SA 2011 | ||
3 | * | ||
4 | * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson. | ||
5 | * License terms: GNU General Public License (GPL), version 2 | ||
6 | */ | ||
7 | |||
8 | #include <linux/sysfs.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/stat.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/idr.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/sys_soc.h> | ||
16 | #include <linux/err.h> | ||
17 | |||
18 | static DEFINE_IDR(soc_ida); | ||
19 | static DEFINE_SPINLOCK(soc_lock); | ||
20 | |||
21 | static ssize_t soc_info_get(struct device *dev, | ||
22 | struct device_attribute *attr, | ||
23 | char *buf); | ||
24 | |||
25 | struct soc_device { | ||
26 | struct device dev; | ||
27 | struct soc_device_attribute *attr; | ||
28 | int soc_dev_num; | ||
29 | }; | ||
30 | |||
31 | static struct bus_type soc_bus_type = { | ||
32 | .name = "soc", | ||
33 | }; | ||
34 | |||
35 | static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL); | ||
36 | static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL); | ||
37 | static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL); | ||
38 | static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL); | ||
39 | |||
40 | struct device *soc_device_to_device(struct soc_device *soc_dev) | ||
41 | { | ||
42 | return &soc_dev->dev; | ||
43 | } | ||
44 | |||
45 | static mode_t soc_attribute_mode(struct kobject *kobj, | ||
46 | struct attribute *attr, | ||
47 | int index) | ||
48 | { | ||
49 | struct device *dev = container_of(kobj, struct device, kobj); | ||
50 | struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); | ||
51 | |||
52 | if ((attr == &dev_attr_machine.attr) | ||
53 | && (soc_dev->attr->machine != NULL)) | ||
54 | return attr->mode; | ||
55 | if ((attr == &dev_attr_family.attr) | ||
56 | && (soc_dev->attr->family != NULL)) | ||
57 | return attr->mode; | ||
58 | if ((attr == &dev_attr_revision.attr) | ||
59 | && (soc_dev->attr->revision != NULL)) | ||
60 | return attr->mode; | ||
61 | if ((attr == &dev_attr_soc_id.attr) | ||
62 | && (soc_dev->attr->soc_id != NULL)) | ||
63 | return attr->mode; | ||
64 | |||
65 | /* Unknown or unfilled attribute. */ | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static ssize_t soc_info_get(struct device *dev, | ||
70 | struct device_attribute *attr, | ||
71 | char *buf) | ||
72 | { | ||
73 | struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); | ||
74 | |||
75 | if (attr == &dev_attr_machine) | ||
76 | return sprintf(buf, "%s\n", soc_dev->attr->machine); | ||
77 | if (attr == &dev_attr_family) | ||
78 | return sprintf(buf, "%s\n", soc_dev->attr->family); | ||
79 | if (attr == &dev_attr_revision) | ||
80 | return sprintf(buf, "%s\n", soc_dev->attr->revision); | ||
81 | if (attr == &dev_attr_soc_id) | ||
82 | return sprintf(buf, "%s\n", soc_dev->attr->soc_id); | ||
83 | |||
84 | return -EINVAL; | ||
85 | |||
86 | } | ||
87 | |||
88 | static struct attribute *soc_attr[] = { | ||
89 | &dev_attr_machine.attr, | ||
90 | &dev_attr_family.attr, | ||
91 | &dev_attr_soc_id.attr, | ||
92 | &dev_attr_revision.attr, | ||
93 | NULL, | ||
94 | }; | ||
95 | |||
96 | static const struct attribute_group soc_attr_group = { | ||
97 | .attrs = soc_attr, | ||
98 | .is_visible = soc_attribute_mode, | ||
99 | }; | ||
100 | |||
101 | static const struct attribute_group *soc_attr_groups[] = { | ||
102 | &soc_attr_group, | ||
103 | NULL, | ||
104 | }; | ||
105 | |||
106 | static void soc_release(struct device *dev) | ||
107 | { | ||
108 | struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); | ||
109 | |||
110 | kfree(soc_dev); | ||
111 | } | ||
112 | |||
113 | struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr) | ||
114 | { | ||
115 | struct soc_device *soc_dev; | ||
116 | int ret; | ||
117 | |||
118 | soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL); | ||
119 | if (!soc_dev) { | ||
120 | ret = -ENOMEM; | ||
121 | goto out1; | ||
122 | } | ||
123 | |||
124 | /* Fetch a unique (reclaimable) SOC ID. */ | ||
125 | do { | ||
126 | if (!ida_pre_get(&soc_ida, GFP_KERNEL)) { | ||
127 | ret = -ENOMEM; | ||
128 | goto out2; | ||
129 | } | ||
130 | |||
131 | spin_lock(&soc_lock); | ||
132 | ret = ida_get_new(&soc_ida, &soc_dev->soc_dev_num); | ||
133 | spin_unlock(&soc_lock); | ||
134 | |||
135 | } while (ret == -EAGAIN); | ||
136 | |||
137 | if (ret) | ||
138 | goto out2; | ||
139 | |||
140 | soc_dev->attr = soc_dev_attr; | ||
141 | soc_dev->dev.bus = &soc_bus_type; | ||
142 | soc_dev->dev.groups = soc_attr_groups; | ||
143 | soc_dev->dev.release = soc_release; | ||
144 | |||
145 | dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num); | ||
146 | |||
147 | ret = device_register(&soc_dev->dev); | ||
148 | if (ret) | ||
149 | goto out3; | ||
150 | |||
151 | return soc_dev; | ||
152 | |||
153 | out3: | ||
154 | ida_remove(&soc_ida, soc_dev->soc_dev_num); | ||
155 | out2: | ||
156 | kfree(soc_dev); | ||
157 | out1: | ||
158 | return ERR_PTR(ret); | ||
159 | } | ||
160 | |||
161 | /* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ | ||
162 | void soc_device_unregister(struct soc_device *soc_dev) | ||
163 | { | ||
164 | ida_remove(&soc_ida, soc_dev->soc_dev_num); | ||
165 | |||
166 | device_unregister(&soc_dev->dev); | ||
167 | } | ||
168 | |||
169 | static int __init soc_bus_register(void) | ||
170 | { | ||
171 | spin_lock_init(&soc_lock); | ||
172 | |||
173 | return bus_register(&soc_bus_type); | ||
174 | } | ||
175 | core_initcall(soc_bus_register); | ||
176 | |||
177 | static void __exit soc_bus_unregister(void) | ||
178 | { | ||
179 | ida_destroy(&soc_ida); | ||
180 | |||
181 | bus_unregister(&soc_bus_type); | ||
182 | } | ||
183 | module_exit(soc_bus_unregister); | ||