@@ -55,6 +55,7 @@
#include "pm.h"
#include "sdrc.h"
#include "omap3-opp.h"
+#include "clock3xxx.h"
#ifdef CONFIG_SUSPEND
static suspend_state_t suspend_state = PM_SUSPEND_ON;
@@ -97,6 +98,15 @@ static int (*_omap_save_secure_sram)(u32 *addr);
static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
static struct powerdomain *core_pwrdm, *per_pwrdm;
+static struct powerdomain *dss_pwrdm, *usbhost_pwrdm;
+static struct powerdomain *cam_pwrdm, *sgx_pwrdm;
+static struct clk *dpll3_clk;
+static struct omap_opp *vdd2_opp50, *vdd2_opp100;
+static unsigned long vdd2_opp50_volt, vdd2_opp100_volt;
+
+#define DLL_LOCK_ERRATA_581 (1 << 0)
+static u16 pm34xx_errata;
+#define IS_PM34XX_ERRATA(id) (pm34xx_errata & (id))
static inline void omap3_per_save_context(void)
{
@@ -367,6 +377,7 @@ void omap_sram_idle(void)
int core_next_state = PWRDM_POWER_ON;
int core_prev_state, per_prev_state;
u32 sdrc_pwr = 0;
+ int prev_dpll3_div = 0;
if (!_omap_sram_idle)
return;
@@ -417,9 +428,43 @@ void omap_sram_idle(void)
*/
if (mpu_next_state <= PWRDM_POWER_RET)
omap_smartreflex_disable(VDD1, 1);
- if (core_next_state <= PWRDM_POWER_RET)
+ if (core_next_state <= PWRDM_POWER_RET) {
omap_smartreflex_disable(VDD2, 1);
+ /* Apply the errata if Core is entering RET/OFF */
+ if ((IS_PM34XX_ERRATA(DLL_LOCK_ERRATA_581)) &&
+ (core_next_state <= PWRDM_POWER_RET)) {
+ if (pwrdm_can_idle(core_pwrdm) &&
+ pwrdm_can_idle(per_pwrdm) &&
+ pwrdm_can_idle(dss_pwrdm) &&
+ pwrdm_can_idle(usbhost_pwrdm) &&
+ pwrdm_can_idle(cam_pwrdm) &&
+ pwrdm_can_idle(sgx_pwrdm)) {
+ u32 clksel1_pll;
+ clksel1_pll = cm_read_mod_reg(PLL_MOD,
+ OMAP3430_CM_CLKSEL1_PLL);
+ prev_dpll3_div = clksel1_pll >>
+ OMAP3430_CORE_DPLL_CLKOUT_DIV_SHIFT;
+ if (prev_dpll3_div == 1) {
+ omap3_core_dpll_m2_set_rate(dpll3_clk,
+ opp_get_freq(vdd2_opp50) * 2);
+ if (cpu_is_omap343x())
+ omap_voltage_scale(VDD2, 1200000,
+ vdd2_opp100_volt);
+ } else {
+ if (cpu_is_omap3630())
+ omap_voltage_scale(VDD2, vdd2_opp100_volt,
+ vdd2_opp50_volt);
+ else if (cpu_is_omap343x())
+ omap_voltage_scale(VDD2, 1200000,
+ vdd2_opp50_volt);
+ }
+ cm_rmw_mod_reg_bits(OMAP3430_AUTO_CORE_DPLL_MASK,
+ 0x1, PLL_MOD, CM_AUTOIDLE);
+ }
+ }
+ }
+
/* CORE */
if (core_next_state < PWRDM_POWER_ON) {
omap_uart_prepare_idle(0);
@@ -484,6 +529,44 @@ void omap_sram_idle(void)
if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF)
restore_table_entry();
+ if (IS_PM34XX_ERRATA(DLL_LOCK_ERRATA_581) &&
+ (core_next_state < PWRDM_POWER_INACTIVE)) {
+ if (pwrdm_read_prev_pwrst(core_pwrdm) == PWRDM_POWER_OFF) {
+ u32 clksel1_pll;
+
+ /* ROM code restored the scratchpad settings. So DPLL3
+ * autoidle is disabled and L3 clock is back to the
+ * value before entering this function. This means we
+ * only have to lower the voltage if L3 runs at OPP50
+ */
+
+ clksel1_pll = cm_read_mod_reg(PLL_MOD,
+ OMAP3430_CM_CLKSEL1_PLL);
+ if ((clksel1_pll >> OMAP3430_CORE_DPLL_CLKOUT_DIV_SHIFT) == 2) {
+ /* restore VDD2 OPP2 voltage */
+ if (cpu_is_omap3630())
+ omap_voltage_scale(VDD2, vdd2_opp50_volt, vdd2_opp100_volt);
+ else if (cpu_is_omap343x())
+ omap_voltage_scale(VDD2, vdd2_opp50_volt, 1200000);
+ }
+ } else {
+ /* disable DPLL3 autoidle */
+ cm_rmw_mod_reg_bits(OMAP3430_AUTO_CORE_DPLL_MASK,
+ 0x0, PLL_MOD, CM_AUTOIDLE);
+ if (prev_dpll3_div == 1) {
+ omap3_core_dpll_m2_set_rate(dpll3_clk,
+ opp_get_freq(vdd2_opp100) * 2);
+ if (cpu_is_omap343x())
+ omap_voltage_scale(VDD2, vdd2_opp100_volt, 1200000);
+ } else {
+ if (cpu_is_omap3630())
+ omap_voltage_scale(VDD2, vdd2_opp50_volt, vdd2_opp100_volt);
+ else if (cpu_is_omap343x())
+ omap_voltage_scale(VDD2, vdd2_opp50_volt, 1200000);
+ }
+ }
+ }
+
/* CORE */
if (core_next_state < PWRDM_POWER_ON) {
core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm);
@@ -1006,7 +1089,12 @@ static void __init prcm_setup_regs(void)
cm_write_mod_reg(1 << OMAP3430_AUTO_MPU_DPLL_SHIFT,
MPU_MOD,
CM_AUTOIDLE2);
- cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT) |
+ if (IS_PM34XX_ERRATA(DLL_LOCK_ERRATA_581))
+ cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT),
+ PLL_MOD,
+ CM_AUTOIDLE);
+ else
+ cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT) |
(1 << OMAP3430_AUTO_CORE_DPLL_SHIFT),
PLL_MOD,
CM_AUTOIDLE);
@@ -1178,17 +1266,27 @@ void omap_push_sram_idle(void)
save_secure_ram_context_sz);
}
+void pm_errata_configure()
+{
+ /* TODO: add 3630 && omap_rev() <= OMAP3630_REV_ES1_1 */
+ if (cpu_is_omap343x() || (cpu_is_omap3630()))
+ pm34xx_errata |= DLL_LOCK_ERRATA_581;
+}
+
static int __init omap3_pm_init(void)
{
struct power_state *pwrst, *tmp;
struct clockdomain *neon_clkdm, *per_clkdm, *mpu_clkdm, *core_clkdm;
int ret;
+ unsigned long freq = 0;
if (!cpu_is_omap34xx())
return -ENODEV;
printk(KERN_ERR "Power Management for TI OMAP3.\n");
+ pm_errata_configure();
+
/* XXX prcm_setup_regs needs to be before enabling hw
* supervised mode for powerdomains */
prcm_setup_regs();
@@ -1219,12 +1317,24 @@ static int __init omap3_pm_init(void)
neon_pwrdm = pwrdm_lookup("neon_pwrdm");
per_pwrdm = pwrdm_lookup("per_pwrdm");
core_pwrdm = pwrdm_lookup("core_pwrdm");
+ usbhost_pwrdm = pwrdm_lookup("usbhost_pwrdm");
+ sgx_pwrdm = pwrdm_lookup("sgx_pwrdm");
+ dss_pwrdm = pwrdm_lookup("dss_pwrdm");
+ cam_pwrdm = pwrdm_lookup("cam_pwrdm");
neon_clkdm = clkdm_lookup("neon_clkdm");
mpu_clkdm = clkdm_lookup("mpu_clkdm");
per_clkdm = clkdm_lookup("per_clkdm");
core_clkdm = clkdm_lookup("core_clkdm");
+ dpll3_clk = clk_get(NULL, "dpll3_m2_ck");
+
+ vdd2_opp50 = opp_find_freq_ceil(OPP_L3, &freq);
+ freq = ULONG_MAX;
+ vdd2_opp100 = opp_find_freq_floor(OPP_L3, &freq);
+ vdd2_opp50_volt = opp_get_voltage(vdd2_opp50);
+ vdd2_opp100_volt = opp_get_voltage(vdd2_opp100);
+
omap_push_sram_idle();
#ifdef CONFIG_SUSPEND
suspend_set_ops(&omap_pm_ops);
@@ -173,6 +173,7 @@ static struct omap_volt_data omap34xx_vdd2_volt_data[] = {
{975000, 0, 0xF4, 0x0C},
{1050000, 0, 0xF4, 0x0C},
{1150000, 0, 0xF9, 0x18},
+ {1200000, 0, 0xF9, 0x18},
};
static struct omap_volt_data omap36xx_vdd2_volt_data[] = {