aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/meson/clk-regmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/meson/clk-regmap.c')
-rw-r--r--drivers/clk/meson/clk-regmap.c166
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
7static 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
19static int clk_regmap_gate_enable(struct clk_hw *hw)
20{
21 return clk_regmap_gate_endisable(hw, 1);
22}
23
24static void clk_regmap_gate_disable(struct clk_hw *hw)
25{
26 clk_regmap_gate_endisable(hw, 0);
27}
28
29static 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
44const 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};
49EXPORT_SYMBOL_GPL(clk_regmap_gate_ops);
50
51static 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
70static 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
96static 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
116const 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};
121EXPORT_SYMBOL_GPL(clk_regmap_divider_ops);
122
123const 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};
127EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops);
128
129static 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
145static 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
156const 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};
161EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
162
163const struct clk_ops clk_regmap_mux_ro_ops = {
164 .get_parent = clk_regmap_mux_get_parent,
165};
166EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);