aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/platform.c153
-rw-r--r--include/linux/platform_device.h6
2 files changed, 132 insertions, 27 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index d597c922af11..6d4736e89f1a 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -116,12 +116,115 @@ int platform_add_devices(struct platform_device **devs, int num)
116 return ret; 116 return ret;
117} 117}
118 118
119struct platform_object {
120 struct platform_device pdev;
121 char name[1];
122};
123
119/** 124/**
120 * platform_device_register - add a platform-level device 125 * platform_device_put
126 * @pdev: platform device to free
127 *
128 * Free all memory associated with a platform device. This function
129 * must _only_ be externally called in error cases. All other usage
130 * is a bug.
131 */
132void platform_device_put(struct platform_device *pdev)
133{
134 if (pdev)
135 put_device(&pdev->dev);
136}
137EXPORT_SYMBOL_GPL(platform_device_put);
138
139static void platform_device_release(struct device *dev)
140{
141 struct platform_object *pa = container_of(dev, struct platform_object, pdev.dev);
142
143 kfree(pa->pdev.dev.platform_data);
144 kfree(pa->pdev.resource);
145 kfree(pa);
146}
147
148/**
149 * platform_device_alloc
150 * @name: base name of the device we're adding
151 * @id: instance id
152 *
153 * Create a platform device object which can have other objects attached
154 * to it, and which will have attached objects freed when it is released.
155 */
156struct platform_device *platform_device_alloc(const char *name, unsigned int id)
157{
158 struct platform_object *pa;
159
160 pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
161 if (pa) {
162 strcpy(pa->name, name);
163 pa->pdev.name = pa->name;
164 pa->pdev.id = id;
165 device_initialize(&pa->pdev.dev);
166 pa->pdev.dev.release = platform_device_release;
167 }
168
169 return pa ? &pa->pdev : NULL;
170}
171EXPORT_SYMBOL_GPL(platform_device_alloc);
172
173/**
174 * platform_device_add_resources
175 * @pdev: platform device allocated by platform_device_alloc to add resources to
176 * @res: set of resources that needs to be allocated for the device
177 * @num: number of resources
178 *
179 * Add a copy of the resources to the platform device. The memory
180 * associated with the resources will be freed when the platform
181 * device is released.
182 */
183int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num)
184{
185 struct resource *r;
186
187 r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);
188 if (r) {
189 memcpy(r, res, sizeof(struct resource) * num);
190 pdev->resource = r;
191 pdev->num_resources = num;
192 }
193 return r ? 0 : -ENOMEM;
194}
195EXPORT_SYMBOL_GPL(platform_device_add_resources);
196
197/**
198 * platform_device_add_data
199 * @pdev: platform device allocated by platform_device_alloc to add resources to
200 * @data: platform specific data for this platform device
201 * @size: size of platform specific data
202 *
203 * Add a copy of platform specific data to the platform device's platform_data
204 * pointer. The memory associated with the platform data will be freed
205 * when the platform device is released.
206 */
207int platform_device_add_data(struct platform_device *pdev, void *data, size_t size)
208{
209 void *d;
210
211 d = kmalloc(size, GFP_KERNEL);
212 if (d) {
213 memcpy(d, data, size);
214 pdev->dev.platform_data = d;
215 }
216 return d ? 0 : -ENOMEM;
217}
218EXPORT_SYMBOL_GPL(platform_device_add_data);
219
220/**
221 * platform_device_add - add a platform device to device hierarchy
121 * @pdev: platform device we're adding 222 * @pdev: platform device we're adding
122 * 223 *
224 * This is part 2 of platform_device_register(), though may be called
225 * separately _iff_ pdev was allocated by platform_device_alloc().
123 */ 226 */
124int platform_device_register(struct platform_device * pdev) 227int platform_device_add(struct platform_device *pdev)
125{ 228{
126 int i, ret = 0; 229 int i, ret = 0;
127 230
@@ -174,6 +277,18 @@ int platform_device_register(struct platform_device * pdev)
174 release_resource(&pdev->resource[i]); 277 release_resource(&pdev->resource[i]);
175 return ret; 278 return ret;
176} 279}
280EXPORT_SYMBOL_GPL(platform_device_add);
281
282/**
283 * platform_device_register - add a platform-level device
284 * @pdev: platform device we're adding
285 *
286 */
287int platform_device_register(struct platform_device * pdev)
288{
289 device_initialize(&pdev->dev);
290 return platform_device_add(pdev);
291}
177 292
178/** 293/**
179 * platform_device_unregister - remove a platform-level device 294 * platform_device_unregister - remove a platform-level device
@@ -197,18 +312,6 @@ void platform_device_unregister(struct platform_device * pdev)
197 } 312 }
198} 313}
199 314
200struct platform_object {
201 struct platform_device pdev;
202 struct resource resources[0];
203};
204
205static void platform_device_release_simple(struct device *dev)
206{
207 struct platform_device *pdev = to_platform_device(dev);
208
209 kfree(container_of(pdev, struct platform_object, pdev));
210}
211
212/** 315/**
213 * platform_device_register_simple 316 * platform_device_register_simple
214 * @name: base name of the device we're adding 317 * @name: base name of the device we're adding
@@ -225,33 +328,29 @@ static void platform_device_release_simple(struct device *dev)
225struct platform_device *platform_device_register_simple(char *name, unsigned int id, 328struct platform_device *platform_device_register_simple(char *name, unsigned int id,
226 struct resource *res, unsigned int num) 329 struct resource *res, unsigned int num)
227{ 330{
228 struct platform_object *pobj; 331 struct platform_device *pdev;
229 int retval; 332 int retval;
230 333
231 pobj = kzalloc(sizeof(*pobj) + sizeof(struct resource) * num, GFP_KERNEL); 334 pdev = platform_device_alloc(name, id);
232 if (!pobj) { 335 if (!pdev) {
233 retval = -ENOMEM; 336 retval = -ENOMEM;
234 goto error; 337 goto error;
235 } 338 }
236 339
237 pobj->pdev.name = name;
238 pobj->pdev.id = id;
239 pobj->pdev.dev.release = platform_device_release_simple;
240
241 if (num) { 340 if (num) {
242 memcpy(pobj->resources, res, sizeof(struct resource) * num); 341 retval = platform_device_add_resources(pdev, res, num);
243 pobj->pdev.resource = pobj->resources; 342 if (retval)
244 pobj->pdev.num_resources = num; 343 goto error;
245 } 344 }
246 345
247 retval = platform_device_register(&pobj->pdev); 346 retval = platform_device_add(pdev);
248 if (retval) 347 if (retval)
249 goto error; 348 goto error;
250 349
251 return &pobj->pdev; 350 return pdev;
252 351
253error: 352error:
254 kfree(pobj); 353 platform_device_put(pdev);
255 return ERR_PTR(retval); 354 return ERR_PTR(retval);
256} 355}
257 356
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index a726225e0afe..1a165b7ae01b 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -37,4 +37,10 @@ extern int platform_add_devices(struct platform_device **, int);
37 37
38extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int); 38extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
39 39
40extern struct platform_device *platform_device_alloc(const char *name, unsigned int id);
41extern int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num);
42extern int platform_device_add_data(struct platform_device *pdev, void *data, size_t size);
43extern int platform_device_add(struct platform_device *pdev);
44extern void platform_device_put(struct platform_device *pdev);
45
40#endif /* _PLATFORM_DEVICE_H_ */ 46#endif /* _PLATFORM_DEVICE_H_ */