diff options
Diffstat (limited to 'arch/arm/mach-omap2/dpll44xx.c')
-rw-r--r-- | arch/arm/mach-omap2/dpll44xx.c | 209 |
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); + } +} |