^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (c) 2017 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) * Author: Jerome Brunet <jbrunet@baylibre.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * The AO Domain embeds a dual/divider to generate a more precise
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * 32,768KHz clock for low-power suspend mode and CEC.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * ______ ______
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * | | | |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * | Div1 |-| Cnt1 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * /|______| |______|\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * -| ______ ______ X--> Out
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * \| | | |/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * | Div2 |-| Cnt2 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * |______| |______|
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * The dividing can be switched to single or dual, with a counter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * for each divider to set when the switching is done.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include "clk-regmap.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include "clk-dualdiv.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static inline struct meson_clk_dualdiv_data *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) meson_clk_dualdiv_data(struct clk_regmap *clk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) return (struct meson_clk_dualdiv_data *)clk->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static unsigned long
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) __dualdiv_param_to_rate(unsigned long parent_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) const struct meson_clk_dualdiv_param *p)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) if (!p->dual)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) return DIV_ROUND_CLOSEST(parent_rate, p->n1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) return DIV_ROUND_CLOSEST(parent_rate * (p->m1 + p->m2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) p->n1 * p->m1 + p->n2 * p->m2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) static unsigned long meson_clk_dualdiv_recalc_rate(struct clk_hw *hw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) struct clk_regmap *clk = to_clk_regmap(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) struct meson_clk_dualdiv_param setting;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) setting.dual = meson_parm_read(clk->map, &dualdiv->dual);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) setting.n1 = meson_parm_read(clk->map, &dualdiv->n1) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) setting.m1 = meson_parm_read(clk->map, &dualdiv->m1) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) setting.n2 = meson_parm_read(clk->map, &dualdiv->n2) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) setting.m2 = meson_parm_read(clk->map, &dualdiv->m2) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) return __dualdiv_param_to_rate(parent_rate, &setting);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) static const struct meson_clk_dualdiv_param *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) __dualdiv_get_setting(unsigned long rate, unsigned long parent_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) struct meson_clk_dualdiv_data *dualdiv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) const struct meson_clk_dualdiv_param *table = dualdiv->table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) unsigned long best = 0, now = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) unsigned int i, best_i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) if (!table)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) for (i = 0; table[i].n1; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) now = __dualdiv_param_to_rate(parent_rate, &table[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) /* If we get an exact match, don't bother any further */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (now == rate) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) return &table[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) } else if (abs(now - rate) < abs(best - rate)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) best = now;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) best_i = i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) return (struct meson_clk_dualdiv_param *)&table[best_i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static long meson_clk_dualdiv_round_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) unsigned long *parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) struct clk_regmap *clk = to_clk_regmap(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) const struct meson_clk_dualdiv_param *setting =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) __dualdiv_get_setting(rate, *parent_rate, dualdiv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) if (!setting)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return meson_clk_dualdiv_recalc_rate(hw, *parent_rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) return __dualdiv_param_to_rate(*parent_rate, setting);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) struct clk_regmap *clk = to_clk_regmap(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) const struct meson_clk_dualdiv_param *setting =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) __dualdiv_get_setting(rate, parent_rate, dualdiv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) if (!setting)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) meson_parm_write(clk->map, &dualdiv->dual, setting->dual);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) meson_parm_write(clk->map, &dualdiv->n1, setting->n1 - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) meson_parm_write(clk->map, &dualdiv->m1, setting->m1 - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) meson_parm_write(clk->map, &dualdiv->n2, setting->n2 - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) meson_parm_write(clk->map, &dualdiv->m2, setting->m2 - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) const struct clk_ops meson_clk_dualdiv_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .recalc_rate = meson_clk_dualdiv_recalc_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) .round_rate = meson_clk_dualdiv_round_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) .set_rate = meson_clk_dualdiv_set_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) const struct clk_ops meson_clk_dualdiv_ro_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) .recalc_rate = meson_clk_dualdiv_recalc_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) MODULE_DESCRIPTION("Amlogic dual divider driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) MODULE_LICENSE("GPL v2");