/** * omap36xx_pwrdn_clk_enable_with_hsdiv_restore - enable clocks suffering * from HSDivider PWRDN problem Implements Errata ID: i556. * @clk: DPLL output struct clk * * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset * valueafter their respective PWRDN bits are set. Any dummy write * (Any other value different from the Read value) to the * corresponding CM_CLKSEL register will refresh the dividers. */ int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk) { struct clk_divider *parent; struct clk_hw *parent_hw; u32 dummy_v, orig_v; struct clk_hw_omap *omap_clk = to_clk_hw_omap(clk); int ret; /* Clear PWRDN bit of HSDIVIDER */ ret = omap2_dflt_clk_enable(clk); parent_hw = __clk_get_hw(__clk_get_parent(clk->clk)); parent = to_clk_divider(parent_hw); /* Restore the dividers */ if (!ret) { orig_v = omap2_clk_readl(omap_clk, parent->reg); dummy_v = orig_v; /* Write any other value different from the Read value */ dummy_v ^= (1 << parent->shift); omap2_clk_writel(dummy_v, omap_clk, parent->reg); /* Write the original divider */ omap2_clk_writel(orig_v, omap_clk, parent->reg); } return ret; }
/** * omap2_dflt_clk_disable - disable a clock in the hardware * @hw: struct clk_hw * of the clock to disable * * Disable the clock @hw in the hardware, and call into the OMAP * clockdomain code to "disable" the corresponding clockdomain if all * clocks/hwmods in that clockdomain are now disabled. No return * value. */ void omap2_dflt_clk_disable(struct clk_hw *hw) { struct clk_hw_omap *clk; u32 v; clk = to_clk_hw_omap(hw); if (!clk->enable_reg) { /* * 'independent' here refers to a clock which is not * controlled by its parent. */ pr_err("%s: independent clock %s has no enable_reg\n", __func__, __clk_get_name(hw->clk)); return; } v = omap2_clk_readl(clk, clk->enable_reg); if (clk->flags & INVERT_ENABLE) v |= (1 << clk->enable_bit); else v &= ~(1 << clk->enable_bit); omap2_clk_writel(v, clk, clk->enable_reg); /* No OCP barrier needed here since it is a disable operation */ if (clkdm_control && clk->clkdm) clkdm_clk_disable(clk->clkdm, hw->clk); }
/* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */ static void _omap3_dpll_write_clken(struct clk_hw_omap *clk, u8 clken_bits) { const struct dpll_data *dd; u32 v; dd = clk->dpll_data; v = omap2_clk_readl(clk, dd->control_reg); v &= ~dd->enable_mask; v |= clken_bits << __ffs(dd->enable_mask); omap2_clk_writel(v, clk, dd->control_reg); }
void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk) { u32 v; u32 mask; if (!clk || !clk->clksel_reg) return; mask = clk->flags & CLOCK_CLKOUTX2 ? OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK : OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK; v = omap2_clk_readl(clk, clk->clksel_reg); /* Set the bit to deny gatectrl */ v |= mask; omap2_clk_writel(v, clk, clk->clksel_reg); }
void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk) { u32 v; u32 mask; if (!clk || !clk->clksel_reg || !cpu_is_omap44xx()) return; mask = clk->flags & CLOCK_CLKOUTX2 ? OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK : OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK; v = omap2_clk_readl(clk, clk->clksel_reg); /* Clear the bit to allow gatectrl */ v &= ~mask; omap2_clk_writel(v, clk, clk->clksel_reg); }
/** * omap2_dflt_clk_enable - enable a clock in the hardware * @hw: struct clk_hw * of the clock to enable * * Enable the clock @hw in the hardware. We first call into the OMAP * clockdomain code to "enable" the corresponding clockdomain if this * is the first enabled user of the clockdomain. Then program the * hardware to enable the clock. Then wait for the IP block that uses * this clock to leave idle (if applicable). Returns the error value * from clkdm_clk_enable() if it terminated with an error, or -EINVAL * if @hw has a null clock enable_reg, or zero upon success. */ int omap2_dflt_clk_enable(struct clk_hw *hw) { struct clk_hw_omap *clk; u32 v; int ret = 0; clk = to_clk_hw_omap(hw); if (clkdm_control && clk->clkdm) { ret = clkdm_clk_enable(clk->clkdm, hw->clk); if (ret) { WARN(1, "%s: could not enable %s's clockdomain %s: %d\n", __func__, __clk_get_name(hw->clk), clk->clkdm->name, ret); return ret; } } if (unlikely(clk->enable_reg == NULL)) { pr_err("%s: %s missing enable_reg\n", __func__, __clk_get_name(hw->clk)); ret = -EINVAL; goto err; } /* FIXME should not have INVERT_ENABLE bit here */ v = omap2_clk_readl(clk, clk->enable_reg); if (clk->flags & INVERT_ENABLE) v &= ~(1 << clk->enable_bit); else v |= (1 << clk->enable_bit); omap2_clk_writel(v, clk, clk->enable_reg); v = omap2_clk_readl(clk, clk->enable_reg); /* OCP barrier */ if (clk->ops && clk->ops->find_idlest) _omap2_module_wait_ready(clk); return 0; err: if (clkdm_control && clk->clkdm) clkdm_clk_disable(clk->clkdm, hw->clk); return ret; }
/** * omap3_dpll_deny_idle - prevent DPLL from automatically idling * @clk: struct clk * of the DPLL to operate on * * Disable DPLL automatic idle control. No return value. */ void omap3_dpll_deny_idle(struct clk_hw_omap *clk) { const struct dpll_data *dd; u32 v; if (!clk || !clk->dpll_data) return; dd = clk->dpll_data; if (!dd->autoidle_reg) return; v = omap2_clk_readl(clk, dd->autoidle_reg); v &= ~dd->autoidle_mask; v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask); omap2_clk_writel(v, clk, dd->autoidle_reg); }
/** * omap3_dpll_allow_idle - enable DPLL autoidle bits * @clk: struct clk * of the DPLL to operate on * * Enable DPLL automatic idle control. This automatic idle mode * switching takes effect only when the DPLL is locked, at least on * OMAP3430. The DPLL will enter low-power stop when its downstream * clocks are gated. No return value. */ void omap3_dpll_allow_idle(struct clk_hw_omap *clk) { const struct dpll_data *dd; u32 v; if (!clk || !clk->dpll_data) return; dd = clk->dpll_data; if (!dd->autoidle_reg) return; /* * REVISIT: CORE DPLL can optionally enter low-power bypass * by writing 0x5 instead of 0x1. Add some mechanism to * optionally enter this mode. */ v = omap2_clk_readl(clk, dd->autoidle_reg); v &= ~dd->autoidle_mask; v |= DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask); omap2_clk_writel(v, clk, dd->autoidle_reg); }
/* * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly * @clk: struct clk * of DPLL to set * @freqsel: FREQSEL value to set * * Program the DPLL with the last M, N values calculated, and wait for * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success. */ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel) { struct dpll_data *dd = clk->dpll_data; u8 dco, sd_div; u32 v; /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ _omap3_noncore_dpll_bypass(clk); if (dd->sink_clkdm) clkdm_clk_enable(dd->sink_clkdm, clk->hw.clk); /* * Set jitter correction. Jitter correction applicable for OMAP343X * only since freqsel field is no longer present on other devices. */ if (cpu_is_omap343x()) { v = omap2_clk_readl(clk, dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); omap2_clk_writel(v, clk, dd->control_reg); } /* Set DPLL multiplier, divider */ v = omap2_clk_readl(clk, dd->mult_div1_reg); /* Handle Duty Cycle Correction */ if (dd->dcc_mask) { if (dd->last_rounded_rate >= dd->dcc_rate) v |= dd->dcc_mask; /* Enable DCC */ else v &= ~dd->dcc_mask; /* Disable DCC */ } v &= ~(dd->mult_mask | dd->div1_mask); v |= dd->last_rounded_m << __ffs(dd->mult_mask); v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask); /* Configure dco and sd_div for dplls that have these fields */ if (dd->dco_mask) { _lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n); v &= ~(dd->dco_mask); v |= dco << __ffs(dd->dco_mask); } if (dd->sddiv_mask) { _lookup_sddiv(clk, &sd_div, dd->last_rounded_m, dd->last_rounded_n); v &= ~(dd->sddiv_mask); v |= sd_div << __ffs(dd->sddiv_mask); } omap2_clk_writel(v, clk, dd->mult_div1_reg); /* Set 4X multiplier and low-power mode */ if (dd->m4xen_mask || dd->lpmode_mask) { v = omap2_clk_readl(clk, dd->control_reg); if (dd->m4xen_mask) { if (dd->last_rounded_m4xen) v |= dd->m4xen_mask; else v &= ~dd->m4xen_mask; } if (dd->lpmode_mask) { if (dd->last_rounded_lpmode) v |= dd->lpmode_mask; else v &= ~dd->lpmode_mask; } omap2_clk_writel(v, clk, dd->control_reg); } /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ _omap3_noncore_dpll_lock(clk); if (dd->sink_clkdm) clkdm_clk_disable(dd->sink_clkdm, clk->hw.clk); return 0; }