summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMattias Nilsson <mattias.i.nilsson@stericsson.com>2011-06-07 15:18:19 +0200
committerRobert Marklund <robert.marklund@stericsson.com>2011-10-05 13:01:08 +0200
commit357ae99b51a19334fdb2084852d398c68bbca3a7 (patch)
tree997c520967eb2850792d2942e06266a8a089d092
parentbcd984c63ee3257ce8e5175a89dd430f5aded924 (diff)
arm: ux500: halve the frequencies of some clocks in vc use case
This patch adds a new APE OPP (50% with ACLK and DMACLK frequencies at 25%) which is used during the voice call use case. ST Ericsson ID: 337715, 337718 ST Ericsson FOSS-OUT ID: trivial Change-Id: I14c0bb69aa2e4086d834902f4f961ba2d8c87f66 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32129 Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com> Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
-rw-r--r--drivers/mfd/db8500-prcmu.c69
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h1
-rw-r--r--include/linux/mfd/dbx500-prcmu.h4
3 files changed, 72 insertions, 2 deletions
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 34b6ee32f02..fccdd5c1815 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -343,11 +343,13 @@ static struct {
* mb1_transfer - state needed for mailbox 1 communication.
* @lock: The transaction lock.
* @work: The transaction completion structure.
+ * @ape_opp: The current APE OPP.
* @ack: Reply ("acknowledge") data.
*/
static struct {
struct mutex lock;
struct completion work;
+ u8 ape_opp;
struct {
u8 header;
u8 arm_opp;
@@ -939,6 +941,53 @@ int prcmu_set_ddr_opp(u8 opp)
return 0;
}
+
+/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
+static void request_even_slower_clocks(bool enable)
+{
+ const u8 clock_reg[] = {
+ PRCM_ACLK_MGT_OFF,
+ PRCM_DMACLK_MGT_OFF
+ };
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
+ u32 val;
+ u32 div;
+
+ val = readl(_PRCMU_BASE + clock_reg[i]);
+ div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
+ if (enable) {
+ if ((div <= 1) || (div > 15)) {
+ pr_err("prcmu: Bad clock divider %d in %s\n",
+ div, __func__);
+ goto unlock_and_return;
+ }
+ div <<= 1;
+ } else {
+ if (div <= 2)
+ goto unlock_and_return;
+ div >>= 1;
+ }
+ val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
+ (div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
+ writel(val, (_PRCMU_BASE + clock_reg[i]));
+ }
+
+unlock_and_return:
+ /* Release the HW semaphore. */
+ writel(0, PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+
/**
* set_ape_opp - set the appropriate APE OPP
* @opp: The new APE operating point to which transition is to be made
@@ -950,14 +999,24 @@ int prcmu_set_ape_opp(u8 opp)
{
int r = 0;
+ if (opp == mb1_transfer.ape_opp)
+ return 0;
+
mutex_lock(&mb1_transfer.lock);
+ if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
+ request_even_slower_clocks(false);
+
+ if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP))
+ goto skip_message;
+
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
- writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+ writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
+ (tcdm_base + PRCM_REQ_MB1_APE_OPP));
writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
@@ -966,6 +1025,13 @@ int prcmu_set_ape_opp(u8 opp)
(mb1_transfer.ack.ape_opp != opp))
r = -EIO;
+skip_message:
+ if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
+ (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
+ request_even_slower_clocks(true);
+ if (!r)
+ mb1_transfer.ape_opp = opp;
+
mutex_unlock(&mb1_transfer.lock);
return r;
@@ -2123,6 +2189,7 @@ void __init db8500_prcmu_early_init(void)
init_completion(&mb0_transfer.ac_wake_work);
mutex_init(&mb1_transfer.lock);
init_completion(&mb1_transfer.work);
+ mb1_transfer.ape_opp = APE_NO_CHANGE;
mutex_init(&mb2_transfer.lock);
init_completion(&mb2_transfer.work);
spin_lock_init(&mb2_transfer.auto_pm_lock);
diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index ec22e9f15d3..457fa4dc212 100644
--- a/drivers/mfd/dbx500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -17,6 +17,7 @@
#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+#define PRCM_ACLK_MGT_OFF 0x004
#define PRCM_SVACLK_MGT_OFF 0x008
#define PRCM_SIACLK_MGT_OFF 0x00C
#define PRCM_SGACLK_MGT_OFF 0x014
diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h
index 072a4c70059..a9b660d0527 100644
--- a/include/linux/mfd/dbx500-prcmu.h
+++ b/include/linux/mfd/dbx500-prcmu.h
@@ -504,6 +504,7 @@ int prcmu_qos_add_notifier(int prcmu_qos_class,
struct notifier_block *notifier);
int prcmu_qos_remove_notifier(int prcmu_qos_class,
struct notifier_block *notifier);
+void prcmu_qos_voice_call_override(bool enable);
#else
@@ -547,7 +548,8 @@ static inline int prcmu_qos_remove_notifier(int prcmu_qos_class,
{
return 0;
}
-
+static inline void prcmu_qos_voice_call_override(bool enable) {}
+t
#endif
struct ux500_regulator;