0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #include <linux/clk-provider.h>
0020 #include <linux/module.h>
0021
0022 #include "clk-regmap.h"
0023 #include "sclk-div.h"
0024
0025 static inline struct meson_sclk_div_data *
0026 meson_sclk_div_data(struct clk_regmap *clk)
0027 {
0028 return (struct meson_sclk_div_data *)clk->data;
0029 }
0030
0031 static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
0032 {
0033 return (1 << sclk->div.width) - 1;
0034 }
0035
0036 static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
0037 {
0038 return sclk_div_maxval(sclk) + 1;
0039 }
0040
0041 static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
0042 unsigned long prate, int maxdiv)
0043 {
0044 int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
0045
0046 return clamp(div, 2, maxdiv);
0047 }
0048
0049 static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
0050 unsigned long *prate,
0051 struct meson_sclk_div_data *sclk)
0052 {
0053 struct clk_hw *parent = clk_hw_get_parent(hw);
0054 int bestdiv = 0, i;
0055 unsigned long maxdiv, now, parent_now;
0056 unsigned long best = 0, best_parent = 0;
0057
0058 if (!rate)
0059 rate = 1;
0060
0061 maxdiv = sclk_div_maxdiv(sclk);
0062
0063 if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
0064 return sclk_div_getdiv(hw, rate, *prate, maxdiv);
0065
0066
0067
0068
0069
0070 maxdiv = min(ULONG_MAX / rate, maxdiv);
0071
0072 for (i = 2; i <= maxdiv; i++) {
0073
0074
0075
0076
0077
0078 if (rate * i == *prate)
0079 return i;
0080
0081 parent_now = clk_hw_round_rate(parent, rate * i);
0082 now = DIV_ROUND_UP_ULL((u64)parent_now, i);
0083
0084 if (abs(rate - now) < abs(rate - best)) {
0085 bestdiv = i;
0086 best = now;
0087 best_parent = parent_now;
0088 }
0089 }
0090
0091 if (!bestdiv)
0092 bestdiv = sclk_div_maxdiv(sclk);
0093 else
0094 *prate = best_parent;
0095
0096 return bestdiv;
0097 }
0098
0099 static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
0100 unsigned long *prate)
0101 {
0102 struct clk_regmap *clk = to_clk_regmap(hw);
0103 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0104 int div;
0105
0106 div = sclk_div_bestdiv(hw, rate, prate, sclk);
0107
0108 return DIV_ROUND_UP_ULL((u64)*prate, div);
0109 }
0110
0111 static void sclk_apply_ratio(struct clk_regmap *clk,
0112 struct meson_sclk_div_data *sclk)
0113 {
0114 unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div *
0115 sclk->cached_duty.num,
0116 sclk->cached_duty.den);
0117
0118 if (hi)
0119 hi -= 1;
0120
0121 meson_parm_write(clk->map, &sclk->hi, hi);
0122 }
0123
0124 static int sclk_div_set_duty_cycle(struct clk_hw *hw,
0125 struct clk_duty *duty)
0126 {
0127 struct clk_regmap *clk = to_clk_regmap(hw);
0128 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0129
0130 if (MESON_PARM_APPLICABLE(&sclk->hi)) {
0131 memcpy(&sclk->cached_duty, duty, sizeof(*duty));
0132 sclk_apply_ratio(clk, sclk);
0133 }
0134
0135 return 0;
0136 }
0137
0138 static int sclk_div_get_duty_cycle(struct clk_hw *hw,
0139 struct clk_duty *duty)
0140 {
0141 struct clk_regmap *clk = to_clk_regmap(hw);
0142 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0143 int hi;
0144
0145 if (!MESON_PARM_APPLICABLE(&sclk->hi)) {
0146 duty->num = 1;
0147 duty->den = 2;
0148 return 0;
0149 }
0150
0151 hi = meson_parm_read(clk->map, &sclk->hi);
0152 duty->num = hi + 1;
0153 duty->den = sclk->cached_div;
0154 return 0;
0155 }
0156
0157 static void sclk_apply_divider(struct clk_regmap *clk,
0158 struct meson_sclk_div_data *sclk)
0159 {
0160 if (MESON_PARM_APPLICABLE(&sclk->hi))
0161 sclk_apply_ratio(clk, sclk);
0162
0163 meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
0164 }
0165
0166 static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
0167 unsigned long prate)
0168 {
0169 struct clk_regmap *clk = to_clk_regmap(hw);
0170 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0171 unsigned long maxdiv = sclk_div_maxdiv(sclk);
0172
0173 sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv);
0174
0175 if (clk_hw_is_enabled(hw))
0176 sclk_apply_divider(clk, sclk);
0177
0178 return 0;
0179 }
0180
0181 static unsigned long sclk_div_recalc_rate(struct clk_hw *hw,
0182 unsigned long prate)
0183 {
0184 struct clk_regmap *clk = to_clk_regmap(hw);
0185 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0186
0187 return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div);
0188 }
0189
0190 static int sclk_div_enable(struct clk_hw *hw)
0191 {
0192 struct clk_regmap *clk = to_clk_regmap(hw);
0193 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0194
0195 sclk_apply_divider(clk, sclk);
0196
0197 return 0;
0198 }
0199
0200 static void sclk_div_disable(struct clk_hw *hw)
0201 {
0202 struct clk_regmap *clk = to_clk_regmap(hw);
0203 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0204
0205 meson_parm_write(clk->map, &sclk->div, 0);
0206 }
0207
0208 static int sclk_div_is_enabled(struct clk_hw *hw)
0209 {
0210 struct clk_regmap *clk = to_clk_regmap(hw);
0211 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0212
0213 if (meson_parm_read(clk->map, &sclk->div))
0214 return 1;
0215
0216 return 0;
0217 }
0218
0219 static int sclk_div_init(struct clk_hw *hw)
0220 {
0221 struct clk_regmap *clk = to_clk_regmap(hw);
0222 struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
0223 unsigned int val;
0224
0225 val = meson_parm_read(clk->map, &sclk->div);
0226
0227
0228 if (!val)
0229 sclk->cached_div = sclk_div_maxdiv(sclk);
0230 else
0231 sclk->cached_div = val + 1;
0232
0233 sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
0234
0235 return 0;
0236 }
0237
0238 const struct clk_ops meson_sclk_div_ops = {
0239 .recalc_rate = sclk_div_recalc_rate,
0240 .round_rate = sclk_div_round_rate,
0241 .set_rate = sclk_div_set_rate,
0242 .enable = sclk_div_enable,
0243 .disable = sclk_div_disable,
0244 .is_enabled = sclk_div_is_enabled,
0245 .get_duty_cycle = sclk_div_get_duty_cycle,
0246 .set_duty_cycle = sclk_div_set_duty_cycle,
0247 .init = sclk_div_init,
0248 };
0249 EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
0250
0251 MODULE_DESCRIPTION("Amlogic Sample divider driver");
0252 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0253 MODULE_LICENSE("GPL v2");