aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Nilsson <mattias.i.nilsson@stericsson.com>2012-01-13 10:20:43 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2012-03-06 12:46:33 -0500
commit4d64d2e34bc415b05eb77a2732a3164313cf6de3 (patch)
treef4331b9e59754a52f83cfc79b6ca1547861a1a89
parent992b133a5d85ced4ff0fbdab22e9196cf571e0c9 (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.c73
-rw-r--r--include/linux/mfd/db8500-prcmu.h25
-rw-r--r--include/linux/mfd/dbx500-prcmu.h15
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 */
348static struct { 349static 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
821u8 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. */
820static void config_wakeups(void) 827static 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. */
976static 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
1014unlock_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
1058skip_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 */
470enum 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
545void db8500_prcmu_system_reset(u16 reset_code); 564void db8500_prcmu_system_reset(u16 reset_code);
546int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); 565int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll);
566u8 db8500_prcmu_get_power_state_result(void);
547void db8500_prcmu_enable_wakeups(u32 wakeups); 567void db8500_prcmu_enable_wakeups(u32 wakeups);
548int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state); 568int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state);
549int db8500_prcmu_request_clock(u8 clock, bool enable); 569int 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
722static inline u8 db8500_prcmu_get_power_state_result(void)
723{
724 return 0;
725}
726
702static inline void db8500_prcmu_enable_wakeups(u32 wakeups) {} 727static inline void db8500_prcmu_enable_wakeups(u32 wakeups) {}
703 728
704static inline int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) 729static 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 */
189enum ape_opp { 190enum 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
276static 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
274static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) 284static 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
670unsigned long prcmu_qos_get_cpufreq_opp_delay(void); 681unsigned long prcmu_qos_get_cpufreq_opp_delay(void);
671void prcmu_qos_set_cpufreq_opp_delay(unsigned long); 682void prcmu_qos_set_cpufreq_opp_delay(unsigned long);