diff options
Diffstat (limited to 'drivers/clk/meson/clk-regmap.c')
-rw-r--r-- | drivers/clk/meson/clk-regmap.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c new file mode 100644 index 000000000000..3645fdb62343 --- /dev/null +++ b/drivers/clk/meson/clk-regmap.c | |||
@@ -0,0 +1,166 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (c) 2018 BayLibre, SAS. | ||
3 | // Author: Jerome Brunet <jbrunet@baylibre.com> | ||
4 | |||
5 | #include "clk-regmap.h" | ||
6 | |||
7 | static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) | ||
8 | { | ||
9 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
10 | struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); | ||
11 | int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; | ||
12 | |||
13 | set ^= enable; | ||
14 | |||
15 | return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx), | ||
16 | set ? BIT(gate->bit_idx) : 0); | ||
17 | } | ||
18 | |||
19 | static int clk_regmap_gate_enable(struct clk_hw *hw) | ||
20 | { | ||
21 | return clk_regmap_gate_endisable(hw, 1); | ||
22 | } | ||
23 | |||
24 | static void clk_regmap_gate_disable(struct clk_hw *hw) | ||
25 | { | ||
26 | clk_regmap_gate_endisable(hw, 0); | ||
27 | } | ||
28 | |||
29 | static int clk_regmap_gate_is_enabled(struct clk_hw *hw) | ||
30 | { | ||
31 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
32 | struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); | ||
33 | unsigned int val; | ||
34 | |||
35 | regmap_read(clk->map, gate->offset, &val); | ||
36 | if (gate->flags & CLK_GATE_SET_TO_DISABLE) | ||
37 | val ^= BIT(gate->bit_idx); | ||
38 | |||
39 | val &= BIT(gate->bit_idx); | ||
40 | |||
41 | return val ? 1 : 0; | ||
42 | } | ||
43 | |||
44 | const struct clk_ops clk_regmap_gate_ops = { | ||
45 | .enable = clk_regmap_gate_enable, | ||
46 | .disable = clk_regmap_gate_disable, | ||
47 | .is_enabled = clk_regmap_gate_is_enabled, | ||
48 | }; | ||
49 | EXPORT_SYMBOL_GPL(clk_regmap_gate_ops); | ||
50 | |||
51 | static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw, | ||
52 | unsigned long prate) | ||
53 | { | ||
54 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
55 | struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); | ||
56 | unsigned int val; | ||
57 | int ret; | ||
58 | |||
59 | ret = regmap_read(clk->map, div->offset, &val); | ||
60 | if (ret) | ||
61 | /* Gives a hint that something is wrong */ | ||
62 | return 0; | ||
63 | |||
64 | val >>= div->shift; | ||
65 | val &= clk_div_mask(div->width); | ||
66 | return divider_recalc_rate(hw, prate, val, div->table, div->flags, | ||
67 | div->width); | ||
68 | } | ||
69 | |||
70 | static long clk_regmap_div_round_rate(struct clk_hw *hw, unsigned long rate, | ||
71 | unsigned long *prate) | ||
72 | { | ||
73 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
74 | struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); | ||
75 | unsigned int val; | ||
76 | int ret; | ||
77 | |||
78 | /* if read only, just return current value */ | ||
79 | if (div->flags & CLK_DIVIDER_READ_ONLY) { | ||
80 | ret = regmap_read(clk->map, div->offset, &val); | ||
81 | if (ret) | ||
82 | /* Gives a hint that something is wrong */ | ||
83 | return 0; | ||
84 | |||
85 | val >>= div->shift; | ||
86 | val &= clk_div_mask(div->width); | ||
87 | |||
88 | return divider_ro_round_rate(hw, rate, prate, div->table, | ||
89 | div->width, div->flags, val); | ||
90 | } | ||
91 | |||
92 | return divider_round_rate(hw, rate, prate, div->table, div->width, | ||
93 | div->flags); | ||
94 | } | ||
95 | |||
96 | static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate, | ||
97 | unsigned long parent_rate) | ||
98 | { | ||
99 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
100 | struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); | ||
101 | unsigned int val; | ||
102 | int ret; | ||
103 | |||
104 | ret = divider_get_val(rate, parent_rate, div->table, div->width, | ||
105 | div->flags); | ||
106 | if (ret < 0) | ||
107 | return ret; | ||
108 | |||
109 | val = (unsigned int)ret << div->shift; | ||
110 | return regmap_update_bits(clk->map, div->offset, | ||
111 | clk_div_mask(div->width) << div->shift, val); | ||
112 | }; | ||
113 | |||
114 | /* Would prefer clk_regmap_div_ro_ops but clashes with qcom */ | ||
115 | |||
116 | const struct clk_ops clk_regmap_divider_ops = { | ||
117 | .recalc_rate = clk_regmap_div_recalc_rate, | ||
118 | .round_rate = clk_regmap_div_round_rate, | ||
119 | .set_rate = clk_regmap_div_set_rate, | ||
120 | }; | ||
121 | EXPORT_SYMBOL_GPL(clk_regmap_divider_ops); | ||
122 | |||
123 | const struct clk_ops clk_regmap_divider_ro_ops = { | ||
124 | .recalc_rate = clk_regmap_div_recalc_rate, | ||
125 | .round_rate = clk_regmap_div_round_rate, | ||
126 | }; | ||
127 | EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops); | ||
128 | |||
129 | static u8 clk_regmap_mux_get_parent(struct clk_hw *hw) | ||
130 | { | ||
131 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
132 | struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); | ||
133 | unsigned int val; | ||
134 | int ret; | ||
135 | |||
136 | ret = regmap_read(clk->map, mux->offset, &val); | ||
137 | if (ret) | ||
138 | return ret; | ||
139 | |||
140 | val >>= mux->shift; | ||
141 | val &= mux->mask; | ||
142 | return clk_mux_val_to_index(hw, mux->table, mux->flags, val); | ||
143 | } | ||
144 | |||
145 | static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index) | ||
146 | { | ||
147 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
148 | struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); | ||
149 | unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index); | ||
150 | |||
151 | return regmap_update_bits(clk->map, mux->offset, | ||
152 | mux->mask << mux->shift, | ||
153 | val << mux->shift); | ||
154 | } | ||
155 | |||
156 | const struct clk_ops clk_regmap_mux_ops = { | ||
157 | .get_parent = clk_regmap_mux_get_parent, | ||
158 | .set_parent = clk_regmap_mux_set_parent, | ||
159 | .determine_rate = __clk_mux_determine_rate, | ||
160 | }; | ||
161 | EXPORT_SYMBOL_GPL(clk_regmap_mux_ops); | ||
162 | |||
163 | const struct clk_ops clk_regmap_mux_ro_ops = { | ||
164 | .get_parent = clk_regmap_mux_get_parent, | ||
165 | }; | ||
166 | EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); | ||