This source file includes following definitions.
- to_clk_divider_gate
- clk_divider_gate_recalc_rate_ro
- clk_divider_gate_recalc_rate
- clk_divider_round_rate
- clk_divider_gate_set_rate
- clk_divider_enable
- clk_divider_disable
- clk_divider_is_enabled
- imx_clk_divider_gate
1
2
3
4
5
6
7 #include <linux/clk-provider.h>
8 #include <linux/err.h>
9 #include <linux/io.h>
10 #include <linux/slab.h>
11
12 #include "clk.h"
13
14 struct clk_divider_gate {
15 struct clk_divider divider;
16 u32 cached_val;
17 };
18
19 static inline struct clk_divider_gate *to_clk_divider_gate(struct clk_hw *hw)
20 {
21 struct clk_divider *div = to_clk_divider(hw);
22
23 return container_of(div, struct clk_divider_gate, divider);
24 }
25
26 static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw,
27 unsigned long parent_rate)
28 {
29 struct clk_divider *div = to_clk_divider(hw);
30 unsigned int val;
31
32 val = readl(div->reg) >> div->shift;
33 val &= clk_div_mask(div->width);
34 if (!val)
35 return 0;
36
37 return divider_recalc_rate(hw, parent_rate, val, div->table,
38 div->flags, div->width);
39 }
40
41 static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
42 unsigned long parent_rate)
43 {
44 struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
45 struct clk_divider *div = to_clk_divider(hw);
46 unsigned long flags = 0;
47 unsigned int val;
48
49 spin_lock_irqsave(div->lock, flags);
50
51 if (!clk_hw_is_enabled(hw)) {
52 val = div_gate->cached_val;
53 } else {
54 val = readl(div->reg) >> div->shift;
55 val &= clk_div_mask(div->width);
56 }
57
58 spin_unlock_irqrestore(div->lock, flags);
59
60 if (!val)
61 return 0;
62
63 return divider_recalc_rate(hw, parent_rate, val, div->table,
64 div->flags, div->width);
65 }
66
67 static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
68 unsigned long *prate)
69 {
70 return clk_divider_ops.round_rate(hw, rate, prate);
71 }
72
73 static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
74 unsigned long parent_rate)
75 {
76 struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
77 struct clk_divider *div = to_clk_divider(hw);
78 unsigned long flags = 0;
79 int value;
80 u32 val;
81
82 value = divider_get_val(rate, parent_rate, div->table,
83 div->width, div->flags);
84 if (value < 0)
85 return value;
86
87 spin_lock_irqsave(div->lock, flags);
88
89 if (clk_hw_is_enabled(hw)) {
90 val = readl(div->reg);
91 val &= ~(clk_div_mask(div->width) << div->shift);
92 val |= (u32)value << div->shift;
93 writel(val, div->reg);
94 } else {
95 div_gate->cached_val = value;
96 }
97
98 spin_unlock_irqrestore(div->lock, flags);
99
100 return 0;
101 }
102
103 static int clk_divider_enable(struct clk_hw *hw)
104 {
105 struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
106 struct clk_divider *div = to_clk_divider(hw);
107 unsigned long flags = 0;
108 u32 val;
109
110 if (!div_gate->cached_val) {
111 pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw));
112 return -EINVAL;
113 }
114
115 spin_lock_irqsave(div->lock, flags);
116
117 val = readl(div->reg);
118 val |= div_gate->cached_val << div->shift;
119 writel(val, div->reg);
120
121 spin_unlock_irqrestore(div->lock, flags);
122
123 return 0;
124 }
125
126 static void clk_divider_disable(struct clk_hw *hw)
127 {
128 struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
129 struct clk_divider *div = to_clk_divider(hw);
130 unsigned long flags = 0;
131 u32 val;
132
133 spin_lock_irqsave(div->lock, flags);
134
135
136 val = readl(div->reg) >> div->shift;
137 val &= clk_div_mask(div->width);
138 div_gate->cached_val = val;
139 writel(0, div->reg);
140
141 spin_unlock_irqrestore(div->lock, flags);
142 }
143
144 static int clk_divider_is_enabled(struct clk_hw *hw)
145 {
146 struct clk_divider *div = to_clk_divider(hw);
147 u32 val;
148
149 val = readl(div->reg) >> div->shift;
150 val &= clk_div_mask(div->width);
151
152 return val ? 1 : 0;
153 }
154
155 static const struct clk_ops clk_divider_gate_ro_ops = {
156 .recalc_rate = clk_divider_gate_recalc_rate_ro,
157 .round_rate = clk_divider_round_rate,
158 };
159
160 static const struct clk_ops clk_divider_gate_ops = {
161 .recalc_rate = clk_divider_gate_recalc_rate,
162 .round_rate = clk_divider_round_rate,
163 .set_rate = clk_divider_gate_set_rate,
164 .enable = clk_divider_enable,
165 .disable = clk_divider_disable,
166 .is_enabled = clk_divider_is_enabled,
167 };
168
169
170
171
172
173
174
175
176 struct clk_hw *imx_clk_divider_gate(const char *name, const char *parent_name,
177 unsigned long flags, void __iomem *reg,
178 u8 shift, u8 width, u8 clk_divider_flags,
179 const struct clk_div_table *table,
180 spinlock_t *lock)
181 {
182 struct clk_init_data init;
183 struct clk_divider_gate *div_gate;
184 struct clk_hw *hw;
185 u32 val;
186 int ret;
187
188 div_gate = kzalloc(sizeof(*div_gate), GFP_KERNEL);
189 if (!div_gate)
190 return ERR_PTR(-ENOMEM);
191
192 init.name = name;
193 if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
194 init.ops = &clk_divider_gate_ro_ops;
195 else
196 init.ops = &clk_divider_gate_ops;
197 init.flags = flags;
198 init.parent_names = parent_name ? &parent_name : NULL;
199 init.num_parents = parent_name ? 1 : 0;
200
201 div_gate->divider.reg = reg;
202 div_gate->divider.shift = shift;
203 div_gate->divider.width = width;
204 div_gate->divider.lock = lock;
205 div_gate->divider.table = table;
206 div_gate->divider.hw.init = &init;
207 div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags;
208
209 val = readl(reg) >> shift;
210 val &= clk_div_mask(width);
211 div_gate->cached_val = val;
212
213 hw = &div_gate->divider.hw;
214 ret = clk_hw_register(NULL, hw);
215 if (ret) {
216 kfree(div_gate);
217 hw = ERR_PTR(ret);
218 }
219
220 return hw;
221 }