diff options
Diffstat (limited to 'arch/arm/plat-omap/omap_device.c')
| -rw-r--r-- | arch/arm/plat-omap/omap_device.c | 456 |
1 files changed, 341 insertions, 115 deletions
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c index 9a6a53854911..e8d98693d2dd 100644 --- a/arch/arm/plat-omap/omap_device.c +++ b/arch/arm/plat-omap/omap_device.c | |||
| @@ -78,6 +78,7 @@ | |||
| 78 | #undef DEBUG | 78 | #undef DEBUG |
| 79 | 79 | ||
| 80 | #include <linux/kernel.h> | 80 | #include <linux/kernel.h> |
| 81 | #include <linux/export.h> | ||
| 81 | #include <linux/platform_device.h> | 82 | #include <linux/platform_device.h> |
| 82 | #include <linux/slab.h> | 83 | #include <linux/slab.h> |
| 83 | #include <linux/err.h> | 84 | #include <linux/err.h> |
| @@ -85,6 +86,8 @@ | |||
| 85 | #include <linux/clk.h> | 86 | #include <linux/clk.h> |
| 86 | #include <linux/clkdev.h> | 87 | #include <linux/clkdev.h> |
| 87 | #include <linux/pm_runtime.h> | 88 | #include <linux/pm_runtime.h> |
| 89 | #include <linux/of.h> | ||
| 90 | #include <linux/notifier.h> | ||
| 88 | 91 | ||
| 89 | #include <plat/omap_device.h> | 92 | #include <plat/omap_device.h> |
| 90 | #include <plat/omap_hwmod.h> | 93 | #include <plat/omap_hwmod.h> |
| @@ -94,6 +97,23 @@ | |||
| 94 | #define USE_WAKEUP_LAT 0 | 97 | #define USE_WAKEUP_LAT 0 |
| 95 | #define IGNORE_WAKEUP_LAT 1 | 98 | #define IGNORE_WAKEUP_LAT 1 |
| 96 | 99 | ||
| 100 | static int omap_device_register(struct platform_device *pdev); | ||
| 101 | static int omap_early_device_register(struct platform_device *pdev); | ||
| 102 | static struct omap_device *omap_device_alloc(struct platform_device *pdev, | ||
| 103 | struct omap_hwmod **ohs, int oh_cnt, | ||
| 104 | struct omap_device_pm_latency *pm_lats, | ||
| 105 | int pm_lats_cnt); | ||
| 106 | static void omap_device_delete(struct omap_device *od); | ||
| 107 | |||
| 108 | |||
| 109 | static struct omap_device_pm_latency omap_default_latency[] = { | ||
| 110 | { | ||
| 111 | .deactivate_func = omap_device_idle_hwmods, | ||
| 112 | .activate_func = omap_device_enable_hwmods, | ||
| 113 | .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST, | ||
| 114 | } | ||
| 115 | }; | ||
| 116 | |||
| 97 | /* Private functions */ | 117 | /* Private functions */ |
| 98 | 118 | ||
| 99 | /** | 119 | /** |
| @@ -114,7 +134,7 @@ static int _omap_device_activate(struct omap_device *od, u8 ignore_lat) | |||
| 114 | { | 134 | { |
| 115 | struct timespec a, b, c; | 135 | struct timespec a, b, c; |
| 116 | 136 | ||
| 117 | pr_debug("omap_device: %s: activating\n", od->pdev.name); | 137 | dev_dbg(&od->pdev->dev, "omap_device: activating\n"); |
| 118 | 138 | ||
| 119 | while (od->pm_lat_level > 0) { | 139 | while (od->pm_lat_level > 0) { |
| 120 | struct omap_device_pm_latency *odpl; | 140 | struct omap_device_pm_latency *odpl; |
| @@ -138,25 +158,24 @@ static int _omap_device_activate(struct omap_device *od, u8 ignore_lat) | |||
| 138 | c = timespec_sub(b, a); | 158 | c = timespec_sub(b, a); |
| 139 | act_lat = timespec_to_ns(&c); | 159 | act_lat = timespec_to_ns(&c); |
| 140 | 160 | ||
| 141 | pr_debug("omap_device: %s: pm_lat %d: activate: elapsed time " | 161 | dev_dbg(&od->pdev->dev, |
| 142 | "%llu nsec\n", od->pdev.name, od->pm_lat_level, | 162 | "omap_device: pm_lat %d: activate: elapsed time " |
| 143 | act_lat); | 163 | "%llu nsec\n", od->pm_lat_level, act_lat); |
| 144 | 164 | ||
| 145 | if (act_lat > odpl->activate_lat) { | 165 | if (act_lat > odpl->activate_lat) { |
| 146 | odpl->activate_lat_worst = act_lat; | 166 | odpl->activate_lat_worst = act_lat; |
| 147 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { | 167 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { |
| 148 | odpl->activate_lat = act_lat; | 168 | odpl->activate_lat = act_lat; |
| 149 | pr_warning("omap_device: %s.%d: new worst case " | 169 | dev_dbg(&od->pdev->dev, |
| 150 | "activate latency %d: %llu\n", | 170 | "new worst case activate latency " |
| 151 | od->pdev.name, od->pdev.id, | 171 | "%d: %llu\n", |
| 152 | od->pm_lat_level, act_lat); | 172 | od->pm_lat_level, act_lat); |
| 153 | } else | 173 | } else |
| 154 | pr_warning("omap_device: %s.%d: activate " | 174 | dev_warn(&od->pdev->dev, |
| 155 | "latency %d higher than exptected. " | 175 | "activate latency %d " |
| 156 | "(%llu > %d)\n", | 176 | "higher than exptected. (%llu > %d)\n", |
| 157 | od->pdev.name, od->pdev.id, | 177 | od->pm_lat_level, act_lat, |
| 158 | od->pm_lat_level, act_lat, | 178 | odpl->activate_lat); |
| 159 | odpl->activate_lat); | ||
| 160 | } | 179 | } |
| 161 | 180 | ||
| 162 | od->dev_wakeup_lat -= odpl->activate_lat; | 181 | od->dev_wakeup_lat -= odpl->activate_lat; |
| @@ -183,7 +202,7 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat) | |||
| 183 | { | 202 | { |
| 184 | struct timespec a, b, c; | 203 | struct timespec a, b, c; |
| 185 | 204 | ||
| 186 | pr_debug("omap_device: %s: deactivating\n", od->pdev.name); | 205 | dev_dbg(&od->pdev->dev, "omap_device: deactivating\n"); |
| 187 | 206 | ||
| 188 | while (od->pm_lat_level < od->pm_lats_cnt) { | 207 | while (od->pm_lat_level < od->pm_lats_cnt) { |
| 189 | struct omap_device_pm_latency *odpl; | 208 | struct omap_device_pm_latency *odpl; |
| @@ -206,28 +225,26 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat) | |||
| 206 | c = timespec_sub(b, a); | 225 | c = timespec_sub(b, a); |
| 207 | deact_lat = timespec_to_ns(&c); | 226 | deact_lat = timespec_to_ns(&c); |
| 208 | 227 | ||
| 209 | pr_debug("omap_device: %s: pm_lat %d: deactivate: elapsed time " | 228 | dev_dbg(&od->pdev->dev, |
| 210 | "%llu nsec\n", od->pdev.name, od->pm_lat_level, | 229 | "omap_device: pm_lat %d: deactivate: elapsed time " |
| 211 | deact_lat); | 230 | "%llu nsec\n", od->pm_lat_level, deact_lat); |
| 212 | 231 | ||
| 213 | if (deact_lat > odpl->deactivate_lat) { | 232 | if (deact_lat > odpl->deactivate_lat) { |
| 214 | odpl->deactivate_lat_worst = deact_lat; | 233 | odpl->deactivate_lat_worst = deact_lat; |
| 215 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { | 234 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { |
| 216 | odpl->deactivate_lat = deact_lat; | 235 | odpl->deactivate_lat = deact_lat; |
| 217 | pr_warning("omap_device: %s.%d: new worst case " | 236 | dev_dbg(&od->pdev->dev, |
| 218 | "deactivate latency %d: %llu\n", | 237 | "new worst case deactivate latency " |
| 219 | od->pdev.name, od->pdev.id, | 238 | "%d: %llu\n", |
| 220 | od->pm_lat_level, deact_lat); | 239 | od->pm_lat_level, deact_lat); |
| 221 | } else | 240 | } else |
| 222 | pr_warning("omap_device: %s.%d: deactivate " | 241 | dev_warn(&od->pdev->dev, |
| 223 | "latency %d higher than exptected. " | 242 | "deactivate latency %d " |
| 224 | "(%llu > %d)\n", | 243 | "higher than exptected. (%llu > %d)\n", |
| 225 | od->pdev.name, od->pdev.id, | 244 | od->pm_lat_level, deact_lat, |
| 226 | od->pm_lat_level, deact_lat, | 245 | odpl->deactivate_lat); |
| 227 | odpl->deactivate_lat); | ||
| 228 | } | 246 | } |
| 229 | 247 | ||
| 230 | |||
| 231 | od->dev_wakeup_lat += odpl->activate_lat; | 248 | od->dev_wakeup_lat += odpl->activate_lat; |
| 232 | 249 | ||
| 233 | od->pm_lat_level++; | 250 | od->pm_lat_level++; |
| @@ -245,28 +262,27 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias, | |||
| 245 | if (!clk_alias || !clk_name) | 262 | if (!clk_alias || !clk_name) |
| 246 | return; | 263 | return; |
| 247 | 264 | ||
| 248 | pr_debug("omap_device: %s: Creating %s -> %s\n", | 265 | dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name); |
| 249 | dev_name(&od->pdev.dev), clk_alias, clk_name); | ||
| 250 | 266 | ||
| 251 | r = clk_get_sys(dev_name(&od->pdev.dev), clk_alias); | 267 | r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias); |
| 252 | if (!IS_ERR(r)) { | 268 | if (!IS_ERR(r)) { |
| 253 | pr_warning("omap_device: %s: alias %s already exists\n", | 269 | dev_warn(&od->pdev->dev, |
| 254 | dev_name(&od->pdev.dev), clk_alias); | 270 | "alias %s already exists\n", clk_alias); |
| 255 | clk_put(r); | 271 | clk_put(r); |
| 256 | return; | 272 | return; |
| 257 | } | 273 | } |
| 258 | 274 | ||
| 259 | r = omap_clk_get_by_name(clk_name); | 275 | r = omap_clk_get_by_name(clk_name); |
| 260 | if (IS_ERR(r)) { | 276 | if (IS_ERR(r)) { |
| 261 | pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n", | 277 | dev_err(&od->pdev->dev, |
| 262 | dev_name(&od->pdev.dev), clk_name); | 278 | "omap_clk_get_by_name for %s failed\n", clk_name); |
| 263 | return; | 279 | return; |
| 264 | } | 280 | } |
| 265 | 281 | ||
| 266 | l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev.dev)); | 282 | l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev->dev)); |
| 267 | if (!l) { | 283 | if (!l) { |
| 268 | pr_err("omap_device: %s: clkdev_alloc for %s failed\n", | 284 | dev_err(&od->pdev->dev, |
| 269 | dev_name(&od->pdev.dev), clk_alias); | 285 | "clkdev_alloc for %s failed\n", clk_alias); |
| 270 | return; | 286 | return; |
| 271 | } | 287 | } |
| 272 | 288 | ||
| @@ -304,6 +320,96 @@ static void _add_hwmod_clocks_clkdev(struct omap_device *od, | |||
| 304 | } | 320 | } |
| 305 | 321 | ||
| 306 | 322 | ||
| 323 | static struct dev_pm_domain omap_device_pm_domain; | ||
| 324 | |||
| 325 | /** | ||
| 326 | * omap_device_build_from_dt - build an omap_device with multiple hwmods | ||
| 327 | * @pdev_name: name of the platform_device driver to use | ||
| 328 | * @pdev_id: this platform_device's connection ID | ||
| 329 | * @oh: ptr to the single omap_hwmod that backs this omap_device | ||
| 330 | * @pdata: platform_data ptr to associate with the platform_device | ||
| 331 | * @pdata_len: amount of memory pointed to by @pdata | ||
| 332 | * @pm_lats: pointer to a omap_device_pm_latency array for this device | ||
| 333 | * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats | ||
| 334 | * @is_early_device: should the device be registered as an early device or not | ||
| 335 | * | ||
| 336 | * Function for building an omap_device already registered from device-tree | ||
| 337 | * | ||
| 338 | * Returns 0 or PTR_ERR() on error. | ||
| 339 | */ | ||
| 340 | static int omap_device_build_from_dt(struct platform_device *pdev) | ||
| 341 | { | ||
| 342 | struct omap_hwmod **hwmods; | ||
| 343 | struct omap_device *od; | ||
| 344 | struct omap_hwmod *oh; | ||
| 345 | struct device_node *node = pdev->dev.of_node; | ||
| 346 | const char *oh_name; | ||
| 347 | int oh_cnt, i, ret = 0; | ||
| 348 | |||
| 349 | oh_cnt = of_property_count_strings(node, "ti,hwmods"); | ||
| 350 | if (!oh_cnt || IS_ERR_VALUE(oh_cnt)) { | ||
| 351 | dev_warn(&pdev->dev, "No 'hwmods' to build omap_device\n"); | ||
| 352 | return -ENODEV; | ||
| 353 | } | ||
| 354 | |||
| 355 | hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); | ||
| 356 | if (!hwmods) { | ||
| 357 | ret = -ENOMEM; | ||
| 358 | goto odbfd_exit; | ||
| 359 | } | ||
| 360 | |||
| 361 | for (i = 0; i < oh_cnt; i++) { | ||
| 362 | of_property_read_string_index(node, "ti,hwmods", i, &oh_name); | ||
| 363 | oh = omap_hwmod_lookup(oh_name); | ||
| 364 | if (!oh) { | ||
| 365 | dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n", | ||
| 366 | oh_name); | ||
| 367 | ret = -EINVAL; | ||
| 368 | goto odbfd_exit1; | ||
| 369 | } | ||
| 370 | hwmods[i] = oh; | ||
| 371 | } | ||
| 372 | |||
| 373 | od = omap_device_alloc(pdev, hwmods, oh_cnt, NULL, 0); | ||
| 374 | if (!od) { | ||
| 375 | dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n", | ||
| 376 | oh_name); | ||
| 377 | ret = PTR_ERR(od); | ||
| 378 | goto odbfd_exit1; | ||
| 379 | } | ||
| 380 | |||
| 381 | if (of_get_property(node, "ti,no_idle_on_suspend", NULL)) | ||
| 382 | omap_device_disable_idle_on_suspend(pdev); | ||
| 383 | |||
| 384 | pdev->dev.pm_domain = &omap_device_pm_domain; | ||
| 385 | |||
| 386 | odbfd_exit1: | ||
| 387 | kfree(hwmods); | ||
| 388 | odbfd_exit: | ||
| 389 | return ret; | ||
| 390 | } | ||
| 391 | |||
| 392 | static int _omap_device_notifier_call(struct notifier_block *nb, | ||
| 393 | unsigned long event, void *dev) | ||
| 394 | { | ||
| 395 | struct platform_device *pdev = to_platform_device(dev); | ||
| 396 | |||
| 397 | switch (event) { | ||
| 398 | case BUS_NOTIFY_ADD_DEVICE: | ||
| 399 | if (pdev->dev.of_node) | ||
| 400 | omap_device_build_from_dt(pdev); | ||
| 401 | break; | ||
| 402 | |||
| 403 | case BUS_NOTIFY_DEL_DEVICE: | ||
| 404 | if (pdev->archdata.od) | ||
| 405 | omap_device_delete(pdev->archdata.od); | ||
| 406 | break; | ||
| 407 | } | ||
| 408 | |||
| 409 | return NOTIFY_DONE; | ||
| 410 | } | ||
| 411 | |||
| 412 | |||
| 307 | /* Public functions for use by core code */ | 413 | /* Public functions for use by core code */ |
| 308 | 414 | ||
| 309 | /** | 415 | /** |
| @@ -321,7 +427,7 @@ static void _add_hwmod_clocks_clkdev(struct omap_device *od, | |||
| 321 | * return the context loss counter for that hwmod, otherwise return | 427 | * return the context loss counter for that hwmod, otherwise return |
| 322 | * zero. | 428 | * zero. |
| 323 | */ | 429 | */ |
| 324 | u32 omap_device_get_context_loss_count(struct platform_device *pdev) | 430 | int omap_device_get_context_loss_count(struct platform_device *pdev) |
| 325 | { | 431 | { |
| 326 | struct omap_device *od; | 432 | struct omap_device *od; |
| 327 | u32 ret = 0; | 433 | u32 ret = 0; |
| @@ -343,7 +449,7 @@ u32 omap_device_get_context_loss_count(struct platform_device *pdev) | |||
| 343 | * much memory to allocate before calling | 449 | * much memory to allocate before calling |
| 344 | * omap_device_fill_resources(). Returns the count. | 450 | * omap_device_fill_resources(). Returns the count. |
| 345 | */ | 451 | */ |
| 346 | int omap_device_count_resources(struct omap_device *od) | 452 | static int omap_device_count_resources(struct omap_device *od) |
| 347 | { | 453 | { |
| 348 | int c = 0; | 454 | int c = 0; |
| 349 | int i; | 455 | int i; |
| @@ -352,7 +458,7 @@ int omap_device_count_resources(struct omap_device *od) | |||
| 352 | c += omap_hwmod_count_resources(od->hwmods[i]); | 458 | c += omap_hwmod_count_resources(od->hwmods[i]); |
| 353 | 459 | ||
| 354 | pr_debug("omap_device: %s: counted %d total resources across %d " | 460 | pr_debug("omap_device: %s: counted %d total resources across %d " |
| 355 | "hwmods\n", od->pdev.name, c, od->hwmods_cnt); | 461 | "hwmods\n", od->pdev->name, c, od->hwmods_cnt); |
| 356 | 462 | ||
| 357 | return c; | 463 | return c; |
| 358 | } | 464 | } |
| @@ -374,7 +480,8 @@ int omap_device_count_resources(struct omap_device *od) | |||
| 374 | * functions to get device resources. Hacking around the existing | 480 | * functions to get device resources. Hacking around the existing |
| 375 | * platform_device code wastes memory. Returns 0. | 481 | * platform_device code wastes memory. Returns 0. |
| 376 | */ | 482 | */ |
| 377 | int omap_device_fill_resources(struct omap_device *od, struct resource *res) | 483 | static int omap_device_fill_resources(struct omap_device *od, |
| 484 | struct resource *res) | ||
| 378 | { | 485 | { |
| 379 | int c = 0; | 486 | int c = 0; |
| 380 | int i, r; | 487 | int i, r; |
| @@ -389,6 +496,113 @@ int omap_device_fill_resources(struct omap_device *od, struct resource *res) | |||
| 389 | } | 496 | } |
| 390 | 497 | ||
| 391 | /** | 498 | /** |
| 499 | * omap_device_alloc - allocate an omap_device | ||
| 500 | * @pdev: platform_device that will be included in this omap_device | ||
| 501 | * @oh: ptr to the single omap_hwmod that backs this omap_device | ||
| 502 | * @pdata: platform_data ptr to associate with the platform_device | ||
| 503 | * @pdata_len: amount of memory pointed to by @pdata | ||
| 504 | * @pm_lats: pointer to a omap_device_pm_latency array for this device | ||
| 505 | * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats | ||
| 506 | * | ||
| 507 | * Convenience function for allocating an omap_device structure and filling | ||
| 508 | * hwmods, resources and pm_latency attributes. | ||
| 509 | * | ||
| 510 | * Returns an struct omap_device pointer or ERR_PTR() on error; | ||
| 511 | */ | ||
| 512 | static struct omap_device *omap_device_alloc(struct platform_device *pdev, | ||
| 513 | struct omap_hwmod **ohs, int oh_cnt, | ||
| 514 | struct omap_device_pm_latency *pm_lats, | ||
| 515 | int pm_lats_cnt) | ||
| 516 | { | ||
| 517 | int ret = -ENOMEM; | ||
| 518 | struct omap_device *od; | ||
| 519 | struct resource *res = NULL; | ||
| 520 | int i, res_count; | ||
| 521 | struct omap_hwmod **hwmods; | ||
| 522 | |||
| 523 | od = kzalloc(sizeof(struct omap_device), GFP_KERNEL); | ||
| 524 | if (!od) { | ||
| 525 | ret = -ENOMEM; | ||
| 526 | goto oda_exit1; | ||
| 527 | } | ||
| 528 | od->hwmods_cnt = oh_cnt; | ||
| 529 | |||
| 530 | hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); | ||
| 531 | if (!hwmods) | ||
| 532 | goto oda_exit2; | ||
| 533 | |||
| 534 | od->hwmods = hwmods; | ||
| 535 | od->pdev = pdev; | ||
| 536 | |||
| 537 | /* | ||
| 538 | * HACK: Ideally the resources from DT should match, and hwmod | ||
| 539 | * should just add the missing ones. Since the name is not | ||
| 540 | * properly populated by DT, stick to hwmod resources only. | ||
| 541 | */ | ||
| 542 | if (pdev->num_resources && pdev->resource) | ||
| 543 | dev_warn(&pdev->dev, "%s(): resources already allocated %d\n", | ||
| 544 | __func__, pdev->num_resources); | ||
| 545 | |||
| 546 | res_count = omap_device_count_resources(od); | ||
| 547 | if (res_count > 0) { | ||
| 548 | dev_dbg(&pdev->dev, "%s(): resources allocated from hwmod %d\n", | ||
| 549 | __func__, res_count); | ||
| 550 | res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL); | ||
| 551 | if (!res) | ||
| 552 | goto oda_exit3; | ||
| 553 | |||
| 554 | omap_device_fill_resources(od, res); | ||
| 555 | |||
| 556 | ret = platform_device_add_resources(pdev, res, res_count); | ||
| 557 | kfree(res); | ||
| 558 | |||
| 559 | if (ret) | ||
| 560 | goto oda_exit3; | ||
| 561 | } | ||
| 562 | |||
| 563 | if (!pm_lats) { | ||
| 564 | pm_lats = omap_default_latency; | ||
| 565 | pm_lats_cnt = ARRAY_SIZE(omap_default_latency); | ||
| 566 | } | ||
| 567 | |||
| 568 | od->pm_lats_cnt = pm_lats_cnt; | ||
| 569 | od->pm_lats = kmemdup(pm_lats, | ||
| 570 | sizeof(struct omap_device_pm_latency) * pm_lats_cnt, | ||
| 571 | GFP_KERNEL); | ||
| 572 | if (!od->pm_lats) | ||
| 573 | goto oda_exit3; | ||
| 574 | |||
| 575 | pdev->archdata.od = od; | ||
| 576 | |||
| 577 | for (i = 0; i < oh_cnt; i++) { | ||
| 578 | hwmods[i]->od = od; | ||
| 579 | _add_hwmod_clocks_clkdev(od, hwmods[i]); | ||
| 580 | } | ||
| 581 | |||
| 582 | return od; | ||
| 583 | |||
| 584 | oda_exit3: | ||
| 585 | kfree(hwmods); | ||
| 586 | oda_exit2: | ||
| 587 | kfree(od); | ||
| 588 | oda_exit1: | ||
| 589 | dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret); | ||
| 590 | |||
| 591 | return ERR_PTR(ret); | ||
| 592 | } | ||
| 593 | |||
| 594 | static void omap_device_delete(struct omap_device *od) | ||
| 595 | { | ||
| 596 | if (!od) | ||
| 597 | return; | ||
| 598 | |||
| 599 | od->pdev->archdata.od = NULL; | ||
| 600 | kfree(od->pm_lats); | ||
| 601 | kfree(od->hwmods); | ||
| 602 | kfree(od); | ||
| 603 | } | ||
| 604 | |||
| 605 | /** | ||
| 392 | * omap_device_build - build and register an omap_device with one omap_hwmod | 606 | * omap_device_build - build and register an omap_device with one omap_hwmod |
| 393 | * @pdev_name: name of the platform_device driver to use | 607 | * @pdev_name: name of the platform_device driver to use |
| 394 | * @pdev_id: this platform_device's connection ID | 608 | * @pdev_id: this platform_device's connection ID |
| @@ -405,7 +619,7 @@ int omap_device_fill_resources(struct omap_device *od, struct resource *res) | |||
| 405 | * information. Returns ERR_PTR(-EINVAL) if @oh is NULL; otherwise, | 619 | * information. Returns ERR_PTR(-EINVAL) if @oh is NULL; otherwise, |
| 406 | * passes along the return value of omap_device_build_ss(). | 620 | * passes along the return value of omap_device_build_ss(). |
| 407 | */ | 621 | */ |
| 408 | struct omap_device *omap_device_build(const char *pdev_name, int pdev_id, | 622 | struct platform_device *omap_device_build(const char *pdev_name, int pdev_id, |
| 409 | struct omap_hwmod *oh, void *pdata, | 623 | struct omap_hwmod *oh, void *pdata, |
| 410 | int pdata_len, | 624 | int pdata_len, |
| 411 | struct omap_device_pm_latency *pm_lats, | 625 | struct omap_device_pm_latency *pm_lats, |
| @@ -438,18 +652,15 @@ struct omap_device *omap_device_build(const char *pdev_name, int pdev_id, | |||
| 438 | * platform_device record. Returns an ERR_PTR() on error, or passes | 652 | * platform_device record. Returns an ERR_PTR() on error, or passes |
| 439 | * along the return value of omap_device_register(). | 653 | * along the return value of omap_device_register(). |
| 440 | */ | 654 | */ |
| 441 | struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id, | 655 | struct platform_device *omap_device_build_ss(const char *pdev_name, int pdev_id, |
| 442 | struct omap_hwmod **ohs, int oh_cnt, | 656 | struct omap_hwmod **ohs, int oh_cnt, |
| 443 | void *pdata, int pdata_len, | 657 | void *pdata, int pdata_len, |
| 444 | struct omap_device_pm_latency *pm_lats, | 658 | struct omap_device_pm_latency *pm_lats, |
| 445 | int pm_lats_cnt, int is_early_device) | 659 | int pm_lats_cnt, int is_early_device) |
| 446 | { | 660 | { |
| 447 | int ret = -ENOMEM; | 661 | int ret = -ENOMEM; |
| 662 | struct platform_device *pdev; | ||
| 448 | struct omap_device *od; | 663 | struct omap_device *od; |
| 449 | char *pdev_name2; | ||
| 450 | struct resource *res = NULL; | ||
| 451 | int i, res_count; | ||
| 452 | struct omap_hwmod **hwmods; | ||
| 453 | 664 | ||
| 454 | if (!ohs || oh_cnt == 0 || !pdev_name) | 665 | if (!ohs || oh_cnt == 0 || !pdev_name) |
| 455 | return ERR_PTR(-EINVAL); | 666 | return ERR_PTR(-EINVAL); |
| @@ -457,72 +668,40 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id, | |||
| 457 | if (!pdata && pdata_len > 0) | 668 | if (!pdata && pdata_len > 0) |
| 458 | return ERR_PTR(-EINVAL); | 669 | return ERR_PTR(-EINVAL); |
| 459 | 670 | ||
| 460 | pr_debug("omap_device: %s: building with %d hwmods\n", pdev_name, | 671 | pdev = platform_device_alloc(pdev_name, pdev_id); |
| 461 | oh_cnt); | 672 | if (!pdev) { |
| 462 | 673 | ret = -ENOMEM; | |
| 463 | od = kzalloc(sizeof(struct omap_device), GFP_KERNEL); | 674 | goto odbs_exit; |
| 464 | if (!od) | 675 | } |
| 465 | return ERR_PTR(-ENOMEM); | ||
| 466 | 676 | ||
| 467 | od->hwmods_cnt = oh_cnt; | 677 | /* Set the dev_name early to allow dev_xxx in omap_device_alloc */ |
| 678 | if (pdev->id != -1) | ||
| 679 | dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); | ||
| 680 | else | ||
| 681 | dev_set_name(&pdev->dev, "%s", pdev->name); | ||
| 468 | 682 | ||
| 469 | hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, | 683 | od = omap_device_alloc(pdev, ohs, oh_cnt, pm_lats, pm_lats_cnt); |
| 470 | GFP_KERNEL); | 684 | if (!od) |
| 471 | if (!hwmods) | ||
| 472 | goto odbs_exit1; | 685 | goto odbs_exit1; |
| 473 | 686 | ||
| 474 | memcpy(hwmods, ohs, sizeof(struct omap_hwmod *) * oh_cnt); | 687 | ret = platform_device_add_data(pdev, pdata, pdata_len); |
| 475 | od->hwmods = hwmods; | ||
| 476 | |||
| 477 | pdev_name2 = kzalloc(strlen(pdev_name) + 1, GFP_KERNEL); | ||
| 478 | if (!pdev_name2) | ||
| 479 | goto odbs_exit2; | ||
| 480 | strcpy(pdev_name2, pdev_name); | ||
| 481 | |||
| 482 | od->pdev.name = pdev_name2; | ||
| 483 | od->pdev.id = pdev_id; | ||
| 484 | |||
| 485 | res_count = omap_device_count_resources(od); | ||
| 486 | if (res_count > 0) { | ||
| 487 | res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL); | ||
| 488 | if (!res) | ||
| 489 | goto odbs_exit3; | ||
| 490 | } | ||
| 491 | omap_device_fill_resources(od, res); | ||
| 492 | |||
| 493 | od->pdev.num_resources = res_count; | ||
| 494 | od->pdev.resource = res; | ||
| 495 | |||
| 496 | ret = platform_device_add_data(&od->pdev, pdata, pdata_len); | ||
| 497 | if (ret) | 688 | if (ret) |
| 498 | goto odbs_exit4; | 689 | goto odbs_exit2; |
| 499 | |||
| 500 | od->pm_lats = pm_lats; | ||
| 501 | od->pm_lats_cnt = pm_lats_cnt; | ||
| 502 | 690 | ||
| 503 | if (is_early_device) | 691 | if (is_early_device) |
| 504 | ret = omap_early_device_register(od); | 692 | ret = omap_early_device_register(pdev); |
| 505 | else | 693 | else |
| 506 | ret = omap_device_register(od); | 694 | ret = omap_device_register(pdev); |
| 507 | |||
| 508 | for (i = 0; i < oh_cnt; i++) { | ||
| 509 | hwmods[i]->od = od; | ||
| 510 | _add_hwmod_clocks_clkdev(od, hwmods[i]); | ||
| 511 | } | ||
| 512 | |||
| 513 | if (ret) | 695 | if (ret) |
| 514 | goto odbs_exit4; | 696 | goto odbs_exit2; |
| 515 | 697 | ||
| 516 | return od; | 698 | return pdev; |
| 517 | 699 | ||
| 518 | odbs_exit4: | ||
| 519 | kfree(res); | ||
| 520 | odbs_exit3: | ||
| 521 | kfree(pdev_name2); | ||
| 522 | odbs_exit2: | 700 | odbs_exit2: |
| 523 | kfree(hwmods); | 701 | omap_device_delete(od); |
| 524 | odbs_exit1: | 702 | odbs_exit1: |
| 525 | kfree(od); | 703 | platform_device_put(pdev); |
| 704 | odbs_exit: | ||
| 526 | 705 | ||
| 527 | pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret); | 706 | pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret); |
| 528 | 707 | ||
| @@ -538,11 +717,11 @@ odbs_exit1: | |||
| 538 | * platform_early_add_device() on the underlying platform_device. | 717 | * platform_early_add_device() on the underlying platform_device. |
| 539 | * Returns 0 by default. | 718 | * Returns 0 by default. |
| 540 | */ | 719 | */ |
| 541 | int omap_early_device_register(struct omap_device *od) | 720 | static int omap_early_device_register(struct platform_device *pdev) |
| 542 | { | 721 | { |
| 543 | struct platform_device *devices[1]; | 722 | struct platform_device *devices[1]; |
| 544 | 723 | ||
| 545 | devices[0] = &(od->pdev); | 724 | devices[0] = pdev; |
| 546 | early_platform_add_devices(devices, 1); | 725 | early_platform_add_devices(devices, 1); |
| 547 | return 0; | 726 | return 0; |
| 548 | } | 727 | } |
| @@ -615,6 +794,9 @@ static int _od_resume_noirq(struct device *dev) | |||
| 615 | 794 | ||
| 616 | return pm_generic_resume_noirq(dev); | 795 | return pm_generic_resume_noirq(dev); |
| 617 | } | 796 | } |
| 797 | #else | ||
| 798 | #define _od_suspend_noirq NULL | ||
| 799 | #define _od_resume_noirq NULL | ||
| 618 | #endif | 800 | #endif |
| 619 | 801 | ||
| 620 | static struct dev_pm_domain omap_device_pm_domain = { | 802 | static struct dev_pm_domain omap_device_pm_domain = { |
| @@ -635,13 +817,13 @@ static struct dev_pm_domain omap_device_pm_domain = { | |||
| 635 | * platform_device_register() on the underlying platform_device. | 817 | * platform_device_register() on the underlying platform_device. |
| 636 | * Returns the return value of platform_device_register(). | 818 | * Returns the return value of platform_device_register(). |
| 637 | */ | 819 | */ |
| 638 | int omap_device_register(struct omap_device *od) | 820 | static int omap_device_register(struct platform_device *pdev) |
| 639 | { | 821 | { |
| 640 | pr_debug("omap_device: %s: registering\n", od->pdev.name); | 822 | pr_debug("omap_device: %s: registering\n", pdev->name); |
| 641 | 823 | ||
| 642 | od->pdev.dev.parent = &omap_device_parent; | 824 | pdev->dev.parent = &omap_device_parent; |
| 643 | od->pdev.dev.pm_domain = &omap_device_pm_domain; | 825 | pdev->dev.pm_domain = &omap_device_pm_domain; |
| 644 | return platform_device_register(&od->pdev); | 826 | return platform_device_add(pdev); |
| 645 | } | 827 | } |
| 646 | 828 | ||
| 647 | 829 | ||
| @@ -668,8 +850,9 @@ int omap_device_enable(struct platform_device *pdev) | |||
| 668 | od = to_omap_device(pdev); | 850 | od = to_omap_device(pdev); |
| 669 | 851 | ||
| 670 | if (od->_state == OMAP_DEVICE_STATE_ENABLED) { | 852 | if (od->_state == OMAP_DEVICE_STATE_ENABLED) { |
| 671 | WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n", | 853 | dev_warn(&pdev->dev, |
| 672 | od->pdev.name, od->pdev.id, __func__, od->_state); | 854 | "omap_device: %s() called from invalid state %d\n", |
| 855 | __func__, od->_state); | ||
| 673 | return -EINVAL; | 856 | return -EINVAL; |
| 674 | } | 857 | } |
| 675 | 858 | ||
| @@ -707,8 +890,9 @@ int omap_device_idle(struct platform_device *pdev) | |||
| 707 | od = to_omap_device(pdev); | 890 | od = to_omap_device(pdev); |
| 708 | 891 | ||
| 709 | if (od->_state != OMAP_DEVICE_STATE_ENABLED) { | 892 | if (od->_state != OMAP_DEVICE_STATE_ENABLED) { |
| 710 | WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n", | 893 | dev_warn(&pdev->dev, |
| 711 | od->pdev.name, od->pdev.id, __func__, od->_state); | 894 | "omap_device: %s() called from invalid state %d\n", |
| 895 | __func__, od->_state); | ||
| 712 | return -EINVAL; | 896 | return -EINVAL; |
| 713 | } | 897 | } |
| 714 | 898 | ||
| @@ -739,8 +923,9 @@ int omap_device_shutdown(struct platform_device *pdev) | |||
| 739 | 923 | ||
| 740 | if (od->_state != OMAP_DEVICE_STATE_ENABLED && | 924 | if (od->_state != OMAP_DEVICE_STATE_ENABLED && |
| 741 | od->_state != OMAP_DEVICE_STATE_IDLE) { | 925 | od->_state != OMAP_DEVICE_STATE_IDLE) { |
| 742 | WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n", | 926 | dev_warn(&pdev->dev, |
| 743 | od->pdev.name, od->pdev.id, __func__, od->_state); | 927 | "omap_device: %s() called from invalid state %d\n", |
| 928 | __func__, od->_state); | ||
| 744 | return -EINVAL; | 929 | return -EINVAL; |
| 745 | } | 930 | } |
| 746 | 931 | ||
| @@ -834,6 +1019,42 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od) | |||
| 834 | return omap_hwmod_get_mpu_rt_va(od->hwmods[0]); | 1019 | return omap_hwmod_get_mpu_rt_va(od->hwmods[0]); |
| 835 | } | 1020 | } |
| 836 | 1021 | ||
| 1022 | /** | ||
| 1023 | * omap_device_get_by_hwmod_name() - convert a hwmod name to | ||
| 1024 | * device pointer. | ||
| 1025 | * @oh_name: name of the hwmod device | ||
| 1026 | * | ||
| 1027 | * Returns back a struct device * pointer associated with a hwmod | ||
| 1028 | * device represented by a hwmod_name | ||
| 1029 | */ | ||
| 1030 | struct device *omap_device_get_by_hwmod_name(const char *oh_name) | ||
| 1031 | { | ||
| 1032 | struct omap_hwmod *oh; | ||
| 1033 | |||
| 1034 | if (!oh_name) { | ||
| 1035 | WARN(1, "%s: no hwmod name!\n", __func__); | ||
| 1036 | return ERR_PTR(-EINVAL); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | oh = omap_hwmod_lookup(oh_name); | ||
| 1040 | if (IS_ERR_OR_NULL(oh)) { | ||
| 1041 | WARN(1, "%s: no hwmod for %s\n", __func__, | ||
| 1042 | oh_name); | ||
| 1043 | return ERR_PTR(oh ? PTR_ERR(oh) : -ENODEV); | ||
| 1044 | } | ||
| 1045 | if (IS_ERR_OR_NULL(oh->od)) { | ||
| 1046 | WARN(1, "%s: no omap_device for %s\n", __func__, | ||
| 1047 | oh_name); | ||
| 1048 | return ERR_PTR(oh->od ? PTR_ERR(oh->od) : -ENODEV); | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | if (IS_ERR_OR_NULL(oh->od->pdev)) | ||
| 1052 | return ERR_PTR(oh->od->pdev ? PTR_ERR(oh->od->pdev) : -ENODEV); | ||
| 1053 | |||
| 1054 | return &oh->od->pdev->dev; | ||
| 1055 | } | ||
| 1056 | EXPORT_SYMBOL(omap_device_get_by_hwmod_name); | ||
| 1057 | |||
| 837 | /* | 1058 | /* |
| 838 | * Public functions intended for use in omap_device_pm_latency | 1059 | * Public functions intended for use in omap_device_pm_latency |
| 839 | * .activate_func and .deactivate_func function pointers | 1060 | * .activate_func and .deactivate_func function pointers |
| @@ -914,8 +1135,13 @@ struct device omap_device_parent = { | |||
| 914 | .parent = &platform_bus, | 1135 | .parent = &platform_bus, |
| 915 | }; | 1136 | }; |
| 916 | 1137 | ||
| 1138 | static struct notifier_block platform_nb = { | ||
| 1139 | .notifier_call = _omap_device_notifier_call, | ||
| 1140 | }; | ||
| 1141 | |||
| 917 | static int __init omap_device_init(void) | 1142 | static int __init omap_device_init(void) |
| 918 | { | 1143 | { |
| 1144 | bus_register_notifier(&platform_bus_type, &platform_nb); | ||
| 919 | return device_register(&omap_device_parent); | 1145 | return device_register(&omap_device_parent); |
| 920 | } | 1146 | } |
| 921 | core_initcall(omap_device_init); | 1147 | core_initcall(omap_device_init); |
