arm/arm64: unexport restart handlers
[cascardo/linux.git] / drivers / clk / sunxi / clk-sun6i-ar100.c
1 /*
2  * Copyright (C) 2014 Free Electrons
3  *
4  * License Terms: GNU General Public License v2
5  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6  *
7  * Allwinner A31 AR100 clock driver
8  *
9  */
10
11 #include <linux/clk-provider.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15
16 #define SUN6I_AR100_MAX_PARENTS         4
17 #define SUN6I_AR100_SHIFT_MASK          0x3
18 #define SUN6I_AR100_SHIFT_MAX           SUN6I_AR100_SHIFT_MASK
19 #define SUN6I_AR100_SHIFT_SHIFT         4
20 #define SUN6I_AR100_DIV_MASK            0x1f
21 #define SUN6I_AR100_DIV_MAX             (SUN6I_AR100_DIV_MASK + 1)
22 #define SUN6I_AR100_DIV_SHIFT           8
23 #define SUN6I_AR100_MUX_MASK            0x3
24 #define SUN6I_AR100_MUX_SHIFT           16
25
26 struct ar100_clk {
27         struct clk_hw hw;
28         void __iomem *reg;
29 };
30
31 static inline struct ar100_clk *to_ar100_clk(struct clk_hw *hw)
32 {
33         return container_of(hw, struct ar100_clk, hw);
34 }
35
36 static unsigned long ar100_recalc_rate(struct clk_hw *hw,
37                                        unsigned long parent_rate)
38 {
39         struct ar100_clk *clk = to_ar100_clk(hw);
40         u32 val = readl(clk->reg);
41         int shift = (val >> SUN6I_AR100_SHIFT_SHIFT) & SUN6I_AR100_SHIFT_MASK;
42         int div = (val >> SUN6I_AR100_DIV_SHIFT) & SUN6I_AR100_DIV_MASK;
43
44         return (parent_rate >> shift) / (div + 1);
45 }
46
47 static long ar100_determine_rate(struct clk_hw *hw, unsigned long rate,
48                                  unsigned long *best_parent_rate,
49                                  struct clk **best_parent_clk)
50 {
51         int nparents = __clk_get_num_parents(hw->clk);
52         long best_rate = -EINVAL;
53         int i;
54
55         *best_parent_clk = NULL;
56
57         for (i = 0; i < nparents; i++) {
58                 unsigned long parent_rate;
59                 unsigned long tmp_rate;
60                 struct clk *parent;
61                 unsigned long div;
62                 int shift;
63
64                 parent = clk_get_parent_by_index(hw->clk, i);
65                 parent_rate = __clk_get_rate(parent);
66                 div = DIV_ROUND_UP(parent_rate, rate);
67
68                 /*
69                  * The AR100 clk contains 2 divisors:
70                  * - one power of 2 divisor
71                  * - one regular divisor
72                  *
73                  * First check if we can safely shift (or divide by a power
74                  * of 2) without losing precision on the requested rate.
75                  */
76                 shift = ffs(div) - 1;
77                 if (shift > SUN6I_AR100_SHIFT_MAX)
78                         shift = SUN6I_AR100_SHIFT_MAX;
79
80                 div >>= shift;
81
82                 /*
83                  * Then if the divisor is still bigger than what the HW
84                  * actually supports, use a bigger shift (or power of 2
85                  * divider) value and accept to lose some precision.
86                  */
87                 while (div > SUN6I_AR100_DIV_MAX) {
88                         shift++;
89                         div >>= 1;
90                         if (shift > SUN6I_AR100_SHIFT_MAX)
91                                 break;
92                 }
93
94                 /*
95                  * If the shift value (or power of 2 divider) is bigger
96                  * than what the HW actually support, skip this parent.
97                  */
98                 if (shift > SUN6I_AR100_SHIFT_MAX)
99                         continue;
100
101                 tmp_rate = (parent_rate >> shift) / div;
102                 if (!*best_parent_clk || tmp_rate > best_rate) {
103                         *best_parent_clk = parent;
104                         *best_parent_rate = parent_rate;
105                         best_rate = tmp_rate;
106                 }
107         }
108
109         return best_rate;
110 }
111
112 static int ar100_set_parent(struct clk_hw *hw, u8 index)
113 {
114         struct ar100_clk *clk = to_ar100_clk(hw);
115         u32 val = readl(clk->reg);
116
117         if (index >= SUN6I_AR100_MAX_PARENTS)
118                 return -EINVAL;
119
120         val &= ~(SUN6I_AR100_MUX_MASK << SUN6I_AR100_MUX_SHIFT);
121         val |= (index << SUN6I_AR100_MUX_SHIFT);
122         writel(val, clk->reg);
123
124         return 0;
125 }
126
127 static u8 ar100_get_parent(struct clk_hw *hw)
128 {
129         struct ar100_clk *clk = to_ar100_clk(hw);
130         return (readl(clk->reg) >> SUN6I_AR100_MUX_SHIFT) &
131                SUN6I_AR100_MUX_MASK;
132 }
133
134 static int ar100_set_rate(struct clk_hw *hw, unsigned long rate,
135                           unsigned long parent_rate)
136 {
137         unsigned long div = parent_rate / rate;
138         struct ar100_clk *clk = to_ar100_clk(hw);
139         u32 val = readl(clk->reg);
140         int shift;
141
142         if (parent_rate % rate)
143                 return -EINVAL;
144
145         shift = ffs(div) - 1;
146         if (shift > SUN6I_AR100_SHIFT_MAX)
147                 shift = SUN6I_AR100_SHIFT_MAX;
148
149         div >>= shift;
150
151         if (div > SUN6I_AR100_DIV_MAX)
152                 return -EINVAL;
153
154         val &= ~((SUN6I_AR100_SHIFT_MASK << SUN6I_AR100_SHIFT_SHIFT) |
155                  (SUN6I_AR100_DIV_MASK << SUN6I_AR100_DIV_SHIFT));
156         val |= (shift << SUN6I_AR100_SHIFT_SHIFT) |
157                (div << SUN6I_AR100_DIV_SHIFT);
158         writel(val, clk->reg);
159
160         return 0;
161 }
162
163 static struct clk_ops ar100_ops = {
164         .recalc_rate = ar100_recalc_rate,
165         .determine_rate = ar100_determine_rate,
166         .set_parent = ar100_set_parent,
167         .get_parent = ar100_get_parent,
168         .set_rate = ar100_set_rate,
169 };
170
171 static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev)
172 {
173         const char *parents[SUN6I_AR100_MAX_PARENTS];
174         struct device_node *np = pdev->dev.of_node;
175         const char *clk_name = np->name;
176         struct clk_init_data init;
177         struct ar100_clk *ar100;
178         struct resource *r;
179         struct clk *clk;
180         int nparents;
181         int i;
182
183         ar100 = devm_kzalloc(&pdev->dev, sizeof(*ar100), GFP_KERNEL);
184         if (!ar100)
185                 return -ENOMEM;
186
187         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
188         ar100->reg = devm_ioremap_resource(&pdev->dev, r);
189         if (IS_ERR(ar100->reg))
190                 return PTR_ERR(ar100->reg);
191
192         nparents = of_clk_get_parent_count(np);
193         if (nparents > SUN6I_AR100_MAX_PARENTS)
194                 nparents = SUN6I_AR100_MAX_PARENTS;
195
196         for (i = 0; i < nparents; i++)
197                 parents[i] = of_clk_get_parent_name(np, i);
198
199         of_property_read_string(np, "clock-output-names", &clk_name);
200
201         init.name = clk_name;
202         init.ops = &ar100_ops;
203         init.parent_names = parents;
204         init.num_parents = nparents;
205         init.flags = 0;
206
207         ar100->hw.init = &init;
208
209         clk = clk_register(&pdev->dev, &ar100->hw);
210         if (IS_ERR(clk))
211                 return PTR_ERR(clk);
212
213         return of_clk_add_provider(np, of_clk_src_simple_get, clk);
214 }
215
216 static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = {
217         { .compatible = "allwinner,sun6i-a31-ar100-clk" },
218         { /* sentinel */ }
219 };
220
221 static struct platform_driver sun6i_a31_ar100_clk_driver = {
222         .driver = {
223                 .name = "sun6i-a31-ar100-clk",
224                 .owner = THIS_MODULE,
225                 .of_match_table = sun6i_a31_ar100_clk_dt_ids,
226         },
227         .probe = sun6i_a31_ar100_clk_probe,
228 };
229 module_platform_driver(sun6i_a31_ar100_clk_driver);
230
231 MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
232 MODULE_DESCRIPTION("Allwinner A31 AR100 clock Driver");
233 MODULE_LICENSE("GPL v2");