/* * Add half of the divisor so the result will be rounded to closest * instead of rounded down.
*/
residual += (parent_rate / 2);
ndiv_frac = div64_u64((u64)residual, (u64)parent_rate);
/* * Based on the target frequency, find a match from the VCO frequency parameter * table and return its index
*/ staticint pll_get_rate_index(struct iproc_pll *pll, unsignedint target_rate)
{ int i;
for (i = 0; i < pll->num_vco_entries; i++) if (target_rate == pll->vco_param[i].rate) break;
if (i >= pll->num_vco_entries) return -EINVAL;
return i;
}
staticint get_kp(unsignedlong ref_freq, enum kp_band kp_index)
{ int i;
if (ref_freq < ref_freq_table[0][0]) return -EINVAL;
for (i = 0; i < NUM_FREQ_BANDS; i++) { if (ref_freq >= ref_freq_table[i][0] &&
ref_freq < ref_freq_table[i][1]) return kp_table[kp_index][i];
} return -EINVAL;
}
if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
val = readl(pll->asiu_base + ctrl->asiu.offset);
val &= ~(1 << ctrl->asiu.en_shift);
iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val);
}
if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) {
val = readl(pll->control_base + ctrl->aon.offset);
val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val);
}
if (pll->pwr_base) { /* latch input value so core power can be shut down */
val = readl(pll->pwr_base + ctrl->aon.offset);
val |= 1 << ctrl->aon.iso_shift;
iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
/* power down the core */
val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
}
}
if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) {
val = readl(pll->control_base + ctrl->aon.offset);
val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val);
}
if (pll->pwr_base) { /* power up the PLL and make sure it's not latched */
val = readl(pll->pwr_base + ctrl->aon.offset);
val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
val &= ~(1 << ctrl->aon.iso_shift);
iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
}
/* certain PLLs also need to be ungated from the ASIU top level */ if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
val = readl(pll->asiu_base + ctrl->asiu.offset);
val |= (1 << ctrl->asiu.en_shift);
iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val);
}
val = readl(pll->control_base + dig_filter->offset);
val &= ~(bit_mask(dig_filter->ki_width) << dig_filter->ki_shift |
bit_mask(dig_filter->kp_width) << dig_filter->kp_shift |
bit_mask(dig_filter->ka_width) << dig_filter->ka_shift);
val |= ki << dig_filter->ki_shift | kp << dig_filter->kp_shift |
ka << dig_filter->ka_shift;
iproc_pll_write(pll, pll->control_base, dig_filter->offset, val);
val = readl(pll->control_base + reset->offset); if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW)
val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift)); else
val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift);
iproc_pll_write(pll, pll->control_base, reset->offset, val);
}
/* * Determines if the change to be applied to the PLL is minor (just an update * or the fractional divider). If so, then we can avoid going through a * disruptive reset and lock sequence.
*/ staticbool pll_fractional_change_only(struct iproc_pll *pll, struct iproc_pll_vco_param *vco)
{ conststruct iproc_pll_ctrl *ctrl = pll->ctrl;
u32 val;
u32 ndiv_int; unsignedint pdiv;
/* PLL needs to be locked */
val = readl(pll->status_base + ctrl->status.offset); if ((val & (1 << ctrl->status.shift)) == 0) returnfalse;
/* * reference frequency = parent frequency / PDIV * If PDIV = 0, then it becomes a multiplier (x2)
*/ if (vco->pdiv == 0)
ref_freq = parent_rate * 2; else
ref_freq = parent_rate / vco->pdiv;
/* determine Ki and Kp index based on target VCO frequency */ if (rate >= VCO_LOW && rate < VCO_HIGH) {
ki = 4;
kp_index = KP_BAND_MID;
} elseif (rate >= VCO_HIGH && rate < VCO_HIGH_HIGH) {
ki = 3;
kp_index = KP_BAND_HIGH;
} elseif (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) {
ki = 3;
kp_index = KP_BAND_HIGH_HIGH;
} else {
pr_err("%s: pll: %s has invalid rate: %lu\n", __func__,
clk_name, rate); return -EINVAL;
}
kp = get_kp(ref_freq, kp_index); if (kp < 0) {
pr_err("%s: pll: %s has invalid kp\n", __func__, clk_name); return kp;
}
ret = __pll_enable(pll); if (ret) {
pr_err("%s: pll: %s fails to enable\n", __func__, clk_name); return ret;
}
if (pll_fractional_change_only(clk->pll, vco)) { /* program fractional part of NDIV */ if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
val = readl(pll->control_base + ctrl->ndiv_frac.offset);
val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
ctrl->ndiv_frac.shift);
val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
iproc_pll_write(pll, pll->control_base,
ctrl->ndiv_frac.offset, val); return 0;
}
}
/* put PLL in reset */
__pll_put_in_reset(pll);
/* set PLL in user mode before modifying PLL controls */ if (ctrl->flags & IPROC_CLK_PLL_USER_MODE_ON) {
val = readl(pll->control_base + ctrl->macro_mode.offset);
val &= ~(bit_mask(ctrl->macro_mode.width) <<
ctrl->macro_mode.shift);
val |= PLL_USER_MODE << ctrl->macro_mode.shift;
iproc_pll_write(pll, pll->control_base,
ctrl->macro_mode.offset, val);
}
/* program integer part of NDIV */
val = readl(pll->control_base + ctrl->ndiv_int.offset);
val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift);
val |= vco->ndiv_int << ctrl->ndiv_int.shift;
iproc_pll_write(pll, pll->control_base, ctrl->ndiv_int.offset, val);
/* program fractional part of NDIV */ if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
val = readl(pll->control_base + ctrl->ndiv_frac.offset);
val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
ctrl->ndiv_frac.shift);
val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
iproc_pll_write(pll, pll->control_base, ctrl->ndiv_frac.offset,
val);
}
/* program PDIV */
val = readl(pll->control_base + ctrl->pdiv.offset);
val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift);
val |= vco->pdiv << ctrl->pdiv.shift;
iproc_pll_write(pll, pll->control_base, ctrl->pdiv.offset, val);
__pll_bring_out_reset(pll, kp, ka, ki);
ret = pll_wait_for_lock(pll); if (ret < 0) {
pr_err("%s: pll: %s failed to lock\n", __func__, clk_name); return ret;
}
/* channel enable is active low */
val = readl(pll->control_base + ctrl->enable.offset);
val &= ~(1 << ctrl->enable.enable_shift);
iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
/* also make sure channel is not held */
val = readl(pll->control_base + ctrl->enable.offset);
val &= ~(1 << ctrl->enable.hold_shift);
iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
/* * Some PLLs require the PLL SW override bit to be set before changes can be * applied to the PLL
*/ staticvoid iproc_pll_sw_cfg(struct iproc_pll *pll)
{ conststruct iproc_pll_ctrl *ctrl = pll->ctrl;
if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) {
u32 val;
val = readl(pll->control_base + ctrl->sw_ctrl.offset);
val |= BIT(ctrl->sw_ctrl.shift);
iproc_pll_write(pll, pll->control_base, ctrl->sw_ctrl.offset,
val);
}
}
iclk_array = kcalloc(num_clks, sizeof(struct iproc_clk), GFP_KERNEL); if (WARN_ON(!iclk_array)) goto err_clks;
pll->control_base = of_iomap(node, 0); if (WARN_ON(!pll->control_base)) goto err_pll_iomap;
/* Some SoCs do not require the pwr_base, thus failing is not fatal */
pll->pwr_base = of_iomap(node, 1);
/* some PLLs require gating control at the top ASIU level */ if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) {
pll->asiu_base = of_iomap(node, 2); if (WARN_ON(!pll->asiu_base)) goto err_asiu_iomap;
}
if (pll_ctrl->flags & IPROC_CLK_PLL_SPLIT_STAT_CTRL) { /* Some SoCs have a split status/control. If this does not * exist, assume they are unified.
*/
pll->status_base = of_iomap(node, 2); if (!pll->status_base) goto err_status_iomap;
} else
pll->status_base = pll->control_base;
/* initialize and register the PLL itself */
pll->ctrl = pll_ctrl;
iclk = &iclk_array[0];
iclk->pll = pll;
ret = of_property_read_string_index(node, "clock-output-names",
0, &clk_name); if (WARN_ON(ret)) goto err_pll_register;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.