1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78#undef DEBUG
79
80#include <linux/kernel.h>
81#include <linux/platform_device.h>
82#include <linux/slab.h>
83#include <linux/err.h>
84#include <linux/io.h>
85#include <linux/clk.h>
86#include <linux/clkdev.h>
87#include <linux/pm_runtime.h>
88
89#include <plat/omap_device.h>
90#include <plat/omap_hwmod.h>
91#include <plat/clock.h>
92
93
94#define USE_WAKEUP_LAT 0
95#define IGNORE_WAKEUP_LAT 1
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113static int _omap_device_activate(struct omap_device *od, u8 ignore_lat)
114{
115 struct timespec a, b, c;
116
117 pr_debug("omap_device: %s: activating\n", od->pdev.name);
118
119 while (od->pm_lat_level > 0) {
120 struct omap_device_pm_latency *odpl;
121 unsigned long long act_lat = 0;
122
123 od->pm_lat_level--;
124
125 odpl = od->pm_lats + od->pm_lat_level;
126
127 if (!ignore_lat &&
128 (od->dev_wakeup_lat <= od->_dev_wakeup_lat_limit))
129 break;
130
131 read_persistent_clock(&a);
132
133
134 odpl->activate_func(od);
135
136 read_persistent_clock(&b);
137
138 c = timespec_sub(b, a);
139 act_lat = timespec_to_ns(&c);
140
141 pr_debug("omap_device: %s: pm_lat %d: activate: elapsed time "
142 "%llu nsec\n", od->pdev.name, od->pm_lat_level,
143 act_lat);
144
145 if (act_lat > odpl->activate_lat) {
146 odpl->activate_lat_worst = act_lat;
147 if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) {
148 odpl->activate_lat = act_lat;
149 pr_warning("omap_device: %s.%d: new worst case "
150 "activate latency %d: %llu\n",
151 od->pdev.name, od->pdev.id,
152 od->pm_lat_level, act_lat);
153 } else
154 pr_warning("omap_device: %s.%d: activate "
155 "latency %d higher than exptected. "
156 "(%llu > %d)\n",
157 od->pdev.name, od->pdev.id,
158 od->pm_lat_level, act_lat,
159 odpl->activate_lat);
160 }
161
162 od->dev_wakeup_lat -= odpl->activate_lat;
163 }
164
165 return 0;
166}
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat)
183{
184 struct timespec a, b, c;
185
186 pr_debug("omap_device: %s: deactivating\n", od->pdev.name);
187
188 while (od->pm_lat_level < od->pm_lats_cnt) {
189 struct omap_device_pm_latency *odpl;
190 unsigned long long deact_lat = 0;
191
192 odpl = od->pm_lats + od->pm_lat_level;
193
194 if (!ignore_lat &&
195 ((od->dev_wakeup_lat + odpl->activate_lat) >
196 od->_dev_wakeup_lat_limit))
197 break;
198
199 read_persistent_clock(&a);
200
201
202 odpl->deactivate_func(od);
203
204 read_persistent_clock(&b);
205
206 c = timespec_sub(b, a);
207 deact_lat = timespec_to_ns(&c);
208
209 pr_debug("omap_device: %s: pm_lat %d: deactivate: elapsed time "
210 "%llu nsec\n", od->pdev.name, od->pm_lat_level,
211 deact_lat);
212
213 if (deact_lat > odpl->deactivate_lat) {
214 odpl->deactivate_lat_worst = deact_lat;
215 if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) {
216 odpl->deactivate_lat = deact_lat;
217 pr_warning("omap_device: %s.%d: new worst case "
218 "deactivate latency %d: %llu\n",
219 od->pdev.name, od->pdev.id,
220 od->pm_lat_level, deact_lat);
221 } else
222 pr_warning("omap_device: %s.%d: deactivate "
223 "latency %d higher than exptected. "
224 "(%llu > %d)\n",
225 od->pdev.name, od->pdev.id,
226 od->pm_lat_level, deact_lat,
227 odpl->deactivate_lat);
228 }
229
230
231 od->dev_wakeup_lat += odpl->activate_lat;
232
233 od->pm_lat_level++;
234 }
235
236 return 0;
237}
238
239static inline struct omap_device *_find_by_pdev(struct platform_device *pdev)
240{
241 return container_of(pdev, struct omap_device, pdev);
242}
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260static 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;
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314u32 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
328
329
330
331
332
333
334
335
336int omap_device_count_resources(struct omap_device *od)
337{
338 int c = 0;
339 int i;
340
341 for (i = 0; i < od->hwmods_cnt; i++)
342 c += omap_hwmod_count_resources(od->hwmods[i]);
343
344 pr_debug("omap_device: %s: counted %d total resources across %d "
345 "hwmods\n", od->pdev.name, c, od->hwmods_cnt);
346
347 return c;
348}
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367int omap_device_fill_resources(struct omap_device *od, struct resource *res)
368{
369 int c = 0;
370 int i, r;
371
372 for (i = 0; i < od->hwmods_cnt; i++) {
373 r = omap_hwmod_fill_resources(od->hwmods[i], res);
374 res += r;
375 c += r;
376 }
377
378 return 0;
379}
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398struct omap_device *omap_device_build(const char *pdev_name, int pdev_id,
399 struct omap_hwmod *oh, void *pdata,
400 int pdata_len,
401 struct omap_device_pm_latency *pm_lats,
402 int pm_lats_cnt, int is_early_device)
403{
404 struct omap_hwmod *ohs[] = { oh };
405
406 if (!oh)
407 return ERR_PTR(-EINVAL);
408
409 return omap_device_build_ss(pdev_name, pdev_id, ohs, 1, pdata,
410 pdata_len, pm_lats, pm_lats_cnt,
411 is_early_device);
412}
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
432 struct omap_hwmod **ohs, int oh_cnt,
433 void *pdata, int pdata_len,
434 struct omap_device_pm_latency *pm_lats,
435 int pm_lats_cnt, int is_early_device)
436{
437 int ret = -ENOMEM;
438 struct omap_device *od;
439 char *pdev_name2;
440 struct resource *res = NULL;
441 int i, res_count;
442 struct omap_hwmod **hwmods;
443
444 if (!ohs || oh_cnt == 0 || !pdev_name)
445 return ERR_PTR(-EINVAL);
446
447 if (!pdata && pdata_len > 0)
448 return ERR_PTR(-EINVAL);
449
450 pr_debug("omap_device: %s: building with %d hwmods\n", pdev_name,
451 oh_cnt);
452
453 od = kzalloc(sizeof(struct omap_device), GFP_KERNEL);
454 if (!od)
455 return ERR_PTR(-ENOMEM);
456
457 od->hwmods_cnt = oh_cnt;
458
459 hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt,
460 GFP_KERNEL);
461 if (!hwmods)
462 goto odbs_exit1;
463
464 memcpy(hwmods, ohs, sizeof(struct omap_hwmod *) * oh_cnt);
465 od->hwmods = hwmods;
466
467 pdev_name2 = kzalloc(strlen(pdev_name) + 1, GFP_KERNEL);
468 if (!pdev_name2)
469 goto odbs_exit2;
470 strcpy(pdev_name2, pdev_name);
471
472 od->pdev.name = pdev_name2;
473 od->pdev.id = pdev_id;
474
475 res_count = omap_device_count_resources(od);
476 if (res_count > 0) {
477 res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL);
478 if (!res)
479 goto odbs_exit3;
480 }
481 omap_device_fill_resources(od, res);
482
483 od->pdev.num_resources = res_count;
484 od->pdev.resource = res;
485
486 ret = platform_device_add_data(&od->pdev, pdata, pdata_len);
487 if (ret)
488 goto odbs_exit4;
489
490 od->pm_lats = pm_lats;
491 od->pm_lats_cnt = pm_lats_cnt;
492
493 if (is_early_device)
494 ret = omap_early_device_register(od);
495 else
496 ret = omap_device_register(od);
497
498 for (i = 0; i < oh_cnt; i++) {
499 hwmods[i]->od = od;
500 _add_optional_clock_clkdev(od, hwmods[i]);
501 }
502
503 if (ret)
504 goto odbs_exit4;
505
506 return od;
507
508odbs_exit4:
509 kfree(res);
510odbs_exit3:
511 kfree(pdev_name2);
512odbs_exit2:
513 kfree(hwmods);
514odbs_exit1:
515 kfree(od);
516
517 pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret);
518
519 return ERR_PTR(ret);
520}
521
522
523
524
525
526
527
528
529
530
531int omap_early_device_register(struct omap_device *od)
532{
533 struct platform_device *devices[1];
534
535 devices[0] = &(od->pdev);
536 early_platform_add_devices(devices, 1);
537 return 0;
538}
539
540static 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
553static int _od_runtime_idle(struct device *dev)
554{
555 return pm_generic_runtime_idle(dev);
556}
557
558static 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
567static 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
576
577
578
579
580
581
582
583
584int omap_device_register(struct omap_device *od)
585{
586 pr_debug("omap_device: %s: registering\n", od->pdev.name);
587
588 od->pdev.dev.parent = &omap_device_parent;
589 od->pdev.dev.pwr_domain = &omap_device_power_domain;
590 return platform_device_register(&od->pdev);
591}
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609int omap_device_enable(struct platform_device *pdev)
610{
611 int ret;
612 struct omap_device *od;
613
614 od = _find_by_pdev(pdev);
615
616 if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
617 WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n",
618 od->pdev.name, od->pdev.id, __func__, od->_state);
619 return -EINVAL;
620 }
621
622
623 if (od->_state == OMAP_DEVICE_STATE_UNKNOWN)
624 od->pm_lat_level = od->pm_lats_cnt;
625
626 ret = _omap_device_activate(od, IGNORE_WAKEUP_LAT);
627
628 od->dev_wakeup_lat = 0;
629 od->_dev_wakeup_lat_limit = UINT_MAX;
630 od->_state = OMAP_DEVICE_STATE_ENABLED;
631
632 return ret;
633}
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648int omap_device_idle(struct platform_device *pdev)
649{
650 int ret;
651 struct omap_device *od;
652
653 od = _find_by_pdev(pdev);
654
655 if (od->_state != OMAP_DEVICE_STATE_ENABLED) {
656 WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n",
657 od->pdev.name, od->pdev.id, __func__, od->_state);
658 return -EINVAL;
659 }
660
661 ret = _omap_device_deactivate(od, USE_WAKEUP_LAT);
662
663 od->_state = OMAP_DEVICE_STATE_IDLE;
664
665 return ret;
666}
667
668
669
670
671
672
673
674
675
676
677
678
679int omap_device_shutdown(struct platform_device *pdev)
680{
681 int ret, i;
682 struct omap_device *od;
683
684 od = _find_by_pdev(pdev);
685
686 if (od->_state != OMAP_DEVICE_STATE_ENABLED &&
687 od->_state != OMAP_DEVICE_STATE_IDLE) {
688 WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n",
689 od->pdev.name, od->pdev.id, __func__, od->_state);
690 return -EINVAL;
691 }
692
693 ret = _omap_device_deactivate(od, IGNORE_WAKEUP_LAT);
694
695 for (i = 0; i < od->hwmods_cnt; i++)
696 omap_hwmod_shutdown(od->hwmods[i]);
697
698 od->_state = OMAP_DEVICE_STATE_SHUTDOWN;
699
700 return ret;
701}
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719int omap_device_align_pm_lat(struct platform_device *pdev,
720 u32 new_wakeup_lat_limit)
721{
722 int ret = -EINVAL;
723 struct omap_device *od;
724
725 od = _find_by_pdev(pdev);
726
727 if (new_wakeup_lat_limit == od->dev_wakeup_lat)
728 return 0;
729
730 od->_dev_wakeup_lat_limit = new_wakeup_lat_limit;
731
732 if (od->_state != OMAP_DEVICE_STATE_IDLE)
733 return 0;
734 else if (new_wakeup_lat_limit > od->dev_wakeup_lat)
735 ret = _omap_device_deactivate(od, USE_WAKEUP_LAT);
736 else if (new_wakeup_lat_limit < od->dev_wakeup_lat)
737 ret = _omap_device_activate(od, USE_WAKEUP_LAT);
738
739 return ret;
740}
741
742
743
744
745
746
747
748
749
750
751struct powerdomain *omap_device_get_pwrdm(struct omap_device *od)
752{
753
754
755
756
757
758 if (!od->hwmods_cnt)
759 return NULL;
760
761 return omap_hwmod_get_pwrdm(od->hwmods[0]);
762}
763
764
765
766
767
768
769
770
771
772
773
774
775void __iomem *omap_device_get_rt_va(struct omap_device *od)
776{
777 if (od->hwmods_cnt != 1)
778 return NULL;
779
780 return omap_hwmod_get_mpu_rt_va(od->hwmods[0]);
781}
782
783
784
785
786
787
788
789
790
791
792
793
794int omap_device_enable_hwmods(struct omap_device *od)
795{
796 int i;
797
798 for (i = 0; i < od->hwmods_cnt; i++)
799 omap_hwmod_enable(od->hwmods[i]);
800
801
802 return 0;
803}
804
805
806
807
808
809
810
811int omap_device_idle_hwmods(struct omap_device *od)
812{
813 int i;
814
815 for (i = 0; i < od->hwmods_cnt; i++)
816 omap_hwmod_idle(od->hwmods[i]);
817
818
819 return 0;
820}
821
822
823
824
825
826
827
828
829int omap_device_disable_clocks(struct omap_device *od)
830{
831 int i;
832
833 for (i = 0; i < od->hwmods_cnt; i++)
834 omap_hwmod_disable_clocks(od->hwmods[i]);
835
836
837 return 0;
838}
839
840
841
842
843
844
845
846
847int omap_device_enable_clocks(struct omap_device *od)
848{
849 int i;
850
851 for (i = 0; i < od->hwmods_cnt; i++)
852 omap_hwmod_enable_clocks(od->hwmods[i]);
853
854
855 return 0;
856}
857
858struct device omap_device_parent = {
859 .init_name = "omap",
860 .parent = &platform_bus,
861};
862
863static int __init omap_device_init(void)
864{
865 return device_register(&omap_device_parent);
866}
867core_initcall(omap_device_init);
868