/* All following shift value are the same for all DIV registers */ #define SHIFT_DIV_RESET_CTRL 0 #define SHIFT_DIV_FACTOR_SEL 3 #define SHIFT_DIV_FACTOR 16
/** * struct sg2042_divider_clock - Divider clock * @hw: clk_hw for initialization * @id: used to map clk_onecell_data * @reg: used for readl/writel. * **NOTE**: DIV registers are ALL in CLOCK! * @lock: spinlock to protect register access, modification of * frequency can only be served one at the time * @offset_ctrl: offset of divider control registers * @shift: shift of "Clock Divider Factor" in divider control register * @width: width of "Clock Divider Factor" in divider control register * @div_flags: private flags for this clock, not for framework-specific * @initval: In the divider control register, we can configure whether * to use the value of "Clock Divider Factor" or just use * the initial value pre-configured by IC. BIT[3] controls * this and by default (value is 0), means initial value * is used. * **NOTE** that we cannot read the initial value (default * value when poweron) and default value of "Clock Divider * Factor" is zero, which I think is a hardware design flaw * and should be sync-ed with the initial value. So in * software we have to add a configuration item (initval) * to manually configure this value and use it when BIT[3] * is zero.
*/ struct sg2042_divider_clock { struct clk_hw hw;
/** * struct sg2042_gate_clock - Gate clock * @hw: clk_hw for initialization * @id: used to map clk_onecell_data * @offset_enable: offset of gate enable registers * @bit_idx: which bit in the register controls gating of this clock
*/ struct sg2042_gate_clock { struct clk_hw hw;
unsignedint id;
u32 offset_enable;
u8 bit_idx;
};
/** * struct sg2042_mux_clock - Mux clock * @hw: clk_hw for initialization * @id: used to map clk_onecell_data * @offset_select: offset of mux selection registers * **NOTE**: MUX registers are ALL in CLOCK! * @shift: shift of "Clock Select" in mux selection register * @width: width of "Clock Select" in mux selection register * @clk_nb: used for notification * @original_index: set by notifier callback
*/ struct sg2042_mux_clock { struct clk_hw hw;
if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
val = divider->initval;
} else {
val = readl(divider->reg) >> divider->shift;
val &= clk_div_mask(divider->width);
}
value = divider_get_val(rate, parent_rate, NULL,
divider->width, divider->div_flags);
if (divider->lock)
spin_lock_irqsave(divider->lock, flags); else
__acquire(divider->lock);
/* * The sequence of clock frequency modification is: * Assert to reset divider. * Modify the value of Clock Divide Factor (and High Wide if needed). * De-assert to restore divided clock with new frequency.
*/
val = readl(divider->reg);
/* assert */
val &= ~BIT(SHIFT_DIV_RESET_CTRL);
writel(val, divider->reg);
if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {
val = clk_div_mask(divider->width) << (divider->shift + 16);
} else {
val = readl(divider->reg);
val &= ~(clk_div_mask(divider->width) << divider->shift);
}
val |= value << divider->shift;
val |= BIT(SHIFT_DIV_FACTOR_SEL);
writel(val, divider->reg);
val2 = val;
/* de-assert */
val |= BIT(SHIFT_DIV_RESET_CTRL);
writel(val, divider->reg);
if (divider->lock)
spin_unlock_irqrestore(divider->lock, flags); else
__release(divider->lock);
/* * Clock items in the array are sorted according to the clock-tree diagram, * from top to bottom, from upstream to downstream. Read TRM for details.
*/
/* * Note: regarding names for mux clock, "0/1" or "div0/div1" means the * first/second parent input source, not the register value. * For example: * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0", * they are both controlled by register CLKDIVREG27; * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1", * they are both controlled by register CLKDIVREG28; * While for register value of mux selection, use Clock Select for DDR01’s clock * as example, see CLKSELREG0, bit[2]. * 1: Select in_dpll0_clk as clock source, correspondng to the parent input * source from "clk_div_ddr01_0". * 0: Select in_fpll_clk as clock source, corresponding to the parent input * source from "clk_div_ddr01_1". * So we need a table to define the array of register values corresponding to * the parent index and tell CCF about this when registering mux clock.
*/ staticconst u32 sg2042_mux_table[] = {1, 0};
/* * downstream of clk_gate_rp_cpu_normal * * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal * and clk_gate_axi_pcie0/clk_gate_axi_pcie1. * But the 1/2 DIV is fixed and no configurable register exported, so * when reading from these two clocks, the rate value are still the * same as that of clk_gate_rp_cpu_normal, it's not correct. * This just affects the value read.
*/
SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0, "clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),
SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1, "clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),
hw = &div->hw;
ret = devm_clk_hw_register(dev, hw); if (ret) {
pr_err("failed to register clock %s\n", div->hw.init->name); break;
}
clk_data->onecell_data.hws[div->id] = hw;
}
return ret;
}
staticint sg2042_clk_register_gates(struct device *dev, struct sg2042_clk_data *clk_data, conststruct sg2042_gate_clock gate_clks[], int num_gate_clks)
{ conststruct sg2042_gate_clock *gate; struct clk_hw *hw; int i, ret = 0;
for (i = 0; i < num_gate_clks; i++) {
gate = &gate_clks[i];
hw = __devm_clk_hw_register_gate
(dev,
NULL,
gate->hw.init->name,
NULL,
gate->hw.init->parent_hws[0],
NULL,
gate->hw.init->flags,
clk_data->iobase + gate->offset_enable,
gate->bit_idx,
0,
&sg2042_clk_lock); if (IS_ERR(hw)) {
pr_err("failed to register clock %s\n", gate->hw.init->name);
ret = PTR_ERR(hw); break;
}
clk_data->onecell_data.hws[gate->id] = hw;
/* Updated some clocks which take the role of parent */ switch (gate->id) { case GATE_CLK_RP_CPU_NORMAL:
*clk_gate_rp_cpu_normal = hw; break; case GATE_CLK_TOP_RP_CMN_DIV2:
*clk_gate_top_rp_cmn_div2 = hw; break;
}
}
return ret;
}
staticint sg2042_clk_register_gates_fw(struct device *dev, struct sg2042_clk_data *clk_data, conststruct sg2042_gate_clock gate_clks[], int num_gate_clks)
{ conststruct sg2042_gate_clock *gate; struct clk_hw *hw; int i, ret = 0;
for (i = 0; i < num_gate_clks; i++) {
gate = &gate_clks[i];
hw = devm_clk_hw_register_gate_parent_data
(dev,
gate->hw.init->name,
gate->hw.init->parent_data,
gate->hw.init->flags,
clk_data->iobase + gate->offset_enable,
gate->bit_idx,
0,
&sg2042_clk_lock); if (IS_ERR(hw)) {
pr_err("failed to register clock %s\n", gate->hw.init->name);
ret = PTR_ERR(hw); break;
}
clk_data->onecell_data.hws[gate->id] = hw;
/* Updated some clocks which take the role of parent */ switch (gate->id) { case GATE_CLK_DDR01_DIV0:
*clk_gate_ddr01_div0 = hw; break; case GATE_CLK_DDR01_DIV1:
*clk_gate_ddr01_div1 = hw; break; case GATE_CLK_DDR23_DIV0:
*clk_gate_ddr23_div0 = hw; break; case GATE_CLK_DDR23_DIV1:
*clk_gate_ddr23_div1 = hw; break; case GATE_CLK_RP_CPU_NORMAL_DIV0:
*clk_gate_rp_cpu_normal_div0 = hw; break; case GATE_CLK_RP_CPU_NORMAL_DIV1:
*clk_gate_rp_cpu_normal_div1 = hw; break; case GATE_CLK_AXI_DDR_DIV0:
*clk_gate_axi_ddr_div0 = hw; break; case GATE_CLK_AXI_DDR_DIV1:
*clk_gate_axi_ddr_div1 = hw; break;
}
}
/* To switch to fpll before changing rate and restore after that */ if (event == PRE_RATE_CHANGE) {
mux->original_index = ops->get_parent(hw);
/* * "1" is the array index of the second parent input source of * mux. For SG2042, it's fpll for all mux clocks. * "0" is the array index of the first parent input source of * mux, For SG2042, it's mpll. * FIXME, any good idea to avoid magic number?
*/ if (mux->original_index == 0)
ret = ops->set_parent(hw, 1);
} elseif (event == POST_RATE_CHANGE) {
ret = ops->set_parent(hw, mux->original_index);
}
return notifier_from_errno(ret);
}
staticint sg2042_clk_register_muxs(struct device *dev, struct sg2042_clk_data *clk_data, struct sg2042_mux_clock mux_clks[], int num_mux_clks)
{ struct sg2042_mux_clock *mux; struct clk_hw *hw; int i, ret = 0;
for (i = 0; i < num_mux_clks; i++) {
mux = &mux_clks[i];
/* Updated some clocks which takes the role of parent */ switch (mux->id) { case MUX_CLK_DDR01:
*clk_mux_ddr01 = hw; break; case MUX_CLK_DDR23:
*clk_mux_ddr23 = hw; break; case MUX_CLK_RP_CPU_NORMAL:
*clk_mux_rp_cpu_normal = hw; break; case MUX_CLK_AXI_DDR:
*clk_mux_axi_ddr = hw; break;
}
/* * FIXME: Theoretically, we should set parent for the * mux, but seems hardware has done this for us with * default value, so we don't set parent again here.
*/
if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {
mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;
ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb); if (ret) {
pr_err("failed to register clock notifier for %s\n",
mux->hw.init->name); break;
}
}
}
ret = sg2042_init_clkdata(pdev, num_clks, &clk_data); if (ret) goto error_out;
/* level-1 gates */
ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,
sg2042_gate_clks_level_1,
ARRAY_SIZE(sg2042_gate_clks_level_1)); if (ret) goto error_out;
/* level-1 div */
ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,
ARRAY_SIZE(sg2042_div_clks_level_1)); if (ret) goto error_out;
/* mux */
ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,
ARRAY_SIZE(sg2042_mux_clks)); if (ret) goto error_out;
/* level 2 div */
ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,
ARRAY_SIZE(sg2042_div_clks_level_2)); if (ret) goto error_out;
/* level 2 gate */
ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,
ARRAY_SIZE(sg2042_gate_clks_level_2)); if (ret) goto error_out;
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.