GNU Linux-libre 5.10.215-gnu1
[releases.git] / drivers / clk / x86 / clk-pmc-atom.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs
4  *
5  * Copyright (C) 2016, Intel Corporation
6  * Author: Irina Tirdea <irina.tirdea@intel.com>
7  */
8
9 #include <linux/clk-provider.h>
10 #include <linux/clkdev.h>
11 #include <linux/err.h>
12 #include <linux/io.h>
13 #include <linux/platform_data/x86/clk-pmc-atom.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16
17 #define PLT_CLK_NAME_BASE       "pmc_plt_clk"
18
19 #define PMC_CLK_CTL_OFFSET              0x60
20 #define PMC_CLK_CTL_SIZE                4
21 #define PMC_CLK_NUM                     6
22 #define PMC_CLK_CTL_GATED_ON_D3         0x0
23 #define PMC_CLK_CTL_FORCE_ON            0x1
24 #define PMC_CLK_CTL_FORCE_OFF           0x2
25 #define PMC_CLK_CTL_RESERVED            0x3
26 #define PMC_MASK_CLK_CTL                GENMASK(1, 0)
27 #define PMC_MASK_CLK_FREQ               BIT(2)
28 #define PMC_CLK_FREQ_XTAL               (0 << 2)        /* 25 MHz */
29 #define PMC_CLK_FREQ_PLL                (1 << 2)        /* 19.2 MHz */
30
31 struct clk_plt_fixed {
32         struct clk_hw *clk;
33         struct clk_lookup *lookup;
34 };
35
36 struct clk_plt {
37         struct clk_hw hw;
38         void __iomem *reg;
39         struct clk_lookup *lookup;
40         /* protect access to PMC registers */
41         spinlock_t lock;
42 };
43
44 #define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
45
46 struct clk_plt_data {
47         struct clk_plt_fixed **parents;
48         u8 nparents;
49         struct clk_plt *clks[PMC_CLK_NUM];
50         struct clk_lookup *mclk_lookup;
51         struct clk_lookup *ether_clk_lookup;
52 };
53
54 /* Return an index in parent table */
55 static inline int plt_reg_to_parent(int reg)
56 {
57         switch (reg & PMC_MASK_CLK_FREQ) {
58         default:
59         case PMC_CLK_FREQ_XTAL:
60                 return 0;
61         case PMC_CLK_FREQ_PLL:
62                 return 1;
63         }
64 }
65
66 /* Return clk index of parent */
67 static inline int plt_parent_to_reg(int index)
68 {
69         switch (index) {
70         default:
71         case 0:
72                 return PMC_CLK_FREQ_XTAL;
73         case 1:
74                 return PMC_CLK_FREQ_PLL;
75         }
76 }
77
78 /* Abstract status in simpler enabled/disabled value */
79 static inline int plt_reg_to_enabled(int reg)
80 {
81         switch (reg & PMC_MASK_CLK_CTL) {
82         case PMC_CLK_CTL_GATED_ON_D3:
83         case PMC_CLK_CTL_FORCE_ON:
84                 return 1;       /* enabled */
85         case PMC_CLK_CTL_FORCE_OFF:
86         case PMC_CLK_CTL_RESERVED:
87         default:
88                 return 0;       /* disabled */
89         }
90 }
91
92 static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val)
93 {
94         u32 tmp;
95         unsigned long flags;
96
97         spin_lock_irqsave(&clk->lock, flags);
98
99         tmp = readl(clk->reg);
100         tmp = (tmp & ~mask) | (val & mask);
101         writel(tmp, clk->reg);
102
103         spin_unlock_irqrestore(&clk->lock, flags);
104 }
105
106 static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
107 {
108         struct clk_plt *clk = to_clk_plt(hw);
109
110         plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index));
111
112         return 0;
113 }
114
115 static u8 plt_clk_get_parent(struct clk_hw *hw)
116 {
117         struct clk_plt *clk = to_clk_plt(hw);
118         u32 value;
119
120         value = readl(clk->reg);
121
122         return plt_reg_to_parent(value);
123 }
124
125 static int plt_clk_enable(struct clk_hw *hw)
126 {
127         struct clk_plt *clk = to_clk_plt(hw);
128
129         plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
130
131         return 0;
132 }
133
134 static void plt_clk_disable(struct clk_hw *hw)
135 {
136         struct clk_plt *clk = to_clk_plt(hw);
137
138         plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
139 }
140
141 static int plt_clk_is_enabled(struct clk_hw *hw)
142 {
143         struct clk_plt *clk = to_clk_plt(hw);
144         u32 value;
145
146         value = readl(clk->reg);
147
148         return plt_reg_to_enabled(value);
149 }
150
151 static const struct clk_ops plt_clk_ops = {
152         .enable = plt_clk_enable,
153         .disable = plt_clk_disable,
154         .is_enabled = plt_clk_is_enabled,
155         .get_parent = plt_clk_get_parent,
156         .set_parent = plt_clk_set_parent,
157         .determine_rate = __clk_mux_determine_rate,
158 };
159
160 static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
161                                         const struct pmc_clk_data *pmc_data,
162                                         const char **parent_names,
163                                         int num_parents)
164 {
165         struct clk_plt *pclk;
166         struct clk_init_data init;
167         int ret;
168
169         pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
170         if (!pclk)
171                 return ERR_PTR(-ENOMEM);
172
173         init.name =  kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
174         init.ops = &plt_clk_ops;
175         init.flags = 0;
176         init.parent_names = parent_names;
177         init.num_parents = num_parents;
178
179         pclk->hw.init = &init;
180         pclk->reg = pmc_data->base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
181         spin_lock_init(&pclk->lock);
182
183         /*
184          * On some systems, the pmc_plt_clocks already enabled by the
185          * firmware are being marked as critical to avoid them being
186          * gated by the clock framework.
187          */
188         if (pmc_data->critical && plt_clk_is_enabled(&pclk->hw))
189                 init.flags |= CLK_IS_CRITICAL;
190
191         ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
192         if (ret) {
193                 pclk = ERR_PTR(ret);
194                 goto err_free_init;
195         }
196
197         pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL);
198         if (!pclk->lookup) {
199                 pclk = ERR_PTR(-ENOMEM);
200                 goto err_free_init;
201         }
202
203 err_free_init:
204         kfree(init.name);
205         return pclk;
206 }
207
208 static void plt_clk_unregister(struct clk_plt *pclk)
209 {
210         clkdev_drop(pclk->lookup);
211 }
212
213 static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
214                                                  const char *name,
215                                                  const char *parent_name,
216                                                  unsigned long fixed_rate)
217 {
218         struct clk_plt_fixed *pclk;
219
220         pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
221         if (!pclk)
222                 return ERR_PTR(-ENOMEM);
223
224         pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name,
225                                                0, fixed_rate);
226         if (IS_ERR(pclk->clk))
227                 return ERR_CAST(pclk->clk);
228
229         pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL);
230         if (!pclk->lookup) {
231                 clk_hw_unregister_fixed_rate(pclk->clk);
232                 return ERR_PTR(-ENOMEM);
233         }
234
235         return pclk;
236 }
237
238 static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
239 {
240         clkdev_drop(pclk->lookup);
241         clk_hw_unregister_fixed_rate(pclk->clk);
242 }
243
244 static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data,
245                                                unsigned int i)
246 {
247         while (i--)
248                 plt_clk_unregister_fixed_rate(data->parents[i]);
249 }
250
251 static void plt_clk_free_parent_names_loop(const char **parent_names,
252                                            unsigned int i)
253 {
254         while (i--)
255                 kfree_const(parent_names[i]);
256         kfree(parent_names);
257 }
258
259 static void plt_clk_unregister_loop(struct clk_plt_data *data,
260                                     unsigned int i)
261 {
262         while (i--)
263                 plt_clk_unregister(data->clks[i]);
264 }
265
266 static const char **plt_clk_register_parents(struct platform_device *pdev,
267                                              struct clk_plt_data *data,
268                                              const struct pmc_clk *clks)
269 {
270         const char **parent_names;
271         unsigned int i;
272         int err;
273         int nparents = 0;
274
275         data->nparents = 0;
276         while (clks[nparents].name)
277                 nparents++;
278
279         data->parents = devm_kcalloc(&pdev->dev, nparents,
280                                      sizeof(*data->parents), GFP_KERNEL);
281         if (!data->parents)
282                 return ERR_PTR(-ENOMEM);
283
284         parent_names = kcalloc(nparents, sizeof(*parent_names),
285                                GFP_KERNEL);
286         if (!parent_names)
287                 return ERR_PTR(-ENOMEM);
288
289         for (i = 0; i < nparents; i++) {
290                 data->parents[i] =
291                         plt_clk_register_fixed_rate(pdev, clks[i].name,
292                                                     clks[i].parent_name,
293                                                     clks[i].freq);
294                 if (IS_ERR(data->parents[i])) {
295                         err = PTR_ERR(data->parents[i]);
296                         goto err_unreg;
297                 }
298                 parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
299         }
300
301         data->nparents = nparents;
302         return parent_names;
303
304 err_unreg:
305         plt_clk_unregister_fixed_rate_loop(data, i);
306         plt_clk_free_parent_names_loop(parent_names, i);
307         return ERR_PTR(err);
308 }
309
310 static void plt_clk_unregister_parents(struct clk_plt_data *data)
311 {
312         plt_clk_unregister_fixed_rate_loop(data, data->nparents);
313 }
314
315 static int plt_clk_probe(struct platform_device *pdev)
316 {
317         const struct pmc_clk_data *pmc_data;
318         const char **parent_names;
319         struct clk_plt_data *data;
320         unsigned int i;
321         int err;
322
323         pmc_data = dev_get_platdata(&pdev->dev);
324         if (!pmc_data || !pmc_data->clks)
325                 return -EINVAL;
326
327         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
328         if (!data)
329                 return -ENOMEM;
330
331         parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks);
332         if (IS_ERR(parent_names))
333                 return PTR_ERR(parent_names);
334
335         for (i = 0; i < PMC_CLK_NUM; i++) {
336                 data->clks[i] = plt_clk_register(pdev, i, pmc_data,
337                                                  parent_names, data->nparents);
338                 if (IS_ERR(data->clks[i])) {
339                         err = PTR_ERR(data->clks[i]);
340                         goto err_unreg_clk_plt;
341                 }
342         }
343         data->mclk_lookup = clkdev_hw_create(&data->clks[3]->hw, "mclk", NULL);
344         if (!data->mclk_lookup) {
345                 err = -ENOMEM;
346                 goto err_unreg_clk_plt;
347         }
348
349         data->ether_clk_lookup = clkdev_hw_create(&data->clks[4]->hw,
350                                                   "ether_clk", NULL);
351         if (!data->ether_clk_lookup) {
352                 err = -ENOMEM;
353                 goto err_drop_mclk;
354         }
355
356         plt_clk_free_parent_names_loop(parent_names, data->nparents);
357
358         platform_set_drvdata(pdev, data);
359         return 0;
360
361 err_drop_mclk:
362         clkdev_drop(data->mclk_lookup);
363 err_unreg_clk_plt:
364         plt_clk_unregister_loop(data, i);
365         plt_clk_unregister_parents(data);
366         plt_clk_free_parent_names_loop(parent_names, data->nparents);
367         return err;
368 }
369
370 static int plt_clk_remove(struct platform_device *pdev)
371 {
372         struct clk_plt_data *data;
373
374         data = platform_get_drvdata(pdev);
375
376         clkdev_drop(data->ether_clk_lookup);
377         clkdev_drop(data->mclk_lookup);
378         plt_clk_unregister_loop(data, PMC_CLK_NUM);
379         plt_clk_unregister_parents(data);
380         return 0;
381 }
382
383 static struct platform_driver plt_clk_driver = {
384         .driver = {
385                 .name = "clk-pmc-atom",
386         },
387         .probe = plt_clk_probe,
388         .remove = plt_clk_remove,
389 };
390 builtin_platform_driver(plt_clk_driver);