diff options
Diffstat (limited to 'arch/arm/plat-omap/omap_device.c')
-rw-r--r-- | arch/arm/plat-omap/omap_device.c | 160 |
1 files changed, 128 insertions, 32 deletions
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c index c490240bb82c..d5f617c542d3 100644 --- a/arch/arm/plat-omap/omap_device.c +++ b/arch/arm/plat-omap/omap_device.c | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | /* | 1 | /* |
3 | * omap_device implementation | 2 | * omap_device implementation |
4 | * | 3 | * |
@@ -153,21 +152,19 @@ static int _omap_device_activate(struct omap_device *od, u8 ignore_lat) | |||
153 | act_lat = timespec_to_ns(&c); | 152 | act_lat = timespec_to_ns(&c); |
154 | 153 | ||
155 | dev_dbg(&od->pdev->dev, | 154 | dev_dbg(&od->pdev->dev, |
156 | "omap_device: pm_lat %d: activate: elapsed time " | 155 | "omap_device: pm_lat %d: activate: elapsed time %llu nsec\n", |
157 | "%llu nsec\n", od->pm_lat_level, act_lat); | 156 | od->pm_lat_level, act_lat); |
158 | 157 | ||
159 | if (act_lat > odpl->activate_lat) { | 158 | if (act_lat > odpl->activate_lat) { |
160 | odpl->activate_lat_worst = act_lat; | 159 | odpl->activate_lat_worst = act_lat; |
161 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { | 160 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { |
162 | odpl->activate_lat = act_lat; | 161 | odpl->activate_lat = act_lat; |
163 | dev_dbg(&od->pdev->dev, | 162 | dev_dbg(&od->pdev->dev, |
164 | "new worst case activate latency " | 163 | "new worst case activate latency %d: %llu\n", |
165 | "%d: %llu\n", | ||
166 | od->pm_lat_level, act_lat); | 164 | od->pm_lat_level, act_lat); |
167 | } else | 165 | } else |
168 | dev_warn(&od->pdev->dev, | 166 | dev_warn(&od->pdev->dev, |
169 | "activate latency %d " | 167 | "activate latency %d higher than expected. (%llu > %d)\n", |
170 | "higher than exptected. (%llu > %d)\n", | ||
171 | od->pm_lat_level, act_lat, | 168 | od->pm_lat_level, act_lat, |
172 | odpl->activate_lat); | 169 | odpl->activate_lat); |
173 | } | 170 | } |
@@ -220,21 +217,19 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat) | |||
220 | deact_lat = timespec_to_ns(&c); | 217 | deact_lat = timespec_to_ns(&c); |
221 | 218 | ||
222 | dev_dbg(&od->pdev->dev, | 219 | dev_dbg(&od->pdev->dev, |
223 | "omap_device: pm_lat %d: deactivate: elapsed time " | 220 | "omap_device: pm_lat %d: deactivate: elapsed time %llu nsec\n", |
224 | "%llu nsec\n", od->pm_lat_level, deact_lat); | 221 | od->pm_lat_level, deact_lat); |
225 | 222 | ||
226 | if (deact_lat > odpl->deactivate_lat) { | 223 | if (deact_lat > odpl->deactivate_lat) { |
227 | odpl->deactivate_lat_worst = deact_lat; | 224 | odpl->deactivate_lat_worst = deact_lat; |
228 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { | 225 | if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { |
229 | odpl->deactivate_lat = deact_lat; | 226 | odpl->deactivate_lat = deact_lat; |
230 | dev_dbg(&od->pdev->dev, | 227 | dev_dbg(&od->pdev->dev, |
231 | "new worst case deactivate latency " | 228 | "new worst case deactivate latency %d: %llu\n", |
232 | "%d: %llu\n", | ||
233 | od->pm_lat_level, deact_lat); | 229 | od->pm_lat_level, deact_lat); |
234 | } else | 230 | } else |
235 | dev_warn(&od->pdev->dev, | 231 | dev_warn(&od->pdev->dev, |
236 | "deactivate latency %d " | 232 | "deactivate latency %d higher than expected. (%llu > %d)\n", |
237 | "higher than exptected. (%llu > %d)\n", | ||
238 | od->pm_lat_level, deact_lat, | 233 | od->pm_lat_level, deact_lat, |
239 | odpl->deactivate_lat); | 234 | odpl->deactivate_lat); |
240 | } | 235 | } |
@@ -370,6 +365,14 @@ static int omap_device_build_from_dt(struct platform_device *pdev) | |||
370 | goto odbfd_exit1; | 365 | goto odbfd_exit1; |
371 | } | 366 | } |
372 | 367 | ||
368 | /* Fix up missing resource names */ | ||
369 | for (i = 0; i < pdev->num_resources; i++) { | ||
370 | struct resource *r = &pdev->resource[i]; | ||
371 | |||
372 | if (r->name == NULL) | ||
373 | r->name = dev_name(&pdev->dev); | ||
374 | } | ||
375 | |||
373 | if (of_get_property(node, "ti,no_idle_on_suspend", NULL)) | 376 | if (of_get_property(node, "ti,no_idle_on_suspend", NULL)) |
374 | omap_device_disable_idle_on_suspend(pdev); | 377 | omap_device_disable_idle_on_suspend(pdev); |
375 | 378 | ||
@@ -385,17 +388,21 @@ static int _omap_device_notifier_call(struct notifier_block *nb, | |||
385 | unsigned long event, void *dev) | 388 | unsigned long event, void *dev) |
386 | { | 389 | { |
387 | struct platform_device *pdev = to_platform_device(dev); | 390 | struct platform_device *pdev = to_platform_device(dev); |
391 | struct omap_device *od; | ||
388 | 392 | ||
389 | switch (event) { | 393 | switch (event) { |
390 | case BUS_NOTIFY_ADD_DEVICE: | ||
391 | if (pdev->dev.of_node) | ||
392 | omap_device_build_from_dt(pdev); | ||
393 | break; | ||
394 | |||
395 | case BUS_NOTIFY_DEL_DEVICE: | 394 | case BUS_NOTIFY_DEL_DEVICE: |
396 | if (pdev->archdata.od) | 395 | if (pdev->archdata.od) |
397 | omap_device_delete(pdev->archdata.od); | 396 | omap_device_delete(pdev->archdata.od); |
398 | break; | 397 | break; |
398 | case BUS_NOTIFY_ADD_DEVICE: | ||
399 | if (pdev->dev.of_node) | ||
400 | omap_device_build_from_dt(pdev); | ||
401 | /* fall through */ | ||
402 | default: | ||
403 | od = to_omap_device(pdev); | ||
404 | if (od) | ||
405 | od->_driver_status = event; | ||
399 | } | 406 | } |
400 | 407 | ||
401 | return NOTIFY_DONE; | 408 | return NOTIFY_DONE; |
@@ -449,8 +456,8 @@ static int omap_device_count_resources(struct omap_device *od) | |||
449 | for (i = 0; i < od->hwmods_cnt; i++) | 456 | for (i = 0; i < od->hwmods_cnt; i++) |
450 | c += omap_hwmod_count_resources(od->hwmods[i]); | 457 | c += omap_hwmod_count_resources(od->hwmods[i]); |
451 | 458 | ||
452 | pr_debug("omap_device: %s: counted %d total resources across %d " | 459 | pr_debug("omap_device: %s: counted %d total resources across %d hwmods\n", |
453 | "hwmods\n", od->pdev->name, c, od->hwmods_cnt); | 460 | od->pdev->name, c, od->hwmods_cnt); |
454 | 461 | ||
455 | return c; | 462 | return c; |
456 | } | 463 | } |
@@ -486,6 +493,33 @@ static int omap_device_fill_resources(struct omap_device *od, | |||
486 | } | 493 | } |
487 | 494 | ||
488 | /** | 495 | /** |
496 | * _od_fill_dma_resources - fill in array of struct resource with dma resources | ||
497 | * @od: struct omap_device * | ||
498 | * @res: pointer to an array of struct resource to be filled in | ||
499 | * | ||
500 | * Populate one or more empty struct resource pointed to by @res with | ||
501 | * the dma resource data for this omap_device @od. Used by | ||
502 | * omap_device_alloc() after calling omap_device_count_resources(). | ||
503 | * | ||
504 | * Ideally this function would not be needed at all. If we have | ||
505 | * mechanism to get dma resources from DT. | ||
506 | * | ||
507 | * Returns 0. | ||
508 | */ | ||
509 | static int _od_fill_dma_resources(struct omap_device *od, | ||
510 | struct resource *res) | ||
511 | { | ||
512 | int i, r; | ||
513 | |||
514 | for (i = 0; i < od->hwmods_cnt; i++) { | ||
515 | r = omap_hwmod_fill_dma_resources(od->hwmods[i], res); | ||
516 | res += r; | ||
517 | } | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | /** | ||
489 | * omap_device_alloc - allocate an omap_device | 523 | * omap_device_alloc - allocate an omap_device |
490 | * @pdev: platform_device that will be included in this omap_device | 524 | * @pdev: platform_device that will be included in this omap_device |
491 | * @oh: ptr to the single omap_hwmod that backs this omap_device | 525 | * @oh: ptr to the single omap_hwmod that backs this omap_device |
@@ -524,24 +558,44 @@ struct omap_device *omap_device_alloc(struct platform_device *pdev, | |||
524 | od->hwmods = hwmods; | 558 | od->hwmods = hwmods; |
525 | od->pdev = pdev; | 559 | od->pdev = pdev; |
526 | 560 | ||
561 | res_count = omap_device_count_resources(od); | ||
527 | /* | 562 | /* |
528 | * HACK: Ideally the resources from DT should match, and hwmod | 563 | * DT Boot: |
529 | * should just add the missing ones. Since the name is not | 564 | * OF framework will construct the resource structure (currently |
530 | * properly populated by DT, stick to hwmod resources only. | 565 | * does for MEM & IRQ resource) and we should respect/use these |
566 | * resources, killing hwmod dependency. | ||
567 | * If pdev->num_resources > 0, we assume that MEM & IRQ resources | ||
568 | * have been allocated by OF layer already (through DTB). | ||
569 | * | ||
570 | * Non-DT Boot: | ||
571 | * Here, pdev->num_resources = 0, and we should get all the | ||
572 | * resources from hwmod. | ||
573 | * | ||
574 | * TODO: Once DMA resource is available from OF layer, we should | ||
575 | * kill filling any resources from hwmod. | ||
531 | */ | 576 | */ |
532 | if (pdev->num_resources && pdev->resource) | 577 | if (res_count > pdev->num_resources) { |
533 | dev_warn(&pdev->dev, "%s(): resources already allocated %d\n", | 578 | /* Allocate resources memory to account for new resources */ |
534 | __func__, pdev->num_resources); | ||
535 | |||
536 | res_count = omap_device_count_resources(od); | ||
537 | if (res_count > 0) { | ||
538 | dev_dbg(&pdev->dev, "%s(): resources allocated from hwmod %d\n", | ||
539 | __func__, res_count); | ||
540 | res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL); | 579 | res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL); |
541 | if (!res) | 580 | if (!res) |
542 | goto oda_exit3; | 581 | goto oda_exit3; |
543 | 582 | ||
544 | omap_device_fill_resources(od, res); | 583 | /* |
584 | * If pdev->num_resources > 0, then assume that, | ||
585 | * MEM and IRQ resources will only come from DT and only | ||
586 | * fill DMA resource from hwmod layer. | ||
587 | */ | ||
588 | if (pdev->num_resources && pdev->resource) { | ||
589 | dev_dbg(&pdev->dev, "%s(): resources already allocated %d\n", | ||
590 | __func__, res_count); | ||
591 | memcpy(res, pdev->resource, | ||
592 | sizeof(struct resource) * pdev->num_resources); | ||
593 | _od_fill_dma_resources(od, &res[pdev->num_resources]); | ||
594 | } else { | ||
595 | dev_dbg(&pdev->dev, "%s(): using resources from hwmod %d\n", | ||
596 | __func__, res_count); | ||
597 | omap_device_fill_resources(od, res); | ||
598 | } | ||
545 | 599 | ||
546 | ret = platform_device_add_resources(pdev, res, res_count); | 600 | ret = platform_device_add_resources(pdev, res, res_count); |
547 | kfree(res); | 601 | kfree(res); |
@@ -752,6 +806,10 @@ static int _od_suspend_noirq(struct device *dev) | |||
752 | struct omap_device *od = to_omap_device(pdev); | 806 | struct omap_device *od = to_omap_device(pdev); |
753 | int ret; | 807 | int ret; |
754 | 808 | ||
809 | /* Don't attempt late suspend on a driver that is not bound */ | ||
810 | if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) | ||
811 | return 0; | ||
812 | |||
755 | ret = pm_generic_suspend_noirq(dev); | 813 | ret = pm_generic_suspend_noirq(dev); |
756 | 814 | ||
757 | if (!ret && !pm_runtime_status_suspended(dev)) { | 815 | if (!ret && !pm_runtime_status_suspended(dev)) { |
@@ -1125,3 +1183,41 @@ static int __init omap_device_init(void) | |||
1125 | return 0; | 1183 | return 0; |
1126 | } | 1184 | } |
1127 | core_initcall(omap_device_init); | 1185 | core_initcall(omap_device_init); |
1186 | |||
1187 | /** | ||
1188 | * omap_device_late_idle - idle devices without drivers | ||
1189 | * @dev: struct device * associated with omap_device | ||
1190 | * @data: unused | ||
1191 | * | ||
1192 | * Check the driver bound status of this device, and idle it | ||
1193 | * if there is no driver attached. | ||
1194 | */ | ||
1195 | static int __init omap_device_late_idle(struct device *dev, void *data) | ||
1196 | { | ||
1197 | struct platform_device *pdev = to_platform_device(dev); | ||
1198 | struct omap_device *od = to_omap_device(pdev); | ||
1199 | |||
1200 | if (!od) | ||
1201 | return 0; | ||
1202 | |||
1203 | /* | ||
1204 | * If omap_device state is enabled, but has no driver bound, | ||
1205 | * idle it. | ||
1206 | */ | ||
1207 | if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) { | ||
1208 | if (od->_state == OMAP_DEVICE_STATE_ENABLED) { | ||
1209 | dev_warn(dev, "%s: enabled but no driver. Idling\n", | ||
1210 | __func__); | ||
1211 | omap_device_idle(pdev); | ||
1212 | } | ||
1213 | } | ||
1214 | |||
1215 | return 0; | ||
1216 | } | ||
1217 | |||
1218 | static int __init omap_device_late_init(void) | ||
1219 | { | ||
1220 | bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle); | ||
1221 | return 0; | ||
1222 | } | ||
1223 | late_initcall(omap_device_late_init); | ||