summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2014-05-26 13:24:56 -0700
committerOlof Johansson <olof@lixom.net>2014-05-26 13:24:56 -0700
commit2ca602de42863436cce275dde35c22496344d539 (patch)
tree9111b81359168bb61767101fb71219a3a6416fd6 /arch/arm/mach-omap2
parent98954f4b122c2c5ac03bb3d21fd171a062dc294f (diff)
parent57b05572267581c2320462e9b606709fa6da4c5a (diff)
Merge tag 'omap-for-v3.16/pm-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/soc
Merge "ARM: omap pm changes for v3.16 merge window, resend" from Tony Lindgren: PM related fixes for omap3 that were discovered during omap3 conversion to device tree. This series sets up the PMIC signaling in a way where we can test for PM regressions easily by looking at state of the the sys_clkreq and sys_off_mode pins. Note that this series alone does not make omap3 PM to cut off core voltage during off-idle, changes to twl4030-power.c configurations are still needed. Those will be posted separately. * tag 'omap-for-v3.16/pm-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap: ARM: OMAP2+: Enable CPUidle in omap2plus_defconfig ARM: dts: Enable N900 keyboard sleep leds by default ARM: OMAP2+: Fix voltage scaling init for device tree ARM: dts: Configure omap3 twl4030 I2C4 pins by default ARM: OMAP3: Fix voltage control for deeper idle states ARM: OMAP3: Disable broken omap3_set_off_timings function ARM: OMAP3: Fix idle mode signaling for sys_clkreq and sys_off_mode ARM: dts: Fix omap serial wake-up when booted with device tree mfd: twl-core: Fix idle mode signaling for omaps when booted with device tree Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r--arch/arm/mach-omap2/omap_twl.c60
-rw-r--r--arch/arm/mach-omap2/pm.c28
-rw-r--r--arch/arm/mach-omap2/pm34xx.c4
-rw-r--r--arch/arm/mach-omap2/prm-regbits-34xx.h11
-rw-r--r--arch/arm/mach-omap2/vc.c232
-rw-r--r--arch/arm/mach-omap2/vc.h3
6 files changed, 184 insertions, 154 deletions
diff --git a/arch/arm/mach-omap2/omap_twl.c b/arch/arm/mach-omap2/omap_twl.c
index 615e5b1fb02..6bf62670055 100644
--- a/arch/arm/mach-omap2/omap_twl.c
+++ b/arch/arm/mach-omap2/omap_twl.c
@@ -46,15 +46,8 @@
static bool is_offset_valid;
static u8 smps_offset;
-/*
- * Flag to ensure Smartreflex bit in TWL
- * being cleared in board file is not overwritten.
- */
-static bool __initdata twl_sr_enable_autoinit;
-#define TWL4030_DCDC_GLOBAL_CFG 0x06
#define REG_SMPS_OFFSET 0xE0
-#define SMARTREFLEX_ENABLE BIT(3)
static unsigned long twl4030_vsel_to_uv(const u8 vsel)
{
@@ -251,18 +244,6 @@ int __init omap3_twl_init(void)
if (!cpu_is_omap34xx())
return -ENODEV;
- /*
- * The smartreflex bit on twl4030 specifies if the setting of voltage
- * is done over the I2C_SR path. Since this setting is independent of
- * the actual usage of smartreflex AVS module, we enable TWL SR bit
- * by default irrespective of whether smartreflex AVS module is enabled
- * on the OMAP side or not. This is because without this bit enabled,
- * the voltage scaling through vp forceupdate/bypass mechanism of
- * voltage scaling will not function on TWL over I2C_SR.
- */
- if (!twl_sr_enable_autoinit)
- omap3_twl_set_sr_bit(true);
-
voltdm = voltdm_lookup("mpu_iva");
omap_voltage_register_pmic(voltdm, &omap3_mpu_pmic);
@@ -271,44 +252,3 @@ int __init omap3_twl_init(void)
return 0;
}
-
-/**
- * omap3_twl_set_sr_bit() - Set/Clear SR bit on TWL
- * @enable: enable SR mode in twl or not
- *
- * If 'enable' is true, enables Smartreflex bit on TWL 4030 to make sure
- * voltage scaling through OMAP SR works. Else, the smartreflex bit
- * on twl4030 is cleared as there are platforms which use OMAP3 and T2 but
- * use Synchronized Scaling Hardware Strategy (ENABLE_VMODE=1) and Direct
- * Strategy Software Scaling Mode (ENABLE_VMODE=0), for setting the voltages,
- * in those scenarios this bit is to be cleared (enable = false).
- *
- * Returns 0 on success, error is returned if I2C read/write fails.
- */
-int __init omap3_twl_set_sr_bit(bool enable)
-{
- u8 temp;
- int ret;
- if (twl_sr_enable_autoinit)
- pr_warning("%s: unexpected multiple calls\n", __func__);
-
- ret = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &temp,
- TWL4030_DCDC_GLOBAL_CFG);
- if (ret)
- goto err;
-
- if (enable)
- temp |= SMARTREFLEX_ENABLE;
- else
- temp &= ~SMARTREFLEX_ENABLE;
-
- ret = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, temp,
- TWL4030_DCDC_GLOBAL_CFG);
- if (!ret) {
- twl_sr_enable_autoinit = true;
- return 0;
- }
-err:
- pr_err("%s: Error access to TWL4030 (%d)\n", __func__, ret);
- return ret;
-}
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index dd31212d5e2..828aee9ea6a 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -298,25 +298,21 @@ omap_postcore_initcall(omap2_common_pm_init);
int __init omap2_common_pm_late_init(void)
{
- /*
- * In the case of DT, the PMIC and SR initialization will be done using
- * a completely different mechanism.
- * Disable this part if a DT blob is available.
- */
- if (!of_have_populated_dt()) {
-
- /* Init the voltage layer */
- omap_pmic_late_init();
- omap_voltage_late_init();
+ if (of_have_populated_dt()) {
+ omap3_twl_init();
+ omap4_twl_init();
+ }
- /* Initialize the voltages */
- omap3_init_voltages();
- omap4_init_voltages();
+ /* Init the voltage layer */
+ omap_pmic_late_init();
+ omap_voltage_late_init();
- /* Smartreflex device init */
- omap_devinit_smartreflex();
+ /* Initialize the voltages */
+ omap3_init_voltages();
+ omap4_init_voltages();
- }
+ /* Smartreflex device init */
+ omap_devinit_smartreflex();
/* cpufreq dummy device instantiation */
omap_init_cpufreq();
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 90ea2d3ab40..507d8eeaab9 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -50,6 +50,7 @@
#include "sdrc.h"
#include "sram.h"
#include "control.h"
+#include "vc.h"
/* pm34xx errata defined in pm.h */
u16 pm34xx_errata;
@@ -288,6 +289,9 @@ void omap_sram_idle(void)
}
}
+ /* Configure PMIC signaling for I2C4 or sys_off_mode */
+ omap3_vc_set_pmic_signaling(core_next_state);
+
omap3_intc_prepare_idle();
/*
diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h b/arch/arm/mach-omap2/prm-regbits-34xx.h
index cebad565ed3..106132db532 100644
--- a/arch/arm/mach-omap2/prm-regbits-34xx.h
+++ b/arch/arm/mach-omap2/prm-regbits-34xx.h
@@ -123,8 +123,15 @@
#define OMAP3430_GLOBAL_SW_RST_SHIFT 1
#define OMAP3430_GLOBAL_COLD_RST_SHIFT 0
#define OMAP3430_GLOBAL_COLD_RST_MASK (1 << 0)
-#define OMAP3430_SEL_OFF_MASK (1 << 3)
-#define OMAP3430_AUTO_OFF_MASK (1 << 2)
+#define OMAP3430_PRM_VOLTCTRL_SEL_VMODE (1 << 4)
+#define OMAP3430_PRM_VOLTCTRL_SEL_OFF (1 << 3)
+#define OMAP3430_PRM_VOLTCTRL_AUTO_OFF (1 << 2)
+#define OMAP3430_PRM_VOLTCTRL_AUTO_RET (1 << 1)
+#define OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP (1 << 0)
#define OMAP3430_SETUP_TIME2_MASK (0xffff << 16)
#define OMAP3430_SETUP_TIME1_MASK (0xffff << 0)
+#define OMAP3430_PRM_POLCTRL_OFFMODE_POL (1 << 3)
+#define OMAP3430_PRM_POLCTRL_CLKOUT_POL (1 << 2)
+#define OMAP3430_PRM_POLCTRL_CLKREQ_POL (1 << 1)
+#define OMAP3430_PRM_POLCTRL_EXTVOL_POL (1 << 0)
#endif
diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c
index 49ac7977e03..0d1629c5f84 100644
--- a/arch/arm/mach-omap2/vc.c
+++ b/arch/arm/mach-omap2/vc.c
@@ -220,10 +220,126 @@ static inline u32 omap_usec_to_32k(u32 usec)
return DIV_ROUND_UP_ULL(32768ULL * (u64)usec, 1000000ULL);
}
-/* Set oscillator setup time for omap3 */
-static void omap3_set_clksetup(u32 usec, struct voltagedomain *voltdm)
+struct omap3_vc_timings {
+ u32 voltsetup1;
+ u32 voltsetup2;
+};
+
+struct omap3_vc {
+ struct voltagedomain *vd;
+ u32 voltctrl;
+ u32 voltsetup1;
+ u32 voltsetup2;
+ struct omap3_vc_timings timings[2];
+};
+static struct omap3_vc vc;
+
+void omap3_vc_set_pmic_signaling(int core_next_state)
+{
+ struct voltagedomain *vd = vc.vd;
+ struct omap3_vc_timings *c = vc.timings;
+ u32 voltctrl, voltsetup1, voltsetup2;
+
+ voltctrl = vc.voltctrl;
+ voltsetup1 = vc.voltsetup1;
+ voltsetup2 = vc.voltsetup2;
+
+ switch (core_next_state) {
+ case PWRDM_POWER_OFF:
+ voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_RET |
+ OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
+ voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_OFF;
+ if (voltctrl & OMAP3430_PRM_VOLTCTRL_SEL_OFF)
+ voltsetup2 = c->voltsetup2;
+ else
+ voltsetup1 = c->voltsetup1;
+ break;
+ case PWRDM_POWER_RET:
+ default:
+ c++;
+ voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_OFF |
+ OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
+ voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_RET;
+ voltsetup1 = c->voltsetup1;
+ break;
+ }
+
+ if (voltctrl != vc.voltctrl) {
+ vd->write(voltctrl, OMAP3_PRM_VOLTCTRL_OFFSET);
+ vc.voltctrl = voltctrl;
+ }
+ if (voltsetup1 != vc.voltsetup1) {
+ vd->write(c->voltsetup1,
+ OMAP3_PRM_VOLTSETUP1_OFFSET);
+ vc.voltsetup1 = voltsetup1;
+ }
+ if (voltsetup2 != vc.voltsetup2) {
+ vd->write(c->voltsetup2,
+ OMAP3_PRM_VOLTSETUP2_OFFSET);
+ vc.voltsetup2 = voltsetup2;
+ }
+}
+
+#define PRM_POLCTRL_TWL_MASK (OMAP3430_PRM_POLCTRL_CLKREQ_POL | \
+ OMAP3430_PRM_POLCTRL_CLKREQ_POL)
+#define PRM_POLCTRL_TWL_VAL OMAP3430_PRM_POLCTRL_CLKREQ_POL
+
+/*
+ * Configure signal polarity for sys_clkreq and sys_off_mode pins
+ * as the default values are wrong and can cause the system to hang
+ * if any twl4030 scripts are loaded.
+ */
+static void __init omap3_vc_init_pmic_signaling(struct voltagedomain *voltdm)
+{
+ u32 val;
+
+ if (vc.vd)
+ return;
+
+ vc.vd = voltdm;
+
+ val = voltdm->read(OMAP3_PRM_POLCTRL_OFFSET);
+ if (!(val & OMAP3430_PRM_POLCTRL_CLKREQ_POL) ||
+ (val & OMAP3430_PRM_POLCTRL_CLKREQ_POL)) {
+ val |= OMAP3430_PRM_POLCTRL_CLKREQ_POL;
+ val &= ~OMAP3430_PRM_POLCTRL_OFFMODE_POL;
+ pr_debug("PM: fixing sys_clkreq and sys_off_mode polarity to 0x%x\n",
+ val);
+ voltdm->write(val, OMAP3_PRM_POLCTRL_OFFSET);
+ }
+
+ /*
+ * By default let's use I2C4 signaling for retention idle
+ * and sys_off_mode pin signaling for off idle. This way we
+ * have sys_clk_req pin go down for retention and both
+ * sys_clk_req and sys_off_mode pins will go down for off
+ * idle. And we can also scale voltages to zero for off-idle.
+ * Note that no actual voltage scaling during off-idle will
+ * happen unless the board specific twl4030 PMIC scripts are
+ * loaded.
+ */
+ val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET);
+ if (!(val & OMAP3430_PRM_VOLTCTRL_SEL_OFF)) {
+ val |= OMAP3430_PRM_VOLTCTRL_SEL_OFF;
+ pr_debug("PM: setting voltctrl sys_off_mode signaling to 0x%x\n",
+ val);
+ voltdm->write(val, OMAP3_PRM_VOLTCTRL_OFFSET);
+ }
+ vc.voltctrl = val;
+
+ omap3_vc_set_pmic_signaling(PWRDM_POWER_ON);
+}
+
+static void omap3_init_voltsetup1(struct voltagedomain *voltdm,
+ struct omap3_vc_timings *c, u32 idle)
{
- voltdm->write(omap_usec_to_32k(usec), OMAP3_PRM_CLKSETUP_OFFSET);
+ unsigned long val;
+
+ val = (voltdm->vc_param->on - idle) / voltdm->pmic->slew_rate;
+ val *= voltdm->sys_clk.rate / 8 / 1000000 + 1;
+ val <<= __ffs(voltdm->vfsm->voltsetup_mask);
+ c->voltsetup1 &= ~voltdm->vfsm->voltsetup_mask;
+ c->voltsetup1 |= val;
}
/**
@@ -236,37 +352,21 @@ static void omap3_set_clksetup(u32 usec, struct voltagedomain *voltdm)
* or retention. Off mode has additionally an option to use sys_off_mode
* pad, which uses a global signal to program the whole power IC to
* off-mode.
+ *
+ * Note that pmic is not controlling the voltage scaling during
+ * retention signaled over I2C4, so we can keep voltsetup2 as 0.
+ * And the oscillator is not shut off over I2C4, so no need to
+ * set clksetup.
*/
-static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode)
+static void omap3_set_i2c_timings(struct voltagedomain *voltdm)
{
- unsigned long voltsetup1;
- u32 tgt_volt;
-
- /*
- * Oscillator is shut down only if we are using sys_off_mode pad,
- * thus we set a minimal setup time here
- */
- omap3_set_clksetup(1, voltdm);
+ struct omap3_vc_timings *c = vc.timings;
- if (off_mode)
- tgt_volt = voltdm->vc_param->off;
- else
- tgt_volt = voltdm->vc_param->ret;
-
- voltsetup1 = (voltdm->vc_param->on - tgt_volt) /
- voltdm->pmic->slew_rate;
-
- voltsetup1 = voltsetup1 * voltdm->sys_clk.rate / 8 / 1000000 + 1;
-
- voltdm->rmw(voltdm->vfsm->voltsetup_mask,
- voltsetup1 << __ffs(voltdm->vfsm->voltsetup_mask),
- voltdm->vfsm->voltsetup_reg);
-
- /*
- * pmic is not controlling the voltage scaling during retention,
- * thus set voltsetup2 to 0
- */
- voltdm->write(0, OMAP3_PRM_VOLTSETUP2_OFFSET);
+ /* Configure PRWDM_POWER_OFF over I2C4 */
+ omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->off);
+ c++;
+ /* Configure PRWDM_POWER_RET over I2C4 */
+ omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->ret);
}
/**
@@ -275,69 +375,49 @@ static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode)
*
* Calculates and sets up off-mode timings for a channel. Off-mode
* can use either I2C based voltage scaling, or alternatively
- * sys_off_mode pad can be used to send a global command to power IC.
- * This function first checks which mode is being used, and calls
- * omap3_set_i2c_timings() if the system is using I2C control mode.
+ * sys_off_mode pad can be used to send a global command to power IC.n,
* sys_off_mode has the additional benefit that voltages can be
* scaled to zero volt level with TWL4030 / TWL5030, I2C can only
* scale to 600mV.
+ *
+ * Note that omap is not controlling the voltage scaling during
+ * off idle signaled by sys_off_mode, so we can keep voltsetup1
+ * as 0.
*/
static void omap3_set_off_timings(struct voltagedomain *voltdm)
{
- unsigned long clksetup;
- unsigned long voltsetup2;
- unsigned long voltsetup2_old;
- u32 val;
- u32 tstart, tshut;
+ struct omap3_vc_timings *c = vc.timings;
+ u32 tstart, tshut, clksetup, voltoffset;
- /* check if sys_off_mode is used to control off-mode voltages */
- val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET);
- if (!(val & OMAP3430_SEL_OFF_MASK)) {
- /* No, omap is controlling them over I2C */
- omap3_set_i2c_timings(voltdm, true);
+ if (c->voltsetup2)
return;
- }
omap_pm_get_oscillator(&tstart, &tshut);
- omap3_set_clksetup(tstart, voltdm);
-
- clksetup = voltdm->read(OMAP3_PRM_CLKSETUP_OFFSET);
-
- /* voltsetup 2 in us */
- voltsetup2 = voltdm->vc_param->on / voltdm->pmic->slew_rate;
-
- /* convert to 32k clk cycles */
- voltsetup2 = DIV_ROUND_UP(voltsetup2 * 32768, 1000000);
-
- voltsetup2_old = voltdm->read(OMAP3_PRM_VOLTSETUP2_OFFSET);
-
- /*
- * Update voltsetup2 if higher than current value (needed because
- * we have multiple channels with different ramp times), also
- * update voltoffset always to value recommended by TRM
- */
- if (voltsetup2 > voltsetup2_old) {
- voltdm->write(voltsetup2, OMAP3_PRM_VOLTSETUP2_OFFSET);
- voltdm->write(clksetup - voltsetup2,
- OMAP3_PRM_VOLTOFFSET_OFFSET);
- } else
- voltdm->write(clksetup - voltsetup2_old,
- OMAP3_PRM_VOLTOFFSET_OFFSET);
+ if (tstart == ULONG_MAX) {
+ pr_debug("PM: oscillator start-up time not initialized, using 10ms\n");
+ clksetup = omap_usec_to_32k(10000);
+ } else {
+ clksetup = omap_usec_to_32k(tstart);
+ }
/*
- * omap is not controlling voltage scaling during off-mode,
- * thus set voltsetup1 to 0
+ * For twl4030 errata 27, we need to allow minimum ~488.32 us wait to
+ * switch from HFCLKIN to internal oscillator. That means timings
+ * have voltoffset fixed to 0xa in rounded up 32 KiHz cycles. And
+ * that means we can calculate the value based on the oscillator
+ * start-up time since voltoffset2 = clksetup - voltoffset.
*/
- voltdm->rmw(voltdm->vfsm->voltsetup_mask, 0,
- voltdm->vfsm->voltsetup_reg);
-
- /* voltoffset must be clksetup minus voltsetup2 according to TRM */
- voltdm->write(clksetup - voltsetup2, OMAP3_PRM_VOLTOFFSET_OFFSET);
+ voltoffset = omap_usec_to_32k(488);
+ c->voltsetup2 = clksetup - voltoffset;
+ voltdm->write(clksetup, OMAP3_PRM_CLKSETUP_OFFSET);
+ voltdm->write(voltoffset, OMAP3_PRM_VOLTOFFSET_OFFSET);
}
static void __init omap3_vc_init_channel(struct voltagedomain *voltdm)
{
+ omap3_vc_init_pmic_signaling(voltdm);
omap3_set_off_timings(voltdm);
+ omap3_set_i2c_timings(voltdm);
}
/**
diff --git a/arch/arm/mach-omap2/vc.h b/arch/arm/mach-omap2/vc.h
index 91c8d75bf2e..cdbdd78e755 100644
--- a/arch/arm/mach-omap2/vc.h
+++ b/arch/arm/mach-omap2/vc.h
@@ -117,6 +117,9 @@ extern struct omap_vc_param omap4_mpu_vc_data;
extern struct omap_vc_param omap4_iva_vc_data;
extern struct omap_vc_param omap4_core_vc_data;
+void omap3_vc_set_pmic_signaling(int core_next_state);
+
+
void omap_vc_init_channel(struct voltagedomain *voltdm);
int omap_vc_pre_scale(struct voltagedomain *voltdm,
unsigned long target_volt,