/** * 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 (IS_ERR(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_hw_get_name(hw)); return; } v = ti_clk_ll_ops->clk_readl(clk->enable_reg); if (clk->flags & INVERT_ENABLE) v |= (1 << clk->enable_bit); else v &= ~(1 << clk->enable_bit); ti_clk_ll_ops->clk_writel(v, clk->enable_reg); /* No OCP barrier needed here since it is a disable operation */ if (!(ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) && clk->clkdm) ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); }
/** * omap3_noncore_dpll_set_rate - set rate for a DPLL clock * @hw: pointer to the clock to set parent for * @rate: target rate for the clock * @parent_rate: rate of the parent clock * * Sets rate for a DPLL clock. First checks if the clock parent is * reference clock (in bypass mode, the rate of the clock can't be * changed) and proceeds with the rate change operation. Returns 0 * with success, negative error value otherwise. */ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); struct dpll_data *dd; u16 freqsel = 0; int ret; if (!hw || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk_hw_get_parent(hw) != dd->clk_ref) return -EINVAL; if (dd->last_rounded_rate == 0) return -EINVAL; /* Freqsel is available only on OMAP343X devices */ if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) { freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); WARN_ON(!freqsel); } pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, clk_hw_get_name(hw), rate); ret = omap3_noncore_dpll_program(clk, freqsel); return ret; }
/** * omap3_dpll4_set_rate_and_parent - set rate and parent for omap3 per-dpll * @hw: clock to change * @rate: target rate for clock * @parent_rate: rate of the parent clock * @index: parent index, 0 - reference clock, 1 - bypass clock * * Check if the current SoC support the per-dpll reprogram operation * or not, and then do the rate + parent change if supported. Returns * -EINVAL if not supported, 0 for success, and potential error codes * from the clock rate change. */ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index) { if (ti_clk_get_features()->flags & TI_CLK_DPLL4_DENY_REPROGRAM) { pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n"); return -EINVAL; } return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, index); }
/** * 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; bool clkdm_control; if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) clkdm_control = false; else clkdm_control = true; clk = to_clk_hw_omap(hw); if (clkdm_control && clk->clkdm) { ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); if (ret) { WARN(1, "%s: could not enable %s's clockdomain %s: %d\n", __func__, clk_hw_get_name(hw), clk->clkdm_name, ret); return ret; } } if (IS_ERR(clk->enable_reg)) { pr_err("%s: %s missing enable_reg\n", __func__, clk_hw_get_name(hw)); ret = -EINVAL; goto err; } /* FIXME should not have INVERT_ENABLE bit here */ v = ti_clk_ll_ops->clk_readl(clk->enable_reg); if (clk->flags & INVERT_ENABLE) v &= ~(1 << clk->enable_bit); else v |= (1 << clk->enable_bit); ti_clk_ll_ops->clk_writel(v, clk->enable_reg); v = ti_clk_ll_ops->clk_readl(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) ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); return ret; }
/** * omap3_dpll4_set_rate - set rate for omap3 per-dpll * @hw: clock to change * @rate: target rate for clock * @parent_rate: rate of the parent clock * * Check if the current SoC supports the per-dpll reprogram operation * or not, and then do the rate change if supported. Returns -EINVAL * if not supported, 0 for success, and potential error codes from the * clock rate change. */ int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { /* * According to the 12-5 CDP code from TI, "Limitation 2.5" * on 3430ES1 prevents us from changing DPLL multipliers or dividers * on DPLL4. */ if (ti_clk_get_features()->flags & TI_CLK_DPLL4_DENY_REPROGRAM) { pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n"); return -EINVAL; } return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); }
/** * omap2_clk_dflt_find_idlest - find CM_IDLEST reg va, bit shift for @clk * @clk: struct clk * to find IDLEST info for * @idlest_reg: void __iomem ** to return the CM_IDLEST va in * @idlest_bit: u8 * to return the CM_IDLEST bit shift in * @idlest_val: u8 * to return the idle status indicator * * Return the CM_IDLEST register address and bit shift corresponding * to the module that "owns" this clock. This default code assumes * that the CM_IDLEST bit shift is the CM_*CLKEN bit shift, and that * the IDLEST register address ID corresponds to the CM_*CLKEN * register address ID (e.g., that CM_FCLKEN2 corresponds to * CM_IDLEST2). This is not true for all modules. No return value. */ void omap2_clk_dflt_find_idlest(struct clk_hw_omap *clk, void __iomem **idlest_reg, u8 *idlest_bit, u8 *idlest_val) { u32 r; r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20); *idlest_reg = (__force void __iomem *)r; *idlest_bit = clk->enable_bit; /* * 24xx uses 0 to indicate not ready, and 1 to indicate ready. * 34xx reverses this, just to keep us on our toes * AM35xx uses both, depending on the module. */ *idlest_val = ti_clk_get_features()->cm_idlest_val; }
/* * _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, ai = 0; u32 v; bool errata_i810; /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ _omap3_noncore_dpll_bypass(clk); /* * Set jitter correction. Jitter correction applicable for OMAP343X * only since freqsel field is no longer present on other devices. */ if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) { v = ti_clk_ll_ops->clk_readl(&dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); ti_clk_ll_ops->clk_writel(v, &dd->control_reg); } /* Set DPLL multiplier, divider */ v = ti_clk_ll_ops->clk_readl(&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); } /* * Errata i810 - DPLL controller can get stuck while transitioning * to a power saving state. Software must ensure the DPLL can not * transition to a low power state while changing M/N values. * Easiest way to accomplish this is to prevent DPLL autoidle * before doing the M/N re-program. */ errata_i810 = ti_clk_get_features()->flags & TI_CLK_ERRATA_I810; if (errata_i810) { ai = omap3_dpll_autoidle_read(clk); if (ai) { omap3_dpll_deny_idle(clk); /* OCP barrier */ omap3_dpll_autoidle_read(clk); } } ti_clk_ll_ops->clk_writel(v, &dd->mult_div1_reg); /* Set 4X multiplier and low-power mode */ if (dd->m4xen_mask || dd->lpmode_mask) { v = ti_clk_ll_ops->clk_readl(&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; } ti_clk_ll_ops->clk_writel(v, &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 (errata_i810 && ai) omap3_dpll_allow_idle(clk); return 0; }
/* * _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); /* * Set jitter correction. Jitter correction applicable for OMAP343X * only since freqsel field is no longer present on other devices. */ if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) { v = ti_clk_ll_ops->clk_readl(dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); ti_clk_ll_ops->clk_writel(v, dd->control_reg); } /* Set DPLL multiplier, divider */ v = ti_clk_ll_ops->clk_readl(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); } ti_clk_ll_ops->clk_writel(v, dd->mult_div1_reg); /* Set 4X multiplier and low-power mode */ if (dd->m4xen_mask || dd->lpmode_mask) { v = ti_clk_ll_ops->clk_readl(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; } ti_clk_ll_ops->clk_writel(v, dd->control_reg); } /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ _omap3_noncore_dpll_lock(clk); return 0; }