diff options
author | Joel Stanley <joel@jms.id.au> | 2017-12-21 21:45:21 -0500 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2018-01-26 19:22:45 -0500 |
commit | 15ed8ce5f84e2b3718690915dbee12ebd497dc0f (patch) | |
tree | 5172f55b266a83d7a9bb1ce5b0addf40723a5ce3 | |
parent | 98f3118debb3876399a8da59d72b4908431f1027 (diff) |
clk: aspeed: Register gated clocks
The majority of the clocks in the system are gates paired with a reset
controller that holds the IP in reset.
This borrows from clk_hw_register_gate, but registers two 'gates', one
to control the clock enable register and the other to control the reset
IP. This allows us to enforce the ordering:
1. Place IP in reset
2. Enable clock
3. Delay
4. Release reset
There are some gates that do not have an associated reset; these are
handled by using -1 as the index for the reset.
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r-- | drivers/clk/clk-aspeed.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c index cf5ea63feb31..dbd3c7774831 100644 --- a/drivers/clk/clk-aspeed.c +++ b/drivers/clk/clk-aspeed.c | |||
@@ -204,6 +204,106 @@ static const struct aspeed_clk_soc_data ast2400_data = { | |||
204 | .calc_pll = aspeed_ast2400_calc_pll, | 204 | .calc_pll = aspeed_ast2400_calc_pll, |
205 | }; | 205 | }; |
206 | 206 | ||
207 | static int aspeed_clk_enable(struct clk_hw *hw) | ||
208 | { | ||
209 | struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw); | ||
210 | unsigned long flags; | ||
211 | u32 clk = BIT(gate->clock_idx); | ||
212 | u32 rst = BIT(gate->reset_idx); | ||
213 | |||
214 | spin_lock_irqsave(gate->lock, flags); | ||
215 | |||
216 | if (gate->reset_idx >= 0) { | ||
217 | /* Put IP in reset */ | ||
218 | regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, rst); | ||
219 | |||
220 | /* Delay 100us */ | ||
221 | udelay(100); | ||
222 | } | ||
223 | |||
224 | /* Enable clock */ | ||
225 | regmap_update_bits(gate->map, ASPEED_CLK_STOP_CTRL, clk, 0); | ||
226 | |||
227 | if (gate->reset_idx >= 0) { | ||
228 | /* A delay of 10ms is specified by the ASPEED docs */ | ||
229 | mdelay(10); | ||
230 | |||
231 | /* Take IP out of reset */ | ||
232 | regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, 0); | ||
233 | } | ||
234 | |||
235 | spin_unlock_irqrestore(gate->lock, flags); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static void aspeed_clk_disable(struct clk_hw *hw) | ||
241 | { | ||
242 | struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw); | ||
243 | unsigned long flags; | ||
244 | u32 clk = BIT(gate->clock_idx); | ||
245 | |||
246 | spin_lock_irqsave(gate->lock, flags); | ||
247 | |||
248 | regmap_update_bits(gate->map, ASPEED_CLK_STOP_CTRL, clk, clk); | ||
249 | |||
250 | spin_unlock_irqrestore(gate->lock, flags); | ||
251 | } | ||
252 | |||
253 | static int aspeed_clk_is_enabled(struct clk_hw *hw) | ||
254 | { | ||
255 | struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw); | ||
256 | u32 clk = BIT(gate->clock_idx); | ||
257 | u32 reg; | ||
258 | |||
259 | regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, ®); | ||
260 | |||
261 | return (reg & clk) ? 0 : 1; | ||
262 | } | ||
263 | |||
264 | static const struct clk_ops aspeed_clk_gate_ops = { | ||
265 | .enable = aspeed_clk_enable, | ||
266 | .disable = aspeed_clk_disable, | ||
267 | .is_enabled = aspeed_clk_is_enabled, | ||
268 | }; | ||
269 | |||
270 | static struct clk_hw *aspeed_clk_hw_register_gate(struct device *dev, | ||
271 | const char *name, const char *parent_name, unsigned long flags, | ||
272 | struct regmap *map, u8 clock_idx, u8 reset_idx, | ||
273 | u8 clk_gate_flags, spinlock_t *lock) | ||
274 | { | ||
275 | struct aspeed_clk_gate *gate; | ||
276 | struct clk_init_data init; | ||
277 | struct clk_hw *hw; | ||
278 | int ret; | ||
279 | |||
280 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | ||
281 | if (!gate) | ||
282 | return ERR_PTR(-ENOMEM); | ||
283 | |||
284 | init.name = name; | ||
285 | init.ops = &aspeed_clk_gate_ops; | ||
286 | init.flags = flags; | ||
287 | init.parent_names = parent_name ? &parent_name : NULL; | ||
288 | init.num_parents = parent_name ? 1 : 0; | ||
289 | |||
290 | gate->map = map; | ||
291 | gate->clock_idx = clock_idx; | ||
292 | gate->reset_idx = reset_idx; | ||
293 | gate->flags = clk_gate_flags; | ||
294 | gate->lock = lock; | ||
295 | gate->hw.init = &init; | ||
296 | |||
297 | hw = &gate->hw; | ||
298 | ret = clk_hw_register(dev, hw); | ||
299 | if (ret) { | ||
300 | kfree(gate); | ||
301 | hw = ERR_PTR(ret); | ||
302 | } | ||
303 | |||
304 | return hw; | ||
305 | } | ||
306 | |||
207 | static int aspeed_clk_probe(struct platform_device *pdev) | 307 | static int aspeed_clk_probe(struct platform_device *pdev) |
208 | { | 308 | { |
209 | const struct aspeed_clk_soc_data *soc_data; | 309 | const struct aspeed_clk_soc_data *soc_data; |
@@ -211,6 +311,7 @@ static int aspeed_clk_probe(struct platform_device *pdev) | |||
211 | struct regmap *map; | 311 | struct regmap *map; |
212 | struct clk_hw *hw; | 312 | struct clk_hw *hw; |
213 | u32 val, rate; | 313 | u32 val, rate; |
314 | int i; | ||
214 | 315 | ||
215 | map = syscon_node_to_regmap(dev->of_node); | 316 | map = syscon_node_to_regmap(dev->of_node); |
216 | if (IS_ERR(map)) { | 317 | if (IS_ERR(map)) { |
@@ -283,6 +384,35 @@ static int aspeed_clk_probe(struct platform_device *pdev) | |||
283 | return PTR_ERR(hw); | 384 | return PTR_ERR(hw); |
284 | aspeed_clk_data->hws[ASPEED_CLK_BCLK] = hw; | 385 | aspeed_clk_data->hws[ASPEED_CLK_BCLK] = hw; |
285 | 386 | ||
387 | /* | ||
388 | * TODO: There are a number of clocks that not included in this driver | ||
389 | * as more information is required: | ||
390 | * D2-PLL | ||
391 | * D-PLL | ||
392 | * YCLK | ||
393 | * RGMII | ||
394 | * RMII | ||
395 | * UART[1..5] clock source mux | ||
396 | * Video Engine (ECLK) mux and clock divider | ||
397 | */ | ||
398 | |||
399 | for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) { | ||
400 | const struct aspeed_gate_data *gd = &aspeed_gates[i]; | ||
401 | |||
402 | hw = aspeed_clk_hw_register_gate(dev, | ||
403 | gd->name, | ||
404 | gd->parent_name, | ||
405 | gd->flags, | ||
406 | map, | ||
407 | gd->clock_idx, | ||
408 | gd->reset_idx, | ||
409 | CLK_GATE_SET_TO_DISABLE, | ||
410 | &aspeed_clk_lock); | ||
411 | if (IS_ERR(hw)) | ||
412 | return PTR_ERR(hw); | ||
413 | aspeed_clk_data->hws[i] = hw; | ||
414 | } | ||
415 | |||
286 | return 0; | 416 | return 0; |
287 | }; | 417 | }; |
288 | 418 | ||