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.c209
1 files changed, 209 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c
index 6e26ba7..e4051c2 100644
--- a/arch/arm/mach-omap2/dpll44xx.c
+++ b/arch/arm/mach-omap2/dpll44xx.c
@@ -24,10 +24,15 @@
#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;
@@ -164,6 +169,9 @@ int omap4_prcm_freq_update(void)
return 0;
}
+/* Use a very high retry count - we should not hit this condition */
+#define MAX_DPLL_WAIT_TRIES 1000000
+
#define OMAP_1GHz 1000000000
#define OMAP_920MHz 920000000
#define OMAP_748MHz 748000000
@@ -381,3 +389,204 @@ long omap4_dpll_regm4xen_round_rate(struct clk *clk, unsigned long target_rate)
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);
+ }
+}