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