Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^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");