diff options
Diffstat (limited to 'arch/arm/plat-omap/omap_device.c')
-rw-r--r-- | arch/arm/plat-omap/omap_device.c | 192 |
1 files changed, 150 insertions, 42 deletions
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c index d2b160942ccc..49fc0df0c21f 100644 --- a/arch/arm/plat-omap/omap_device.c +++ b/arch/arm/plat-omap/omap_device.c | |||
@@ -82,20 +82,18 @@ | |||
82 | #include <linux/slab.h> | 82 | #include <linux/slab.h> |
83 | #include <linux/err.h> | 83 | #include <linux/err.h> |
84 | #include <linux/io.h> | 84 | #include <linux/io.h> |
85 | #include <linux/clk.h> | ||
86 | #include <linux/clkdev.h> | ||
87 | #include <linux/pm_runtime.h> | ||
85 | 88 | ||
86 | #include <plat/omap_device.h> | 89 | #include <plat/omap_device.h> |
87 | #include <plat/omap_hwmod.h> | 90 | #include <plat/omap_hwmod.h> |
91 | #include <plat/clock.h> | ||
88 | 92 | ||
89 | /* These parameters are passed to _omap_device_{de,}activate() */ | 93 | /* These parameters are passed to _omap_device_{de,}activate() */ |
90 | #define USE_WAKEUP_LAT 0 | 94 | #define USE_WAKEUP_LAT 0 |
91 | #define IGNORE_WAKEUP_LAT 1 | 95 | #define IGNORE_WAKEUP_LAT 1 |
92 | 96 | ||
93 | /* | ||
94 | * OMAP_DEVICE_MAGIC: used to determine whether a struct omap_device | ||
95 | * obtained via container_of() is in fact a struct omap_device | ||
96 | */ | ||
97 | #define OMAP_DEVICE_MAGIC 0xf00dcafe | ||
98 | |||
99 | /* Private functions */ | 97 | /* Private functions */ |
100 | 98 | ||
101 | /** | 99 | /** |
@@ -243,10 +241,90 @@ static inline struct omap_device *_find_by_pdev(struct platform_device *pdev) | |||
243 | return container_of(pdev, struct omap_device, pdev); | 241 | return container_of(pdev, struct omap_device, pdev); |
244 | } | 242 | } |
245 | 243 | ||
244 | /** | ||
245 | * _add_optional_clock_clkdev - Add clkdev entry for hwmod optional clocks | ||
246 | * @od: struct omap_device *od | ||
247 | * | ||
248 | * For every optional clock present per hwmod per omap_device, this function | ||
249 | * adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role> | ||
250 | * if it does not exist already. | ||
251 | * | ||
252 | * The function is called from inside omap_device_build_ss(), after | ||
253 | * omap_device_register. | ||
254 | * | ||
255 | * This allows drivers to get a pointer to its optional clocks based on its role | ||
256 | * by calling clk_get(<dev*>, <role>). | ||
257 | * | ||
258 | * No return value. | ||
259 | */ | ||
260 | static void _add_optional_clock_clkdev(struct omap_device *od, | ||
261 | struct omap_hwmod *oh) | ||
262 | { | ||
263 | int i; | ||
264 | |||
265 | for (i = 0; i < oh->opt_clks_cnt; i++) { | ||
266 | struct omap_hwmod_opt_clk *oc; | ||
267 | struct clk *r; | ||
268 | struct clk_lookup *l; | ||
269 | |||
270 | oc = &oh->opt_clks[i]; | ||
271 | |||
272 | if (!oc->_clk) | ||
273 | continue; | ||
274 | |||
275 | r = clk_get_sys(dev_name(&od->pdev.dev), oc->role); | ||
276 | if (!IS_ERR(r)) | ||
277 | continue; /* clkdev entry exists */ | ||
278 | |||
279 | r = omap_clk_get_by_name((char *)oc->clk); | ||
280 | if (IS_ERR(r)) { | ||
281 | pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n", | ||
282 | dev_name(&od->pdev.dev), oc->clk); | ||
283 | continue; | ||
284 | } | ||
285 | |||
286 | l = clkdev_alloc(r, oc->role, dev_name(&od->pdev.dev)); | ||
287 | if (!l) { | ||
288 | pr_err("omap_device: %s: clkdev_alloc for %s failed\n", | ||
289 | dev_name(&od->pdev.dev), oc->role); | ||
290 | return; | ||
291 | } | ||
292 | clkdev_add(l); | ||
293 | } | ||
294 | } | ||
295 | |||
246 | 296 | ||
247 | /* Public functions for use by core code */ | 297 | /* Public functions for use by core code */ |
248 | 298 | ||
249 | /** | 299 | /** |
300 | * omap_device_get_context_loss_count - get lost context count | ||
301 | * @od: struct omap_device * | ||
302 | * | ||
303 | * Using the primary hwmod, query the context loss count for this | ||
304 | * device. | ||
305 | * | ||
306 | * Callers should consider context for this device lost any time this | ||
307 | * function returns a value different than the value the caller got | ||
308 | * the last time it called this function. | ||
309 | * | ||
310 | * If any hwmods exist for the omap_device assoiated with @pdev, | ||
311 | * return the context loss counter for that hwmod, otherwise return | ||
312 | * zero. | ||
313 | */ | ||
314 | u32 omap_device_get_context_loss_count(struct platform_device *pdev) | ||
315 | { | ||
316 | struct omap_device *od; | ||
317 | u32 ret = 0; | ||
318 | |||
319 | od = _find_by_pdev(pdev); | ||
320 | |||
321 | if (od->hwmods_cnt) | ||
322 | ret = omap_hwmod_get_context_loss_count(od->hwmods[0]); | ||
323 | |||
324 | return ret; | ||
325 | } | ||
326 | |||
327 | /** | ||
250 | * omap_device_count_resources - count number of struct resource entries needed | 328 | * omap_device_count_resources - count number of struct resource entries needed |
251 | * @od: struct omap_device * | 329 | * @od: struct omap_device * |
252 | * | 330 | * |
@@ -257,12 +335,11 @@ static inline struct omap_device *_find_by_pdev(struct platform_device *pdev) | |||
257 | */ | 335 | */ |
258 | int omap_device_count_resources(struct omap_device *od) | 336 | int omap_device_count_resources(struct omap_device *od) |
259 | { | 337 | { |
260 | struct omap_hwmod *oh; | ||
261 | int c = 0; | 338 | int c = 0; |
262 | int i; | 339 | int i; |
263 | 340 | ||
264 | for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++) | 341 | for (i = 0; i < od->hwmods_cnt; i++) |
265 | c += omap_hwmod_count_resources(oh); | 342 | c += omap_hwmod_count_resources(od->hwmods[i]); |
266 | 343 | ||
267 | pr_debug("omap_device: %s: counted %d total resources across %d " | 344 | pr_debug("omap_device: %s: counted %d total resources across %d " |
268 | "hwmods\n", od->pdev.name, c, od->hwmods_cnt); | 345 | "hwmods\n", od->pdev.name, c, od->hwmods_cnt); |
@@ -289,12 +366,11 @@ int omap_device_count_resources(struct omap_device *od) | |||
289 | */ | 366 | */ |
290 | int omap_device_fill_resources(struct omap_device *od, struct resource *res) | 367 | int omap_device_fill_resources(struct omap_device *od, struct resource *res) |
291 | { | 368 | { |
292 | struct omap_hwmod *oh; | ||
293 | int c = 0; | 369 | int c = 0; |
294 | int i, r; | 370 | int i, r; |
295 | 371 | ||
296 | for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++) { | 372 | for (i = 0; i < od->hwmods_cnt; i++) { |
297 | r = omap_hwmod_fill_resources(oh, res); | 373 | r = omap_hwmod_fill_resources(od->hwmods[i], res); |
298 | res += r; | 374 | res += r; |
299 | c += r; | 375 | c += r; |
300 | } | 376 | } |
@@ -414,15 +490,15 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id, | |||
414 | od->pm_lats = pm_lats; | 490 | od->pm_lats = pm_lats; |
415 | od->pm_lats_cnt = pm_lats_cnt; | 491 | od->pm_lats_cnt = pm_lats_cnt; |
416 | 492 | ||
417 | od->magic = OMAP_DEVICE_MAGIC; | ||
418 | |||
419 | if (is_early_device) | 493 | if (is_early_device) |
420 | ret = omap_early_device_register(od); | 494 | ret = omap_early_device_register(od); |
421 | else | 495 | else |
422 | ret = omap_device_register(od); | 496 | ret = omap_device_register(od); |
423 | 497 | ||
424 | for (i = 0; i < oh_cnt; i++) | 498 | for (i = 0; i < oh_cnt; i++) { |
425 | hwmods[i]->od = od; | 499 | hwmods[i]->od = od; |
500 | _add_optional_clock_clkdev(od, hwmods[i]); | ||
501 | } | ||
426 | 502 | ||
427 | if (ret) | 503 | if (ret) |
428 | goto odbs_exit4; | 504 | goto odbs_exit4; |
@@ -461,6 +537,42 @@ int omap_early_device_register(struct omap_device *od) | |||
461 | return 0; | 537 | return 0; |
462 | } | 538 | } |
463 | 539 | ||
540 | static int _od_runtime_suspend(struct device *dev) | ||
541 | { | ||
542 | struct platform_device *pdev = to_platform_device(dev); | ||
543 | int ret; | ||
544 | |||
545 | ret = pm_generic_runtime_suspend(dev); | ||
546 | |||
547 | if (!ret) | ||
548 | omap_device_idle(pdev); | ||
549 | |||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | static int _od_runtime_idle(struct device *dev) | ||
554 | { | ||
555 | return pm_generic_runtime_idle(dev); | ||
556 | } | ||
557 | |||
558 | static int _od_runtime_resume(struct device *dev) | ||
559 | { | ||
560 | struct platform_device *pdev = to_platform_device(dev); | ||
561 | |||
562 | omap_device_enable(pdev); | ||
563 | |||
564 | return pm_generic_runtime_resume(dev); | ||
565 | } | ||
566 | |||
567 | static struct dev_power_domain omap_device_power_domain = { | ||
568 | .ops = { | ||
569 | .runtime_suspend = _od_runtime_suspend, | ||
570 | .runtime_idle = _od_runtime_idle, | ||
571 | .runtime_resume = _od_runtime_resume, | ||
572 | USE_PLATFORM_PM_SLEEP_OPS | ||
573 | } | ||
574 | }; | ||
575 | |||
464 | /** | 576 | /** |
465 | * omap_device_register - register an omap_device with one omap_hwmod | 577 | * omap_device_register - register an omap_device with one omap_hwmod |
466 | * @od: struct omap_device * to register | 578 | * @od: struct omap_device * to register |
@@ -473,6 +585,8 @@ int omap_device_register(struct omap_device *od) | |||
473 | { | 585 | { |
474 | pr_debug("omap_device: %s: registering\n", od->pdev.name); | 586 | pr_debug("omap_device: %s: registering\n", od->pdev.name); |
475 | 587 | ||
588 | od->pdev.dev.parent = &omap_device_parent; | ||
589 | od->pdev.dev.pwr_domain = &omap_device_power_domain; | ||
476 | return platform_device_register(&od->pdev); | 590 | return platform_device_register(&od->pdev); |
477 | } | 591 | } |
478 | 592 | ||
@@ -566,7 +680,6 @@ int omap_device_shutdown(struct platform_device *pdev) | |||
566 | { | 680 | { |
567 | int ret, i; | 681 | int ret, i; |
568 | struct omap_device *od; | 682 | struct omap_device *od; |
569 | struct omap_hwmod *oh; | ||
570 | 683 | ||
571 | od = _find_by_pdev(pdev); | 684 | od = _find_by_pdev(pdev); |
572 | 685 | ||
@@ -579,8 +692,8 @@ int omap_device_shutdown(struct platform_device *pdev) | |||
579 | 692 | ||
580 | ret = _omap_device_deactivate(od, IGNORE_WAKEUP_LAT); | 693 | ret = _omap_device_deactivate(od, IGNORE_WAKEUP_LAT); |
581 | 694 | ||
582 | for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++) | 695 | for (i = 0; i < od->hwmods_cnt; i++) |
583 | omap_hwmod_shutdown(oh); | 696 | omap_hwmod_shutdown(od->hwmods[i]); |
584 | 697 | ||
585 | od->_state = OMAP_DEVICE_STATE_SHUTDOWN; | 698 | od->_state = OMAP_DEVICE_STATE_SHUTDOWN; |
586 | 699 | ||
@@ -627,18 +740,6 @@ int omap_device_align_pm_lat(struct platform_device *pdev, | |||
627 | } | 740 | } |
628 | 741 | ||
629 | /** | 742 | /** |
630 | * omap_device_is_valid - Check if pointer is a valid omap_device | ||
631 | * @od: struct omap_device * | ||
632 | * | ||
633 | * Return whether struct omap_device pointer @od points to a valid | ||
634 | * omap_device. | ||
635 | */ | ||
636 | bool omap_device_is_valid(struct omap_device *od) | ||
637 | { | ||
638 | return (od && od->magic == OMAP_DEVICE_MAGIC); | ||
639 | } | ||
640 | |||
641 | /** | ||
642 | * omap_device_get_pwrdm - return the powerdomain * associated with @od | 743 | * omap_device_get_pwrdm - return the powerdomain * associated with @od |
643 | * @od: struct omap_device * | 744 | * @od: struct omap_device * |
644 | * | 745 | * |
@@ -692,11 +793,10 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od) | |||
692 | */ | 793 | */ |
693 | int omap_device_enable_hwmods(struct omap_device *od) | 794 | int omap_device_enable_hwmods(struct omap_device *od) |
694 | { | 795 | { |
695 | struct omap_hwmod *oh; | ||
696 | int i; | 796 | int i; |
697 | 797 | ||
698 | for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++) | 798 | for (i = 0; i < od->hwmods_cnt; i++) |
699 | omap_hwmod_enable(oh); | 799 | omap_hwmod_enable(od->hwmods[i]); |
700 | 800 | ||
701 | /* XXX pass along return value here? */ | 801 | /* XXX pass along return value here? */ |
702 | return 0; | 802 | return 0; |
@@ -710,11 +810,10 @@ int omap_device_enable_hwmods(struct omap_device *od) | |||
710 | */ | 810 | */ |
711 | int omap_device_idle_hwmods(struct omap_device *od) | 811 | int omap_device_idle_hwmods(struct omap_device *od) |
712 | { | 812 | { |
713 | struct omap_hwmod *oh; | ||
714 | int i; | 813 | int i; |
715 | 814 | ||
716 | for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++) | 815 | for (i = 0; i < od->hwmods_cnt; i++) |
717 | omap_hwmod_idle(oh); | 816 | omap_hwmod_idle(od->hwmods[i]); |
718 | 817 | ||
719 | /* XXX pass along return value here? */ | 818 | /* XXX pass along return value here? */ |
720 | return 0; | 819 | return 0; |
@@ -729,11 +828,10 @@ int omap_device_idle_hwmods(struct omap_device *od) | |||
729 | */ | 828 | */ |
730 | int omap_device_disable_clocks(struct omap_device *od) | 829 | int omap_device_disable_clocks(struct omap_device *od) |
731 | { | 830 | { |
732 | struct omap_hwmod *oh; | ||
733 | int i; | 831 | int i; |
734 | 832 | ||
735 | for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++) | 833 | for (i = 0; i < od->hwmods_cnt; i++) |
736 | omap_hwmod_disable_clocks(oh); | 834 | omap_hwmod_disable_clocks(od->hwmods[i]); |
737 | 835 | ||
738 | /* XXX pass along return value here? */ | 836 | /* XXX pass along return value here? */ |
739 | return 0; | 837 | return 0; |
@@ -748,12 +846,22 @@ int omap_device_disable_clocks(struct omap_device *od) | |||
748 | */ | 846 | */ |
749 | int omap_device_enable_clocks(struct omap_device *od) | 847 | int omap_device_enable_clocks(struct omap_device *od) |
750 | { | 848 | { |
751 | struct omap_hwmod *oh; | ||
752 | int i; | 849 | int i; |
753 | 850 | ||
754 | for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++) | 851 | for (i = 0; i < od->hwmods_cnt; i++) |
755 | omap_hwmod_enable_clocks(oh); | 852 | omap_hwmod_enable_clocks(od->hwmods[i]); |
756 | 853 | ||
757 | /* XXX pass along return value here? */ | 854 | /* XXX pass along return value here? */ |
758 | return 0; | 855 | return 0; |
759 | } | 856 | } |
857 | |||
858 | struct device omap_device_parent = { | ||
859 | .init_name = "omap", | ||
860 | .parent = &platform_bus, | ||
861 | }; | ||
862 | |||
863 | static int __init omap_device_init(void) | ||
864 | { | ||
865 | return device_register(&omap_device_parent); | ||
866 | } | ||
867 | core_initcall(omap_device_init); | ||