/* * Set KICK bit in FRQCRB to update hardware setting and wait for * clock change completion.
*/
cpg_reg_modify(zclk->kick_reg, 0, CPG_FRQCRB_KICK);
/* * Note: There is no HW information about the worst case latency. * * Using experimental measurements, it seems that no more than * ~10 iterations are needed, independently of the CPU rate. * Since this value might be dependent on external xtal rate, pll1 * rate or even the other emulation clocks rate, use 1000 as a * "super" safe value.
*/ for (i = 1000; i; i--) { if (!(readl(zclk->kick_reg) & CPG_FRQCRB_KICK)) return 0;
parent = clks[core->parent & 0xffff]; /* some types use high bits */ if (IS_ERR(parent)) return ERR_CAST(parent);
switch (core->type) { case CLK_TYPE_GEN3_MAIN:
div = cpg_pll_config->extal_div; break;
case CLK_TYPE_GEN3_PLL0: /* * PLL0 is implemented as a custom clock, to change the * multiplier when cpufreq changes between normal and boost * modes.
*/ return cpg_pll_clk_register(core->name, __clk_get_name(parent),
base, 2, CPG_PLL0CR, 0);
case CLK_TYPE_GEN3_PLL1:
mult = cpg_pll_config->pll1_mult;
div = cpg_pll_config->pll1_div; break;
case CLK_TYPE_GEN3_PLL2: /* * PLL2 is implemented as a custom clock, to change the * multiplier when cpufreq changes between normal and boost * modes.
*/ return cpg_pll_clk_register(core->name, __clk_get_name(parent),
base, 2, CPG_PLL2CR, 2);
case CLK_TYPE_GEN3_PLL3:
mult = cpg_pll_config->pll3_mult;
div = cpg_pll_config->pll3_div; break;
case CLK_TYPE_GEN3_PLL4: /* * PLL4 is a configurable multiplier clock. Register it as a * fixed factor clock for now as there's no generic multiplier * clock implementation and we currently have no need to change * the multiplier value.
*/
value = readl(base + CPG_PLL4CR);
mult = (((value >> 24) & 0x7f) + 1) * 2; break;
case CLK_TYPE_GEN3_SDH: return cpg_sdh_clk_register(core->name, base + core->offset,
__clk_get_name(parent), notifiers);
case CLK_TYPE_GEN3_SD: return cpg_sd_clk_register(core->name, base + core->offset,
__clk_get_name(parent));
case CLK_TYPE_GEN3_R: if (cpg_quirks & RCKCR_CKSEL) { struct cpg_simple_notifier *csn;
csn = kzalloc(sizeof(*csn), GFP_KERNEL); if (!csn) return ERR_PTR(-ENOMEM);
csn->reg = base + CPG_RCKCR;
/* * RINT is default. * Only if EXTALR is populated, we switch to it.
*/
value = readl(csn->reg) & 0x3f;
if (clk_get_rate(clks[cpg_clk_extalr])) {
parent = clks[cpg_clk_extalr];
value |= CPG_RCKCR_CKSEL;
}
/* Select parent clock of RCLK by MD28 */ if (cpg_mode & BIT(28))
parent = clks[cpg_clk_extalr]; break;
case CLK_TYPE_GEN3_MDSEL: /* * Clock selectable between two parents and two fixed dividers * using a mode pin
*/ if (cpg_mode & BIT(core->offset)) {
div = core->div & 0xffff;
} else {
parent = clks[core->parent >> 16]; if (IS_ERR(parent)) return ERR_CAST(parent);
div = core->div >> 16;
}
mult = 1; break;
case CLK_TYPE_GEN3_Z: return cpg_z_clk_register(core->name, __clk_get_name(parent),
base, core->div, core->offset);
case CLK_TYPE_GEN3_ZG: return cpg_zg_clk_register(core->name, __clk_get_name(parent),
base, core->div, core->offset);
case CLK_TYPE_GEN3_OSC: /* * Clock combining OSC EXTAL predivider and a fixed divider
*/
div = cpg_pll_config->osc_prediv * core->div; break;
case CLK_TYPE_GEN3_RCKSEL: /* * Clock selectable between two parents and two fixed dividers * using RCKCR.CKSEL
*/ if (readl(base + CPG_RCKCR) & CPG_RCKCR_CKSEL) {
div = core->div & 0xffff;
} else {
parent = clks[core->parent >> 16]; if (IS_ERR(parent)) return ERR_CAST(parent);
div = core->div >> 16;
} break;
case CLK_TYPE_GEN3_RPCSRC: return clk_register_divider_table(NULL, core->name,
__clk_get_name(parent), 0,
base + CPG_RPCCKCR, 3, 2, 0,
cpg_rpcsrc_div_table,
&cpg_lock);
case CLK_TYPE_GEN3_E3_RPCSRC: /* * Register RPCSRC as fixed factor clock based on the * MD[4:1] pins and CPG_RPCCKCR[4:3] register value for * which has been set prior to booting the kernel.
*/
value = (readl(base + CPG_RPCCKCR) & GENMASK(4, 3)) >> 3;
switch (value) { case 0:
div = 5; break; case 1:
div = 3; break; case 2:
parent = clks[core->parent >> 16]; if (IS_ERR(parent)) return ERR_CAST(parent);
div = core->div; break; case 3: default:
div = 2; break;
} break;
case CLK_TYPE_GEN3_RPC: return cpg_rpc_clk_register(core->name, base + CPG_RPCCKCR,
__clk_get_name(parent), notifiers);
case CLK_TYPE_GEN3_RPCD2: return cpg_rpcd2_clk_register(core->name, base + CPG_RPCCKCR,
__clk_get_name(parent));
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.