Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * ocp.c |
| 3 | * |
| 4 | * (c) Benjamin Herrenschmidt (benh@kernel.crashing.org) |
| 5 | * Mipsys - France |
| 6 | * |
| 7 | * Derived from work (c) Armin Kuster akuster@pacbell.net |
| 8 | * |
| 9 | * Additional support and port to 2.6 LDM/sysfs by |
| 10 | * Matt Porter <mporter@kernel.crashing.org> |
| 11 | * Copyright 2004 MontaVista Software, Inc. |
| 12 | * |
| 13 | * This program is free software; you can redistribute it and/or modify it |
| 14 | * under the terms of the GNU General Public License as published by the |
| 15 | * Free Software Foundation; either version 2 of the License, or (at your |
| 16 | * option) any later version. |
| 17 | * |
| 18 | * OCP (On Chip Peripheral) is a software emulated "bus" with a |
| 19 | * pseudo discovery method for dumb peripherals. Usually these type |
| 20 | * of peripherals are found on embedded SoC (System On a Chip) |
| 21 | * processors or highly integrated system controllers that have |
| 22 | * a host bridge and many peripherals. Common examples where |
| 23 | * this is already used include the PPC4xx, PPC85xx, MPC52xx, |
| 24 | * and MV64xxx parts. |
| 25 | * |
| 26 | * This subsystem creates a standard OCP bus type within the |
| 27 | * device model. The devices on the OCP bus are seeded by an |
| 28 | * an initial OCP device array created by the arch-specific |
| 29 | * Device entries can be added/removed/modified through OCP |
Simon Arlott | a8de5ce | 2007-05-12 05:42:54 +1000 | [diff] [blame] | 30 | * helper functions to accommodate system and board-specific |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | * parameters commonly found in embedded systems. OCP also |
| 32 | * provides a standard method for devices to describe extended |
| 33 | * attributes about themselves to the system. A standard access |
| 34 | * method allows OCP drivers to obtain the information, both |
| 35 | * SoC-specific and system/board-specific, needed for operation. |
| 36 | */ |
| 37 | |
| 38 | #include <linux/module.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 39 | #include <linux/list.h> |
| 40 | #include <linux/miscdevice.h> |
| 41 | #include <linux/slab.h> |
| 42 | #include <linux/types.h> |
| 43 | #include <linux/init.h> |
| 44 | #include <linux/pm.h> |
| 45 | #include <linux/bootmem.h> |
| 46 | #include <linux/device.h> |
Robert P. J. Day | 20b31b5 | 2007-07-18 23:36:36 +1000 | [diff] [blame] | 47 | #include <linux/rwsem.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 | |
| 49 | #include <asm/io.h> |
| 50 | #include <asm/ocp.h> |
| 51 | #include <asm/errno.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | #include <asm/semaphore.h> |
| 53 | |
| 54 | //#define DBG(x) printk x |
| 55 | #define DBG(x) |
| 56 | |
| 57 | extern int mem_init_done; |
| 58 | |
| 59 | extern struct ocp_def core_ocp[]; /* Static list of devices, provided by |
| 60 | CPU core */ |
| 61 | |
| 62 | LIST_HEAD(ocp_devices); /* List of all OCP devices */ |
| 63 | DECLARE_RWSEM(ocp_devices_sem); /* Global semaphores for those lists */ |
| 64 | |
| 65 | static int ocp_inited; |
| 66 | |
| 67 | /* Sysfs support */ |
| 68 | #define OCP_DEF_ATTR(field, format_string) \ |
| 69 | static ssize_t \ |
Yani Ioannou | ff381d2 | 2005-05-17 06:40:51 -0400 | [diff] [blame] | 70 | show_##field(struct device *dev, struct device_attribute *attr, char *buf) \ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 71 | { \ |
| 72 | struct ocp_device *odev = to_ocp_dev(dev); \ |
| 73 | \ |
| 74 | return sprintf(buf, format_string, odev->def->field); \ |
| 75 | } \ |
| 76 | static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); |
| 77 | |
| 78 | OCP_DEF_ATTR(vendor, "0x%04x\n"); |
| 79 | OCP_DEF_ATTR(function, "0x%04x\n"); |
| 80 | OCP_DEF_ATTR(index, "0x%04x\n"); |
| 81 | #ifdef CONFIG_PTE_64BIT |
| 82 | OCP_DEF_ATTR(paddr, "0x%016Lx\n"); |
| 83 | #else |
| 84 | OCP_DEF_ATTR(paddr, "0x%08lx\n"); |
| 85 | #endif |
| 86 | OCP_DEF_ATTR(irq, "%d\n"); |
| 87 | OCP_DEF_ATTR(pm, "%lu\n"); |
| 88 | |
| 89 | void ocp_create_sysfs_dev_files(struct ocp_device *odev) |
| 90 | { |
| 91 | struct device *dev = &odev->dev; |
| 92 | |
| 93 | /* Current OCP device def attributes */ |
| 94 | device_create_file(dev, &dev_attr_vendor); |
| 95 | device_create_file(dev, &dev_attr_function); |
| 96 | device_create_file(dev, &dev_attr_index); |
| 97 | device_create_file(dev, &dev_attr_paddr); |
| 98 | device_create_file(dev, &dev_attr_irq); |
| 99 | device_create_file(dev, &dev_attr_pm); |
| 100 | /* Current OCP device additions attributes */ |
| 101 | if (odev->def->additions && odev->def->show) |
| 102 | odev->def->show(dev); |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * ocp_device_match - Match one driver to one device |
| 107 | * @drv: driver to match |
| 108 | * @dev: device to match |
| 109 | * |
| 110 | * This function returns 0 if the driver and device don't match |
| 111 | */ |
| 112 | static int |
| 113 | ocp_device_match(struct device *dev, struct device_driver *drv) |
| 114 | { |
| 115 | struct ocp_device *ocp_dev = to_ocp_dev(dev); |
| 116 | struct ocp_driver *ocp_drv = to_ocp_drv(drv); |
| 117 | const struct ocp_device_id *ids = ocp_drv->id_table; |
| 118 | |
| 119 | if (!ids) |
| 120 | return 0; |
| 121 | |
| 122 | while (ids->vendor || ids->function) { |
| 123 | if ((ids->vendor == OCP_ANY_ID |
| 124 | || ids->vendor == ocp_dev->def->vendor) |
| 125 | && (ids->function == OCP_ANY_ID |
| 126 | || ids->function == ocp_dev->def->function)) |
| 127 | return 1; |
| 128 | ids++; |
| 129 | } |
| 130 | return 0; |
| 131 | } |
| 132 | |
| 133 | static int |
| 134 | ocp_device_probe(struct device *dev) |
| 135 | { |
| 136 | int error = 0; |
| 137 | struct ocp_driver *drv; |
| 138 | struct ocp_device *ocp_dev; |
| 139 | |
| 140 | drv = to_ocp_drv(dev->driver); |
| 141 | ocp_dev = to_ocp_dev(dev); |
| 142 | |
| 143 | if (drv->probe) { |
| 144 | error = drv->probe(ocp_dev); |
| 145 | if (error >= 0) { |
| 146 | ocp_dev->driver = drv; |
| 147 | error = 0; |
| 148 | } |
| 149 | } |
| 150 | return error; |
| 151 | } |
| 152 | |
| 153 | static int |
| 154 | ocp_device_remove(struct device *dev) |
| 155 | { |
| 156 | struct ocp_device *ocp_dev = to_ocp_dev(dev); |
| 157 | |
| 158 | if (ocp_dev->driver) { |
| 159 | if (ocp_dev->driver->remove) |
| 160 | ocp_dev->driver->remove(ocp_dev); |
| 161 | ocp_dev->driver = NULL; |
| 162 | } |
| 163 | return 0; |
| 164 | } |
| 165 | |
| 166 | static int |
Eugene Surovegin | 842363f | 2005-09-06 15:16:13 -0700 | [diff] [blame] | 167 | ocp_device_suspend(struct device *dev, pm_message_t state) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | { |
| 169 | struct ocp_device *ocp_dev = to_ocp_dev(dev); |
| 170 | struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); |
| 171 | |
| 172 | if (dev->driver && ocp_drv->suspend) |
| 173 | return ocp_drv->suspend(ocp_dev, state); |
| 174 | return 0; |
| 175 | } |
| 176 | |
| 177 | static int |
| 178 | ocp_device_resume(struct device *dev) |
| 179 | { |
| 180 | struct ocp_device *ocp_dev = to_ocp_dev(dev); |
| 181 | struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); |
| 182 | |
| 183 | if (dev->driver && ocp_drv->resume) |
| 184 | return ocp_drv->resume(ocp_dev); |
| 185 | return 0; |
| 186 | } |
| 187 | |
| 188 | struct bus_type ocp_bus_type = { |
| 189 | .name = "ocp", |
| 190 | .match = ocp_device_match, |
Al Viro | 83ec98be | 2006-01-18 18:40:16 -0500 | [diff] [blame] | 191 | .probe = ocp_device_probe, |
| 192 | .remove = ocp_device_remove, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 193 | .suspend = ocp_device_suspend, |
| 194 | .resume = ocp_device_resume, |
| 195 | }; |
| 196 | |
| 197 | /** |
| 198 | * ocp_register_driver - Register an OCP driver |
| 199 | * @drv: pointer to statically defined ocp_driver structure |
| 200 | * |
| 201 | * The driver's probe() callback is called either recursively |
| 202 | * by this function or upon later call of ocp_driver_init |
| 203 | * |
| 204 | * NOTE: Detection of devices is a 2 pass step on this implementation, |
| 205 | * hotswap isn't supported. First, all OCP devices are put in the device |
| 206 | * list, _then_ all drivers are probed on each match. |
| 207 | */ |
| 208 | int |
| 209 | ocp_register_driver(struct ocp_driver *drv) |
| 210 | { |
| 211 | /* initialize common driver fields */ |
| 212 | drv->driver.name = drv->name; |
| 213 | drv->driver.bus = &ocp_bus_type; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | |
| 215 | /* register with core */ |
| 216 | return driver_register(&drv->driver); |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * ocp_unregister_driver - Unregister an OCP driver |
| 221 | * @drv: pointer to statically defined ocp_driver structure |
| 222 | * |
| 223 | * The driver's remove() callback is called recursively |
| 224 | * by this function for any device already registered |
| 225 | */ |
| 226 | void |
| 227 | ocp_unregister_driver(struct ocp_driver *drv) |
| 228 | { |
| 229 | DBG(("ocp: ocp_unregister_driver(%s)...\n", drv->name)); |
| 230 | |
| 231 | driver_unregister(&drv->driver); |
| 232 | |
| 233 | DBG(("ocp: ocp_unregister_driver(%s)... done.\n", drv->name)); |
| 234 | } |
| 235 | |
| 236 | /* Core of ocp_find_device(). Caller must hold ocp_devices_sem */ |
| 237 | static struct ocp_device * |
| 238 | __ocp_find_device(unsigned int vendor, unsigned int function, int index) |
| 239 | { |
| 240 | struct list_head *entry; |
| 241 | struct ocp_device *dev, *found = NULL; |
| 242 | |
| 243 | DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); |
| 244 | |
| 245 | list_for_each(entry, &ocp_devices) { |
| 246 | dev = list_entry(entry, struct ocp_device, link); |
| 247 | if (vendor != OCP_ANY_ID && vendor != dev->def->vendor) |
| 248 | continue; |
| 249 | if (function != OCP_ANY_ID && function != dev->def->function) |
| 250 | continue; |
| 251 | if (index != OCP_ANY_INDEX && index != dev->def->index) |
| 252 | continue; |
| 253 | found = dev; |
| 254 | break; |
| 255 | } |
| 256 | |
| 257 | DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)... done\n", vendor, function, index)); |
| 258 | |
| 259 | return found; |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * ocp_find_device - Find a device by function & index |
| 264 | * @vendor: vendor ID of the device (or OCP_ANY_ID) |
| 265 | * @function: function code of the device (or OCP_ANY_ID) |
| 266 | * @idx: index of the device (or OCP_ANY_INDEX) |
| 267 | * |
| 268 | * This function allows a lookup of a given function by it's |
| 269 | * index, it's typically used to find the MAL or ZMII associated |
| 270 | * with an EMAC or similar horrors. |
| 271 | * You can pass vendor, though you usually want OCP_ANY_ID there... |
| 272 | */ |
| 273 | struct ocp_device * |
| 274 | ocp_find_device(unsigned int vendor, unsigned int function, int index) |
| 275 | { |
| 276 | struct ocp_device *dev; |
| 277 | |
| 278 | down_read(&ocp_devices_sem); |
| 279 | dev = __ocp_find_device(vendor, function, index); |
| 280 | up_read(&ocp_devices_sem); |
| 281 | |
| 282 | return dev; |
| 283 | } |
| 284 | |
| 285 | /** |
| 286 | * ocp_get_one_device - Find a def by function & index |
| 287 | * @vendor: vendor ID of the device (or OCP_ANY_ID) |
| 288 | * @function: function code of the device (or OCP_ANY_ID) |
| 289 | * @idx: index of the device (or OCP_ANY_INDEX) |
| 290 | * |
| 291 | * This function allows a lookup of a given ocp_def by it's |
| 292 | * vendor, function, and index. The main purpose for is to |
| 293 | * allow modification of the def before binding to the driver |
| 294 | */ |
| 295 | struct ocp_def * |
| 296 | ocp_get_one_device(unsigned int vendor, unsigned int function, int index) |
| 297 | { |
| 298 | struct ocp_device *dev; |
| 299 | struct ocp_def *found = NULL; |
| 300 | |
| 301 | DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)...\n", |
| 302 | vendor, function, index)); |
| 303 | |
| 304 | dev = ocp_find_device(vendor, function, index); |
| 305 | |
| 306 | if (dev) |
| 307 | found = dev->def; |
| 308 | |
| 309 | DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)... done.\n", |
| 310 | vendor, function, index)); |
| 311 | |
| 312 | return found; |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * ocp_add_one_device - Add a device |
| 317 | * @def: static device definition structure |
| 318 | * |
| 319 | * This function adds a device definition to the |
| 320 | * device list. It may only be called before |
| 321 | * ocp_driver_init() and will return an error |
| 322 | * otherwise. |
| 323 | */ |
| 324 | int |
| 325 | ocp_add_one_device(struct ocp_def *def) |
| 326 | { |
| 327 | struct ocp_device *dev; |
| 328 | |
| 329 | DBG(("ocp: ocp_add_one_device()...\n")); |
| 330 | |
| 331 | /* Can't be called after ocp driver init */ |
| 332 | if (ocp_inited) |
| 333 | return 1; |
| 334 | |
| 335 | if (mem_init_done) |
| 336 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); |
| 337 | else |
| 338 | dev = alloc_bootmem(sizeof(*dev)); |
| 339 | |
| 340 | if (dev == NULL) |
| 341 | return 1; |
| 342 | memset(dev, 0, sizeof(*dev)); |
| 343 | dev->def = def; |
| 344 | dev->current_state = 4; |
| 345 | sprintf(dev->name, "OCP device %04x:%04x:%04x", |
| 346 | dev->def->vendor, dev->def->function, dev->def->index); |
| 347 | down_write(&ocp_devices_sem); |
| 348 | list_add_tail(&dev->link, &ocp_devices); |
| 349 | up_write(&ocp_devices_sem); |
| 350 | |
| 351 | DBG(("ocp: ocp_add_one_device()...done\n")); |
| 352 | |
| 353 | return 0; |
| 354 | } |
| 355 | |
| 356 | /** |
| 357 | * ocp_remove_one_device - Remove a device by function & index |
| 358 | * @vendor: vendor ID of the device (or OCP_ANY_ID) |
| 359 | * @function: function code of the device (or OCP_ANY_ID) |
| 360 | * @idx: index of the device (or OCP_ANY_INDEX) |
| 361 | * |
| 362 | * This function allows removal of a given function by its |
| 363 | * index. It may only be called before ocp_driver_init() |
| 364 | * and will return an error otherwise. |
| 365 | */ |
| 366 | int |
| 367 | ocp_remove_one_device(unsigned int vendor, unsigned int function, int index) |
| 368 | { |
| 369 | struct ocp_device *dev; |
| 370 | |
| 371 | DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); |
| 372 | |
| 373 | /* Can't be called after ocp driver init */ |
| 374 | if (ocp_inited) |
| 375 | return 1; |
| 376 | |
| 377 | down_write(&ocp_devices_sem); |
| 378 | dev = __ocp_find_device(vendor, function, index); |
| 379 | list_del((struct list_head *)dev); |
| 380 | up_write(&ocp_devices_sem); |
| 381 | |
| 382 | DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)... done.\n", vendor, function, index)); |
| 383 | |
| 384 | return 0; |
| 385 | } |
| 386 | |
| 387 | /** |
| 388 | * ocp_for_each_device - Iterate over OCP devices |
| 389 | * @callback: routine to execute for each ocp device. |
| 390 | * @arg: user data to be passed to callback routine. |
| 391 | * |
| 392 | * This routine holds the ocp_device semaphore, so the |
| 393 | * callback routine cannot modify the ocp_device list. |
| 394 | */ |
| 395 | void |
| 396 | ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg) |
| 397 | { |
| 398 | struct list_head *entry; |
| 399 | |
| 400 | if (callback) { |
| 401 | down_read(&ocp_devices_sem); |
| 402 | list_for_each(entry, &ocp_devices) |
| 403 | callback(list_entry(entry, struct ocp_device, link), |
| 404 | arg); |
| 405 | up_read(&ocp_devices_sem); |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | /** |
| 410 | * ocp_early_init - Init OCP device management |
| 411 | * |
| 412 | * This function builds the list of devices before setup_arch. |
| 413 | * This allows platform code to modify the device lists before |
| 414 | * they are bound to drivers (changes to paddr, removing devices |
| 415 | * etc) |
| 416 | */ |
| 417 | int __init |
| 418 | ocp_early_init(void) |
| 419 | { |
| 420 | struct ocp_def *def; |
| 421 | |
| 422 | DBG(("ocp: ocp_early_init()...\n")); |
| 423 | |
| 424 | /* Fill the devices list */ |
| 425 | for (def = core_ocp; def->vendor != OCP_VENDOR_INVALID; def++) |
| 426 | ocp_add_one_device(def); |
| 427 | |
| 428 | DBG(("ocp: ocp_early_init()... done.\n")); |
| 429 | |
| 430 | return 0; |
| 431 | } |
| 432 | |
| 433 | /** |
| 434 | * ocp_driver_init - Init OCP device management |
| 435 | * |
| 436 | * This function is meant to be called via OCP bus registration. |
| 437 | */ |
| 438 | static int __init |
| 439 | ocp_driver_init(void) |
| 440 | { |
| 441 | int ret = 0, index = 0; |
| 442 | struct device *ocp_bus; |
| 443 | struct list_head *entry; |
| 444 | struct ocp_device *dev; |
| 445 | |
| 446 | if (ocp_inited) |
| 447 | return ret; |
| 448 | ocp_inited = 1; |
| 449 | |
| 450 | DBG(("ocp: ocp_driver_init()...\n")); |
| 451 | |
| 452 | /* Allocate/register primary OCP bus */ |
Eric Sesterhenn | d116fe5 | 2006-03-06 21:13:32 +0100 | [diff] [blame] | 453 | ocp_bus = kzalloc(sizeof(struct device), GFP_KERNEL); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 454 | if (ocp_bus == NULL) |
| 455 | return 1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 456 | strcpy(ocp_bus->bus_id, "ocp"); |
| 457 | |
| 458 | bus_register(&ocp_bus_type); |
| 459 | |
| 460 | device_register(ocp_bus); |
| 461 | |
| 462 | /* Put each OCP device into global device list */ |
| 463 | list_for_each(entry, &ocp_devices) { |
| 464 | dev = list_entry(entry, struct ocp_device, link); |
| 465 | sprintf(dev->dev.bus_id, "%2.2x", index); |
| 466 | dev->dev.parent = ocp_bus; |
| 467 | dev->dev.bus = &ocp_bus_type; |
| 468 | device_register(&dev->dev); |
| 469 | ocp_create_sysfs_dev_files(dev); |
| 470 | index++; |
| 471 | } |
| 472 | |
| 473 | DBG(("ocp: ocp_driver_init()... done.\n")); |
| 474 | |
| 475 | return 0; |
| 476 | } |
| 477 | |
| 478 | postcore_initcall(ocp_driver_init); |
| 479 | |
| 480 | EXPORT_SYMBOL(ocp_bus_type); |
| 481 | EXPORT_SYMBOL(ocp_find_device); |
| 482 | EXPORT_SYMBOL(ocp_register_driver); |
| 483 | EXPORT_SYMBOL(ocp_unregister_driver); |