static int clkout_clksel_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_hw_clkout *clkout_clk = to_clk_hw_clkout(hw); struct clk_hw_omap *omap_clk = to_clk_hw_omap(hw); struct clkout_match *m = &clkout_clk->match; struct clk *clk = hw->clk; int ret; u32 v; ret = clkout_find_match(hw, rate, m); if (ret != 0) return ret; /* have to be exact now */ if (rate != m->best_rate) { pr_err("%s: Failed to find exact rate %lu (was %lu)\n", __func__, rate, m->best_rate); return -EINVAL; } /* switch parent */ if (m->best_clks->parent != __clk_get_parent(clk)) { __clk_set_parent(clk, m->best_clks->parent); __clk_reparent(clk, m->best_clks->parent); parent_rate = __clk_get_rate(__clk_get_parent(clk)); } /* we need to write the new value */ if (omap_clk->clksel_reg != clkout_clk->div_reg) { v = __raw_readl(omap_clk->clksel_reg); v &= ~omap_clk->clksel_mask; v |= m->best_clkr->val << __ffs(omap_clk->clksel_mask); __raw_writel(v, omap_clk->clksel_reg); v = __raw_readl(omap_clk->clksel_reg); /* OCP barrier */ v = __raw_readl(clkout_clk->div_reg); v &= ~clkout_clk->div_mask; v |= m->best_clkd->val << __ffs(clkout_clk->div_mask); __raw_writel(v, clkout_clk->div_reg); v = __raw_readl(clkout_clk->div_reg); /* OCP barrier */ } else { v = __raw_readl(omap_clk->clksel_reg); v &= ~(omap_clk->clksel_mask | clkout_clk->div_mask); v |= (m->best_clkr->val << __ffs(omap_clk->clksel_mask)) | (m->best_clkd->val << __ffs(clkout_clk->div_mask)); __raw_writel(v, omap_clk->clksel_reg); v = __raw_readl(omap_clk->clksel_reg); /* OCP barrier */ } return ret; }
/** * omap3_noncore_dpll_set_rate - set non-core DPLL rate * @clk: struct clk * of DPLL to set * @rate: rounded target rate * * Set the DPLL CLKOUT to the target rate. If the DPLL can enter * low-power bypass, and the target rate is the bypass source clock * rate, then configure the DPLL for bypass. Otherwise, round the * target rate if it hasn't been done already, then program and lock * the DPLL. Returns -EINVAL upon error, or 0 upon success. */ 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 clk *new_parent = NULL; unsigned long rrate; u16 freqsel = 0; struct dpll_data *dd; int ret; if (!hw || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (__clk_get_rate(dd->clk_bypass) == rate && (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("%s: %s: set rate: entering bypass.\n", __func__, __clk_get_name(hw->clk)); __clk_prepare(dd->clk_bypass); clk_enable(dd->clk_bypass); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) new_parent = dd->clk_bypass; clk_disable(dd->clk_bypass); __clk_unprepare(dd->clk_bypass); } else { __clk_prepare(dd->clk_ref); clk_enable(dd->clk_ref); if (dd->last_rounded_rate != rate) { rrate = __clk_round_rate(hw->clk, rate); if (rrate != rate) { pr_warn("%s: %s: final rate %lu does not match desired rate %lu\n", __func__, __clk_get_name(hw->clk), rrate, rate); rate = rrate; } } if (dd->last_rounded_rate == 0) return -EINVAL; /* Freqsel is available only on OMAP343X devices */ if (cpu_is_omap343x()) { 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_get_name(hw->clk), rate); ret = omap3_noncore_dpll_program(clk, freqsel); if (!ret) new_parent = dd->clk_ref; clk_disable(dd->clk_ref); __clk_unprepare(dd->clk_ref); } /* * FIXME - this is all wrong. common code handles reparenting and * migrating prepare/enable counts. dplls should be a multiplexer * clock and this should be a set_parent operation so that all of that * stuff is inherited for free */ if (!ret && clk_get_parent(hw->clk) != new_parent) __clk_reparent(hw->clk, new_parent); return 0; }
/** * omap3_noncore_dpll_set_rate - set non-core DPLL rate * @clk: struct clk * of DPLL to set * @rate: rounded target rate * * Set the DPLL CLKOUT to the target rate. If the DPLL can enter * low-power bypass, and the target rate is the bypass source clock * rate, then configure the DPLL for bypass. Otherwise, round the * target rate if it hasn't been done already, then program and lock * the DPLL. Returns -EINVAL upon error, or 0 upon success. */ 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 clk *new_parent = NULL; u16 freqsel = 0; struct dpll_data *dd; int ret; if (!hw || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; __clk_prepare(dd->clk_bypass); clk_enable(dd->clk_bypass); __clk_prepare(dd->clk_ref); clk_enable(dd->clk_ref); if (__clk_get_rate(dd->clk_bypass) == rate && (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("%s: %s: set rate: entering bypass.\n", __func__, __clk_get_name(hw->clk)); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) new_parent = dd->clk_bypass; } else { if (dd->last_rounded_rate != rate) rate = __clk_round_rate(hw->clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; /* No freqsel on OMAP4 and OMAP3630 */ if (!cpu_is_omap44xx() && !cpu_is_omap3630()) { freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); } pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, __clk_get_name(hw->clk), rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel); if (!ret) new_parent = dd->clk_ref; } /* * FIXME - this is all wrong. common code handles reparenting and * migrating prepare/enable counts. dplls should be a multiplexer * clock and this should be a set_parent operation so that all of that * stuff is inherited for free */ if (!ret) __clk_reparent(hw->clk, new_parent); clk_disable(dd->clk_ref); __clk_unprepare(dd->clk_ref); clk_disable(dd->clk_bypass); __clk_unprepare(dd->clk_bypass); return 0; }