diff options
author | Joachim Fenkes <fenkes@de.ibm.com> | 2007-03-09 13:00:32 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-03-16 01:38:19 -0400 |
commit | 6bccf755ff53241d46c01c229b3c2452b9029ec4 (patch) | |
tree | 41ecb5e2a78120b95224c674dac0f57bbbff5e57 /arch/powerpc/kernel/ibmebus.c | |
parent | a83088003cd53f3cd8d550ab5d7778866568d204 (diff) |
[POWERPC] ibmebus: dynamic addition/removal of adapters, some code cleanup
This adds two sysfs attributes to /sys/bus/ibmebus which can be used to
notify the ebus driver of added / removed ebus devices in the OF device
tree.
Echoing the device's location code (as found in the OFDT "ibm,loc-code"
property) into the "probe" attribute will notify ebus of addition of the
device and cause the appropriate device driver's probe function to be called
on the device.
Likewise, echoing the location code into the "remove" attribute will cause
the device to be removed from the system.
The writes will block until the respective operation has finished and return
an error code if the operation failed.
In addition, two minor tidbits are fixed:
- The fake root device used to provide a common parent for all ebus devices
is now based on device instead of of_device - it had no associated devtree
node. This saves several checks throughout the ebus driver.
- The sysfs attributes are now generated automagically by device_register()
instead of by the ibmebus code, which saves a few compiler warnings about
unused return codes.
Signed-off-by: Joachim Fenkes <fenkes@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/ibmebus.c')
-rw-r--r-- | arch/powerpc/kernel/ibmebus.c | 167 |
1 files changed, 133 insertions, 34 deletions
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 061df3a066ae..8ed1163c0bd4 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * IBM PowerPC IBM eBus Infrastructure Support. | 2 | * IBM PowerPC IBM eBus Infrastructure Support. |
3 | * | 3 | * |
4 | * Copyright (c) 2005 IBM Corporation | 4 | * Copyright (c) 2005 IBM Corporation |
5 | * Joachim Fenkes <fenkes@de.ibm.com> | ||
5 | * Heiko J Schick <schickhj@de.ibm.com> | 6 | * Heiko J Schick <schickhj@de.ibm.com> |
6 | * | 7 | * |
7 | * All rights reserved. | 8 | * All rights reserved. |
@@ -43,12 +44,14 @@ | |||
43 | #include <asm/ibmebus.h> | 44 | #include <asm/ibmebus.h> |
44 | #include <asm/abs_addr.h> | 45 | #include <asm/abs_addr.h> |
45 | 46 | ||
46 | static struct ibmebus_dev ibmebus_bus_device = { /* fake "parent" device */ | 47 | #define MAX_LOC_CODE_LENGTH 80 |
47 | .name = ibmebus_bus_device.ofdev.dev.bus_id, | 48 | |
48 | .ofdev.dev.bus_id = "ibmebus", | 49 | static struct device ibmebus_bus_device = { /* fake "parent" device */ |
49 | .ofdev.dev.bus = &ibmebus_bus_type, | 50 | .bus_id = "ibmebus", |
50 | }; | 51 | }; |
51 | 52 | ||
53 | struct bus_type ibmebus_bus_type; | ||
54 | |||
52 | static void *ibmebus_alloc_coherent(struct device *dev, | 55 | static void *ibmebus_alloc_coherent(struct device *dev, |
53 | size_t size, | 56 | size_t size, |
54 | dma_addr_t *dma_handle, | 57 | dma_addr_t *dma_handle, |
@@ -158,21 +161,12 @@ static void __devinit ibmebus_dev_release(struct device *dev) | |||
158 | kfree(to_ibmebus_dev(dev)); | 161 | kfree(to_ibmebus_dev(dev)); |
159 | } | 162 | } |
160 | 163 | ||
161 | static ssize_t ibmebusdev_show_name(struct device *dev, | 164 | static int __devinit ibmebus_register_device_common( |
162 | struct device_attribute *attr, char *buf) | ||
163 | { | ||
164 | return sprintf(buf, "%s\n", to_ibmebus_dev(dev)->name); | ||
165 | } | ||
166 | static DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, ibmebusdev_show_name, | ||
167 | NULL); | ||
168 | |||
169 | static struct ibmebus_dev* __devinit ibmebus_register_device_common( | ||
170 | struct ibmebus_dev *dev, const char *name) | 165 | struct ibmebus_dev *dev, const char *name) |
171 | { | 166 | { |
172 | int err = 0; | 167 | int err = 0; |
173 | 168 | ||
174 | dev->name = name; | 169 | dev->ofdev.dev.parent = &ibmebus_bus_device; |
175 | dev->ofdev.dev.parent = &ibmebus_bus_device.ofdev.dev; | ||
176 | dev->ofdev.dev.bus = &ibmebus_bus_type; | 170 | dev->ofdev.dev.bus = &ibmebus_bus_type; |
177 | dev->ofdev.dev.release = ibmebus_dev_release; | 171 | dev->ofdev.dev.release = ibmebus_dev_release; |
178 | 172 | ||
@@ -186,12 +180,10 @@ static struct ibmebus_dev* __devinit ibmebus_register_device_common( | |||
186 | if ((err = of_device_register(&dev->ofdev)) != 0) { | 180 | if ((err = of_device_register(&dev->ofdev)) != 0) { |
187 | printk(KERN_ERR "%s: failed to register device (%d).\n", | 181 | printk(KERN_ERR "%s: failed to register device (%d).\n", |
188 | __FUNCTION__, err); | 182 | __FUNCTION__, err); |
189 | return NULL; | 183 | return -ENODEV; |
190 | } | 184 | } |
191 | 185 | ||
192 | device_create_file(&dev->ofdev.dev, &dev_attr_name); | 186 | return 0; |
193 | |||
194 | return dev; | ||
195 | } | 187 | } |
196 | 188 | ||
197 | static struct ibmebus_dev* __devinit ibmebus_register_device_node( | 189 | static struct ibmebus_dev* __devinit ibmebus_register_device_node( |
@@ -205,18 +197,18 @@ static struct ibmebus_dev* __devinit ibmebus_register_device_node( | |||
205 | if (!loc_code) { | 197 | if (!loc_code) { |
206 | printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n", | 198 | printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n", |
207 | __FUNCTION__, dn->name ? dn->name : "<unknown>"); | 199 | __FUNCTION__, dn->name ? dn->name : "<unknown>"); |
208 | return NULL; | 200 | return ERR_PTR(-EINVAL); |
209 | } | 201 | } |
210 | 202 | ||
211 | if (strlen(loc_code) == 0) { | 203 | if (strlen(loc_code) == 0) { |
212 | printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n", | 204 | printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n", |
213 | __FUNCTION__); | 205 | __FUNCTION__); |
214 | return NULL; | 206 | return ERR_PTR(-EINVAL); |
215 | } | 207 | } |
216 | 208 | ||
217 | dev = kzalloc(sizeof(struct ibmebus_dev), GFP_KERNEL); | 209 | dev = kzalloc(sizeof(struct ibmebus_dev), GFP_KERNEL); |
218 | if (!dev) { | 210 | if (!dev) { |
219 | return NULL; | 211 | return ERR_PTR(-ENOMEM); |
220 | } | 212 | } |
221 | 213 | ||
222 | dev->ofdev.node = of_node_get(dn); | 214 | dev->ofdev.node = of_node_get(dn); |
@@ -227,9 +219,9 @@ static struct ibmebus_dev* __devinit ibmebus_register_device_node( | |||
227 | min(length, BUS_ID_SIZE - 1)); | 219 | min(length, BUS_ID_SIZE - 1)); |
228 | 220 | ||
229 | /* Register with generic device framework. */ | 221 | /* Register with generic device framework. */ |
230 | if (ibmebus_register_device_common(dev, dn->name) == NULL) { | 222 | if (ibmebus_register_device_common(dev, dn->name) != 0) { |
231 | kfree(dev); | 223 | kfree(dev); |
232 | return NULL; | 224 | return ERR_PTR(-ENODEV); |
233 | } | 225 | } |
234 | 226 | ||
235 | return dev; | 227 | return dev; |
@@ -240,9 +232,8 @@ static void ibmebus_probe_of_nodes(char* name) | |||
240 | struct device_node *dn = NULL; | 232 | struct device_node *dn = NULL; |
241 | 233 | ||
242 | while ((dn = of_find_node_by_name(dn, name))) { | 234 | while ((dn = of_find_node_by_name(dn, name))) { |
243 | if (ibmebus_register_device_node(dn) == NULL) { | 235 | if (IS_ERR(ibmebus_register_device_node(dn))) { |
244 | of_node_put(dn); | 236 | of_node_put(dn); |
245 | |||
246 | return; | 237 | return; |
247 | } | 238 | } |
248 | } | 239 | } |
@@ -262,9 +253,15 @@ static void ibmebus_add_devices_by_id(struct of_device_id *idt) | |||
262 | return; | 253 | return; |
263 | } | 254 | } |
264 | 255 | ||
265 | static int ibmebus_match_helper(struct device *dev, void *data) | 256 | static int ibmebus_match_helper_name(struct device *dev, void *data) |
266 | { | 257 | { |
267 | if (strcmp((char*)data, to_ibmebus_dev(dev)->name) == 0) | 258 | const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev); |
259 | char *name; | ||
260 | |||
261 | name = (char*)get_property( | ||
262 | ebus_dev->ofdev.node, "name", NULL); | ||
263 | |||
264 | if (name && (strcmp((char*)data, name) == 0)) | ||
268 | return 1; | 265 | return 1; |
269 | 266 | ||
270 | return 0; | 267 | return 0; |
@@ -272,7 +269,6 @@ static int ibmebus_match_helper(struct device *dev, void *data) | |||
272 | 269 | ||
273 | static int ibmebus_unregister_device(struct device *dev) | 270 | static int ibmebus_unregister_device(struct device *dev) |
274 | { | 271 | { |
275 | device_remove_file(dev, &dev_attr_name); | ||
276 | of_device_unregister(to_of_device(dev)); | 272 | of_device_unregister(to_of_device(dev)); |
277 | 273 | ||
278 | return 0; | 274 | return 0; |
@@ -285,11 +281,10 @@ static void ibmebus_remove_devices_by_id(struct of_device_id *idt) | |||
285 | while (strlen(idt->name) > 0) { | 281 | while (strlen(idt->name) > 0) { |
286 | while ((dev = bus_find_device(&ibmebus_bus_type, NULL, | 282 | while ((dev = bus_find_device(&ibmebus_bus_type, NULL, |
287 | (void*)idt->name, | 283 | (void*)idt->name, |
288 | ibmebus_match_helper))) { | 284 | ibmebus_match_helper_name))) { |
289 | ibmebus_unregister_device(dev); | 285 | ibmebus_unregister_device(dev); |
290 | } | 286 | } |
291 | idt++; | 287 | idt++; |
292 | |||
293 | } | 288 | } |
294 | 289 | ||
295 | return; | 290 | return; |
@@ -307,6 +302,9 @@ int ibmebus_register_driver(struct ibmebus_driver *drv) | |||
307 | if ((err = driver_register(&drv->driver) != 0)) | 302 | if ((err = driver_register(&drv->driver) != 0)) |
308 | return err; | 303 | return err; |
309 | 304 | ||
305 | /* remove all supported devices first, in case someone | ||
306 | * probed them manually before registering the driver */ | ||
307 | ibmebus_remove_devices_by_id(drv->id_table); | ||
310 | ibmebus_add_devices_by_id(drv->id_table); | 308 | ibmebus_add_devices_by_id(drv->id_table); |
311 | 309 | ||
312 | return 0; | 310 | return 0; |
@@ -361,9 +359,110 @@ static int ibmebus_bus_match(struct device *dev, struct device_driver *drv) | |||
361 | return 0; | 359 | return 0; |
362 | } | 360 | } |
363 | 361 | ||
362 | static ssize_t name_show(struct device *dev, | ||
363 | struct device_attribute *attr, char *buf) | ||
364 | { | ||
365 | struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev); | ||
366 | char *name = (char*)get_property(ebus_dev->ofdev.node, "name", NULL); | ||
367 | return sprintf(buf, "%s\n", name); | ||
368 | } | ||
369 | |||
370 | static struct device_attribute ibmebus_dev_attrs[] = { | ||
371 | __ATTR_RO(name), | ||
372 | __ATTR_NULL | ||
373 | }; | ||
374 | |||
375 | static int ibmebus_match_helper_loc_code(struct device *dev, void *data) | ||
376 | { | ||
377 | const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev); | ||
378 | char *loc_code; | ||
379 | |||
380 | loc_code = (char*)get_property( | ||
381 | ebus_dev->ofdev.node, "ibm,loc-code", NULL); | ||
382 | |||
383 | if (loc_code && (strcmp((char*)data, loc_code) == 0)) | ||
384 | return 1; | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static ssize_t ibmebus_store_probe(struct bus_type *bus, | ||
390 | const char *buf, size_t count) | ||
391 | { | ||
392 | struct device_node *dn = NULL; | ||
393 | struct ibmebus_dev *dev; | ||
394 | char *loc_code; | ||
395 | char parm[MAX_LOC_CODE_LENGTH]; | ||
396 | |||
397 | if (count >= MAX_LOC_CODE_LENGTH) | ||
398 | return -EINVAL; | ||
399 | memcpy(parm, buf, count); | ||
400 | parm[count] = '\0'; | ||
401 | if (parm[count-1] == '\n') | ||
402 | parm[count-1] = '\0'; | ||
403 | |||
404 | if (bus_find_device(&ibmebus_bus_type, NULL, parm, | ||
405 | ibmebus_match_helper_loc_code)) { | ||
406 | printk(KERN_WARNING "%s: loc_code %s has already been probed\n", | ||
407 | __FUNCTION__, parm); | ||
408 | return -EINVAL; | ||
409 | } | ||
410 | |||
411 | while ((dn = of_find_all_nodes(dn))) { | ||
412 | loc_code = (char *)get_property(dn, "ibm,loc-code", NULL); | ||
413 | if (loc_code && (strncmp(loc_code, parm, count) == 0)) { | ||
414 | dev = ibmebus_register_device_node(dn); | ||
415 | if (IS_ERR(dev)) { | ||
416 | of_node_put(dn); | ||
417 | return PTR_ERR(dev); | ||
418 | } else | ||
419 | return count; /* success */ | ||
420 | } | ||
421 | } | ||
422 | |||
423 | /* if we drop out of the loop, the loc code was invalid */ | ||
424 | printk(KERN_WARNING "%s: no device with loc_code %s found\n", | ||
425 | __FUNCTION__, parm); | ||
426 | return -ENODEV; | ||
427 | } | ||
428 | |||
429 | static ssize_t ibmebus_store_remove(struct bus_type *bus, | ||
430 | const char *buf, size_t count) | ||
431 | { | ||
432 | struct device *dev; | ||
433 | char parm[MAX_LOC_CODE_LENGTH]; | ||
434 | |||
435 | if (count >= MAX_LOC_CODE_LENGTH) | ||
436 | return -EINVAL; | ||
437 | memcpy(parm, buf, count); | ||
438 | parm[count] = '\0'; | ||
439 | if (parm[count-1] == '\n') | ||
440 | parm[count-1] = '\0'; | ||
441 | |||
442 | /* The location code is unique, so we will find one device at most */ | ||
443 | if ((dev = bus_find_device(&ibmebus_bus_type, NULL, parm, | ||
444 | ibmebus_match_helper_loc_code))) { | ||
445 | ibmebus_unregister_device(dev); | ||
446 | } else { | ||
447 | printk(KERN_WARNING "%s: loc_code %s not on the bus\n", | ||
448 | __FUNCTION__, parm); | ||
449 | return -ENODEV; | ||
450 | } | ||
451 | |||
452 | return count; | ||
453 | } | ||
454 | |||
455 | static struct bus_attribute ibmebus_bus_attrs[] = { | ||
456 | __ATTR(probe, S_IWUSR, NULL, ibmebus_store_probe), | ||
457 | __ATTR(remove, S_IWUSR, NULL, ibmebus_store_remove), | ||
458 | __ATTR_NULL | ||
459 | }; | ||
460 | |||
364 | struct bus_type ibmebus_bus_type = { | 461 | struct bus_type ibmebus_bus_type = { |
365 | .name = "ibmebus", | 462 | .name = "ibmebus", |
366 | .match = ibmebus_bus_match, | 463 | .match = ibmebus_bus_match, |
464 | .dev_attrs = ibmebus_dev_attrs, | ||
465 | .bus_attrs = ibmebus_bus_attrs | ||
367 | }; | 466 | }; |
368 | EXPORT_SYMBOL(ibmebus_bus_type); | 467 | EXPORT_SYMBOL(ibmebus_bus_type); |
369 | 468 | ||
@@ -380,7 +479,7 @@ static int __init ibmebus_bus_init(void) | |||
380 | return err; | 479 | return err; |
381 | } | 480 | } |
382 | 481 | ||
383 | err = device_register(&ibmebus_bus_device.ofdev.dev); | 482 | err = device_register(&ibmebus_bus_device); |
384 | if (err) { | 483 | if (err) { |
385 | printk(KERN_WARNING "%s: device_register returned %i\n", | 484 | printk(KERN_WARNING "%s: device_register returned %i\n", |
386 | __FUNCTION__, err); | 485 | __FUNCTION__, err); |