/** * omap3_noncore_dpll_determine_rate - determine rate for a DPLL * @hw: pointer to the clock to determine rate for * @req: target rate request * * Determines which DPLL mode to use for reaching a desired target rate. * Checks whether the DPLL shall be in bypass or locked mode, and if * locked, calculates the M,N values for the DPLL via round-rate. * Returns a 0 on success, negative error value in failure. */ int omap3_noncore_dpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); struct dpll_data *dd; if (!req->rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk_hw_get_rate(dd->clk_bypass) == req->rate && (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { req->best_parent_hw = dd->clk_bypass; } else { req->rate = omap2_dpll_round_rate(hw, req->rate, &req->best_parent_rate); req->best_parent_hw = dd->clk_ref; } req->best_parent_rate = req->rate; return 0; }
/** * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit * @clk: struct clk * of the DPLL to round a rate for * @target_rate: the desired rate of the DPLL * * Compute the rate that would be programmed into the DPLL hardware * for @clk if set_rate() were to be provided with the rate * @target_rate. Takes the REGM4XEN bit into consideration, which is * needed for the OMAP4 ABE DPLL. Returns the rounded rate (before * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or * ~0 if an error occurred in omap2_dpll_round_rate(). */ long omap4_dpll_regm4xen_round_rate(struct clk *clk, unsigned long target_rate) { u32 v; struct dpll_data *dd; long r; if (!clk || !clk->dpll_data) return -EINVAL; dd = clk->dpll_data; /* regm4xen adds a multiplier of 4 to DPLL calculations */ v = __raw_readl(dd->control_reg) & OMAP4430_DPLL_REGM4XEN_MASK; if (v) target_rate = target_rate / OMAP4430_REGM4XEN_MULT; r = omap2_dpll_round_rate(clk, target_rate); if (r == ~0) return r; if (v) clk->dpll_data->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; return clk->dpll_data->last_rounded_rate; }
/** * omap3_noncore_dpll_determine_rate - determine rate for a DPLL * @hw: pointer to the clock to determine rate for * @rate: target rate for the DPLL * @best_parent_rate: pointer for returning best parent rate * @best_parent_clk: pointer for returning best parent clock * * Determines which DPLL mode to use for reaching a desired target rate. * Checks whether the DPLL shall be in bypass or locked mode, and if * locked, calculates the M,N values for the DPLL via round-rate. * Returns a positive clock rate with success, negative error value * in failure. */ long omap3_noncore_dpll_determine_rate(struct clk_hw *hw, unsigned long rate, unsigned long min_rate, unsigned long max_rate, unsigned long *best_parent_rate, struct clk_hw **best_parent_clk) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); struct dpll_data *dd; 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))) { *best_parent_clk = __clk_get_hw(dd->clk_bypass); } else { rate = omap2_dpll_round_rate(hw, rate, best_parent_rate); *best_parent_clk = __clk_get_hw(dd->clk_ref); } *best_parent_rate = rate; return rate; }
/** * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit * @clk: struct clk * of the DPLL to round a rate for * @target_rate: the desired rate of the DPLL * * Compute the rate that would be programmed into the DPLL hardware * for @clk if set_rate() were to be provided with the rate * @target_rate. Takes the REGM4XEN bit into consideration, which is * needed for the OMAP4 ABE DPLL. Returns the rounded rate (before * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or * ~0 if an error occurred in omap2_dpll_round_rate(). */ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, unsigned long target_rate, unsigned long *parent_rate) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); struct dpll_data *dd; long r; if (!clk || !clk->dpll_data) return -EINVAL; dd = clk->dpll_data; dd->last_rounded_m4xen = 0; /* * First try to compute the DPLL configuration for * target rate without using the 4X multiplier. */ r = omap2_dpll_round_rate(hw, target_rate, NULL); if (r != ~0) goto out; /* * If we did not find a valid DPLL configuration, try again, but * this time see if using the 4X multiplier can help. Enabling the * 4X multiplier is equivalent to dividing the target rate by 4. */ r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT, NULL); if (r == ~0) return r; dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; dd->last_rounded_m4xen = 1; out: omap4_dpll_lpmode_recalc(dd); return dd->last_rounded_rate; }
/** * 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. */ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { u16 freqsel; struct dpll_data *dd; int ret; if (!clk || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (rate == omap2_get_dpll_rate(clk, clk->parent->rate)) return 0; if (dd->bypass_clk->rate == rate && (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) clk->rate = rate; } else { if (dd->last_rounded_rate != rate) omap2_dpll_round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); pr_debug("clock: %s: set rate: locking rate to %lu.\n", clk->name, rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel); if (!ret) clk->rate = rate; } return 0; }
long omap4_dpll_regm4xen_round_rate(struct clk *clk, unsigned long target_rate) { u32 reg; struct dpll_data *dd; dd = clk->dpll_data; /* REGM4XEN add 4x multiplier to MN dividers; check if it is set */ reg = __raw_readl(dd->control_reg); reg &= OMAP4430_DPLL_REGM4XEN_MASK; if (reg) dd->max_multiplier = OMAP4430_MAX_DPLL_MULT * OMAP4430_REGM4XEN_MULT; else dd->max_multiplier = OMAP4430_MAX_DPLL_MULT; omap2_dpll_round_rate(clk, target_rate); if (reg) { /* * FIXME this is lazy; we only support values of M that are * divisible by 4 (a safe bet) and for which M/4 is >= 2 */ if (dd->last_rounded_m % OMAP4430_REGM4XEN_MULT) pr_warn("%s: %s's M (%u) is not divisible by 4\n", __func__, clk->name, dd->last_rounded_m); if ((dd->last_rounded_m / OMAP4430_REGM4XEN_MULT) < 2) pr_warn("%s: %s's M (%u) is too low. Try disabling REGM4XEN for this frequency\n", __func__, clk->name, dd->last_rounded_m); dd->last_rounded_m /= OMAP4430_REGM4XEN_MULT; } pr_debug("%s: last_rounded_m is %d, last_rounded_n is %d, last_rounded_rate is %lu\n", __func__, clk->dpll_data->last_rounded_m, clk->dpll_data->last_rounded_n, clk->dpll_data->last_rounded_rate); return clk->dpll_data->last_rounded_rate; }
static int omap2_reprogram_dpll(struct clk *clk, unsigned long rate) { u32 cur_rate, low, mult, div, valid_rate, done_rate; u32 bypass = 0; struct prcm_config tmpset; const struct dpll_data *dd; unsigned long flags; int ret = -EINVAL; local_irq_save(flags); cur_rate = omap2_get_dpll_rate_24xx(&dpll_ck); mult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); mult &= OMAP24XX_CORE_CLK_SRC_MASK; if ((rate == (cur_rate / 2)) && (mult == 2)) { omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL, 1); } else if ((rate == (cur_rate * 2)) && (mult == 1)) { omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1); } else if (rate != cur_rate) { valid_rate = omap2_dpll_round_rate(rate); if (valid_rate != rate) goto dpll_exit; if (mult == 1) low = curr_prcm_set->dpll_speed; else low = curr_prcm_set->dpll_speed / 2; dd = clk->dpll_data; if (!dd) goto dpll_exit; tmpset.cm_clksel1_pll = __raw_readl(dd->mult_div1_reg); tmpset.cm_clksel1_pll &= ~(dd->mult_mask | dd->div1_mask); div = ((curr_prcm_set->xtal_speed / 1000000) - 1); tmpset.cm_clksel2_pll = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); tmpset.cm_clksel2_pll &= ~OMAP24XX_CORE_CLK_SRC_MASK; if (rate > low) { tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL_X2; mult = ((rate / 2) / 1000000); done_rate = CORE_CLK_SRC_DPLL_X2; } else { tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL; mult = (rate / 1000000); done_rate = CORE_CLK_SRC_DPLL; } tmpset.cm_clksel1_pll |= (div << __ffs(dd->mult_mask)); tmpset.cm_clksel1_pll |= (mult << __ffs(dd->div1_mask)); /* Worst case */ tmpset.base_sdrc_rfr = SDRC_RFR_CTRL_BYPASS; if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */ bypass = 1; omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1); /* For init_mem */ /* Force dll lock mode */ omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr, bypass); /* Errata: ret dll entry state */ omap2_init_memory_params(omap2_dll_force_needed()); omap2_reprogram_sdrc(done_rate, 0); } omap2_dpll_recalc(&dpll_ck); ret = 0; dpll_exit: local_irq_restore(flags); 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. */ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { struct clk *new_parent = NULL; u16 freqsel; struct dpll_data *dd; int ret; if (!clk || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (rate == omap2_get_dpll_rate(clk)) return 0; /* * Ensure both the bypass and ref clocks are enabled prior to * doing anything; we need the bypass clock running to reprogram * the DPLL. */ omap2_clk_enable(dd->clk_bypass); omap2_clk_enable(dd->clk_ref); if (dd->clk_bypass->rate == rate && (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) new_parent = dd->clk_bypass; } else { if (dd->last_rounded_rate != rate) omap2_dpll_round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); pr_debug("clock: %s: set rate: locking rate to %lu.\n", clk->name, rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel); if (!ret) new_parent = dd->clk_ref; } if (!ret) { /* * Switch the parent clock in the heirarchy, and make sure * that the new parent's usecount is correct. Note: we * enable the new parent before disabling the old to avoid * any unnecessary hardware disable->enable transitions. */ if (clk->usecount) { omap2_clk_enable(new_parent); omap2_clk_disable(clk->parent); } clk_reparent(clk, new_parent); clk->rate = rate; } omap2_clk_disable(dd->clk_ref); omap2_clk_disable(dd->clk_bypass); return 0; }
static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { struct clk *new_parent = NULL; u16 freqsel; struct dpll_data *dd; int ret; if (!clk || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (rate == omap2_get_dpll_rate(clk)) return 0; omap2_clk_enable(dd->clk_bypass); omap2_clk_enable(dd->clk_ref); if (dd->clk_bypass->rate == rate && (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) new_parent = dd->clk_bypass; } else { if (dd->last_rounded_rate != rate) omap2_dpll_round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); pr_debug("clock: %s: set rate: locking rate to %lu.\n", clk->name, rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel); if (!ret) new_parent = dd->clk_ref; } if (!ret) { if (clk->usecount) { omap2_clk_enable(new_parent); omap2_clk_disable(clk->parent); } clk_reparent(clk, new_parent); clk->rate = rate; } omap2_clk_disable(dd->clk_ref); omap2_clk_disable(dd->clk_bypass); return 0; }