1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2012 Freescale Semiconductor, Inc. |
4 | */ |
5 | |
6 | #include <linux/clk-provider.h> |
7 | #include <linux/err.h> |
8 | #include <linux/slab.h> |
9 | #include "clk.h" |
10 | |
11 | /** |
12 | * struct clk_div - mxs integer divider clock |
13 | * @divider: the parent class |
14 | * @ops: pointer to clk_ops of parent class |
15 | * @reg: register address |
16 | * @busy: busy bit shift |
17 | * |
18 | * The mxs divider clock is a subclass of basic clk_divider with an |
19 | * addtional busy bit. |
20 | */ |
21 | struct clk_div { |
22 | struct clk_divider divider; |
23 | const struct clk_ops *ops; |
24 | void __iomem *reg; |
25 | u8 busy; |
26 | }; |
27 | |
28 | static inline struct clk_div *to_clk_div(struct clk_hw *hw) |
29 | { |
30 | struct clk_divider *divider = to_clk_divider(hw); |
31 | |
32 | return container_of(divider, struct clk_div, divider); |
33 | } |
34 | |
35 | static unsigned long clk_div_recalc_rate(struct clk_hw *hw, |
36 | unsigned long parent_rate) |
37 | { |
38 | struct clk_div *div = to_clk_div(hw); |
39 | |
40 | return div->ops->recalc_rate(&div->divider.hw, parent_rate); |
41 | } |
42 | |
43 | static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate, |
44 | unsigned long *prate) |
45 | { |
46 | struct clk_div *div = to_clk_div(hw); |
47 | |
48 | return div->ops->round_rate(&div->divider.hw, rate, prate); |
49 | } |
50 | |
51 | static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate, |
52 | unsigned long parent_rate) |
53 | { |
54 | struct clk_div *div = to_clk_div(hw); |
55 | int ret; |
56 | |
57 | ret = div->ops->set_rate(&div->divider.hw, rate, parent_rate); |
58 | if (!ret) |
59 | ret = mxs_clk_wait(reg: div->reg, shift: div->busy); |
60 | |
61 | return ret; |
62 | } |
63 | |
64 | static const struct clk_ops clk_div_ops = { |
65 | .recalc_rate = clk_div_recalc_rate, |
66 | .round_rate = clk_div_round_rate, |
67 | .set_rate = clk_div_set_rate, |
68 | }; |
69 | |
70 | struct clk *mxs_clk_div(const char *name, const char *parent_name, |
71 | void __iomem *reg, u8 shift, u8 width, u8 busy) |
72 | { |
73 | struct clk_div *div; |
74 | struct clk *clk; |
75 | struct clk_init_data init; |
76 | |
77 | div = kzalloc(size: sizeof(*div), GFP_KERNEL); |
78 | if (!div) |
79 | return ERR_PTR(error: -ENOMEM); |
80 | |
81 | init.name = name; |
82 | init.ops = &clk_div_ops; |
83 | init.flags = CLK_SET_RATE_PARENT; |
84 | init.parent_names = (parent_name ? &parent_name: NULL); |
85 | init.num_parents = (parent_name ? 1 : 0); |
86 | |
87 | div->reg = reg; |
88 | div->busy = busy; |
89 | |
90 | div->divider.reg = reg; |
91 | div->divider.shift = shift; |
92 | div->divider.width = width; |
93 | div->divider.flags = CLK_DIVIDER_ONE_BASED; |
94 | div->divider.lock = &mxs_lock; |
95 | div->divider.hw.init = &init; |
96 | div->ops = &clk_divider_ops; |
97 | |
98 | clk = clk_register(NULL, hw: &div->divider.hw); |
99 | if (IS_ERR(ptr: clk)) |
100 | kfree(objp: div); |
101 | |
102 | return clk; |
103 | } |
104 | |