0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include <linux/clk-provider.h>
0025 #include <linux/module.h>
0026
0027 #include "clk-regmap.h"
0028 #include "clk-dualdiv.h"
0029
0030 static inline struct meson_clk_dualdiv_data *
0031 meson_clk_dualdiv_data(struct clk_regmap *clk)
0032 {
0033 return (struct meson_clk_dualdiv_data *)clk->data;
0034 }
0035
0036 static unsigned long
0037 __dualdiv_param_to_rate(unsigned long parent_rate,
0038 const struct meson_clk_dualdiv_param *p)
0039 {
0040 if (!p->dual)
0041 return DIV_ROUND_CLOSEST(parent_rate, p->n1);
0042
0043 return DIV_ROUND_CLOSEST(parent_rate * (p->m1 + p->m2),
0044 p->n1 * p->m1 + p->n2 * p->m2);
0045 }
0046
0047 static unsigned long meson_clk_dualdiv_recalc_rate(struct clk_hw *hw,
0048 unsigned long parent_rate)
0049 {
0050 struct clk_regmap *clk = to_clk_regmap(hw);
0051 struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
0052 struct meson_clk_dualdiv_param setting;
0053
0054 setting.dual = meson_parm_read(clk->map, &dualdiv->dual);
0055 setting.n1 = meson_parm_read(clk->map, &dualdiv->n1) + 1;
0056 setting.m1 = meson_parm_read(clk->map, &dualdiv->m1) + 1;
0057 setting.n2 = meson_parm_read(clk->map, &dualdiv->n2) + 1;
0058 setting.m2 = meson_parm_read(clk->map, &dualdiv->m2) + 1;
0059
0060 return __dualdiv_param_to_rate(parent_rate, &setting);
0061 }
0062
0063 static const struct meson_clk_dualdiv_param *
0064 __dualdiv_get_setting(unsigned long rate, unsigned long parent_rate,
0065 struct meson_clk_dualdiv_data *dualdiv)
0066 {
0067 const struct meson_clk_dualdiv_param *table = dualdiv->table;
0068 unsigned long best = 0, now = 0;
0069 unsigned int i, best_i = 0;
0070
0071 if (!table)
0072 return NULL;
0073
0074 for (i = 0; table[i].n1; i++) {
0075 now = __dualdiv_param_to_rate(parent_rate, &table[i]);
0076
0077
0078 if (now == rate) {
0079 return &table[i];
0080 } else if (abs(now - rate) < abs(best - rate)) {
0081 best = now;
0082 best_i = i;
0083 }
0084 }
0085
0086 return (struct meson_clk_dualdiv_param *)&table[best_i];
0087 }
0088
0089 static long meson_clk_dualdiv_round_rate(struct clk_hw *hw, unsigned long rate,
0090 unsigned long *parent_rate)
0091 {
0092 struct clk_regmap *clk = to_clk_regmap(hw);
0093 struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
0094 const struct meson_clk_dualdiv_param *setting =
0095 __dualdiv_get_setting(rate, *parent_rate, dualdiv);
0096
0097 if (!setting)
0098 return meson_clk_dualdiv_recalc_rate(hw, *parent_rate);
0099
0100 return __dualdiv_param_to_rate(*parent_rate, setting);
0101 }
0102
0103 static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate,
0104 unsigned long parent_rate)
0105 {
0106 struct clk_regmap *clk = to_clk_regmap(hw);
0107 struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
0108 const struct meson_clk_dualdiv_param *setting =
0109 __dualdiv_get_setting(rate, parent_rate, dualdiv);
0110
0111 if (!setting)
0112 return -EINVAL;
0113
0114 meson_parm_write(clk->map, &dualdiv->dual, setting->dual);
0115 meson_parm_write(clk->map, &dualdiv->n1, setting->n1 - 1);
0116 meson_parm_write(clk->map, &dualdiv->m1, setting->m1 - 1);
0117 meson_parm_write(clk->map, &dualdiv->n2, setting->n2 - 1);
0118 meson_parm_write(clk->map, &dualdiv->m2, setting->m2 - 1);
0119
0120 return 0;
0121 }
0122
0123 const struct clk_ops meson_clk_dualdiv_ops = {
0124 .recalc_rate = meson_clk_dualdiv_recalc_rate,
0125 .round_rate = meson_clk_dualdiv_round_rate,
0126 .set_rate = meson_clk_dualdiv_set_rate,
0127 };
0128 EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ops);
0129
0130 const struct clk_ops meson_clk_dualdiv_ro_ops = {
0131 .recalc_rate = meson_clk_dualdiv_recalc_rate,
0132 };
0133 EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops);
0134
0135 MODULE_DESCRIPTION("Amlogic dual divider driver");
0136 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
0137 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0138 MODULE_LICENSE("GPL v2");