/* * PRCM Interrupt Handler Helper Function * * The purpose of this function is to clear any wake-up events latched * in the PRCM PM_WKST_x registers. It is possible that a wake-up event * may occur whilst attempting to clear a PM_WKST_x register and thus * set another bit in this register. A while loop is used to ensure * that any peripheral wake-up events occurring while attempting to * clear the PM_WKST_x are detected and cleared. */ static int prcm_clear_mod_irqs(s16 module, u8 regs) { u32 wkst, fclk, iclk, clken; u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1; u16 iclk_off = (regs == 3) ? CM_ICLKEN3 : CM_ICLKEN1; u16 grpsel_off = (regs == 3) ? OMAP3430ES2_PM_MPUGRPSEL3 : OMAP3430_PM_MPUGRPSEL; int c = 0; wkst = omap2_prm_read_mod_reg(module, wkst_off); wkst &= omap2_prm_read_mod_reg(module, grpsel_off); if (wkst) { iclk = omap2_cm_read_mod_reg(module, iclk_off); fclk = omap2_cm_read_mod_reg(module, fclk_off); while (wkst) { clken = wkst; omap2_cm_set_mod_reg_bits(clken, module, iclk_off); /* * For USBHOST, we don't know whether HOST1 or * HOST2 woke us up, so enable both f-clocks */ if (module == OMAP3430ES2_USBHOST_MOD) clken |= 1 << OMAP3430ES2_EN_USBHOST2_SHIFT; omap2_cm_set_mod_reg_bits(clken, module, fclk_off); omap2_prm_write_mod_reg(wkst, module, wkst_off); wkst = omap2_prm_read_mod_reg(module, wkst_off); c++; } omap2_cm_write_mod_reg(iclk, module, iclk_off); omap2_cm_write_mod_reg(fclk, module, fclk_off); } return c; }
int omap2xxx_cm_fclks_active(void) { u32 f1, f2; f1 = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); f2 = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); return (f1 | f2) ? 1 : 0; }
/* Populate the scratchpad structure with restore structure */ void omap3_save_scratchpad_contents(void) { void __iomem *scratchpad_address; u32 arm_context_addr; struct omap3_scratchpad scratchpad_contents; struct omap3_scratchpad_prcm_block prcm_block_contents; struct omap3_scratchpad_sdrc_block sdrc_block_contents; /* * Populate the Scratchpad contents * * The "get_*restore_pointer" functions are used to provide a * physical restore address where the ROM code jumps while waking * up from MPU OFF/OSWR state. * The restore pointer is stored into the scratchpad. */ scratchpad_contents.boot_config_ptr = 0x0; if (cpu_is_omap3630()) scratchpad_contents.public_restore_ptr = virt_to_phys(omap3_restore_3630); else if (omap_rev() != OMAP3430_REV_ES3_0 && omap_rev() != OMAP3430_REV_ES3_1) scratchpad_contents.public_restore_ptr = virt_to_phys(omap3_restore); else scratchpad_contents.public_restore_ptr = virt_to_phys(omap3_restore_es3); if (omap_type() == OMAP2_DEVICE_TYPE_GP) scratchpad_contents.secure_ram_restore_ptr = 0x0; else scratchpad_contents.secure_ram_restore_ptr = (u32) __pa(omap3_secure_ram_storage); scratchpad_contents.sdrc_module_semaphore = 0x0; scratchpad_contents.prcm_block_offset = 0x2C; scratchpad_contents.sdrc_block_offset = 0x64; /* Populate the PRCM block contents */ prcm_block_contents.prm_clksrc_ctrl = omap2_prm_read_mod_reg(OMAP3430_GR_MOD, OMAP3_PRM_CLKSRC_CTRL_OFFSET); prcm_block_contents.prm_clksel = omap2_prm_read_mod_reg(OMAP3430_CCR_MOD, OMAP3_PRM_CLKSEL_OFFSET); prcm_block_contents.cm_clksel_core = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL); prcm_block_contents.cm_clksel_wkup = omap2_cm_read_mod_reg(WKUP_MOD, CM_CLKSEL); prcm_block_contents.cm_clken_pll = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); /* * As per erratum i671, ROM code does not respect the PER DPLL * programming scheme if CM_AUTOIDLE_PLL..AUTO_PERIPH_DPLL == 1. * Then, in anycase, clear these bits to avoid extra latencies. */ prcm_block_contents.cm_autoidle_pll = omap2_cm_read_mod_reg(PLL_
static int omap2_fclks_active(void) { u32 f1, f2; f1 = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); f2 = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); /* Ignore UART clocks. These are handled by UART core (serial.c) */ f1 &= ~(OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_UART2_MASK); f2 &= ~OMAP24XX_EN_UART3_MASK; if (f1 | f2) return 1; return 0; }
/* * Uses the current prcm set to tell if a rate is valid. * You can go slower, but not faster within a given rate set. */ static long omap2_dpllcore_round_rate(unsigned long target_rate) { u32 high, low, core_clk_src; core_clk_src = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); core_clk_src &= OMAP24XX_CORE_CLK_SRC_MASK; if (core_clk_src == CORE_CLK_SRC_DPLL) { /* DPLL clockout */ high = curr_prcm_set->dpll_speed * 2; low = curr_prcm_set->dpll_speed; } else { /* DPLL clockout x 2 */ high = curr_prcm_set->dpll_speed; low = curr_prcm_set->dpll_speed / 2; } #ifdef DOWN_VARIABLE_DPLL if (target_rate > high) return high; else return target_rate; #else if (target_rate > low) return high; else return low; #endif }
static int omap2_i2c_active(void) { u32 l; l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); return l & (OMAP2420_EN_I2C2_MASK | OMAP2420_EN_I2C1_MASK); }
/** * omap2_cm_wait_idlest_ready - wait for a module to leave idle or standby * @prcm_mod: PRCM module offset * @idlest_id: CM_IDLESTx register ID (i.e., x = 1, 2, 3) * @idlest_shift: shift of the bit in the CM_IDLEST* register to check * * XXX document */ int omap2_cm_wait_module_ready(s16 prcm_mod, u8 idlest_id, u8 idlest_shift) { int ena = 0, i = 0; u8 cm_idlest_reg; u32 mask; if (!idlest_id || (idlest_id > ARRAY_SIZE(cm_idlest_offs))) return -EINVAL; cm_idlest_reg = cm_idlest_offs[idlest_id - 1]; mask = 1 << idlest_shift; if (cpu_is_omap24xx()) ena = mask; else if (cpu_is_omap34xx()) ena = 0; else BUG(); omap_test_timeout(((omap2_cm_read_mod_reg(prcm_mod, cm_idlest_reg) & mask) == ena), MAX_MODULE_READY_TIME, i); return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; }
int omap2xxx_cm_mpu_retention_allowed(void) { u32 l; /* Check for MMC, UART2, UART1, McSPI2, McSPI1 and DSS1. */ l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); if (l & (OMAP2420_EN_MMC_MASK | OMAP24XX_EN_UART2_MASK | OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_MCSPI2_MASK | OMAP24XX_EN_MCSPI1_MASK | OMAP24XX_EN_DSS1_MASK)) return 0; /* Check for UART3. */ l = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); if (l & OMAP24XX_EN_UART3_MASK) return 0; return 1; }
/* Stop APLL */ static void _omap2xxx_apll_disable(u8 enable_bit) { u32 v; v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); v &= ~(EN_APLL_LOCKED << enable_bit); omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN); }
static unsigned long omap3_l3_get_rate(struct device *dev) { int l3_div; l3_div = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL) & OMAP3430_CLKSEL_L3_MASK; return dpll3_clk->rate / l3_div; }
/* Stop APLL */ static void omap2_clk_apll_disable(struct clk *clk) { u32 cval; cval = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); cval &= ~(EN_APLL_LOCKED << clk->enable_bit); omap2_cm_write_mod_reg(cval, PLL_MOD, CM_CLKEN); }
u32 omap2xxx_cm_get_core_clk_src(void) { u32 v; v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); v &= OMAP24XX_CORE_CLK_SRC_MASK; return v; }
static void _omap2xxx_set_apll_autoidle(u8 m, u32 mask) { u32 v; v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); v &= ~mask; v |= m << __ffs(mask); omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); }
static void _omap2xxx_set_dpll_autoidle(u8 m) { u32 v; v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); v &= ~OMAP24XX_AUTO_DPLL_MASK; v |= m << OMAP24XX_AUTO_DPLL_SHIFT; omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); }
static int omap3_l3_set_rate(struct device *dev, unsigned long rate) { int l3_div; l3_div = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL) & OMAP3430_CLKSEL_L3_MASK; return clk_set_rate(dpll3_clk, rate * l3_div); }
static void _ti81xx_write_clktrctrl(u8 c, s16 module, u16 idx, u32 mask) { u32 v; v = omap2_cm_read_mod_reg(module, idx); v &= ~mask; v |= c << __ffs(mask); omap2_cm_write_mod_reg(v, module, idx); }
static void _write_clktrctrl(u8 c, s16 module, u32 mask) { u32 v; v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); v &= ~mask; v |= c << __ffs(mask); omap2_cm_write_mod_reg(v, module, OMAP2_CM_CLKSTCTRL); }
static int omap2_allow_mpu_retention(void) { u32 l; l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); if (l & (OMAP2420_EN_MMC_MASK | OMAP24XX_EN_UART2_MASK | OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_MCSPI2_MASK | OMAP24XX_EN_MCSPI1_MASK | OMAP24XX_EN_DSS1_MASK)) return 0; l = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); if (l & OMAP24XX_EN_UART3_MASK) return 0; if (sti_console_enabled) return 0; return 1; }
bool omap2xxx_cm_is_clkdm_in_hwsup(s16 module, u32 mask) { u32 v; v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); v &= mask; v >>= __ffs(mask); return (v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO) ? 1 : 0; }
/** * omap2xxx_clk_apll_locked - is the APLL locked? * @hw: struct clk_hw * of the APLL to check * * If the APLL IP block referred to by @hw indicates that it's locked, * return true; otherwise, return false. */ static bool omap2xxx_clk_apll_locked(struct clk_hw *hw) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); u32 r, apll_mask; apll_mask = EN_APLL_LOCKED << clk->enable_bit; r = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); return ((r & apll_mask) == apll_mask) ? true : false; }
/* Read-modify-write a register in a CM module. Caller must lock */ u32 omap2_cm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, s16 idx) { u32 v; v = omap2_cm_read_mod_reg(module, idx); v &= ~mask; v |= bits; omap2_cm_write_mod_reg(v, module, idx); return v; }
void omap2xxx_cm_set_mod_dividers(u32 mpu, u32 dsp, u32 gfx, u32 core, u32 mdm) { u32 tmp; omap2_cm_write_mod_reg(mpu, MPU_MOD, CM_CLKSEL); omap2_cm_write_mod_reg(dsp, OMAP24XX_DSP_MOD, CM_CLKSEL); omap2_cm_write_mod_reg(gfx, GFX_MOD, CM_CLKSEL); tmp = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & OMAP24XX_CLKSEL_DSS2_MASK; omap2_cm_write_mod_reg(core | tmp, CORE_MOD, CM_CLKSEL1); if (cpu_is_omap2430()) omap2_cm_write_mod_reg(mdm, OMAP2430_MDM_MOD, CM_CLKSEL); }
/** * omap2xxx_clk_get_core_rate - return the CORE_CLK rate * @clk: pointer to the combined dpll_ck + core_ck (currently "dpll_ck") * * Returns the CORE_CLK rate. CORE_CLK can have one of three rate * sources on OMAP2xxx: the DPLL CLKOUT rate, DPLL CLKOUTX2, or 32KHz * (the latter is unusual). This currently should be called with * struct clk *dpll_ck, which is a composite clock of dpll_ck and * core_ck. */ unsigned long omap2xxx_clk_get_core_rate(struct clk *clk) { long long core_clk; u32 v; core_clk = omap2_get_dpll_rate(clk); v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); v &= OMAP24XX_CORE_CLK_SRC_MASK; if (v == CORE_CLK_SRC_32K) core_clk = 32768; else core_clk *= v; return core_clk; }
u32 omap2xxx_get_apll_clkin(void) { u32 aplls, srate = 0; aplls = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL1); aplls &= OMAP24XX_APLLS_CLKIN_MASK; aplls >>= OMAP24XX_APLLS_CLKIN_SHIFT; if (aplls == APLLS_CLKIN_19_2MHZ) srate = 19200000; else if (aplls == APLLS_CLKIN_13MHZ) srate = 13000000; else if (aplls == APLLS_CLKIN_12MHZ) srate = 12000000; return srate; }
static void omap_st_off(struct omap_mcbsp *mcbsp) { unsigned int w; struct omap_device *od; od = find_omap_device_by_dev(mcbsp->dev); w = MCBSP_ST_READ(mcbsp, SSELCR); MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); w = MCBSP_READ(mcbsp, SSELCR); MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); w = omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE); w |= 1 << (mcbsp->id - 2); omap2_cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE); }
bool omap2_cm_is_clkdm_in_hwsup(s16 module, u32 mask) { u32 v; bool ret = 0; BUG_ON(!cpu_is_omap24xx() && !cpu_is_omap34xx()); v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); v &= mask; v >>= __ffs(mask); if (cpu_is_omap24xx()) ret = (v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO) ? 1 : 0; else ret = (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? 1 : 0; return ret; }
/** * omap3xxx_cm_wait_module_ready - wait for a module to leave idle or standby * @part: PRCM partition, ignored for OMAP3 * @prcm_mod: PRCM module offset * @idlest_id: CM_IDLESTx register ID (i.e., x = 1, 2, 3) * @idlest_shift: shift of the bit in the CM_IDLEST* register to check * * Wait for the PRCM to indicate that the module identified by * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon * success or -EBUSY if the module doesn't enable in time. */ static int omap3xxx_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_id, u8 idlest_shift) { int ena = 0, i = 0; u8 cm_idlest_reg; u32 mask; if (!idlest_id || (idlest_id > ARRAY_SIZE(omap3xxx_cm_idlest_offs))) return -EINVAL; cm_idlest_reg = omap3xxx_cm_idlest_offs[idlest_id - 1]; mask = 1 << idlest_shift; ena = 0; omap_test_timeout(((omap2_cm_read_mod_reg(prcm_mod, cm_idlest_reg) & mask) == ena), MAX_MODULE_READY_TIME, i); return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; }
/* Enable an APLL if off */ static int _omap2xxx_apll_enable(u8 enable_bit, u8 status_bit) { u32 v, m; m = EN_APLL_LOCKED << enable_bit; v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); if (v & m) return 0; /* apll already enabled */ v |= m; omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN); omap2xxx_cm_wait_module_ready(PLL_MOD, 1, status_bit); /* * REVISIT: Should we return an error code if * omap2xxx_cm_wait_module_ready() fails? */ return 0; }
static void omap_st_on(struct omap_mcbsp *mcbsp) { unsigned int w; struct omap_device *od; od = find_omap_device_by_dev(mcbsp->dev); /* * Sidetone uses McBSP ICLK - which must not idle when sidetones * are enabled or sidetones start sounding ugly. */ w = omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE); w &= ~(1 << (mcbsp->id - 2)); omap2_cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE); /* Enable McBSP Sidetone */ w = MCBSP_READ(mcbsp, SSELCR); MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); /* Enable Sidetone from Sidetone Core */ w = MCBSP_ST_READ(mcbsp, SSELCR); MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); }
/* Enable an APLL if off */ static int omap2_clk_apll_enable(struct clk *clk, u32 status_mask) { u32 cval, apll_mask; apll_mask = EN_APLL_LOCKED << clk->enable_bit; cval = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); if ((cval & apll_mask) == apll_mask) return 0; /* apll already enabled */ cval &= ~apll_mask; cval |= apll_mask; omap2_cm_write_mod_reg(cval, PLL_MOD, CM_CLKEN); omap2_cm_wait_idlest(cm_idlest_pll, status_mask, OMAP24XX_CM_IDLEST_VAL, clk->name); /* * REVISIT: Should we return an error code if omap2_wait_clock_ready() * fails? */ return 0; }