void omap2_init_dpll_parent(struct clk *clk) { u32 v; struct dpll_data *dd; dd = clk->dpll_data; if (!dd) return; v = __raw_readl(dd->control_reg); v &= dd->enable_mask; v >>= __ffs(dd->enable_mask); /* Reparent the struct clk in case the dpll is in bypass */ if (cpu_is_omap24xx()) { if (v == OMAP2XXX_EN_DPLL_LPBYPASS || v == OMAP2XXX_EN_DPLL_FRBYPASS) clk_reparent(clk, dd->clk_bypass); } else if (cpu_is_omap34xx()) { if (v == OMAP3XXX_EN_DPLL_LPBYPASS || v == OMAP3XXX_EN_DPLL_FRBYPASS) clk_reparent(clk, dd->clk_bypass); } else if (cpu_is_omap44xx()) { if (v == OMAP4XXX_EN_DPLL_LPBYPASS || v == OMAP4XXX_EN_DPLL_FRBYPASS || v == OMAP4XXX_EN_DPLL_MNBYPASS) clk_reparent(clk, dd->clk_bypass); } return; }
static int pll_set_parent(struct clk *clk, struct clk *parent) { u32 val; int i, ret; if (!clk->parent_table || !clk->parent_num) return -EINVAL; /* Search the parent */ for (i = 0; i < clk->parent_num; i++) if (clk->parent_table[i] == parent) break; if (i == clk->parent_num) return -ENODEV; ret = clk_reparent(clk, parent); if (ret < 0) return ret; val = ioread32(clk->mapped_reg) & ~(((1 << clk->src_width) - 1) << clk->src_shift); iowrite32(val | i << clk->src_shift, clk->mapped_reg); return 0; }
/** * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware * @clk: OMAP clock struct ptr to use * * Given a pointer to a source-selectable struct clk, read the hardware * register and determine what its parent is currently set to. Update the * clk->parent field with the appropriate clk ptr. */ void omap2_init_clksel_parent(struct clk *clk) { const struct clksel *clks; const struct clksel_rate *clkr; u32 r, found = 0; if (!clk->clksel) return; r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; r >>= __ffs(clk->clksel_mask); for (clks = clk->clksel; clks->parent && !found; clks++) { for (clkr = clks->rates; clkr->div && !found; clkr++) { if ((clkr->flags & cpu_mask) && (clkr->val == r)) { if (clk->parent != clks->parent) { pr_debug("clock: inited %s parent " "to %s (was %s)\n", clk->name, clks->parent->name, ((clk->parent) ? clk->parent->name : "NULL")); clk_reparent(clk, clks->parent); }; found = 1; } } } if (!found) printk(KERN_ERR "clock: init parent: could not find " "regval %0x for clock %s\n", r, clk->name); return; }
static int usb24s_set_parent(struct clk *clk, struct clk *parent) { int i, ret; u32 val; if (!clk->parent_table || !clk->parent_num) return -EINVAL; /* Search the parent */ for (i = 0; i < clk->parent_num; i++) if (clk->parent_table[i] == parent) break; if (i == clk->parent_num) return -ENODEV; ret = clk_reparent(clk, parent); if (ret < 0) return ret; val = __raw_readl(USBCKCR); val &= ~(1 << 7); val |= i << 7; __raw_writel(val, USBCKCR); return 0; }
/** * omap2_clksel_set_parent() - change a clock's parent clock * @clk: struct clk * of the child clock * @new_parent: struct clk * of the new parent clock * * This function is intended to be called only by the clock framework. * Change the parent clock of clock @clk to @new_parent. This is * intended to be used while @clk is disabled. This function does not * currently check the usecount of the clock, so if multiple drivers * are using the clock, and the parent is changed, they will all be * affected without any notification. Returns -EINVAL upon error, or * 0 upon success. */ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) { u32 field_val = 0; u32 parent_div; if (!clk->clksel || !clk->clksel_mask) return -EINVAL; parent_div = _get_div_and_fieldval(new_parent, clk, &field_val); if (!parent_div) return -EINVAL; _write_clksel_reg(clk, field_val); clk_reparent(clk, new_parent); /* CLKSEL clocks follow their parents' rates, divided by a divisor */ clk->rate = __clk_get_rate(new_parent); if (parent_div > 0) __clk_get_rate(clk) /= parent_div; pr_debug("clock: %s: set parent to %s (new rate %ld)\n", __clk_get_name(clk), __clk_get_name(__clk_get_parent(clk)), __clk_get_rate(clk)); return 0; }
/** * omap2_clksel_set_parent() - change a clock's parent clock * @clk: struct clk * of the child clock * @new_parent: struct clk * of the new parent clock * * This function is intended to be called only by the clock framework. * Change the parent clock of clock @clk to @new_parent. This is * intended to be used while @clk is disabled. This function does not * currently check the usecount of the clock, so if multiple drivers * are using the clock, and the parent is changed, they will all be * affected without any notification. Returns -EINVAL upon error, or * 0 upon success. */ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) { u32 field_val = 0; u32 parent_div; u32 parent_mul = 0; if (!clk->clksel || !clk->clksel_mask) return -EINVAL; parent_div = _get_div_and_fieldval(new_parent, clk, &field_val, &parent_mul); if (!parent_div) return -EINVAL; _write_clksel_reg(clk, field_val); clk_reparent(clk, new_parent); /* CLKSEL clocks follow their parents' rates, divided by a divisor */ clk->rate = _calculate_rate(new_parent->rate, parent_div, parent_mul); pr_debug("clock: %s: set parent to %s (new rate %ld)\n", clk->name, clk->parent->name, clk->rate); return 0; }
int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) { u32 field_val = 0; u32 parent_div; if (!clk->clksel || !clk->clksel_mask) return -EINVAL; parent_div = _get_div_and_fieldval(new_parent, clk, &field_val); if (!parent_div) return -EINVAL; _write_clksel_reg(clk, field_val); clk_reparent(clk, new_parent); clk->rate = new_parent->rate; if (parent_div > 0) clk->rate /= parent_div; pr_debug("clock: %s: set parent to %s (new rate %ld)\n", clk->name, clk->parent->name, clk->rate); return 0; }
int omap4_noncore_dpll_mn_bypass(struct clk *clk) { int i, ret = 0; u32 reg; struct dpll_data *dd; if (!clk || !clk->dpll_data) return -EINVAL; dd = clk->dpll_data; if (!(clk->dpll_data->modes & (1 << DPLL_MN_BYPASS))) return -EINVAL; pr_debug("%s: configuring DPLL %s for MN bypass\n", __func__, clk->name); /* protect the DPLL during programming; usecount++ */ clk_enable(dd->clk_bypass); omap4_prm_rmw_reg_bits(dd->enable_mask, (DPLL_MN_BYPASS << __ffs(dd->enable_mask)), dd->control_reg); /* wait for DPLL to enter bypass */ for (i = 0; i < 1000000; i++) { reg = __raw_readl(dd->idlest_reg) & dd->mn_bypass_st_mask; if (reg) break; } if (reg) { if (clk->usecount) { /* DPLL is actually needed right now; usecount++ */ clk_enable(dd->clk_bypass); clk_disable(clk->parent); } pr_err("%s: reparenting %s to %s, and setting old rate %lu to new rate %lu\n", __func__, clk->name, dd->clk_bypass->name, clk->rate, dd->clk_bypass->rate); clk_reparent(clk, dd->clk_bypass); clk->rate = dd->clk_bypass->rate; } else ret = -ENODEV; /* done programming, no need to protect DPLL; usecount-- */ clk_disable(dd->clk_bypass); return ret; }
/** * omap2_init_clksel_parent() - set a clksel clk's parent field from the hdwr * @clk: OMAP clock struct ptr to use * * Given a pointer @clk to a source-selectable struct clk, read the * hardware register and determine what its parent is currently set * to. Update @clk's .parent field with the appropriate clk ptr. No * return value. */ void omap2_init_clksel_parent(struct clk *clk) { const struct clksel *clks; const struct clksel_rate *clkr; u32 r, found = 0; struct clk *parent; const char *clk_name; if (!clk->clksel || !clk->clksel_mask) return; parent = __clk_get_parent(clk); clk_name = __clk_get_name(clk); r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; r >>= __ffs(clk->clksel_mask); for (clks = clk->clksel; clks->parent && !found; clks++) { for (clkr = clks->rates; clkr->div && !found; clkr++) { if (!(clkr->flags & cpu_mask)) continue; if (clkr->val == r) { if (parent != clks->parent) { pr_debug("clock: %s: inited parent to %s (was %s)\n", clk_name, __clk_get_name(clks->parent), ((parent) ? __clk_get_name(parent) : "NULL")); clk_reparent(clk, clks->parent); }; found = 1; } } } /* This indicates a data error */ WARN(!found, "clock: %s: init parent: could not find regval %0x\n", clk_name, r); return; }
void ti816x_init_fapll_parent(struct clk *clk) { u32 v; struct fapll_data *fd; fd = clk->fapll_data; if (!fd) return; /* Return bypass rate if FAPLL is bypassed */ v = __raw_readl(fd->control_reg); v &= fd->bypass_mask; v >>= __ffs(fd->bypass_mask); /* Reparent in case the fapll is in bypass */ if (v == fd->bypass_en) clk_reparent(clk, fd->clk_bypass); return; }
/** * omap2_init_clksel_parent() - set a clksel clk's parent field from the hdwr * @clk: OMAP clock struct ptr to use * * Given a pointer @clk to a source-selectable struct clk, read the * hardware register and determine what its parent is currently set * to. Update @clk's .parent field with the appropriate clk ptr. No * return value. */ void omap2_init_clksel_parent(struct clk *clk) { const struct clksel *clks; const struct clksel_rate *clkr; u32 r, found = 0; if (!clk->clksel || !clk->clksel_mask) return; //printk("omap2_init_clksel_parent: %s clksel_reg=%x\n", clk->name, clk->clksel_reg); r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; //printk("omap2_init_clksel_parent: __raw_readl %x\n", r); r >>= __ffs(clk->clksel_mask); //printk("omap2_init_clksel_parent: __ffs(%x) %x\n", clk->clksel_mask, r); for (clks = clk->clksel; clks->parent && !found; clks++) { for (clkr = clks->rates; clkr->div && !found; clkr++) { if (!(clkr->flags & cpu_mask)) continue; if (clkr->val == r) { if (clk->parent != clks->parent) { pr_debug("clock: inited %s parent " "to %s (was %s)\n", clk->name, clks->parent->name, ((clk->parent) ? clk->parent->name : "NULL")); clk_reparent(clk, clks->parent); }; found = 1; } } } /* This indicates a data error */ WARN(!found, "clock: %s: init parent: could not find regval %0x\n", clk->name, r); return; }
int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) { u32 field_val, v, parent_div; if (clk->flags & CONFIG_PARTICIPANT) return -EINVAL; if (!clk->clksel) return -EINVAL; parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); if (!parent_div) return -EINVAL; /* Set new source value (previous dividers if any in effect) */ v = __raw_readl(clk->clksel_reg); v &= ~clk->clksel_mask; v |= field_val << __ffs(clk->clksel_mask); __raw_writel(v, clk->clksel_reg); v = __raw_readl(clk->clksel_reg); /* OCP barrier */ _omap2xxx_clk_commit(clk); clk_reparent(clk, new_parent); /* CLKSEL clocks follow their parents' rates, divided by a divisor */ clk->rate = new_parent->rate; if (parent_div > 0) clk->rate /= parent_div; pr_debug("clock: set parent of %s to %s (new rate %ld)\n", clk->name, clk->parent->name, clk->rate); return 0; }
int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) { u32 field_val, v, parent_div; if (clk->flags & CONFIG_PARTICIPANT) return -EINVAL; if (!clk->clksel) return -EINVAL; parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); if (!parent_div) return -EINVAL; v = __raw_readl(clk->clksel_reg); v &= ~clk->clksel_mask; v |= field_val << __ffs(clk->clksel_mask); __raw_writel(v, clk->clksel_reg); v = __raw_readl(clk->clksel_reg); _omap2xxx_clk_commit(clk); clk_reparent(clk, new_parent); clk->rate = new_parent->rate; if (parent_div > 0) clk->rate /= parent_div; pr_debug("clock: set parent of %s to %s (new rate %ld)\n", clk->name, clk->parent->name, clk->rate); 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; }
/** * omap4_core_dpll_set_rate - set the rate for the CORE DPLL * @clk: struct clk * of the DPLL to set * @rate: rounded target rate * * Program the CORE DPLL, including handling of EMIF frequency changes on M2 * divider. Returns 0 on success, otherwise a negative error code. */ int omap4_core_dpll_set_rate(struct clk *clk, unsigned long rate) { int i = 0, m2_div, m5_div; u32 mask, reg; u32 shadow_freq_cfg1 = 0, shadow_freq_cfg2 = 0; struct clk *new_parent; struct dpll_data *dd; if (!clk || !rate) return -EINVAL; if (!clk->dpll_data) return -EINVAL; dd = clk->dpll_data; if (rate == clk->rate) return 0; /* enable reference and bypass clocks */ omap2_clk_enable(dd->clk_bypass); omap2_clk_enable(dd->clk_ref); /* Just to avoid look-up on every call to speed up */ if (!l3_emif_clkdm) l3_emif_clkdm = clkdm_lookup("l3_emif_clkdm"); if (!dpll_core_m2_ck) dpll_core_m2_ck = clk_get(NULL, "dpll_core_m2_ck"); if (!dpll_core_m5x2_ck) dpll_core_m5x2_ck = clk_get(NULL, "dpll_core_m5x2_ck"); if (!gpmc_ick) gpmc_ick = clk_get(NULL, "gpmc_ick"); /* Make sure MEMIF clkdm is in SW_WKUP & GPMC clocks are active */ omap2_clkdm_wakeup(l3_emif_clkdm); omap2_clk_enable(gpmc_ick); /* FIXME set m3, m6 & m7 rates here? */ /* check for bypass rate */ if (rate == dd->clk_bypass->rate && clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS)) { /* * DDR clock = DPLL_CORE_M2_CK / 2. Program EMIF timing * parameters in EMIF shadow registers for bypass clock rate * divided by 2 */ omap_emif_setup_registers(rate / 2, LPDDR2_VOLTAGE_STABLE); /* * program CM_DIV_M5_DPLL_CORE.DPLL_CLKOUT_DIV into shadow * register as well as L3_CLK freq and update GPMC frequency * * HACK: hardcode L3_CLK = CORE_CLK / 2 for DPLL cascading * HACK: hardcode CORE_CLK = CORE_X2_CLK / 2 for DPLL * cascading */ m5_div = omap4_prm_read_bits_shift(dpll_core_m5x2_ck->clksel_reg, dpll_core_m5x2_ck->clksel_mask); shadow_freq_cfg2 = (m5_div << OMAP4430_DPLL_CORE_M5_DIV_SHIFT) | (1 << OMAP4430_CLKSEL_L3_SHADOW_SHIFT) | (0 << OMAP4430_CLKSEL_CORE_1_1_SHIFT) | (1 << OMAP4430_GPMC_FREQ_UPDATE_SHIFT); __raw_writel(shadow_freq_cfg2, OMAP4430_CM_SHADOW_FREQ_CONFIG2); /* * program CM_DIV_M2_DPLL_CORE.DPLL_CLKOUT_DIV for divide by * two and put DPLL_CORE into LP Bypass */ m2_div = omap4_prm_read_bits_shift(dpll_core_m2_ck->clksel_reg, dpll_core_m2_ck->clksel_mask); shadow_freq_cfg1 = (m2_div << OMAP4430_DPLL_CORE_M2_DIV_SHIFT) | (DPLL_LOW_POWER_BYPASS << OMAP4430_DPLL_CORE_DPLL_EN_SHIFT) | (1 << OMAP4430_DLL_RESET_SHIFT) | (1 << OMAP4430_FREQ_UPDATE_SHIFT); __raw_writel(shadow_freq_cfg1, OMAP4430_CM_SHADOW_FREQ_CONFIG1); new_parent = dd->clk_bypass; } else { if (dd->last_rounded_rate != rate) rate = clk->round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; /* * DDR clock = DPLL_CORE_M2_CK / 2. Program EMIF timing * parameters in EMIF shadow registers for rate divided * by 2. */ omap_emif_setup_registers(rate / 2, LPDDR2_VOLTAGE_STABLE); /* * FIXME skipping bypass part of omap3_noncore_dpll_program. * also x-loader's configure_core_dpll_no_lock bypasses * DPLL_CORE directly through CM_CLKMODE_DPLL_CORE via MN * bypass; no shadow register necessary! */ mask = (dd->mult_mask | dd->div1_mask); reg = (dd->last_rounded_m << __ffs(dd->mult_mask)) | ((dd->last_rounded_n - 1) << __ffs(dd->div1_mask)); /* program mn divider values */ omap4_prm_rmw_reg_bits(mask, reg, dd->mult_div1_reg); /* * program CM_DIV_M5_DPLL_CORE.DPLL_CLKOUT_DIV into shadow * register as well as L3_CLK freq and update GPMC frequency * * HACK: hardcode L3_CLK = CORE_CLK / 2 for DPLL cascading * HACK: hardcode CORE_CLK = CORE_X2_CLK / 1 for DPLL * cascading */ m5_div = omap4_prm_read_bits_shift(dpll_core_m5x2_ck->clksel_reg, dpll_core_m5x2_ck->clksel_mask); shadow_freq_cfg2 = (m5_div << OMAP4430_DPLL_CORE_M5_DIV_SHIFT) | (1 << OMAP4430_CLKSEL_L3_SHADOW_SHIFT) | (0 << OMAP4430_CLKSEL_CORE_1_1_SHIFT) | (1 << OMAP4430_GPMC_FREQ_UPDATE_SHIFT); __raw_writel(shadow_freq_cfg2, OMAP4430_CM_SHADOW_FREQ_CONFIG2); /* * program DPLL_CORE_M2_DIV with same value as the one already * in direct register and lock DPLL_CORE */ m2_div = omap4_prm_read_bits_shift(dpll_core_m2_ck->clksel_reg, dpll_core_m2_ck->clksel_mask); shadow_freq_cfg1 = (m2_div << OMAP4430_DPLL_CORE_M2_DIV_SHIFT) | (DPLL_LOCKED << OMAP4430_DPLL_CORE_DPLL_EN_SHIFT) | (1 << OMAP4430_DLL_RESET_SHIFT) | (1 << OMAP4430_FREQ_UPDATE_SHIFT); __raw_writel(shadow_freq_cfg1, OMAP4430_CM_SHADOW_FREQ_CONFIG1); new_parent = dd->clk_ref; } /* wait for the configuration to be applied */ omap_test_timeout(((__raw_readl(OMAP4430_CM_SHADOW_FREQ_CONFIG1) & OMAP4430_FREQ_UPDATE_MASK) == 0), MAX_FREQ_UPDATE_TIMEOUT, i); /* clear GPMC_FREQ_UPDATE bit */ shadow_freq_cfg2 = __raw_readl(OMAP4430_CM_SHADOW_FREQ_CONFIG2); shadow_freq_cfg2 &= ~1; __raw_writel(shadow_freq_cfg2, OMAP4430_CM_SHADOW_FREQ_CONFIG2); /* * 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; /* disable reference and bypass clocks */ omap2_clk_disable(dd->clk_bypass); omap2_clk_disable(dd->clk_ref); /* Configures MEMIF domain back to HW_WKUP & let GPMC clocks to idle */ omap2_clkdm_allow_idle(l3_emif_clkdm); omap2_clk_disable(gpmc_ick); /* * FIXME PRCM functional spec says we should set GPMC_FREQ_UPDATE bit * here, but we're not even handling CM_SHADOW_FREQ_CONFIG2 at all. */ if (i == MAX_FREQ_UPDATE_TIMEOUT) { pr_err("%s: Frequency update for CORE DPLL M2 change failed\n", __func__); return -1; } 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 *clk, unsigned long rate) { struct clk *new_parent = NULL; u16 freqsel = 0; struct dpll_data *dd; int ret; unsigned long orig_rate = 0; 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 { /* * On 4460, the MPU clk for frequencies higher than 1Ghz * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while * value of M3 is fixed to 1. Hence for frequencies higher * than 1 Ghz, lock the DPLL at half the rate so the * CLKOUTX2_M3 then matches the requested rate. */ if (cpu_is_omap4460() && !strcmp(clk->name, "dpll_mpu_ck") && (rate > 1000000000)) { orig_rate = rate; rate = rate/2; } if (dd->last_rounded_rate != rate) rate = clk->round_rate(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); } /* Set the rate back to original for book keeping*/ if (orig_rate) rate = orig_rate; 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, orig_rate); 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; }
/** * 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 *clk, unsigned long rate) { struct clk *new_parent = NULL; u16 freqsel = 0; 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) rate = clk->round_rate(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("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 hierarchy, 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; }