]> Git Repo - linux.git/blob - drivers/clk/at91/clk-programmable.c
Merge tag 'tegra-for-5.2-bus-fixes' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / drivers / clk / at91 / clk-programmable.c
1 /*
2  *  Copyright (C) 2013 Boris BREZILLON <[email protected]>
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/mfd/syscon.h>
16 #include <linux/regmap.h>
17
18 #include "pmc.h"
19
20 #define PROG_ID_MAX             7
21
22 #define PROG_STATUS_MASK(id)    (1 << ((id) + 8))
23 #define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & layout->pres_mask)
24 #define PROG_MAX_RM9200_CSS     3
25
26 struct clk_programmable {
27         struct clk_hw hw;
28         struct regmap *regmap;
29         u8 id;
30         const struct clk_programmable_layout *layout;
31 };
32
33 #define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
34
35 static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
36                                                   unsigned long parent_rate)
37 {
38         struct clk_programmable *prog = to_clk_programmable(hw);
39         const struct clk_programmable_layout *layout = prog->layout;
40         unsigned int pckr;
41         unsigned long rate;
42
43         regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
44
45         if (layout->is_pres_direct)
46                 rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
47         else
48                 rate = parent_rate >> PROG_PRES(layout, pckr);
49
50         return rate;
51 }
52
53 static int clk_programmable_determine_rate(struct clk_hw *hw,
54                                            struct clk_rate_request *req)
55 {
56         struct clk_programmable *prog = to_clk_programmable(hw);
57         const struct clk_programmable_layout *layout = prog->layout;
58         struct clk_hw *parent;
59         long best_rate = -EINVAL;
60         unsigned long parent_rate;
61         unsigned long tmp_rate = 0;
62         int shift;
63         int i;
64
65         for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
66                 parent = clk_hw_get_parent_by_index(hw, i);
67                 if (!parent)
68                         continue;
69
70                 parent_rate = clk_hw_get_rate(parent);
71                 if (layout->is_pres_direct) {
72                         for (shift = 0; shift <= layout->pres_mask; shift++) {
73                                 tmp_rate = parent_rate / (shift + 1);
74                                 if (tmp_rate <= req->rate)
75                                         break;
76                         }
77                 } else {
78                         for (shift = 0; shift < layout->pres_mask; shift++) {
79                                 tmp_rate = parent_rate >> shift;
80                                 if (tmp_rate <= req->rate)
81                                         break;
82                         }
83                 }
84
85                 if (tmp_rate > req->rate)
86                         continue;
87
88                 if (best_rate < 0 ||
89                     (req->rate - tmp_rate) < (req->rate - best_rate)) {
90                         best_rate = tmp_rate;
91                         req->best_parent_rate = parent_rate;
92                         req->best_parent_hw = parent;
93                 }
94
95                 if (!best_rate)
96                         break;
97         }
98
99         if (best_rate < 0)
100                 return best_rate;
101
102         req->rate = best_rate;
103         return 0;
104 }
105
106 static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
107 {
108         struct clk_programmable *prog = to_clk_programmable(hw);
109         const struct clk_programmable_layout *layout = prog->layout;
110         unsigned int mask = layout->css_mask;
111         unsigned int pckr = index;
112
113         if (layout->have_slck_mck)
114                 mask |= AT91_PMC_CSSMCK_MCK;
115
116         if (index > layout->css_mask) {
117                 if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
118                         return -EINVAL;
119
120                 pckr |= AT91_PMC_CSSMCK_MCK;
121         }
122
123         regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
124
125         return 0;
126 }
127
128 static u8 clk_programmable_get_parent(struct clk_hw *hw)
129 {
130         struct clk_programmable *prog = to_clk_programmable(hw);
131         const struct clk_programmable_layout *layout = prog->layout;
132         unsigned int pckr;
133         u8 ret;
134
135         regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
136
137         ret = pckr & layout->css_mask;
138
139         if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
140                 ret = PROG_MAX_RM9200_CSS + 1;
141
142         return ret;
143 }
144
145 static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
146                                      unsigned long parent_rate)
147 {
148         struct clk_programmable *prog = to_clk_programmable(hw);
149         const struct clk_programmable_layout *layout = prog->layout;
150         unsigned long div = parent_rate / rate;
151         int shift = 0;
152
153         if (!div)
154                 return -EINVAL;
155
156         if (layout->is_pres_direct) {
157                 shift = div - 1;
158
159                 if (shift > layout->pres_mask)
160                         return -EINVAL;
161         } else {
162                 shift = fls(div) - 1;
163
164                 if (div != (1 << shift))
165                         return -EINVAL;
166
167                 if (shift >= layout->pres_mask)
168                         return -EINVAL;
169         }
170
171         regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
172                            layout->pres_mask << layout->pres_shift,
173                            shift << layout->pres_shift);
174
175         return 0;
176 }
177
178 static const struct clk_ops programmable_ops = {
179         .recalc_rate = clk_programmable_recalc_rate,
180         .determine_rate = clk_programmable_determine_rate,
181         .get_parent = clk_programmable_get_parent,
182         .set_parent = clk_programmable_set_parent,
183         .set_rate = clk_programmable_set_rate,
184 };
185
186 struct clk_hw * __init
187 at91_clk_register_programmable(struct regmap *regmap,
188                                const char *name, const char **parent_names,
189                                u8 num_parents, u8 id,
190                                const struct clk_programmable_layout *layout)
191 {
192         struct clk_programmable *prog;
193         struct clk_hw *hw;
194         struct clk_init_data init;
195         int ret;
196
197         if (id > PROG_ID_MAX)
198                 return ERR_PTR(-EINVAL);
199
200         prog = kzalloc(sizeof(*prog), GFP_KERNEL);
201         if (!prog)
202                 return ERR_PTR(-ENOMEM);
203
204         init.name = name;
205         init.ops = &programmable_ops;
206         init.parent_names = parent_names;
207         init.num_parents = num_parents;
208         init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
209
210         prog->id = id;
211         prog->layout = layout;
212         prog->hw.init = &init;
213         prog->regmap = regmap;
214
215         hw = &prog->hw;
216         ret = clk_hw_register(NULL, &prog->hw);
217         if (ret) {
218                 kfree(prog);
219                 hw = ERR_PTR(ret);
220         } else {
221                 pmc_register_pck(id);
222         }
223
224         return hw;
225 }
226
227 const struct clk_programmable_layout at91rm9200_programmable_layout = {
228         .pres_mask = 0x7,
229         .pres_shift = 2,
230         .css_mask = 0x3,
231         .have_slck_mck = 0,
232         .is_pres_direct = 0,
233 };
234
235 const struct clk_programmable_layout at91sam9g45_programmable_layout = {
236         .pres_mask = 0x7,
237         .pres_shift = 2,
238         .css_mask = 0x3,
239         .have_slck_mck = 1,
240         .is_pres_direct = 0,
241 };
242
243 const struct clk_programmable_layout at91sam9x5_programmable_layout = {
244         .pres_mask = 0x7,
245         .pres_shift = 4,
246         .css_mask = 0x7,
247         .have_slck_mck = 0,
248         .is_pres_direct = 0,
249 };
This page took 0.04971 seconds and 4 git commands to generate.