| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- /*
- * Copyright (c) 2006-2022, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-3-08 GuEe-GUI the first version
- */
- #include "clk-rk-cpu.h"
- static rt_ubase_t rockchip_cpu_clk_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
- {
- void *base;
- rt_uint32_t clksel0;
- struct rockchip_cpu_clk_cell *cpu_clk_cell = cell_to_rockchip_cpu_cell(cell);
- const struct rockchip_cpu_clk_reg_data *reg_data = cpu_clk_cell->reg_data;
- base = cpu_clk_cell->rk_cell.provider->reg_base;
- clksel0 = HWREG32(base + reg_data->core_reg[0]);
- clksel0 >>= reg_data->div_core_shift[0];
- clksel0 &= reg_data->div_core_mask[0];
- return parent_rate / (clksel0 + 1);
- }
- const struct rt_clk_ops rockchip_cpu_clk_ops =
- {
- .recalc_rate = rockchip_cpu_clk_recalc_rate,
- };
- static const struct rockchip_cpu_clk_rate_table *rockchip_get_cpu_clk_settings(
- struct rockchip_cpu_clk_cell *cpu_clk_cell, rt_ubase_t rate)
- {
- const struct rockchip_cpu_clk_rate_table *rate_table = cpu_clk_cell->rate_table;
- for (int i = 0; i < cpu_clk_cell->rate_count; ++i)
- {
- if (rate == rate_table[i].prate)
- {
- return &rate_table[i];
- }
- }
- return RT_NULL;
- }
- static void rockchip_cpu_clk_set_dividers(struct rockchip_cpu_clk_cell *cpu_clk_cell,
- const struct rockchip_cpu_clk_rate_table *rate)
- {
- void *base = cpu_clk_cell->rk_cell.provider->reg_base;
- /* Alternate parent is active now. set the dividers */
- for (int i = 0; i < RT_ARRAY_SIZE(rate->divs); ++i)
- {
- const struct rockchip_cpu_clk_clksel *clksel = &rate->divs[i];
- if (!clksel->reg)
- {
- continue;
- }
- HWREG32(base + clksel->reg) = clksel->val;
- }
- }
- static void rockchip_cpu_clk_set_pre_muxs(struct rockchip_cpu_clk_cell *cpu_clk_cell,
- const struct rockchip_cpu_clk_rate_table *rate)
- {
- void *base = cpu_clk_cell->rk_cell.provider->reg_base;
- /* Alternate parent is active now. set the pre_muxs */
- for (int i = 0; i < RT_ARRAY_SIZE(rate->pre_muxs); ++i)
- {
- const struct rockchip_cpu_clk_clksel *clksel = &rate->pre_muxs[i];
- if (!clksel->reg)
- {
- break;
- }
- HWREG32(base + clksel->reg) = clksel->val;
- }
- }
- static void rockchip_cpu_clk_set_post_muxs(struct rockchip_cpu_clk_cell *cpu_clk_cell,
- const struct rockchip_cpu_clk_rate_table *rate)
- {
- void *base = cpu_clk_cell->rk_cell.provider->reg_base;
- /* Alternate parent is active now. set the muxs */
- for (int i = 0; i < RT_ARRAY_SIZE(rate->post_muxs); ++i)
- {
- const struct rockchip_cpu_clk_clksel *clksel = &rate->post_muxs[i];
- if (!clksel->reg)
- {
- break;
- }
- HWREG32(base + clksel->reg) = clksel->val;
- }
- }
- static int rockchip_cpu_clk_pre_rate_change(struct rockchip_cpu_clk_cell *cpu_clk_cell,
- rt_ubase_t old_rate, rt_ubase_t new_rate)
- {
- rt_ubase_t alt_prate, alt_div;
- void *base = cpu_clk_cell->rk_cell.provider->reg_base;
- const struct rockchip_cpu_clk_reg_data *reg_data = cpu_clk_cell->reg_data;
- const struct rockchip_cpu_clk_rate_table *rate;
- /* Check validity of the new rate */
- if (!(rate = rockchip_get_cpu_clk_settings(cpu_clk_cell, new_rate)))
- {
- return -RT_EINVAL;
- }
- alt_prate = rt_clk_cell_get_rate(&cpu_clk_cell->rk_cell_alt_parent->cell);
- /*
- * If the old parent clock speed is less than the clock speed
- * of the alternate parent, then it should be ensured that at no point
- * the armclk speed is more than the old_rate until the dividers are
- * set.
- */
- if (alt_prate > old_rate)
- {
- /* Calculate dividers */
- alt_div = RT_DIV_ROUND_UP(alt_prate, old_rate) - 1;
- if (alt_div > reg_data->div_core_mask[0])
- {
- alt_div = reg_data->div_core_mask[0];
- }
- /*
- * Change parents and add dividers in a single transaction.
- *
- * NOTE: we do this in a single transaction so we're never
- * dividing the primary parent by the extra dividers that were
- * needed for the alt.
- */
- for (int i = 0; i < reg_data->num_cores; ++i)
- {
- HWREG32(base + reg_data->core_reg[i]) = HIWORD_UPDATE(
- alt_div,
- reg_data->div_core_mask[i],
- reg_data->div_core_shift[i]);
- }
- }
- rockchip_cpu_clk_set_pre_muxs(cpu_clk_cell, rate);
- /* select alternate parent */
- if (reg_data->mux_core_reg)
- {
- HWREG32(base + reg_data->mux_core_reg) = HIWORD_UPDATE(
- reg_data->mux_core_alt,
- reg_data->mux_core_mask,
- reg_data->mux_core_shift);
- }
- else
- {
- HWREG32(base + reg_data->core_reg[0]) = HIWORD_UPDATE(
- reg_data->mux_core_alt,
- reg_data->mux_core_mask,
- reg_data->mux_core_shift);
- }
- return RT_EOK;
- }
- static rt_err_t rockchip_cpu_clk_post_rate_change(struct rockchip_cpu_clk_cell *cpu_clk_cell,
- rt_ubase_t old_rate, rt_ubase_t new_rate)
- {
- void *base = cpu_clk_cell->rk_cell.provider->reg_base;
- const struct rockchip_cpu_clk_rate_table *rate;
- const struct rockchip_cpu_clk_reg_data *reg_data = cpu_clk_cell->reg_data;
- if (!(rate = rockchip_get_cpu_clk_settings(cpu_clk_cell, new_rate)))
- {
- return -RT_EINVAL;
- }
- if (old_rate < new_rate)
- {
- rockchip_cpu_clk_set_dividers(cpu_clk_cell, rate);
- }
- /*
- * post-rate change event, re-mux to primary parent and remove dividers.
- *
- * NOTE: we do this in a single transaction so we're never dividing the
- * primary parent by the extra dividers that were needed for the alt.
- */
- if (reg_data->mux_core_reg)
- {
- HWREG32(base + reg_data->mux_core_reg) = HIWORD_UPDATE(
- reg_data->mux_core_main,
- reg_data->mux_core_mask,
- reg_data->mux_core_shift);
- }
- else
- {
- HWREG32(base + reg_data->core_reg[0]) = HIWORD_UPDATE(
- reg_data->mux_core_main,
- reg_data->mux_core_mask,
- reg_data->mux_core_shift);
- }
- rockchip_cpu_clk_set_post_muxs(cpu_clk_cell, rate);
- /* Remove dividers */
- for (int i = 0; i < reg_data->num_cores; ++i)
- {
- HWREG32(base + reg_data->core_reg[i]) = HIWORD_UPDATE(
- 0,
- reg_data->div_core_mask[i],
- reg_data->div_core_shift[i]);
- }
- if (old_rate > new_rate)
- {
- rockchip_cpu_clk_set_dividers(cpu_clk_cell, rate);
- }
- return RT_EOK;
- }
- static rt_err_t rockchip_cpu_clk_notify(struct rt_clk_notifier *notifier,
- rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate)
- {
- rt_err_t err = RT_EOK;
- struct rockchip_cpu_clk_cell *cpu_cell;
- cpu_cell = rt_container_of(notifier, struct rockchip_cpu_clk_cell, notifier);
- if (msg == RT_CLK_MSG_PRE_RATE_CHANGE)
- {
- err = rockchip_cpu_clk_pre_rate_change(cpu_cell, old_rate, new_rate);
- }
- else if (msg == RT_CLK_MSG_POST_RATE_CHANGE)
- {
- err = rockchip_cpu_clk_post_rate_change(cpu_cell, old_rate, new_rate);
- }
- return err;
- }
- void rockchip_cpu_clk_cell_init(struct rockchip_clk_cell *rk_cell)
- {
- struct rockchip_cpu_clk_cell *cpu_clk_cell = cell_to_rockchip_cpu_cell(&rk_cell->cell);
- rk_cell->cell.parents_nr = 1;
- rk_cell->cell.parent_name = cpu_clk_cell->rk_cell_parent->cell.name;
- if (cpu_clk_cell->rate_count > 0)
- {
- rk_cell->cell.flags |= RT_CLK_F_SET_RATE_PARENT;
- }
- }
- void rockchip_cpu_clk_cell_setup(struct rockchip_clk_cell *rk_cell)
- {
- struct rockchip_cpu_clk_cell *cpu_clk_cell = cell_to_rockchip_cpu_cell(&rk_cell->cell);
- rt_clk_prepare_enable(rt_clk_cell_get_clk(&cpu_clk_cell->rk_cell_alt_parent->cell, RT_NULL));
- cpu_clk_cell->notifier.callback = rockchip_cpu_clk_notify;
- rt_clk_notifier_register(rt_clk_cell_get_clk(&cpu_clk_cell->rk_cell_parent->cell, RT_NULL),
- &cpu_clk_cell->notifier);
- }
|