Commit | Line | Data |
---|---|---|
e442d234 BB |
1 | /* |
2 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/clk-provider.h> | |
12 | #include <linux/clkdev.h> | |
13 | #include <linux/clk/at91_pmc.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/of_irq.h> | |
17 | #include <linux/io.h> | |
18 | #include <linux/wait.h> | |
19 | #include <linux/sched.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/irq.h> | |
22 | ||
23 | #include "pmc.h" | |
24 | ||
25 | #define MASTER_SOURCE_MAX 4 | |
26 | ||
27 | #define MASTER_PRES_MASK 0x7 | |
28 | #define MASTER_PRES_MAX MASTER_PRES_MASK | |
29 | #define MASTER_DIV_SHIFT 8 | |
30 | #define MASTER_DIV_MASK 0x3 | |
31 | ||
32 | struct clk_master_characteristics { | |
33 | struct clk_range output; | |
34 | u32 divisors[4]; | |
35 | u8 have_div3_pres; | |
36 | }; | |
37 | ||
38 | struct clk_master_layout { | |
39 | u32 mask; | |
40 | u8 pres_shift; | |
41 | }; | |
42 | ||
43 | #define to_clk_master(hw) container_of(hw, struct clk_master, hw) | |
44 | ||
45 | struct clk_master { | |
46 | struct clk_hw hw; | |
47 | struct at91_pmc *pmc; | |
48 | unsigned int irq; | |
49 | wait_queue_head_t wait; | |
50 | const struct clk_master_layout *layout; | |
51 | const struct clk_master_characteristics *characteristics; | |
52 | }; | |
53 | ||
54 | static irqreturn_t clk_master_irq_handler(int irq, void *dev_id) | |
55 | { | |
56 | struct clk_master *master = (struct clk_master *)dev_id; | |
57 | ||
58 | wake_up(&master->wait); | |
59 | disable_irq_nosync(master->irq); | |
60 | ||
61 | return IRQ_HANDLED; | |
62 | } | |
63 | static int clk_master_prepare(struct clk_hw *hw) | |
64 | { | |
65 | struct clk_master *master = to_clk_master(hw); | |
66 | struct at91_pmc *pmc = master->pmc; | |
67 | ||
68 | while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY)) { | |
69 | enable_irq(master->irq); | |
70 | wait_event(master->wait, | |
71 | pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY); | |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | static int clk_master_is_prepared(struct clk_hw *hw) | |
78 | { | |
79 | struct clk_master *master = to_clk_master(hw); | |
80 | ||
81 | return !!(pmc_read(master->pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY); | |
82 | } | |
83 | ||
84 | static unsigned long clk_master_recalc_rate(struct clk_hw *hw, | |
85 | unsigned long parent_rate) | |
86 | { | |
87 | u8 pres; | |
88 | u8 div; | |
89 | unsigned long rate = parent_rate; | |
90 | struct clk_master *master = to_clk_master(hw); | |
91 | struct at91_pmc *pmc = master->pmc; | |
92 | const struct clk_master_layout *layout = master->layout; | |
93 | const struct clk_master_characteristics *characteristics = | |
94 | master->characteristics; | |
95 | u32 tmp; | |
96 | ||
97 | pmc_lock(pmc); | |
98 | tmp = pmc_read(pmc, AT91_PMC_MCKR) & layout->mask; | |
99 | pmc_unlock(pmc); | |
100 | ||
101 | pres = (tmp >> layout->pres_shift) & MASTER_PRES_MASK; | |
102 | div = (tmp >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; | |
103 | ||
104 | if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) | |
105 | rate /= 3; | |
106 | else | |
107 | rate >>= pres; | |
108 | ||
109 | rate /= characteristics->divisors[div]; | |
110 | ||
111 | if (rate < characteristics->output.min) | |
112 | pr_warn("master clk is underclocked"); | |
113 | else if (rate > characteristics->output.max) | |
114 | pr_warn("master clk is overclocked"); | |
115 | ||
116 | return rate; | |
117 | } | |
118 | ||
119 | static u8 clk_master_get_parent(struct clk_hw *hw) | |
120 | { | |
121 | struct clk_master *master = to_clk_master(hw); | |
122 | struct at91_pmc *pmc = master->pmc; | |
123 | ||
124 | return pmc_read(pmc, AT91_PMC_MCKR) & AT91_PMC_CSS; | |
125 | } | |
126 | ||
127 | static const struct clk_ops master_ops = { | |
128 | .prepare = clk_master_prepare, | |
129 | .is_prepared = clk_master_is_prepared, | |
130 | .recalc_rate = clk_master_recalc_rate, | |
131 | .get_parent = clk_master_get_parent, | |
132 | }; | |
133 | ||
134 | static struct clk * __init | |
135 | at91_clk_register_master(struct at91_pmc *pmc, unsigned int irq, | |
136 | const char *name, int num_parents, | |
137 | const char **parent_names, | |
138 | const struct clk_master_layout *layout, | |
139 | const struct clk_master_characteristics *characteristics) | |
140 | { | |
141 | int ret; | |
142 | struct clk_master *master; | |
143 | struct clk *clk = NULL; | |
144 | struct clk_init_data init; | |
145 | ||
146 | if (!pmc || !irq || !name || !num_parents || !parent_names) | |
147 | return ERR_PTR(-EINVAL); | |
148 | ||
149 | master = kzalloc(sizeof(*master), GFP_KERNEL); | |
150 | if (!master) | |
151 | return ERR_PTR(-ENOMEM); | |
152 | ||
153 | init.name = name; | |
154 | init.ops = &master_ops; | |
155 | init.parent_names = parent_names; | |
156 | init.num_parents = num_parents; | |
157 | init.flags = 0; | |
158 | ||
159 | master->hw.init = &init; | |
160 | master->layout = layout; | |
161 | master->characteristics = characteristics; | |
162 | master->pmc = pmc; | |
163 | master->irq = irq; | |
164 | init_waitqueue_head(&master->wait); | |
165 | irq_set_status_flags(master->irq, IRQ_NOAUTOEN); | |
166 | ret = request_irq(master->irq, clk_master_irq_handler, | |
167 | IRQF_TRIGGER_HIGH, "clk-master", master); | |
168 | if (ret) | |
169 | return ERR_PTR(ret); | |
170 | ||
171 | clk = clk_register(NULL, &master->hw); | |
172 | if (IS_ERR(clk)) | |
173 | kfree(master); | |
174 | ||
175 | return clk; | |
176 | } | |
177 | ||
178 | ||
179 | static const struct clk_master_layout at91rm9200_master_layout = { | |
180 | .mask = 0x31F, | |
181 | .pres_shift = 2, | |
182 | }; | |
183 | ||
184 | static const struct clk_master_layout at91sam9x5_master_layout = { | |
185 | .mask = 0x373, | |
186 | .pres_shift = 4, | |
187 | }; | |
188 | ||
189 | ||
190 | static struct clk_master_characteristics * __init | |
191 | of_at91_clk_master_get_characteristics(struct device_node *np) | |
192 | { | |
193 | struct clk_master_characteristics *characteristics; | |
194 | ||
195 | characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL); | |
196 | if (!characteristics) | |
197 | return NULL; | |
198 | ||
199 | if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output)) | |
200 | goto out_free_characteristics; | |
201 | ||
202 | of_property_read_u32_array(np, "atmel,clk-divisors", | |
203 | characteristics->divisors, 4); | |
204 | ||
205 | characteristics->have_div3_pres = | |
206 | of_property_read_bool(np, "atmel,master-clk-have-div3-pres"); | |
207 | ||
208 | return characteristics; | |
209 | ||
210 | out_free_characteristics: | |
211 | kfree(characteristics); | |
212 | return NULL; | |
213 | } | |
214 | ||
215 | static void __init | |
216 | of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc, | |
217 | const struct clk_master_layout *layout) | |
218 | { | |
219 | struct clk *clk; | |
220 | int num_parents; | |
221 | int i; | |
222 | unsigned int irq; | |
223 | const char *parent_names[MASTER_SOURCE_MAX]; | |
224 | const char *name = np->name; | |
225 | struct clk_master_characteristics *characteristics; | |
226 | ||
51a43be9 | 227 | num_parents = of_clk_get_parent_count(np); |
e442d234 BB |
228 | if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX) |
229 | return; | |
230 | ||
231 | for (i = 0; i < num_parents; ++i) { | |
232 | parent_names[i] = of_clk_get_parent_name(np, i); | |
233 | if (!parent_names[i]) | |
234 | return; | |
235 | } | |
236 | ||
237 | of_property_read_string(np, "clock-output-names", &name); | |
238 | ||
239 | characteristics = of_at91_clk_master_get_characteristics(np); | |
240 | if (!characteristics) | |
241 | return; | |
242 | ||
243 | irq = irq_of_parse_and_map(np, 0); | |
244 | if (!irq) | |
f63fcc90 | 245 | goto out_free_characteristics; |
e442d234 BB |
246 | |
247 | clk = at91_clk_register_master(pmc, irq, name, num_parents, | |
248 | parent_names, layout, | |
249 | characteristics); | |
250 | if (IS_ERR(clk)) | |
251 | goto out_free_characteristics; | |
252 | ||
253 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | |
254 | return; | |
255 | ||
256 | out_free_characteristics: | |
257 | kfree(characteristics); | |
258 | } | |
259 | ||
260 | void __init of_at91rm9200_clk_master_setup(struct device_node *np, | |
261 | struct at91_pmc *pmc) | |
262 | { | |
263 | of_at91_clk_master_setup(np, pmc, &at91rm9200_master_layout); | |
264 | } | |
265 | ||
266 | void __init of_at91sam9x5_clk_master_setup(struct device_node *np, | |
267 | struct at91_pmc *pmc) | |
268 | { | |
269 | of_at91_clk_master_setup(np, pmc, &at91sam9x5_master_layout); | |
270 | } |