^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: (GPL-2.0 OR MIT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (c) 2019 BayLibre, SAS.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Author: Neil Armstrong <narmstrong@baylibre.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include "clk-regmap.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include "clk-cpu-dyndiv.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) static inline struct meson_clk_cpu_dyndiv_data *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) meson_clk_cpu_dyndiv_data(struct clk_regmap *clk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) return (struct meson_clk_cpu_dyndiv_data *)clk->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) static unsigned long meson_clk_cpu_dyndiv_recalc_rate(struct clk_hw *hw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) unsigned long prate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) struct clk_regmap *clk = to_clk_regmap(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) return divider_recalc_rate(hw, prate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) meson_parm_read(clk->map, &data->div),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) NULL, 0, data->div.width);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static long meson_clk_cpu_dyndiv_round_rate(struct clk_hw *hw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) unsigned long *prate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct clk_regmap *clk = to_clk_regmap(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) return divider_round_rate(hw, rate, prate, NULL, data->div.width, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static int meson_clk_cpu_dyndiv_set_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) struct clk_regmap *clk = to_clk_regmap(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) ret = divider_get_val(rate, parent_rate, NULL, data->div.width, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) val = (unsigned int)ret << data->div.shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) /* Write the SYS_CPU_DYN_ENABLE bit before changing the divider */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) meson_parm_write(clk->map, &data->dyn, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) /* Update the divider while removing the SYS_CPU_DYN_ENABLE bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return regmap_update_bits(clk->map, data->div.reg_off,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) SETPMASK(data->div.width, data->div.shift) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) SETPMASK(data->dyn.width, data->dyn.shift),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) const struct clk_ops meson_clk_cpu_dyndiv_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) .recalc_rate = meson_clk_cpu_dyndiv_recalc_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) .round_rate = meson_clk_cpu_dyndiv_round_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) .set_rate = meson_clk_cpu_dyndiv_set_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) EXPORT_SYMBOL_GPL(meson_clk_cpu_dyndiv_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) MODULE_DESCRIPTION("Amlogic CPU Dynamic Clock divider");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) MODULE_LICENSE("GPL v2");