diff options
author | Mattias Nilsson <mattias.i.nilsson@stericsson.com> | 2012-01-13 10:20:43 -0500 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2012-03-06 12:46:33 -0500 |
commit | 4d64d2e34bc415b05eb77a2732a3164313cf6de3 (patch) | |
tree | f4331b9e59754a52f83cfc79b6ca1547861a1a89 | |
parent | 992b133a5d85ced4ff0fbdab22e9196cf571e0c9 (diff) |
mfd: db8500 OPP and sleep handling update
This updates the operating point handling code by:
- Supporting the DDR OPP retention state.
- Supporting another low operating point named
APE_50_PARTLY_25_OPP
- Adding an interface to figure out if the sleep state change
was properly achieved.
Signed-off-by: Shreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com>
Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com>
Signed-off-by: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r-- | drivers/mfd/db8500-prcmu.c | 73 | ||||
-rw-r--r-- | include/linux/mfd/db8500-prcmu.h | 25 | ||||
-rw-r--r-- | include/linux/mfd/dbx500-prcmu.h | 15 |
3 files changed, 110 insertions, 3 deletions
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 13856392cb27..8056968da20f 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c | |||
@@ -343,11 +343,13 @@ static struct { | |||
343 | * mb1_transfer - state needed for mailbox 1 communication. | 343 | * mb1_transfer - state needed for mailbox 1 communication. |
344 | * @lock: The transaction lock. | 344 | * @lock: The transaction lock. |
345 | * @work: The transaction completion structure. | 345 | * @work: The transaction completion structure. |
346 | * @ape_opp: The current APE OPP. | ||
346 | * @ack: Reply ("acknowledge") data. | 347 | * @ack: Reply ("acknowledge") data. |
347 | */ | 348 | */ |
348 | static struct { | 349 | static struct { |
349 | struct mutex lock; | 350 | struct mutex lock; |
350 | struct completion work; | 351 | struct completion work; |
352 | u8 ape_opp; | ||
351 | struct { | 353 | struct { |
352 | u8 header; | 354 | u8 header; |
353 | u8 arm_opp; | 355 | u8 arm_opp; |
@@ -816,6 +818,11 @@ int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) | |||
816 | return 0; | 818 | return 0; |
817 | } | 819 | } |
818 | 820 | ||
821 | u8 db8500_prcmu_get_power_state_result(void) | ||
822 | { | ||
823 | return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS); | ||
824 | } | ||
825 | |||
819 | /* This function should only be called while mb0_transfer.lock is held. */ | 826 | /* This function should only be called while mb0_transfer.lock is held. */ |
820 | static void config_wakeups(void) | 827 | static void config_wakeups(void) |
821 | { | 828 | { |
@@ -965,6 +972,52 @@ int db8500_prcmu_set_ddr_opp(u8 opp) | |||
965 | return 0; | 972 | return 0; |
966 | } | 973 | } |
967 | 974 | ||
975 | /* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */ | ||
976 | static void request_even_slower_clocks(bool enable) | ||
977 | { | ||
978 | void __iomem *clock_reg[] = { | ||
979 | PRCM_ACLK_MGT, | ||
980 | PRCM_DMACLK_MGT | ||
981 | }; | ||
982 | unsigned long flags; | ||
983 | unsigned int i; | ||
984 | |||
985 | spin_lock_irqsave(&clk_mgt_lock, flags); | ||
986 | |||
987 | /* Grab the HW semaphore. */ | ||
988 | while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) | ||
989 | cpu_relax(); | ||
990 | |||
991 | for (i = 0; i < ARRAY_SIZE(clock_reg); i++) { | ||
992 | u32 val; | ||
993 | u32 div; | ||
994 | |||
995 | val = readl(clock_reg[i]); | ||
996 | div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK); | ||
997 | if (enable) { | ||
998 | if ((div <= 1) || (div > 15)) { | ||
999 | pr_err("prcmu: Bad clock divider %d in %s\n", | ||
1000 | div, __func__); | ||
1001 | goto unlock_and_return; | ||
1002 | } | ||
1003 | div <<= 1; | ||
1004 | } else { | ||
1005 | if (div <= 2) | ||
1006 | goto unlock_and_return; | ||
1007 | div >>= 1; | ||
1008 | } | ||
1009 | val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) | | ||
1010 | (div & PRCM_CLK_MGT_CLKPLLDIV_MASK)); | ||
1011 | writel(val, clock_reg[i]); | ||
1012 | } | ||
1013 | |||
1014 | unlock_and_return: | ||
1015 | /* Release the HW semaphore. */ | ||
1016 | writel(0, PRCM_SEM); | ||
1017 | |||
1018 | spin_unlock_irqrestore(&clk_mgt_lock, flags); | ||
1019 | } | ||
1020 | |||
968 | /** | 1021 | /** |
969 | * db8500_set_ape_opp - set the appropriate APE OPP | 1022 | * db8500_set_ape_opp - set the appropriate APE OPP |
970 | * @opp: The new APE operating point to which transition is to be made | 1023 | * @opp: The new APE operating point to which transition is to be made |
@@ -976,14 +1029,24 @@ int db8500_prcmu_set_ape_opp(u8 opp) | |||
976 | { | 1029 | { |
977 | int r = 0; | 1030 | int r = 0; |
978 | 1031 | ||
1032 | if (opp == mb1_transfer.ape_opp) | ||
1033 | return 0; | ||
1034 | |||
979 | mutex_lock(&mb1_transfer.lock); | 1035 | mutex_lock(&mb1_transfer.lock); |
980 | 1036 | ||
1037 | if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP) | ||
1038 | request_even_slower_clocks(false); | ||
1039 | |||
1040 | if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP)) | ||
1041 | goto skip_message; | ||
1042 | |||
981 | while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) | 1043 | while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) |
982 | cpu_relax(); | 1044 | cpu_relax(); |
983 | 1045 | ||
984 | writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); | 1046 | writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); |
985 | writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); | 1047 | writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); |
986 | writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP)); | 1048 | writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp), |
1049 | (tcdm_base + PRCM_REQ_MB1_APE_OPP)); | ||
987 | 1050 | ||
988 | writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); | 1051 | writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); |
989 | wait_for_completion(&mb1_transfer.work); | 1052 | wait_for_completion(&mb1_transfer.work); |
@@ -992,6 +1055,13 @@ int db8500_prcmu_set_ape_opp(u8 opp) | |||
992 | (mb1_transfer.ack.ape_opp != opp)) | 1055 | (mb1_transfer.ack.ape_opp != opp)) |
993 | r = -EIO; | 1056 | r = -EIO; |
994 | 1057 | ||
1058 | skip_message: | ||
1059 | if ((!r && (opp == APE_50_PARTLY_25_OPP)) || | ||
1060 | (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP))) | ||
1061 | request_even_slower_clocks(true); | ||
1062 | if (!r) | ||
1063 | mb1_transfer.ape_opp = opp; | ||
1064 | |||
995 | mutex_unlock(&mb1_transfer.lock); | 1065 | mutex_unlock(&mb1_transfer.lock); |
996 | 1066 | ||
997 | return r; | 1067 | return r; |
@@ -2631,6 +2701,7 @@ void __init db8500_prcmu_early_init(void) | |||
2631 | init_completion(&mb0_transfer.ac_wake_work); | 2701 | init_completion(&mb0_transfer.ac_wake_work); |
2632 | mutex_init(&mb1_transfer.lock); | 2702 | mutex_init(&mb1_transfer.lock); |
2633 | init_completion(&mb1_transfer.work); | 2703 | init_completion(&mb1_transfer.work); |
2704 | mb1_transfer.ape_opp = APE_NO_CHANGE; | ||
2634 | mutex_init(&mb2_transfer.lock); | 2705 | mutex_init(&mb2_transfer.lock); |
2635 | init_completion(&mb2_transfer.work); | 2706 | init_completion(&mb2_transfer.work); |
2636 | spin_lock_init(&mb2_transfer.auto_pm_lock); | 2707 | spin_lock_init(&mb2_transfer.auto_pm_lock); |
diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index c5028f1246fc..841342c55451 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h | |||
@@ -457,6 +457,25 @@ enum hw_acc_dev { | |||
457 | NUM_HW_ACC | 457 | NUM_HW_ACC |
458 | }; | 458 | }; |
459 | 459 | ||
460 | /** | ||
461 | * enum prcmu_power_status - results from set_power_state | ||
462 | * @PRCMU_SLEEP_OK: Sleep went ok | ||
463 | * @PRCMU_DEEP_SLEEP_OK: DeepSleep went ok | ||
464 | * @PRCMU_IDLE_OK: Idle went ok | ||
465 | * @PRCMU_DEEPIDLE_OK: DeepIdle went ok | ||
466 | * @PRCMU_PRCMU2ARMPENDINGIT_ER: Pending interrupt detected | ||
467 | * @PRCMU_ARMPENDINGIT_ER: Pending interrupt detected | ||
468 | * | ||
469 | */ | ||
470 | enum prcmu_power_status { | ||
471 | PRCMU_SLEEP_OK = 0xf3, | ||
472 | PRCMU_DEEP_SLEEP_OK = 0xf6, | ||
473 | PRCMU_IDLE_OK = 0xf0, | ||
474 | PRCMU_DEEPIDLE_OK = 0xe3, | ||
475 | PRCMU_PRCMU2ARMPENDINGIT_ER = 0x91, | ||
476 | PRCMU_ARMPENDINGIT_ER = 0x93, | ||
477 | }; | ||
478 | |||
460 | /* | 479 | /* |
461 | * Definitions for autonomous power management configuration. | 480 | * Definitions for autonomous power management configuration. |
462 | */ | 481 | */ |
@@ -544,6 +563,7 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 val); | |||
544 | 563 | ||
545 | void db8500_prcmu_system_reset(u16 reset_code); | 564 | void db8500_prcmu_system_reset(u16 reset_code); |
546 | int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); | 565 | int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); |
566 | u8 db8500_prcmu_get_power_state_result(void); | ||
547 | void db8500_prcmu_enable_wakeups(u32 wakeups); | 567 | void db8500_prcmu_enable_wakeups(u32 wakeups); |
548 | int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state); | 568 | int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state); |
549 | int db8500_prcmu_request_clock(u8 clock, bool enable); | 569 | int db8500_prcmu_request_clock(u8 clock, bool enable); |
@@ -699,6 +719,11 @@ static inline int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, | |||
699 | return 0; | 719 | return 0; |
700 | } | 720 | } |
701 | 721 | ||
722 | static inline u8 db8500_prcmu_get_power_state_result(void) | ||
723 | { | ||
724 | return 0; | ||
725 | } | ||
726 | |||
702 | static inline void db8500_prcmu_enable_wakeups(u32 wakeups) {} | 727 | static inline void db8500_prcmu_enable_wakeups(u32 wakeups) {} |
703 | 728 | ||
704 | static inline int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) | 729 | static inline int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) |
diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 8470c7d7121f..432a2d3fc198 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h | |||
@@ -185,12 +185,14 @@ enum prcmu_clock { | |||
185 | * @APE_NO_CHANGE: The APE operating point is unchanged | 185 | * @APE_NO_CHANGE: The APE operating point is unchanged |
186 | * @APE_100_OPP: The new APE operating point is ape100opp | 186 | * @APE_100_OPP: The new APE operating point is ape100opp |
187 | * @APE_50_OPP: 50% | 187 | * @APE_50_OPP: 50% |
188 | * @APE_50_PARTLY_25_OPP: 50%, except some clocks at 25%. | ||
188 | */ | 189 | */ |
189 | enum ape_opp { | 190 | enum ape_opp { |
190 | APE_OPP_INIT = 0x00, | 191 | APE_OPP_INIT = 0x00, |
191 | APE_NO_CHANGE = 0x01, | 192 | APE_NO_CHANGE = 0x01, |
192 | APE_100_OPP = 0x02, | 193 | APE_100_OPP = 0x02, |
193 | APE_50_OPP = 0x03 | 194 | APE_50_OPP = 0x03, |
195 | APE_50_PARTLY_25_OPP = 0xFF, | ||
194 | }; | 196 | }; |
195 | 197 | ||
196 | /** | 198 | /** |
@@ -271,6 +273,14 @@ static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, | |||
271 | keep_ap_pll); | 273 | keep_ap_pll); |
272 | } | 274 | } |
273 | 275 | ||
276 | static inline u8 prcmu_get_power_state_result(void) | ||
277 | { | ||
278 | if (cpu_is_u5500()) | ||
279 | return -EINVAL; | ||
280 | else | ||
281 | return db8500_prcmu_get_power_state_result(); | ||
282 | } | ||
283 | |||
274 | static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) | 284 | static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) |
275 | { | 285 | { |
276 | if (cpu_is_u5500()) | 286 | if (cpu_is_u5500()) |
@@ -663,9 +673,10 @@ static inline int prcmu_stop_temp_sense(void) | |||
663 | /* PRCMU QoS APE OPP class */ | 673 | /* PRCMU QoS APE OPP class */ |
664 | #define PRCMU_QOS_APE_OPP 1 | 674 | #define PRCMU_QOS_APE_OPP 1 |
665 | #define PRCMU_QOS_DDR_OPP 2 | 675 | #define PRCMU_QOS_DDR_OPP 2 |
676 | #define PRCMU_QOS_ARM_OPP 3 | ||
666 | #define PRCMU_QOS_DEFAULT_VALUE -1 | 677 | #define PRCMU_QOS_DEFAULT_VALUE -1 |
667 | 678 | ||
668 | #ifdef CONFIG_UX500_PRCMU_QOS_POWER | 679 | #ifdef CONFIG_DBX500_PRCMU_QOS_POWER |
669 | 680 | ||
670 | unsigned long prcmu_qos_get_cpufreq_opp_delay(void); | 681 | unsigned long prcmu_qos_get_cpufreq_opp_delay(void); |
671 | void prcmu_qos_set_cpufreq_opp_delay(unsigned long); | 682 | void prcmu_qos_set_cpufreq_opp_delay(unsigned long); |