aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/dpll44xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2/dpll44xx.c')
-rw-r--r--arch/arm/mach-omap2/dpll44xx.c576
1 files changed, 575 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c
index 4e4da61..d4cfe2a 100644
--- a/arch/arm/mach-omap2/dpll44xx.c
+++ b/arch/arm/mach-omap2/dpll44xx.c
@@ -14,12 +14,219 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/bitops.h>
-
+#include <linux/spinlock.h>
#include <plat/cpu.h>
#include <plat/clock.h>
+#include <plat/common.h>
+
+#include <mach/emif.h>
+#include <mach/omap4-common.h>
#include "clock.h"
+#include "clock44xx.h"
+#include "cm.h"
+#include "cm44xx.h"
+#include "cm1_44xx.h"
+#include "cm2_44xx.h"
+#include "cminst44xx.h"
+#include "clock44xx.h"
+#include "clockdomain.h"
#include "cm-regbits-44xx.h"
+#include "prcm44xx.h"
+
+#define MAX_FREQ_UPDATE_TIMEOUT 100000
+
+static struct clockdomain *l3_emif_clkdm;
+static DEFINE_SPINLOCK(l3_emif_lock);
+
+/**
+ * omap4_core_dpll_m2_set_rate - set CORE DPLL M2 divider
+ * @clk: struct clk * of DPLL to set
+ * @rate: rounded target rate
+ *
+ * Programs the CM shadow registers to update CORE DPLL M2
+ * divider. M2 divider is used to clock external DDR and its
+ * reconfiguration on frequency change is managed through a
+ * hardware sequencer. This is managed by the PRCM with EMIF
+ * uding shadow registers.
+ * Returns -EINVAL/-1 on error and 0 on success.
+ */
+int omap4_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
+{
+ int i = 0;
+ u32 validrate = 0, shadow_freq_cfg1 = 0, new_div = 0;
+ unsigned long flags;
+
+ if (!clk || !rate)
+ return -EINVAL;
+
+ validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
+ if (validrate != rate)
+ return -EINVAL;
+
+ /* Just to avoid look-up on every call to speed up */
+ if (!l3_emif_clkdm) {
+ l3_emif_clkdm = clkdm_lookup("l3_emif_clkdm");
+ if (!l3_emif_clkdm) {
+ pr_err("%s: clockdomain lookup failed\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ spin_lock_irqsave(&l3_emif_lock, flags);
+
+ /* Configures MEMIF domain in SW_WKUP */
+ clkdm_wakeup(l3_emif_clkdm);
+
+ /*
+ * Errata ID: i728
+ *
+ * DESCRIPTION:
+ *
+ * If during a small window the following three events occur:
+ *
+ * 1) The EMIF_PWR_MGMT_CTRL[7:4] REG_SR_TIM SR_TIMING counter expires
+ * 2) Frequency change update is requested CM_SHADOW_FREQ_CONFIG1
+ * FREQ_UPDATE set to 1
+ * 3) OCP access is requested
+ *
+ * There will be clock instability on the DDR interface.
+ *
+ * WORKAROUND:
+ *
+ * Prevent event 1) while event 2) is happening.
+ *
+ * Disable the self-refresh when requesting a frequency change.
+ * Before requesting a frequency change, program
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x0
+ * (omap_emif_frequency_pre_notify)
+ *
+ * When the frequency change is completed, reprogram
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x2.
+ * (omap_emif_frequency_post_notify)
+ */
+ omap_emif_frequency_pre_notify();
+
+ /*
+ * Program EMIF timing parameters in EMIF shadow registers
+ * for targetted DRR clock.
+ * DDR Clock = core_dpll_m2 / 2
+ */
+ omap_emif_setup_registers(validrate >> 1, LPDDR2_VOLTAGE_STABLE);
+
+ /*
+ * FREQ_UPDATE sequence:
+ * - DLL_OVERRIDE=0 (DLL lock & code must not be overridden
+ * after CORE DPLL lock)
+ * - DLL_RESET=1 (DLL must be reset upon frequency change)
+ * - DPLL_CORE_M2_DIV with same value as the one already
+ * in direct register
+ * - DPLL_CORE_DPLL_EN=0x7 (to make CORE DPLL lock)
+ * - FREQ_UPDATE=1 (to start HW sequence)
+ */
+ shadow_freq_cfg1 = (1 << OMAP4430_DLL_RESET_SHIFT) |
+ (new_div << OMAP4430_DPLL_CORE_M2_DIV_SHIFT) |
+ (DPLL_LOCKED << OMAP4430_DPLL_CORE_DPLL_EN_SHIFT) |
+ (1 << OMAP4430_FREQ_UPDATE_SHIFT);
+ shadow_freq_cfg1 &= ~OMAP4430_DLL_OVERRIDE_MASK;
+ __raw_writel(shadow_freq_cfg1, OMAP4430_CM_SHADOW_FREQ_CONFIG1);
+
+ /* wait for the configuration to be applied */
+ omap_test_timeout(((__raw_readl(OMAP4430_CM_SHADOW_FREQ_CONFIG1)
+ & OMAP4430_FREQ_UPDATE_MASK) == 0),
+ MAX_FREQ_UPDATE_TIMEOUT, i);
+
+ /* Re-enable DDR self refresh */
+ omap_emif_frequency_post_notify();
+
+ /* Configures MEMIF domain back to HW_WKUP */
+ clkdm_allow_idle(l3_emif_clkdm);
+
+ spin_unlock_irqrestore(&l3_emif_lock, flags);
+
+ if (i == MAX_FREQ_UPDATE_TIMEOUT) {
+ pr_err("%s: Frequency update for CORE DPLL M2 change failed\n",
+ __func__);
+ return -1;
+ }
+
+ /* Update the clock change */
+ clk->rate = validrate;
+
+ return 0;
+}
+
+
+/**
+ * omap4_prcm_freq_update - set freq_update bit
+ *
+ * Programs the CM shadow registers to update EMIF
+ * parametrs. Few usecase only few registers needs to
+ * be updated using prcm freq update sequence.
+ * EMIF read-idle control and zq-config needs to be
+ * updated for temprature alerts and voltage change
+ * Returns -1 on error and 0 on success.
+ */
+int omap4_prcm_freq_update(void)
+{
+ u32 shadow_freq_cfg1;
+ int i = 0;
+ unsigned long flags;
+
+ if (!l3_emif_clkdm) {
+ pr_err("%s: clockdomain lookup failed\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&l3_emif_lock, flags);
+ /* Configures MEMIF domain in SW_WKUP */
+ clkdm_wakeup(l3_emif_clkdm);
+
+ /* Disable DDR self refresh (Errata ID: i728) */
+ omap_emif_frequency_pre_notify();
+
+ /*
+ * FREQ_UPDATE sequence:
+ * - DLL_OVERRIDE=0 (DLL lock & code must not be overridden
+ * after CORE DPLL lock)
+ * - FREQ_UPDATE=1 (to start HW sequence)
+ */
+ shadow_freq_cfg1 = __raw_readl(OMAP4430_CM_SHADOW_FREQ_CONFIG1);
+ shadow_freq_cfg1 |= (1 << OMAP4430_DLL_RESET_SHIFT) |
+ (1 << OMAP4430_FREQ_UPDATE_SHIFT);
+ shadow_freq_cfg1 &= ~OMAP4430_DLL_OVERRIDE_MASK;
+ __raw_writel(shadow_freq_cfg1, OMAP4430_CM_SHADOW_FREQ_CONFIG1);
+
+ /* wait for the configuration to be applied */
+ omap_test_timeout(((__raw_readl(OMAP4430_CM_SHADOW_FREQ_CONFIG1)
+ & OMAP4430_FREQ_UPDATE_MASK) == 0),
+ MAX_FREQ_UPDATE_TIMEOUT, i);
+
+ /* Re-enable DDR self refresh */
+ omap_emif_frequency_post_notify();
+
+ /* Configures MEMIF domain back to HW_WKUP */
+ clkdm_allow_idle(l3_emif_clkdm);
+
+ spin_unlock_irqrestore(&l3_emif_lock, flags);
+
+ if (i == MAX_FREQ_UPDATE_TIMEOUT) {
+ pr_err("%s: Frequency update failed (call from %pF)\n",
+ __func__, (void *)_RET_IP_);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Use a very high retry count - we should not hit this condition */
+#define MAX_DPLL_WAIT_TRIES 1000000
+
+#define OMAP_1_5GHz 1500000000
+#define OMAP_1_2GHz 1200000000
+#define OMAP_1GHz 1000000000
+#define OMAP_920MHz 920000000
+#define OMAP_748MHz 748000000
/* Supported only on OMAP4 */
int omap4_dpllmx_gatectrl_read(struct clk *clk)
@@ -82,3 +289,370 @@ const struct clkops clkops_omap4_dpllmx_ops = {
.deny_idle = omap4_dpllmx_deny_gatectrl,
};
+static void omap4460_mpu_dpll_update_children(unsigned long rate)
+{
+ u32 v;
+
+ /*
+ * The interconnect frequency to EMIF should
+ * be switched between MPU clk divide by 4 (for
+ * frequencies higher than 920Mhz) and MPU clk divide
+ * by 2 (for frequencies lower than or equal to 920Mhz)
+ * Also the async bridge to ABE must be MPU clk divide
+ * by 8 for MPU clk > 748Mhz and MPU clk divide by 4
+ * for lower frequencies.
+ */
+ v = __raw_readl(OMAP4430_CM_MPU_MPU_CLKCTRL);
+ if (rate > OMAP_920MHz)
+ v |= OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK;
+ else
+ v &= ~OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK;
+
+ if (rate > OMAP_748MHz)
+ v |= OMAP4460_CLKSEL_ABE_DIV_MODE_MASK;
+ else
+ v &= ~OMAP4460_CLKSEL_ABE_DIV_MODE_MASK;
+ __raw_writel(v, OMAP4430_CM_MPU_MPU_CLKCTRL);
+}
+
+int omap4460_mpu_dpll_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct dpll_data *dd;
+ u32 v;
+ unsigned long dpll_rate;
+
+ if (!clk || !rate || !clk->parent)
+ return -EINVAL;
+
+ dd = clk->parent->dpll_data;
+
+ if (!dd)
+ return -EINVAL;
+
+ if (!clk->parent->set_rate)
+ return -EINVAL;
+
+ if (rate > clk->rate)
+ omap4460_mpu_dpll_update_children(rate);
+
+ /*
+ * On OMAP4460, to obtain MPU DPLL frequency higher
+ * than 1GHz, DCC (Duty Cycle Correction) needs to
+ * be enabled.
+ * And needs to be kept disabled for < 1 Ghz.
+ */
+ dpll_rate = omap2_get_dpll_rate(clk->parent);
+ if (rate <= OMAP_1_5GHz) {
+ /* If DCC is enabled, disable it */
+ v = __raw_readl(dd->mult_div1_reg);
+ if (v & OMAP4460_DCC_EN_MASK) {
+ v &= ~OMAP4460_DCC_EN_MASK;
+ __raw_writel(v, dd->mult_div1_reg);
+ }
+
+ if (rate != dpll_rate)
+ clk->parent->set_rate(clk->parent, rate);
+ } else {
+ /*
+ * On 4460, the MPU clk for frequencies higher than 1Ghz
+ * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while
+ * value of M3 is fixed to 1. Hence for frequencies higher
+ * than 1 Ghz, lock the DPLL at half the rate so the
+ * CLKOUTX2_M3 then matches the requested rate.
+ */
+ if (rate != dpll_rate * 2)
+ clk->parent->set_rate(clk->parent, rate / 2);
+
+ v = __raw_readl(dd->mult_div1_reg);
+ v &= ~OMAP4460_DCC_COUNT_MAX_MASK;
+ v |= (5 << OMAP4460_DCC_COUNT_MAX_SHIFT);
+ __raw_writel(v, dd->mult_div1_reg);
+
+ v |= OMAP4460_DCC_EN_MASK;
+ __raw_writel(v, dd->mult_div1_reg);
+ }
+
+ if (rate < clk->rate)
+ omap4460_mpu_dpll_update_children(rate);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+long omap4460_mpu_dpll_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (!clk || !rate || !clk->parent)
+ return -EINVAL;
+
+ if (clk->parent->round_rate)
+ return clk->parent->round_rate(clk->parent, rate);
+ else
+ return 0;
+}
+
+unsigned long omap4460_mpu_dpll_recalc(struct clk *clk)
+{
+ struct dpll_data *dd;
+ u32 v;
+
+ if (!clk || !clk->parent)
+ return -EINVAL;
+
+ dd = clk->parent->dpll_data;
+
+ if (!dd)
+ return -EINVAL;
+
+ v = __raw_readl(dd->mult_div1_reg);
+ if (v & OMAP4460_DCC_EN_MASK)
+ return omap2_get_dpll_rate(clk->parent) * 2;
+ else
+ return omap2_get_dpll_rate(clk->parent);
+}
+
+unsigned long omap4_dpll_regm4xen_recalc(struct clk *clk)
+{
+ u32 v;
+ unsigned long rate;
+ struct dpll_data *dd;
+
+ if (!clk || !clk->dpll_data)
+ return -EINVAL;
+
+ dd = clk->dpll_data;
+
+ rate = omap2_get_dpll_rate(clk);
+
+ /* regm4xen adds a multiplier of 4 to DPLL calculations */
+ v = __raw_readl(dd->control_reg);
+ if (v & OMAP4430_DPLL_REGM4XEN_MASK)
+ rate *= OMAP4430_REGM4XEN_MULT;
+
+ return rate;
+}
+
+long omap4_dpll_regm4xen_round_rate(struct clk *clk, unsigned long target_rate)
+{
+ u32 v;
+ struct dpll_data *dd;
+
+ if (!clk || !clk->dpll_data)
+ return -EINVAL;
+
+ dd = clk->dpll_data;
+
+ /* regm4xen adds a multiplier of 4 to DPLL calculations */
+ v = __raw_readl(dd->control_reg) & OMAP4430_DPLL_REGM4XEN_MASK;
+
+ if (v)
+ target_rate = target_rate / OMAP4430_REGM4XEN_MULT;
+
+ omap2_dpll_round_rate(clk, target_rate);
+
+ if (v)
+ clk->dpll_data->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
+
+ return clk->dpll_data->last_rounded_rate;
+}
+
+struct dpll_reg_tuple {
+ u16 addr;
+ u32 val;
+};
+
+struct omap4_dpll_regs {
+ char *name;
+ u32 mod_partition;
+ u32 mod_inst;
+ struct dpll_reg_tuple clkmode;
+ struct dpll_reg_tuple autoidle;
+ struct dpll_reg_tuple idlest;
+ struct dpll_reg_tuple clksel;
+ struct dpll_reg_tuple div_m2;
+ struct dpll_reg_tuple div_m3;
+ struct dpll_reg_tuple div_m4;
+ struct dpll_reg_tuple div_m5;
+ struct dpll_reg_tuple div_m6;
+ struct dpll_reg_tuple div_m7;
+ struct dpll_reg_tuple clkdcoldo;
+};
+
+static struct omap4_dpll_regs dpll_regs[] = {
+ /* MPU DPLL */
+ { .name = "mpu",
+ .mod_partition = OMAP4430_CM1_PARTITION,
+ .mod_inst = OMAP4430_CM1_CKGEN_INST,
+ .clkmode = {.addr = OMAP4_CM_CLKMODE_DPLL_MPU_OFFSET},
+ .autoidle = {.addr = OMAP4_CM_AUTOIDLE_DPLL_MPU_OFFSET},
+ .idlest = {.addr = OMAP4_CM_IDLEST_DPLL_MPU_OFFSET},
+ .clksel = {.addr = OMAP4_CM_CLKSEL_DPLL_MPU_OFFSET},
+ .div_m2 = {.addr = OMAP4_CM_DIV_M2_DPLL_MPU_OFFSET},
+ },
+ /* IVA DPLL */
+ { .name = "iva",
+ .mod_partition = OMAP4430_CM1_PARTITION,
+ .mod_inst = OMAP4430_CM1_CKGEN_INST,
+ .clkmode = {.addr = OMAP4_CM_CLKMODE_DPLL_IVA_OFFSET},
+ .autoidle = {.addr = OMAP4_CM_AUTOIDLE_DPLL_IVA_OFFSET},
+ .idlest = {.addr = OMAP4_CM_IDLEST_DPLL_IVA_OFFSET},
+ .clksel = {.addr = OMAP4_CM_CLKSEL_DPLL_IVA_OFFSET},
+ .div_m4 = {.addr = OMAP4_CM_DIV_M4_DPLL_IVA_OFFSET},
+ .div_m5 = {.addr = OMAP4_CM_DIV_M5_DPLL_IVA_OFFSET},
+ },
+ /* ABE DPLL */
+ { .name = "abe",
+ .mod_partition = OMAP4430_CM1_PARTITION,
+ .mod_inst = OMAP4430_CM1_CKGEN_INST,
+ .clkmode = {.addr = OMAP4_CM_CLKMODE_DPLL_ABE_OFFSET},
+ .autoidle = {.addr = OMAP4_CM_AUTOIDLE_DPLL_ABE_OFFSET},
+ .idlest = {.addr = OMAP4_CM_IDLEST_DPLL_ABE_OFFSET},
+ .clksel = {.addr = OMAP4_CM_CLKSEL_DPLL_ABE_OFFSET},
+ .div_m2 = {.addr = OMAP4_CM_DIV_M2_DPLL_ABE_OFFSET},
+ .div_m3 = {.addr = OMAP4_CM_DIV_M3_DPLL_ABE_OFFSET},
+ },
+ /* USB DPLL */
+ { .name = "usb",
+ .mod_partition = OMAP4430_CM2_PARTITION,
+ .mod_inst = OMAP4430_CM2_CKGEN_INST,
+ .clkmode = {.addr = OMAP4_CM_CLKMODE_DPLL_USB_OFFSET},
+ .autoidle = {.addr = OMAP4_CM_AUTOIDLE_DPLL_USB_OFFSET},
+ .idlest = {.addr = OMAP4_CM_IDLEST_DPLL_USB_OFFSET},
+ .clksel = {.addr = OMAP4_CM_CLKSEL_DPLL_USB_OFFSET},
+ .div_m2 = {.addr = OMAP4_CM_DIV_M2_DPLL_USB_OFFSET},
+ .clkdcoldo = {.addr = OMAP4_CM_CLKDCOLDO_DPLL_USB_OFFSET},
+ },
+ /* PER DPLL */
+ { .name = "per",
+ .mod_partition = OMAP4430_CM2_PARTITION,
+ .mod_inst = OMAP4430_CM2_CKGEN_INST,
+ .clkmode = {.addr = OMAP4_CM_CLKMODE_DPLL_PER_OFFSET},
+ .autoidle = {.addr = OMAP4_CM_AUTOIDLE_DPLL_PER_OFFSET},
+ .idlest = {.addr = OMAP4_CM_IDLEST_DPLL_PER_OFFSET},
+ .clksel = {.addr = OMAP4_CM_CLKSEL_DPLL_PER_OFFSET},
+ .div_m2 = {.addr = OMAP4_CM_DIV_M2_DPLL_PER_OFFSET},
+ .div_m3 = {.addr = OMAP4_CM_DIV_M3_DPLL_PER_OFFSET},
+ .div_m4 = {.addr = OMAP4_CM_DIV_M4_DPLL_PER_OFFSET},
+ .div_m5 = {.addr = OMAP4_CM_DIV_M5_DPLL_PER_OFFSET},
+ .div_m6 = {.addr = OMAP4_CM_DIV_M6_DPLL_PER_OFFSET},
+ .div_m7 = {.addr = OMAP4_CM_DIV_M7_DPLL_PER_OFFSET},
+ },
+};
+
+static inline void omap4_dpll_store_reg(struct omap4_dpll_regs *dpll_reg,
+ struct dpll_reg_tuple *tuple)
+{
+ if (tuple->addr)
+ tuple->val =
+ omap4_cminst_read_inst_reg(dpll_reg->mod_partition,
+ dpll_reg->mod_inst, tuple->addr);
+}
+
+void omap4_dpll_prepare_off(void)
+{
+ u32 i;
+ struct omap4_dpll_regs *dpll_reg = dpll_regs;
+
+ for (i = 0; i < ARRAY_SIZE(dpll_regs); i++, dpll_reg++) {
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->clkmode);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->autoidle);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->clksel);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->div_m2);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->div_m3);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->div_m4);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->div_m5);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->div_m6);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->div_m7);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->clkdcoldo);
+ omap4_dpll_store_reg(dpll_reg, &dpll_reg->idlest);
+ }
+}
+
+static void omap4_dpll_print_reg(struct omap4_dpll_regs *dpll_reg, char *name,
+ struct dpll_reg_tuple *tuple)
+{
+ if (tuple->addr)
+ pr_warn("%s - Address offset = 0x%08x, value=0x%08x\n", name,
+ tuple->addr, tuple->val);
+}
+
+static void omap4_dpll_dump_regs(struct omap4_dpll_regs *dpll_reg)
+{
+ pr_warn("%s: Unable to lock dpll %s[part=%x inst=%x]:\n",
+ __func__, dpll_reg->name, dpll_reg->mod_partition,
+ dpll_reg->mod_inst);
+ omap4_dpll_print_reg(dpll_reg, "clksel", &dpll_reg->clksel);
+ omap4_dpll_print_reg(dpll_reg, "div_m2", &dpll_reg->div_m2);
+ omap4_dpll_print_reg(dpll_reg, "div_m3", &dpll_reg->div_m3);
+ omap4_dpll_print_reg(dpll_reg, "div_m4", &dpll_reg->div_m4);
+ omap4_dpll_print_reg(dpll_reg, "div_m5", &dpll_reg->div_m5);
+ omap4_dpll_print_reg(dpll_reg, "div_m6", &dpll_reg->div_m6);
+ omap4_dpll_print_reg(dpll_reg, "div_m7", &dpll_reg->div_m7);
+ omap4_dpll_print_reg(dpll_reg, "clkdcoldo", &dpll_reg->clkdcoldo);
+ omap4_dpll_print_reg(dpll_reg, "clkmode", &dpll_reg->clkmode);
+ omap4_dpll_print_reg(dpll_reg, "autoidle", &dpll_reg->autoidle);
+ if (dpll_reg->idlest.addr)
+ pr_warn("idlest - Address offset = 0x%08x, before val=0x%08x"
+ " after = 0x%08x\n", dpll_reg->idlest.addr,
+ dpll_reg->idlest.val,
+ omap4_cminst_read_inst_reg(dpll_reg->mod_partition,
+ dpll_reg->mod_inst,
+ dpll_reg->idlest.addr));
+}
+
+static void omap4_wait_dpll_lock(struct omap4_dpll_regs *dpll_reg)
+{
+ int j = 0;
+
+ /* Return if we dont need to lock. */
+ if ((dpll_reg->clkmode.val & OMAP4430_DPLL_EN_MASK) !=
+ DPLL_LOCKED << OMAP4430_DPLL_EN_SHIFT);
+ return;
+
+ while ((omap4_cminst_read_inst_reg(dpll_reg->mod_partition,
+ dpll_reg->mod_inst,
+ dpll_reg->idlest.addr)
+ & OMAP4430_ST_DPLL_CLK_MASK) !=
+ 0x1 << OMAP4430_ST_DPLL_CLK_SHIFT
+ && j < MAX_DPLL_WAIT_TRIES) {
+ j++;
+ udelay(1);
+ }
+
+ /* if we are unable to lock, warn and move on.. */
+ if (j == MAX_DPLL_WAIT_TRIES)
+ omap4_dpll_dump_regs(dpll_reg);
+}
+
+static inline void omap4_dpll_restore_reg(struct omap4_dpll_regs *dpll_reg,
+ struct dpll_reg_tuple *tuple)
+{
+ if (tuple->addr)
+ omap4_cminst_write_inst_reg(tuple->val, dpll_reg->mod_partition,
+ dpll_reg->mod_inst, tuple->addr);
+}
+
+void omap4_dpll_resume_off(void)
+{
+ u32 i;
+ struct omap4_dpll_regs *dpll_reg = dpll_regs;
+
+ for (i = 0; i < ARRAY_SIZE(dpll_regs); i++, dpll_reg++) {
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->clksel);
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->div_m2);
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->div_m3);
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->div_m4);
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->div_m5);
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->div_m6);
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->div_m7);
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->clkdcoldo);
+
+ /* Restore clkmode after the above registers are restored */
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->clkmode);
+
+ omap4_wait_dpll_lock(dpll_reg);
+
+ /* Restore autoidle settings after the dpll is locked */
+ omap4_dpll_restore_reg(dpll_reg, &dpll_reg->autoidle);
+ }
+}