summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com>2011-07-28 20:45:43 +0530
committerRabin VINCENT <rabin.vincent@stericsson.com>2011-09-30 12:25:04 +0200
commite6f2219a1a409227b5958f201bc9262db7cc9c36 (patch)
tree6e12fc8552733f3c8a1382758d4ad10f3192b650
parent8b322de9614db8411139b3c8804282d390ec3db7 (diff)
u5500: prcmu: implement APE OPP handling
ST-Ericsson ID: 348762 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: I7ff9a02de960f251cbbd9f051a1c8050aa340d6d Signed-off-by: Shreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com> Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28000 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32555 Tested-by: Venkata Biswanath DEVARASETTY <venkata.biswanath@stericsson.com>
-rw-r--r--arch/arm/mach-ux500/clock-db5500.c4
-rw-r--r--drivers/mfd/db5500-prcmu-regs.h2
-rw-r--r--drivers/mfd/db5500-prcmu.c239
-rw-r--r--drivers/mfd/db8500-prcmu.c8
-rw-r--r--include/linux/mfd/db5500-prcmu.h11
-rw-r--r--include/linux/mfd/db8500-prcmu.h8
-rw-r--r--include/linux/mfd/dbx500-prcmu.h18
7 files changed, 251 insertions, 39 deletions
diff --git a/arch/arm/mach-ux500/clock-db5500.c b/arch/arm/mach-ux500/clock-db5500.c
index 4c233c77fc7..163c48d9f24 100644
--- a/arch/arm/mach-ux500/clock-db5500.c
+++ b/arch/arm/mach-ux500/clock-db5500.c
@@ -240,8 +240,8 @@ static DEF_PRCMU_CLK(irdaclk, PRCMU_IRDACLK, 48000000);
static DEF_PRCMU_CLK(irrcclk, PRCMU_IRRCCLK, 48000000);
static DEF_PRCMU_CLK(rngclk, PRCMU_RNGCLK, 26000000);
static DEF_PRCMU_CLK(pwmclk, PRCMU_PWMCLK, 26000000);
-static DEF_PRCMU_CLK(sdmmcclk, PRCMU_SDMMCCLK, 100000000);
-static DEF_PRCMU_CLK(spare1clk, PRCMU_SPARE1CLK, 100000000);
+static DEF_PRCMU_CLK(sdmmcclk, PRCMU_SDMMCCLK, 50000000);
+static DEF_PRCMU_CLK(spare1clk, PRCMU_SPARE1CLK, 50000000);
static DEF_PRCMU_CLK(per1clk, PRCMU_PER1CLK, 133330000);
static DEF_PRCMU_CLK(per2clk, PRCMU_PER2CLK, 133330000);
static DEF_PRCMU_CLK(per3clk, PRCMU_PER3CLK, 133330000);
diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/db5500-prcmu-regs.h
index 9e76509b011..12e8d10ca46 100644
--- a/drivers/mfd/db5500-prcmu-regs.h
+++ b/drivers/mfd/db5500-prcmu-regs.h
@@ -18,6 +18,7 @@
#define PRCM_SEM 0x400
#define PRCM_SEM_PRCM_SEM BIT(0)
+#define DB5500_PRCM_ACLK_MGT 0x004
#define DB5500_PRCM_SVACLK_MGT 0x008
#define DB5500_PRCM_SIACLK_MGT 0x00C
#define DB5500_PRCM_SGACLK_MGT 0x014
@@ -45,6 +46,7 @@
#define DB5500_PRCM_RNGCLK_MGT 0x284
#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLDIV_SHIFT 0
#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
#define PRCM_CLK_MGT_CLKEN BIT(8)
diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c
index 7de44e471f9..dc19b13416b 100644
--- a/drivers/mfd/db5500-prcmu.c
+++ b/drivers/mfd/db5500-prcmu.c
@@ -163,6 +163,11 @@ enum db5500_arm_opp {
DB5500_ARM_EXT_OPP,
};
+enum db5500_ape_opp {
+ DB5500_APE_100_OPP = 1,
+ DB5500_APE_50_OPP
+};
+
enum epod_state {
EPOD_OFF,
EPOD_ON,
@@ -423,6 +428,9 @@ static __iomem void *tcdm_base;
struct clk_mgt {
unsigned int offset;
u32 pllsw;
+ u32 div;
+ bool scalable;
+ bool force50;
};
/* PRCMU Firmware Details */
@@ -434,35 +442,47 @@ static struct {
static DEFINE_SPINLOCK(clk_mgt_lock);
-#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { \
- (DB5500_PRCM_##_name##_MGT), 0 \
+#define CLK_MGT_ENTRY(_name, _scalable)[PRCMU_##_name] = { \
+ .offset = DB5500_PRCM_##_name##_MGT, \
+ .scalable = _scalable, \
}
+
static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
- CLK_MGT_ENTRY(SGACLK),
- CLK_MGT_ENTRY(UARTCLK),
- CLK_MGT_ENTRY(MSP02CLK),
- CLK_MGT_ENTRY(I2CCLK),
- CLK_MGT_ENTRY(SDMMCCLK),
- CLK_MGT_ENTRY(SPARE1CLK),
- CLK_MGT_ENTRY(PER1CLK),
- CLK_MGT_ENTRY(PER2CLK),
- CLK_MGT_ENTRY(PER3CLK),
- CLK_MGT_ENTRY(PER5CLK),
- CLK_MGT_ENTRY(PER6CLK),
- CLK_MGT_ENTRY(PWMCLK),
- CLK_MGT_ENTRY(IRDACLK),
- CLK_MGT_ENTRY(IRRCCLK),
- CLK_MGT_ENTRY(HDMICLK),
- CLK_MGT_ENTRY(APEATCLK),
- CLK_MGT_ENTRY(APETRACECLK),
- CLK_MGT_ENTRY(MCDECLK),
- CLK_MGT_ENTRY(DSIALTCLK),
- CLK_MGT_ENTRY(DMACLK),
- CLK_MGT_ENTRY(B2R2CLK),
- CLK_MGT_ENTRY(TVCLK),
- CLK_MGT_ENTRY(RNGCLK),
- CLK_MGT_ENTRY(SIACLK),
- CLK_MGT_ENTRY(SVACLK),
+ CLK_MGT_ENTRY(SGACLK, true),
+ CLK_MGT_ENTRY(UARTCLK, false),
+ CLK_MGT_ENTRY(MSP02CLK, false),
+ CLK_MGT_ENTRY(I2CCLK, false),
+ [PRCMU_SDMMCCLK] {
+ .offset = DB5500_PRCM_SDMMCCLK_MGT,
+ .force50 = true,
+ .scalable = false,
+
+ },
+ [PRCMU_SPARE1CLK] {
+ .offset = DB5500_PRCM_SPARE1CLK_MGT,
+ .force50 = true,
+ .scalable = false,
+
+ },
+ CLK_MGT_ENTRY(PER1CLK, false),
+ CLK_MGT_ENTRY(PER2CLK, true),
+ CLK_MGT_ENTRY(PER3CLK, true),
+ CLK_MGT_ENTRY(PER5CLK, false), /* used for SPI */
+ CLK_MGT_ENTRY(PER6CLK, true),
+ CLK_MGT_ENTRY(PWMCLK, false),
+ CLK_MGT_ENTRY(IRDACLK, false),
+ CLK_MGT_ENTRY(IRRCCLK, false),
+ CLK_MGT_ENTRY(HDMICLK, false),
+ CLK_MGT_ENTRY(APEATCLK, false),
+ CLK_MGT_ENTRY(APETRACECLK, true),
+ CLK_MGT_ENTRY(MCDECLK, true),
+ CLK_MGT_ENTRY(DSIALTCLK, false),
+ CLK_MGT_ENTRY(DMACLK, true),
+ CLK_MGT_ENTRY(B2R2CLK, true),
+ CLK_MGT_ENTRY(TVCLK, false),
+ CLK_MGT_ENTRY(RNGCLK, false),
+ CLK_MGT_ENTRY(SIACLK, false),
+ CLK_MGT_ENTRY(SVACLK, false),
};
bool db5500_prcmu_is_ac_wake_requested(void)
@@ -1049,6 +1069,165 @@ bailout:
return r;
}
+static void __init prcmu_ape_clocks_init(void)
+{
+ u8 opp = db5500_prcmu_get_ape_opp();
+ unsigned long flags;
+ int i;
+
+ WARN(opp != APE_100_OPP, "%s: Initial APE OPP (%u) not 100%%?\n",
+ __func__, opp);
+
+ for (i = 0; i < PRCMU_NUM_REG_CLOCKS; i++) {
+ struct clk_mgt *clkmgt = &clk_mgt[i];
+ u32 clkval;
+ u32 div;
+
+ if (!clkmgt->scalable && !clkmgt->force50)
+ continue;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ clkval = readl(_PRCMU_BASE + clkmgt->offset);
+ div = clkval & PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ div >>= PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+
+ if (clkmgt->force50) {
+ div *= 2;
+
+ clkval &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ clkval |= div << PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+ writel(clkval, _PRCMU_BASE + clkmgt->offset);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+ continue;
+ }
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ clkmgt->div = div;
+ if (!div)
+ pr_err("%s: scalable clock at offset %#x has zero divisor\n",
+ __func__, clkmgt->offset);
+ }
+}
+
+static void prcmu_ape_clocks_scale(u8 opp)
+{
+ unsigned long irqflags;
+ unsigned int i;
+ u32 clkval;
+
+ /*
+ * Note: calling printk() under the following lock can cause lock
+ * recursion via clk_enable() for the console UART!
+ */
+ spin_lock_irqsave(&clk_mgt_lock, irqflags);
+
+ /* take a lock on HW (HWSEM)*/
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < PRCMU_NUM_REG_CLOCKS; i++) {
+ u32 divval;
+
+ if (!clk_mgt[i].scalable)
+ continue;
+
+ clkval = readl(_PRCMU_BASE + clk_mgt[i].offset);
+ divval = clk_mgt[i].div;
+
+ pr_debug("PRCMU: reg %#x prev clk = 0x%x stored div = 0x%x\n",
+ clk_mgt[i].offset, clkval, divval);
+
+ if (opp == DB5500_APE_50_OPP)
+ divval *= 2;
+
+ clkval &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ clkval |= divval << PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+
+ pr_debug("PRCMU: wr 0x%x in reg 0x%x\n",
+ clkval, clk_mgt[i].offset);
+
+ writel(clkval, _PRCMU_BASE + clk_mgt[i].offset);
+ }
+
+ /* release lock */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, irqflags);
+}
+
+int db5500_prcmu_set_ape_opp(u8 opp)
+{
+ int ret = 0;
+ u8 db5500_opp;
+
+ if (opp == db5500_prcmu_get_ape_opp())
+ return ret;
+
+ if (cpu_is_u5500v1())
+ return -EINVAL;
+
+ switch (opp) {
+ case APE_100_OPP:
+ db5500_opp = DB5500_APE_100_OPP;
+ break;
+ case APE_50_OPP:
+ db5500_opp = DB5500_APE_50_OPP;
+ break;
+ default:
+ pr_err("prcmu: %s() received wrong opp value: %d\n",
+ __func__, opp);
+ ret = -EINVAL;
+ goto bailout;
+ }
+
+ mutex_lock(&mb1_transfer.lock);
+
+ prcmu_ape_clocks_scale(db5500_opp);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_APE_OPP, PRCM_REQ_MB1_HEADER);
+ writeb(db5500_opp, PRCM_REQ_MB1_APE_OPP);
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb1_transfer.work,
+ msecs_to_jiffies(500))) {
+ ret = -EIO;
+ WARN(1, "prcmu: failed to set ape opp to %u", opp);
+ goto unlock_and_return;
+ }
+
+ if (mb1_transfer.ack.header != MB1H_APE_OPP ||
+ (mb1_transfer.ack.ape_opp != db5500_opp) ||
+ (mb1_transfer.ack.arm_voltage_st != RC_SUCCESS))
+ ret = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb1_transfer.lock);
+bailout:
+ return ret;
+}
+
+int db5500_prcmu_get_ape_opp(void)
+{
+ u8 opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+
+ switch (opp) {
+ case DB5500_APE_100_OPP:
+ return APE_100_OPP;
+ case DB5500_APE_50_OPP:
+ return APE_50_OPP;
+ default:
+ pr_err("prcmu: %s() read unknown opp value: %d\n",
+ __func__, opp);
+ return APE_100_OPP;
+ }
+}
+
/**
* db5500_prcmu_get_arm_opp - get the current ARM OPP
*
@@ -1321,6 +1500,11 @@ static bool read_mailbox_1(void)
mb1_transfer.ack.arm_voltage_st =
readb(PRCM_ACK_MB1_ARM_VOLT_STATUS);
break;
+ case MB1H_APE_OPP:
+ mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+ mb1_transfer.ack.ape_voltage_st =
+ readb(PRCM_ACK_MB1_APE_VOLT_STATUS);
+ break;
case MB1H_ARM_APE_OPP:
mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
mb1_transfer.ack.ape_voltage_st =
@@ -1557,6 +1741,7 @@ void __init db5500_prcmu_early_init(void)
handle_simple_irq);
set_irq_flags(irq, IRQF_VALID);
}
+ prcmu_ape_clocks_init();
}
/*
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index ec49a9581fd..e833ec096aa 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -998,13 +998,13 @@ unlock_and_return:
}
/**
- * set_ape_opp - set the appropriate APE OPP
+ * db8500_set_ape_opp - set the appropriate APE OPP
* @opp: The new APE operating point to which transition is to be made
* Returns: 0 on success, non-zero on failure
*
* This function sets the operating point of the APE.
*/
-int prcmu_set_ape_opp(u8 opp)
+int db8500_prcmu_set_ape_opp(u8 opp)
{
int r = 0;
@@ -1047,11 +1047,11 @@ skip_message:
}
/**
- * prcmu_get_ape_opp - get the current APE OPP
+ * db8500_prcmu_get_ape_opp - get the current APE OPP
*
* Returns: the current APE OPP
*/
-int prcmu_get_ape_opp(void)
+int db8500_prcmu_get_ape_opp(void)
{
return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP);
}
diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h
index 17eb28ed58e..2edcf0699ad 100644
--- a/include/linux/mfd/db5500-prcmu.h
+++ b/include/linux/mfd/db5500-prcmu.h
@@ -30,6 +30,8 @@ u16 db5500_prcmu_get_reset_code(void);
bool db5500_prcmu_is_ac_wake_requested(void);
int db5500_prcmu_set_arm_opp(u8 opp);
int db5500_prcmu_get_arm_opp(void);
+int db5500_prcmu_set_ape_opp(u8 opp);
+int db5500_prcmu_get_ape_opp(void);
static inline unsigned long prcmu_clock_rate(u8 clock)
{
@@ -142,6 +144,15 @@ static inline int db5500_prcmu_get_arm_opp(void)
return 0;
}
+static inline int db5500_prcmu_set_ape_opp(u8 opp)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_get_ape_opp(void)
+{
+ return 0;
+}
#endif /* CONFIG_MFD_DB5500_PRCMU */
diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h
index 7b1a0740939..97a60866bda 100644
--- a/include/linux/mfd/db8500-prcmu.h
+++ b/include/linux/mfd/db8500-prcmu.h
@@ -501,8 +501,6 @@ enum romcode_read prcmu_get_rc_p2a(void);
enum ap_pwrst prcmu_get_xp70_current_state(void);
bool prcmu_has_arm_maxopp(void);
bool prcmu_is_u8400(void);
-int prcmu_set_ape_opp(u8 opp);
-int prcmu_get_ape_opp(void);
int prcmu_request_ape_opp_100_voltage(bool enable);
int prcmu_release_usb_wakeup_state(void);
int prcmu_set_ddr_opp(u8 opp);
@@ -548,6 +546,8 @@ u16 db8500_prcmu_get_reset_code(void);
bool db8500_prcmu_is_ac_wake_requested(void);
int db8500_prcmu_set_arm_opp(u8 opp);
int db8500_prcmu_get_arm_opp(void);
+int db8500_prcmu_set_ape_opp(u8 opp);
+int db8500_prcmu_get_ape_opp(void);
#else /* !CONFIG_MFD_DB8500_PRCMU */
@@ -578,12 +578,12 @@ static inline bool prcmu_is_u8400(void)
return false;
}
-static inline int prcmu_set_ape_opp(u8 opp)
+static inline int db8500_prcmu_set_ape_opp(u8 opp)
{
return 0;
}
-static inline int prcmu_get_ape_opp(void)
+static inline int db8500_prcmu_get_ape_opp(void)
{
return APE_100_OPP;
}
diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h
index 6df8ef8289d..76c4f27b251 100644
--- a/include/linux/mfd/dbx500-prcmu.h
+++ b/include/linux/mfd/dbx500-prcmu.h
@@ -320,8 +320,6 @@ unsigned long prcmu_clock_rate(u8 clock);
long prcmu_round_clock_rate(u8 clock, unsigned long rate);
int prcmu_set_clock_rate(u8 clock, unsigned long rate);
-int prcmu_set_ape_opp(u8 opp);
-int prcmu_get_ape_opp(void);
int prcmu_set_ddr_opp(u8 opp);
int prcmu_get_ddr_opp(void);
@@ -341,6 +339,22 @@ static inline int prcmu_get_arm_opp(void)
return db8500_prcmu_get_arm_opp();
}
+static inline int prcmu_set_ape_opp(u8 opp)
+{
+ if (cpu_is_u5500())
+ return db5500_prcmu_set_ape_opp(opp);
+ else
+ return db8500_prcmu_set_ape_opp(opp);
+}
+
+static inline int prcmu_get_ape_opp(void)
+{
+ if (cpu_is_u5500())
+ return db5500_prcmu_get_ape_opp();
+ else
+ return db8500_prcmu_get_ape_opp();
+}
+
static inline void prcmu_system_reset(u16 reset_code)
{
if (cpu_is_u5500())