/** * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. * The choice of modes depends on the DPLL's programmed rate: if it is * the same as the DPLL's parent clock, it will enter bypass; * otherwise, it will enter lock. This code will wait for the DPLL to * indicate readiness before returning, unless the DPLL takes too long * to enter the target state. Intended to be used as the struct clk's * enable function. If DPLL3 was passed in, or the DPLL does not * support low-power stop, or if the DPLL took too long to enter * bypass or lock, return -EINVAL; otherwise, return 0. */ static int omap3_noncore_dpll_enable(struct clk *clk) { int r; struct dpll_data *dd; if (clk == &dpll3_ck) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; /* * Ensure M/N register is confgured before * Enable it, otherwise DPLL will fail to lock */ if (clk->rate == 0) { WARN_ON(dd->default_rate == 0); r = omap3_noncore_dpll_set_rate(clk, dd->default_rate); if (r) return r; } if (clk->rate == dd->clk_bypass->rate) { WARN_ON(clk->parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(clk->parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } /* FIXME: this is dubious - if clk->rate has changed, what about propagating? */ if (!r) clk->rate = omap2_get_dpll_rate(clk); return r; }
/** * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. * The choice of modes depends on the DPLL's programmed rate: if it is * the same as the DPLL's parent clock, it will enter bypass; * otherwise, it will enter lock. This code will wait for the DPLL to * indicate readiness before returning, unless the DPLL takes too long * to enter the target state. Intended to be used as the struct clk's * enable function. If DPLL3 was passed in, or the DPLL does not * support low-power stop, or if the DPLL took too long to enter * bypass or lock, return -EINVAL; otherwise, return 0. */ int omap3_noncore_dpll_enable(struct clk *clk) { int r; struct dpll_data *dd; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk->rate == dd->clk_bypass->rate) { WARN_ON(clk->parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(clk->parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } /* *FIXME: this is dubious - if clk->rate has changed, what about * propagating? */ if (!r) clk->rate = omap2_get_dpll_rate(clk); return r; }
/* * omap3_noncore_dpll_program - set non-core DPLL M,N values directly * @clk: struct clk * of DPLL to set * @m: DPLL multiplier to set * @n: DPLL divider to set * @freqsel: FREQSEL value to set * * Program the DPLL with the supplied M, N values, and wait for the DPLL to * lock.. Returns -EINVAL upon error, or 0 upon success. */ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) { struct dpll_data *dd = clk->dpll_data; u32 v; /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ _omap3_noncore_dpll_bypass(clk); /* Set jitter correction */ v = __raw_readl(dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); __raw_writel(v, dd->control_reg); /* Set DPLL multiplier, divider */ v = __raw_readl(dd->mult_div1_reg); v &= ~(dd->mult_mask | dd->div1_mask); v |= m << __ffs(dd->mult_mask); v |= (n - 1) << __ffs(dd->div1_mask); __raw_writel(v, dd->mult_div1_reg); /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ _omap3_noncore_dpll_lock(clk); return 0; }
/** * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. * The choice of modes depends on the DPLL's programmed rate: if it is * the same as the DPLL's parent clock, it will enter bypass; * otherwise, it will enter lock. This code will wait for the DPLL to * indicate readiness before returning, unless the DPLL takes too long * to enter the target state. Intended to be used as the struct clk's * enable function. If DPLL3 was passed in, or the DPLL does not * support low-power stop, or if the DPLL took too long to enter * bypass or lock, return -EINVAL; otherwise, return 0. */ static int omap3_noncore_dpll_enable(struct clk *clk) { int r; struct dpll_data *dd; if (clk == &dpll3_ck) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; /* * Ensure M/N register is confgured before * Enable it, otherwise DPLL will fail to lock */ if (clk->rate == 0) { WARN_ON(dd->default_rate == 0); r = omap3_noncore_dpll_set_rate(clk, dd->default_rate); if (r) return r; } if (clk->rate == dd->bypass_clk->rate) r = _omap3_noncore_dpll_bypass(clk); else r = _omap3_noncore_dpll_lock(clk); return r; }
static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) { struct dpll_data *dd = clk->dpll_data; u32 v; _omap3_noncore_dpll_bypass(clk); v = __raw_readl(dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); __raw_writel(v, dd->control_reg); v = __raw_readl(dd->mult_div1_reg); v &= ~(dd->mult_mask | dd->div1_mask); v |= m << __ffs(dd->mult_mask); v |= (n - 1) << __ffs(dd->div1_mask); __raw_writel(v, dd->mult_div1_reg); _omap3_noncore_dpll_lock(clk); return 0; }
/** * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. * The choice of modes depends on the DPLL's programmed rate: if it is * the same as the DPLL's parent clock, it will enter bypass; * otherwise, it will enter lock. This code will wait for the DPLL to * indicate readiness before returning, unless the DPLL takes too long * to enter the target state. Intended to be used as the struct clk's * enable function. If DPLL3 was passed in, or the DPLL does not * support low-power stop, or if the DPLL took too long to enter * bypass or lock, return -EINVAL; otherwise, return 0. */ int omap3_noncore_dpll_enable(struct clk_hw *hw) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); int r; struct dpll_data *dd; struct clk *parent; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk->clkdm) { r = clkdm_clk_enable(clk->clkdm, hw->clk); if (r) { WARN(1, "%s: could not enable %s's clockdomain %s: %d\n", __func__, __clk_get_name(hw->clk), clk->clkdm->name, r); return r; } } parent = __clk_get_parent(hw->clk); if (__clk_get_rate(hw->clk) == __clk_get_rate(dd->clk_bypass)) { WARN_ON(parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } return r; }
static int omap3_noncore_dpll_enable(struct clk *clk) { int r; struct dpll_data *dd; if (clk == &dpll3_ck) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk->rate == dd->clk_bypass->rate) { WARN_ON(clk->parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(clk->parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } if (!r) clk->rate = omap2_get_dpll_rate(clk); return r; }
/* * omap3_noncore_dpll_program - set non-core DPLL M,N values directly * @clk: struct clk * of DPLL to set * @m: DPLL multiplier to set * @n: DPLL divider to set * @freqsel: FREQSEL value to set * * Program the DPLL with the supplied M, N values, and wait for the DPLL to * lock.. Returns -EINVAL upon error, or 0 upon success. */ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) { struct dpll_data *dd; u32 v; if (!clk) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; /* * 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 (omap_rev() == OMAP3430_REV_ES1_0 && !strcmp("dpll4_ck", clk->name)) { printk(KERN_ERR "clock: DPLL4 cannot change rate due to " "silicon 'Limitation 2.5' on 3430ES1.\n"); return -EINVAL; } /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ _omap3_noncore_dpll_bypass(clk); /* Set jitter correction */ v = cm_read_mod_reg(clk->prcm_mod, dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); cm_write_mod_reg(v, clk->prcm_mod, dd->control_reg); /* Set DPLL multiplier, divider */ v = cm_read_mod_reg(clk->prcm_mod, dd->mult_div1_reg); v &= ~(dd->mult_mask | dd->div1_mask); v |= m << __ffs(dd->mult_mask); v |= (n - 1) << __ffs(dd->div1_mask); if (dd->jtype) { u8 dco, sd_div; lookup_dco_sddiv(clk, &dco, &sd_div, m, n); v &= ~(dd->dco_sel_mask | dd->sd_div_mask); v |= dco << __ffs(dd->dco_sel_mask); v |= sd_div << __ffs(dd->sd_div_mask); } cm_write_mod_reg(v, clk->prcm_mod, dd->mult_div1_reg); /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ _omap3_noncore_dpll_lock(clk); return 0; }
/* * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly * @clk: struct clk * of DPLL to set * @m: DPLL multiplier to set * @n: DPLL divider to set * @freqsel: FREQSEL value to set * * Program the DPLL with the supplied M, N values, 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 m, u8 n, 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. No jitter correction for OMAP4 and 3630 * since freqsel field is no longer present */ if (!soc_is_am33xx() && !cpu_is_omap44xx() && !cpu_is_omap3630()) { v = __raw_readl(dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); __raw_writel(v, dd->control_reg); } /* Set DPLL multiplier, divider */ v = __raw_readl(dd->mult_div1_reg); v &= ~(dd->mult_mask | dd->div1_mask); v |= m << __ffs(dd->mult_mask); v |= (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, m, n); v &= ~(dd->dco_mask); v |= dco << __ffs(dd->dco_mask); } if (dd->sddiv_mask) { _lookup_sddiv(clk, &sd_div, m, n); v &= ~(dd->sddiv_mask); v |= sd_div << __ffs(dd->sddiv_mask); } __raw_writel(v, dd->mult_div1_reg); /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ _omap3_noncore_dpll_lock(clk); return 0; }
/** * omap3_noncore_dpll_set_parent - set parent for a DPLL clock * @hw: pointer to the clock to set parent for * @index: parent index to select * * Sets parent for a DPLL clock. This sets the DPLL into bypass or * locked mode. Returns 0 with success, negative error value otherwise. */ int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); int ret; if (!hw) return -EINVAL; if (index) ret = _omap3_noncore_dpll_bypass(clk); else ret = _omap3_noncore_dpll_lock(clk); return ret; }
/* * omap3_noncore_dpll_program - set non-core DPLL M,N values directly * @clk: struct clk * of DPLL to set * @m: DPLL multiplier to set * @n: DPLL divider to set * @freqsel: FREQSEL value to set * * Program the DPLL with the supplied M, N values, and wait for the DPLL to * lock.. Returns -EINVAL upon error, or 0 upon success. */ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) { struct dpll_data *dd = clk->dpll_data; u32 v; /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ _omap3_noncore_dpll_bypass(clk); /* Set jitter correction. No jitter correction for OMAP4 and OMAP3630 * since those silicon do not have freqsel field */ if (!cpu_is_omap44xx() && !cpu_is_omap3630()) { v = __raw_readl(dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); __raw_writel(v, dd->control_reg); } /* Set DPLL multiplier, divider */ v = __raw_readl(dd->mult_div1_reg); v &= ~(dd->mult_mask | dd->div1_mask); v |= m << __ffs(dd->mult_mask); v |= (n - 1) << __ffs(dd->div1_mask); __raw_writel(v, dd->mult_div1_reg); /* * XXX This code is not needed for 3430/AM35XX; can it be optimized * out in non-multi-OMAP builds for those chips? */ if ((dd->flags & DPLL_J_TYPE) && !(dd->flags & DPLL_NO_DCO_SEL)) { u8 dco, sd_div; lookup_dco_sddiv(clk, &dco, &sd_div, m, n); /* XXX This probably will need revision for OMAP4 */ v &= ~(OMAP3630_PERIPH_DPLL_DCO_SEL_MASK | OMAP3630_PERIPH_DPLL_SD_DIV_MASK); v |= dco << __ffs(OMAP3630_PERIPH_DPLL_DCO_SEL_MASK); v |= sd_div << __ffs(OMAP3630_PERIPH_DPLL_SD_DIV_MASK); } /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ _omap3_noncore_dpll_lock(clk); return 0; }
/** * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. * The choice of modes depends on the DPLL's programmed rate: if it is * the same as the DPLL's parent clock, it will enter bypass; * otherwise, it will enter lock. This code will wait for the DPLL to * indicate readiness before returning, unless the DPLL takes too long * to enter the target state. Intended to be used as the struct clk's * enable function. If DPLL3 was passed in, or the DPLL does not * support low-power stop, or if the DPLL took too long to enter * bypass or lock, return -EINVAL; otherwise, return 0. */ static int omap3_noncore_dpll_enable(struct clk *clk) { int r; struct dpll_data *dd; if (clk == &dpll3_ck) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk->rate == dd->bypass_clk->rate) r = _omap3_noncore_dpll_bypass(clk); else r = _omap3_noncore_dpll_lock(clk); return r; }
/* * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly * @clk: struct clk * of DPLL to set * @m: DPLL multiplier to set * @n: DPLL divider to set * @freqsel: FREQSEL value to set * * Program the DPLL with the supplied M, N values, and wait for the DPLL to * lock.. Returns -EINVAL upon error, or 0 upon success. */ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel, unsigned long orig_rate) { struct dpll_data *dd = clk->dpll_data; u8 dco, sd_div; u32 v; /* * On OMAP4460, to obtain MPU DPLL frequency higher * than 1GHz, DCC (Duty Cycle Correction) needs to * be enabled. * Also the interconnect frequency to EMIF should * be switched between MPU clk divide by 4 (for * frequencies higher than 920Mhz) and MPU clk divide * by 2 (for frequencies lower than or equal to 920Mhz) * Lastly the async bridge to ABE must be MPU clk divide * by 8 for MPU clk > 748Mhz and MPU clk divide by 4 * for lower frequencies. * TODO: For now use a strcmp, but need to find a * better way to identify the MPU dpll. */ if (cpu_is_omap4460() && !strcmp(clk->name, "dpll_mpu_ck")) { /* DCC control */ v = __raw_readl(dd->mult_div1_reg); if ((orig_rate <= 1000000000) && (v & OMAP4460_DCC_EN_MASK)) { v &= ~OMAP4460_DCC_EN_MASK; /* Disable DCC */ __raw_writel(v, dd->mult_div1_reg); } } /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ _omap3_noncore_dpll_bypass(clk); /* * Set jitter correction. No jitter correction for OMAP4 and 3630 * since freqsel field is no longer present */ if (!cpu_is_omap44xx() && !cpu_is_omap3630()) { v = __raw_readl(dd->control_reg); v &= ~dd->freqsel_mask; v |= freqsel << __ffs(dd->freqsel_mask); __raw_writel(v, dd->control_reg); } /* Set DPLL multiplier, divider */ v = __raw_readl(dd->mult_div1_reg); v &= ~(dd->mult_mask | dd->div1_mask); v |= m << __ffs(dd->mult_mask); v |= (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, m, n); v &= ~(dd->dco_mask); v |= dco << __ffs(dd->dco_mask); } if (dd->sddiv_mask) { _lookup_sddiv(clk, &sd_div, m, n); v &= ~(dd->sddiv_mask); v |= sd_div << __ffs(dd->sddiv_mask); } __raw_writel(v, dd->mult_div1_reg); /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ _omap3_noncore_dpll_lock(clk); if (cpu_is_omap4460() && !strcmp(clk->name, "dpll_mpu_ck")) { /* DCC control */ if (orig_rate > 1000000000) { v &= ~OMAP4460_DCC_COUNT_MAX_MASK; v |= (5 << OMAP4460_DCC_COUNT_MAX_SHIFT); __raw_writel(v, dd->mult_div1_reg); v |= OMAP4460_DCC_EN_MASK; /* Enable DCC */ __raw_writel(v, dd->mult_div1_reg); } /* EMIF/ABE clock rate control */ v = __raw_readl(OMAP4430_CM_MPU_MPU_CLKCTRL); if (orig_rate > 920000000) v |= OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK; else v &= ~OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK; if (orig_rate > 748000000) v |= OMAP4460_CLKSEL_ABE_DIV_MODE_MASK; else v &= ~OMAP4460_CLKSEL_ABE_DIV_MODE_MASK; __raw_writel(v, OMAP4430_CM_MPU_MPU_CLKCTRL); } 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); 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; }
/* * _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; }