diff options
Diffstat (limited to 'arch/arm/mach-omap2/prm44xx.c')
-rw-r--r-- | arch/arm/mach-omap2/prm44xx.c | 391 |
1 files changed, 384 insertions, 7 deletions
diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index f0c4d5f4a174..7498bc77fe8b 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c | |||
@@ -1,10 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * OMAP4 PRM module functions | 2 | * OMAP4 PRM module functions |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Texas Instruments, Inc. | 4 | * Copyright (C) 2011-2012 Texas Instruments, Inc. |
5 | * Copyright (C) 2010 Nokia Corporation | 5 | * Copyright (C) 2010 Nokia Corporation |
6 | * Benoît Cousson | 6 | * Benoît Cousson |
7 | * Paul Walmsley | 7 | * Paul Walmsley |
8 | * Rajendra Nayak <rnayak@ti.com> | ||
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
@@ -17,7 +18,6 @@ | |||
17 | #include <linux/err.h> | 18 | #include <linux/err.h> |
18 | #include <linux/io.h> | 19 | #include <linux/io.h> |
19 | 20 | ||
20 | #include <plat/prcm.h> | ||
21 | 21 | ||
22 | #include "soc.h" | 22 | #include "soc.h" |
23 | #include "iomap.h" | 23 | #include "iomap.h" |
@@ -27,6 +27,9 @@ | |||
27 | #include "prm-regbits-44xx.h" | 27 | #include "prm-regbits-44xx.h" |
28 | #include "prcm44xx.h" | 28 | #include "prcm44xx.h" |
29 | #include "prminst44xx.h" | 29 | #include "prminst44xx.h" |
30 | #include "powerdomain.h" | ||
31 | |||
32 | /* Static data */ | ||
30 | 33 | ||
31 | static const struct omap_prcm_irq omap4_prcm_irqs[] = { | 34 | static const struct omap_prcm_irq omap4_prcm_irqs[] = { |
32 | OMAP_PRCM_IRQ("wkup", 0, 0), | 35 | OMAP_PRCM_IRQ("wkup", 0, 0), |
@@ -46,6 +49,33 @@ static struct omap_prcm_irq_setup omap4_prcm_irq_setup = { | |||
46 | .restore_irqen = &omap44xx_prm_restore_irqen, | 49 | .restore_irqen = &omap44xx_prm_restore_irqen, |
47 | }; | 50 | }; |
48 | 51 | ||
52 | /* | ||
53 | * omap44xx_prm_reset_src_map - map from bits in the PRM_RSTST | ||
54 | * hardware register (which are specific to OMAP44xx SoCs) to reset | ||
55 | * source ID bit shifts (which is an OMAP SoC-independent | ||
56 | * enumeration) | ||
57 | */ | ||
58 | static struct prm_reset_src_map omap44xx_prm_reset_src_map[] = { | ||
59 | { OMAP4430_RST_GLOBAL_WARM_SW_SHIFT, | ||
60 | OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT }, | ||
61 | { OMAP4430_RST_GLOBAL_COLD_SW_SHIFT, | ||
62 | OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT }, | ||
63 | { OMAP4430_MPU_SECURITY_VIOL_RST_SHIFT, | ||
64 | OMAP_SECU_VIOL_RST_SRC_ID_SHIFT }, | ||
65 | { OMAP4430_MPU_WDT_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT }, | ||
66 | { OMAP4430_SECURE_WDT_RST_SHIFT, OMAP_SECU_WD_RST_SRC_ID_SHIFT }, | ||
67 | { OMAP4430_EXTERNAL_WARM_RST_SHIFT, OMAP_EXTWARM_RST_SRC_ID_SHIFT }, | ||
68 | { OMAP4430_VDD_MPU_VOLT_MGR_RST_SHIFT, | ||
69 | OMAP_VDD_MPU_VM_RST_SRC_ID_SHIFT }, | ||
70 | { OMAP4430_VDD_IVA_VOLT_MGR_RST_SHIFT, | ||
71 | OMAP_VDD_IVA_VM_RST_SRC_ID_SHIFT }, | ||
72 | { OMAP4430_VDD_CORE_VOLT_MGR_RST_SHIFT, | ||
73 | OMAP_VDD_CORE_VM_RST_SRC_ID_SHIFT }, | ||
74 | { OMAP4430_ICEPICK_RST_SHIFT, OMAP_ICEPICK_RST_SRC_ID_SHIFT }, | ||
75 | { OMAP4430_C2C_RST_SHIFT, OMAP_C2C_RST_SRC_ID_SHIFT }, | ||
76 | { -1, -1 }, | ||
77 | }; | ||
78 | |||
49 | /* PRM low-level functions */ | 79 | /* PRM low-level functions */ |
50 | 80 | ||
51 | /* Read a register in a CM/PRM instance in the PRM module */ | 81 | /* Read a register in a CM/PRM instance in the PRM module */ |
@@ -291,12 +321,359 @@ static void __init omap44xx_prm_enable_io_wakeup(void) | |||
291 | OMAP4_PRM_IO_PMCTRL_OFFSET); | 321 | OMAP4_PRM_IO_PMCTRL_OFFSET); |
292 | } | 322 | } |
293 | 323 | ||
294 | static int __init omap4xxx_prcm_init(void) | 324 | /** |
325 | * omap44xx_prm_read_reset_sources - return the last SoC reset source | ||
326 | * | ||
327 | * Return a u32 representing the last reset sources of the SoC. The | ||
328 | * returned reset source bits are standardized across OMAP SoCs. | ||
329 | */ | ||
330 | static u32 omap44xx_prm_read_reset_sources(void) | ||
331 | { | ||
332 | struct prm_reset_src_map *p; | ||
333 | u32 r = 0; | ||
334 | u32 v; | ||
335 | |||
336 | v = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, | ||
337 | OMAP4_RM_RSTST); | ||
338 | |||
339 | p = omap44xx_prm_reset_src_map; | ||
340 | while (p->reg_shift >= 0 && p->std_shift >= 0) { | ||
341 | if (v & (1 << p->reg_shift)) | ||
342 | r |= 1 << p->std_shift; | ||
343 | p++; | ||
344 | } | ||
345 | |||
346 | return r; | ||
347 | } | ||
348 | |||
349 | /** | ||
350 | * omap44xx_prm_was_any_context_lost_old - was module hardware context lost? | ||
351 | * @part: PRM partition ID (e.g., OMAP4430_PRM_PARTITION) | ||
352 | * @inst: PRM instance offset (e.g., OMAP4430_PRM_MPU_INST) | ||
353 | * @idx: CONTEXT register offset | ||
354 | * | ||
355 | * Return 1 if any bits were set in the *_CONTEXT_* register | ||
356 | * identified by (@part, @inst, @idx), which means that some context | ||
357 | * was lost for that module; otherwise, return 0. | ||
358 | */ | ||
359 | static bool omap44xx_prm_was_any_context_lost_old(u8 part, s16 inst, u16 idx) | ||
360 | { | ||
361 | return (omap4_prminst_read_inst_reg(part, inst, idx)) ? 1 : 0; | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * omap44xx_prm_clear_context_lost_flags_old - clear context loss flags | ||
366 | * @part: PRM partition ID (e.g., OMAP4430_PRM_PARTITION) | ||
367 | * @inst: PRM instance offset (e.g., OMAP4430_PRM_MPU_INST) | ||
368 | * @idx: CONTEXT register offset | ||
369 | * | ||
370 | * Clear hardware context loss bits for the module identified by | ||
371 | * (@part, @inst, @idx). No return value. XXX Writes to reserved bits; | ||
372 | * is there a way to avoid this? | ||
373 | */ | ||
374 | static void omap44xx_prm_clear_context_loss_flags_old(u8 part, s16 inst, | ||
375 | u16 idx) | ||
376 | { | ||
377 | omap4_prminst_write_inst_reg(0xffffffff, part, inst, idx); | ||
378 | } | ||
379 | |||
380 | /* Powerdomain low-level functions */ | ||
381 | |||
382 | static int omap4_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) | ||
383 | { | ||
384 | omap4_prminst_rmw_inst_reg_bits(OMAP_POWERSTATE_MASK, | ||
385 | (pwrst << OMAP_POWERSTATE_SHIFT), | ||
386 | pwrdm->prcm_partition, | ||
387 | pwrdm->prcm_offs, OMAP4_PM_PWSTCTRL); | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | static int omap4_pwrdm_read_next_pwrst(struct powerdomain *pwrdm) | ||
392 | { | ||
393 | u32 v; | ||
394 | |||
395 | v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
396 | OMAP4_PM_PWSTCTRL); | ||
397 | v &= OMAP_POWERSTATE_MASK; | ||
398 | v >>= OMAP_POWERSTATE_SHIFT; | ||
399 | |||
400 | return v; | ||
401 | } | ||
402 | |||
403 | static int omap4_pwrdm_read_pwrst(struct powerdomain *pwrdm) | ||
404 | { | ||
405 | u32 v; | ||
406 | |||
407 | v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
408 | OMAP4_PM_PWSTST); | ||
409 | v &= OMAP_POWERSTATEST_MASK; | ||
410 | v >>= OMAP_POWERSTATEST_SHIFT; | ||
411 | |||
412 | return v; | ||
413 | } | ||
414 | |||
415 | static int omap4_pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) | ||
416 | { | ||
417 | u32 v; | ||
418 | |||
419 | v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
420 | OMAP4_PM_PWSTST); | ||
421 | v &= OMAP4430_LASTPOWERSTATEENTERED_MASK; | ||
422 | v >>= OMAP4430_LASTPOWERSTATEENTERED_SHIFT; | ||
423 | |||
424 | return v; | ||
425 | } | ||
426 | |||
427 | static int omap4_pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm) | ||
428 | { | ||
429 | omap4_prminst_rmw_inst_reg_bits(OMAP4430_LOWPOWERSTATECHANGE_MASK, | ||
430 | (1 << OMAP4430_LOWPOWERSTATECHANGE_SHIFT), | ||
431 | pwrdm->prcm_partition, | ||
432 | pwrdm->prcm_offs, OMAP4_PM_PWSTCTRL); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static int omap4_pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) | ||
437 | { | ||
438 | omap4_prminst_rmw_inst_reg_bits(OMAP4430_LASTPOWERSTATEENTERED_MASK, | ||
439 | OMAP4430_LASTPOWERSTATEENTERED_MASK, | ||
440 | pwrdm->prcm_partition, | ||
441 | pwrdm->prcm_offs, OMAP4_PM_PWSTST); | ||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static int omap4_pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) | ||
446 | { | ||
447 | u32 v; | ||
448 | |||
449 | v = pwrst << __ffs(OMAP4430_LOGICRETSTATE_MASK); | ||
450 | omap4_prminst_rmw_inst_reg_bits(OMAP4430_LOGICRETSTATE_MASK, v, | ||
451 | pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
452 | OMAP4_PM_PWSTCTRL); | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static int omap4_pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, | ||
458 | u8 pwrst) | ||
459 | { | ||
460 | u32 m; | ||
461 | |||
462 | m = omap2_pwrdm_get_mem_bank_onstate_mask(bank); | ||
463 | |||
464 | omap4_prminst_rmw_inst_reg_bits(m, (pwrst << __ffs(m)), | ||
465 | pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
466 | OMAP4_PM_PWSTCTRL); | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static int omap4_pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, | ||
472 | u8 pwrst) | ||
295 | { | 473 | { |
296 | if (cpu_is_omap44xx()) { | 474 | u32 m; |
297 | omap44xx_prm_enable_io_wakeup(); | 475 | |
298 | return omap_prcm_register_chain_handler(&omap4_prcm_irq_setup); | 476 | m = omap2_pwrdm_get_mem_bank_retst_mask(bank); |
477 | |||
478 | omap4_prminst_rmw_inst_reg_bits(m, (pwrst << __ffs(m)), | ||
479 | pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
480 | OMAP4_PM_PWSTCTRL); | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int omap4_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) | ||
486 | { | ||
487 | u32 v; | ||
488 | |||
489 | v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
490 | OMAP4_PM_PWSTST); | ||
491 | v &= OMAP4430_LOGICSTATEST_MASK; | ||
492 | v >>= OMAP4430_LOGICSTATEST_SHIFT; | ||
493 | |||
494 | return v; | ||
495 | } | ||
496 | |||
497 | static int omap4_pwrdm_read_logic_retst(struct powerdomain *pwrdm) | ||
498 | { | ||
499 | u32 v; | ||
500 | |||
501 | v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
502 | OMAP4_PM_PWSTCTRL); | ||
503 | v &= OMAP4430_LOGICRETSTATE_MASK; | ||
504 | v >>= OMAP4430_LOGICRETSTATE_SHIFT; | ||
505 | |||
506 | return v; | ||
507 | } | ||
508 | |||
509 | /** | ||
510 | * omap4_pwrdm_read_prev_logic_pwrst - read the previous logic powerstate | ||
511 | * @pwrdm: struct powerdomain * to read the state for | ||
512 | * | ||
513 | * Reads the previous logic powerstate for a powerdomain. This | ||
514 | * function must determine the previous logic powerstate by first | ||
515 | * checking the previous powerstate for the domain. If that was OFF, | ||
516 | * then logic has been lost. If previous state was RETENTION, the | ||
517 | * function reads the setting for the next retention logic state to | ||
518 | * see the actual value. In every other case, the logic is | ||
519 | * retained. Returns either PWRDM_POWER_OFF or PWRDM_POWER_RET | ||
520 | * depending whether the logic was retained or not. | ||
521 | */ | ||
522 | static int omap4_pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) | ||
523 | { | ||
524 | int state; | ||
525 | |||
526 | state = omap4_pwrdm_read_prev_pwrst(pwrdm); | ||
527 | |||
528 | if (state == PWRDM_POWER_OFF) | ||
529 | return PWRDM_POWER_OFF; | ||
530 | |||
531 | if (state != PWRDM_POWER_RET) | ||
532 | return PWRDM_POWER_RET; | ||
533 | |||
534 | return omap4_pwrdm_read_logic_retst(pwrdm); | ||
535 | } | ||
536 | |||
537 | static int omap4_pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | ||
538 | { | ||
539 | u32 m, v; | ||
540 | |||
541 | m = omap2_pwrdm_get_mem_bank_stst_mask(bank); | ||
542 | |||
543 | v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
544 | OMAP4_PM_PWSTST); | ||
545 | v &= m; | ||
546 | v >>= __ffs(m); | ||
547 | |||
548 | return v; | ||
549 | } | ||
550 | |||
551 | static int omap4_pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank) | ||
552 | { | ||
553 | u32 m, v; | ||
554 | |||
555 | m = omap2_pwrdm_get_mem_bank_retst_mask(bank); | ||
556 | |||
557 | v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, | ||
558 | OMAP4_PM_PWSTCTRL); | ||
559 | v &= m; | ||
560 | v >>= __ffs(m); | ||
561 | |||
562 | return v; | ||
563 | } | ||
564 | |||
565 | /** | ||
566 | * omap4_pwrdm_read_prev_mem_pwrst - reads the previous memory powerstate | ||
567 | * @pwrdm: struct powerdomain * to read mem powerstate for | ||
568 | * @bank: memory bank index | ||
569 | * | ||
570 | * Reads the previous memory powerstate for a powerdomain. This | ||
571 | * function must determine the previous memory powerstate by first | ||
572 | * checking the previous powerstate for the domain. If that was OFF, | ||
573 | * then logic has been lost. If previous state was RETENTION, the | ||
574 | * function reads the setting for the next memory retention state to | ||
575 | * see the actual value. In every other case, the logic is | ||
576 | * retained. Returns either PWRDM_POWER_OFF or PWRDM_POWER_RET | ||
577 | * depending whether logic was retained or not. | ||
578 | */ | ||
579 | static int omap4_pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | ||
580 | { | ||
581 | int state; | ||
582 | |||
583 | state = omap4_pwrdm_read_prev_pwrst(pwrdm); | ||
584 | |||
585 | if (state == PWRDM_POWER_OFF) | ||
586 | return PWRDM_POWER_OFF; | ||
587 | |||
588 | if (state != PWRDM_POWER_RET) | ||
589 | return PWRDM_POWER_RET; | ||
590 | |||
591 | return omap4_pwrdm_read_mem_retst(pwrdm, bank); | ||
592 | } | ||
593 | |||
594 | static int omap4_pwrdm_wait_transition(struct powerdomain *pwrdm) | ||
595 | { | ||
596 | u32 c = 0; | ||
597 | |||
598 | /* | ||
599 | * REVISIT: pwrdm_wait_transition() may be better implemented | ||
600 | * via a callback and a periodic timer check -- how long do we expect | ||
601 | * powerdomain transitions to take? | ||
602 | */ | ||
603 | |||
604 | /* XXX Is this udelay() value meaningful? */ | ||
605 | while ((omap4_prminst_read_inst_reg(pwrdm->prcm_partition, | ||
606 | pwrdm->prcm_offs, | ||
607 | OMAP4_PM_PWSTST) & | ||
608 | OMAP_INTRANSITION_MASK) && | ||
609 | (c++ < PWRDM_TRANSITION_BAILOUT)) | ||
610 | udelay(1); | ||
611 | |||
612 | if (c > PWRDM_TRANSITION_BAILOUT) { | ||
613 | pr_err("powerdomain: %s: waited too long to complete transition\n", | ||
614 | pwrdm->name); | ||
615 | return -EAGAIN; | ||
299 | } | 616 | } |
617 | |||
618 | pr_debug("powerdomain: completed transition in %d loops\n", c); | ||
619 | |||
300 | return 0; | 620 | return 0; |
301 | } | 621 | } |
302 | subsys_initcall(omap4xxx_prcm_init); | 622 | |
623 | struct pwrdm_ops omap4_pwrdm_operations = { | ||
624 | .pwrdm_set_next_pwrst = omap4_pwrdm_set_next_pwrst, | ||
625 | .pwrdm_read_next_pwrst = omap4_pwrdm_read_next_pwrst, | ||
626 | .pwrdm_read_pwrst = omap4_pwrdm_read_pwrst, | ||
627 | .pwrdm_read_prev_pwrst = omap4_pwrdm_read_prev_pwrst, | ||
628 | .pwrdm_set_lowpwrstchange = omap4_pwrdm_set_lowpwrstchange, | ||
629 | .pwrdm_clear_all_prev_pwrst = omap4_pwrdm_clear_all_prev_pwrst, | ||
630 | .pwrdm_set_logic_retst = omap4_pwrdm_set_logic_retst, | ||
631 | .pwrdm_read_logic_pwrst = omap4_pwrdm_read_logic_pwrst, | ||
632 | .pwrdm_read_prev_logic_pwrst = omap4_pwrdm_read_prev_logic_pwrst, | ||
633 | .pwrdm_read_logic_retst = omap4_pwrdm_read_logic_retst, | ||
634 | .pwrdm_read_mem_pwrst = omap4_pwrdm_read_mem_pwrst, | ||
635 | .pwrdm_read_mem_retst = omap4_pwrdm_read_mem_retst, | ||
636 | .pwrdm_read_prev_mem_pwrst = omap4_pwrdm_read_prev_mem_pwrst, | ||
637 | .pwrdm_set_mem_onst = omap4_pwrdm_set_mem_onst, | ||
638 | .pwrdm_set_mem_retst = omap4_pwrdm_set_mem_retst, | ||
639 | .pwrdm_wait_transition = omap4_pwrdm_wait_transition, | ||
640 | }; | ||
641 | |||
642 | /* | ||
643 | * XXX document | ||
644 | */ | ||
645 | static struct prm_ll_data omap44xx_prm_ll_data = { | ||
646 | .read_reset_sources = &omap44xx_prm_read_reset_sources, | ||
647 | .was_any_context_lost_old = &omap44xx_prm_was_any_context_lost_old, | ||
648 | .clear_context_loss_flags_old = &omap44xx_prm_clear_context_loss_flags_old, | ||
649 | }; | ||
650 | |||
651 | int __init omap44xx_prm_init(void) | ||
652 | { | ||
653 | if (!cpu_is_omap44xx()) | ||
654 | return 0; | ||
655 | |||
656 | return prm_register(&omap44xx_prm_ll_data); | ||
657 | } | ||
658 | |||
659 | static int __init omap44xx_prm_late_init(void) | ||
660 | { | ||
661 | if (!cpu_is_omap44xx()) | ||
662 | return 0; | ||
663 | |||
664 | omap44xx_prm_enable_io_wakeup(); | ||
665 | |||
666 | return omap_prcm_register_chain_handler(&omap4_prcm_irq_setup); | ||
667 | } | ||
668 | subsys_initcall(omap44xx_prm_late_init); | ||
669 | |||
670 | static void __exit omap44xx_prm_exit(void) | ||
671 | { | ||
672 | if (!cpu_is_omap44xx()) | ||
673 | return; | ||
674 | |||
675 | /* Should never happen */ | ||
676 | WARN(prm_unregister(&omap44xx_prm_ll_data), | ||
677 | "%s: prm_ll_data function pointer mismatch\n", __func__); | ||
678 | } | ||
679 | __exitcall(omap44xx_prm_exit); | ||