Merge tag 'mac80211-for-davem-2018-05-09' of git://git.kernel.org/pub/scm/linux/kerne...
[sfrench/cifs-2.6.git] / drivers / clk / samsung / clk-exynos-audss.c
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3  * Author: Padmavathi Venna <padma.v@samsung.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Common Clock Framework support for Audio Subsystem Clock Controller.
10 */
11
12 #include <linux/slab.h>
13 #include <linux/io.h>
14 #include <linux/clk.h>
15 #include <linux/clk-provider.h>
16 #include <linux/of_address.h>
17 #include <linux/of_device.h>
18 #include <linux/syscore_ops.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <linux/pm_runtime.h>
22
23 #include <dt-bindings/clock/exynos-audss-clk.h>
24
25 static DEFINE_SPINLOCK(lock);
26 static void __iomem *reg_base;
27 static struct clk_hw_onecell_data *clk_data;
28 /*
29  * On Exynos5420 this will be a clock which has to be enabled before any
30  * access to audss registers. Typically a child of EPLL.
31  *
32  * On other platforms this will be -ENODEV.
33  */
34 static struct clk *epll;
35
36 #define ASS_CLK_SRC 0x0
37 #define ASS_CLK_DIV 0x4
38 #define ASS_CLK_GATE 0x8
39
40 static unsigned long reg_save[][2] = {
41         { ASS_CLK_SRC,  0 },
42         { ASS_CLK_DIV,  0 },
43         { ASS_CLK_GATE, 0 },
44 };
45
46 static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
47 {
48         int i;
49
50         for (i = 0; i < ARRAY_SIZE(reg_save); i++)
51                 reg_save[i][1] = readl(reg_base + reg_save[i][0]);
52
53         return 0;
54 }
55
56 static int __maybe_unused exynos_audss_clk_resume(struct device *dev)
57 {
58         int i;
59
60         for (i = 0; i < ARRAY_SIZE(reg_save); i++)
61                 writel(reg_save[i][1], reg_base + reg_save[i][0]);
62
63         return 0;
64 }
65
66 struct exynos_audss_clk_drvdata {
67         unsigned int has_adma_clk:1;
68         unsigned int has_mst_clk:1;
69         unsigned int enable_epll:1;
70         unsigned int num_clks;
71 };
72
73 static const struct exynos_audss_clk_drvdata exynos4210_drvdata = {
74         .num_clks       = EXYNOS_AUDSS_MAX_CLKS - 1,
75         .enable_epll    = 1,
76 };
77
78 static const struct exynos_audss_clk_drvdata exynos5410_drvdata = {
79         .num_clks       = EXYNOS_AUDSS_MAX_CLKS - 1,
80         .has_mst_clk    = 1,
81 };
82
83 static const struct exynos_audss_clk_drvdata exynos5420_drvdata = {
84         .num_clks       = EXYNOS_AUDSS_MAX_CLKS,
85         .has_adma_clk   = 1,
86         .enable_epll    = 1,
87 };
88
89 static const struct of_device_id exynos_audss_clk_of_match[] = {
90         {
91                 .compatible     = "samsung,exynos4210-audss-clock",
92                 .data           = &exynos4210_drvdata,
93         }, {
94                 .compatible     = "samsung,exynos5250-audss-clock",
95                 .data           = &exynos4210_drvdata,
96         }, {
97                 .compatible     = "samsung,exynos5410-audss-clock",
98                 .data           = &exynos5410_drvdata,
99         }, {
100                 .compatible     = "samsung,exynos5420-audss-clock",
101                 .data           = &exynos5420_drvdata,
102         },
103         { },
104 };
105 MODULE_DEVICE_TABLE(of, exynos_audss_clk_of_match);
106
107 static void exynos_audss_clk_teardown(void)
108 {
109         int i;
110
111         for (i = EXYNOS_MOUT_AUDSS; i < EXYNOS_DOUT_SRP; i++) {
112                 if (!IS_ERR(clk_data->hws[i]))
113                         clk_hw_unregister_mux(clk_data->hws[i]);
114         }
115
116         for (; i < EXYNOS_SRP_CLK; i++) {
117                 if (!IS_ERR(clk_data->hws[i]))
118                         clk_hw_unregister_divider(clk_data->hws[i]);
119         }
120
121         for (; i < clk_data->num; i++) {
122                 if (!IS_ERR(clk_data->hws[i]))
123                         clk_hw_unregister_gate(clk_data->hws[i]);
124         }
125 }
126
127 /* register exynos_audss clocks */
128 static int exynos_audss_clk_probe(struct platform_device *pdev)
129 {
130         const char *mout_audss_p[] = {"fin_pll", "fout_epll"};
131         const char *mout_i2s_p[] = {"mout_audss", "cdclk0", "sclk_audio0"};
132         const char *sclk_pcm_p = "sclk_pcm0";
133         struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
134         const struct exynos_audss_clk_drvdata *variant;
135         struct clk_hw **clk_table;
136         struct resource *res;
137         struct device *dev = &pdev->dev;
138         int i, ret = 0;
139
140         variant = of_device_get_match_data(&pdev->dev);
141         if (!variant)
142                 return -EINVAL;
143
144         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
145         reg_base = devm_ioremap_resource(dev, res);
146         if (IS_ERR(reg_base))
147                 return PTR_ERR(reg_base);
148
149         epll = ERR_PTR(-ENODEV);
150
151         clk_data = devm_kzalloc(dev,
152                                 sizeof(*clk_data) +
153                                 sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS,
154                                 GFP_KERNEL);
155         if (!clk_data)
156                 return -ENOMEM;
157
158         clk_data->num = variant->num_clks;
159         clk_table = clk_data->hws;
160
161         pll_ref = devm_clk_get(dev, "pll_ref");
162         pll_in = devm_clk_get(dev, "pll_in");
163         if (!IS_ERR(pll_ref))
164                 mout_audss_p[0] = __clk_get_name(pll_ref);
165         if (!IS_ERR(pll_in)) {
166                 mout_audss_p[1] = __clk_get_name(pll_in);
167
168                 if (variant->enable_epll) {
169                         epll = pll_in;
170
171                         ret = clk_prepare_enable(epll);
172                         if (ret) {
173                                 dev_err(dev,
174                                         "failed to prepare the epll clock\n");
175                                 return ret;
176                         }
177                 }
178         }
179
180         /*
181          * Enable runtime PM here to allow the clock core using runtime PM
182          * for the registered clocks. Additionally, we increase the runtime
183          * PM usage count before registering the clocks, to prevent the
184          * clock core from runtime suspending the device.
185          */
186         pm_runtime_get_noresume(dev);
187         pm_runtime_set_active(dev);
188         pm_runtime_enable(dev);
189
190         clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(dev, "mout_audss",
191                                 mout_audss_p, ARRAY_SIZE(mout_audss_p),
192                                 CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
193                                 reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
194
195         cdclk = devm_clk_get(dev, "cdclk");
196         sclk_audio = devm_clk_get(dev, "sclk_audio");
197         if (!IS_ERR(cdclk))
198                 mout_i2s_p[1] = __clk_get_name(cdclk);
199         if (!IS_ERR(sclk_audio))
200                 mout_i2s_p[2] = __clk_get_name(sclk_audio);
201         clk_table[EXYNOS_MOUT_I2S] = clk_hw_register_mux(dev, "mout_i2s",
202                                 mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
203                                 CLK_SET_RATE_NO_REPARENT,
204                                 reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
205
206         clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp",
207                                 "mout_audss", CLK_SET_RATE_PARENT,
208                                 reg_base + ASS_CLK_DIV, 0, 4, 0, &lock);
209
210         clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev,
211                                 "dout_aud_bus", "dout_srp", CLK_SET_RATE_PARENT,
212                                 reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
213
214         clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s",
215                                 "mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
216                                 &lock);
217
218         clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(dev, "srp_clk",
219                                 "dout_srp", CLK_SET_RATE_PARENT,
220                                 reg_base + ASS_CLK_GATE, 0, 0, &lock);
221
222         clk_table[EXYNOS_I2S_BUS] = clk_hw_register_gate(dev, "i2s_bus",
223                                 "dout_aud_bus", CLK_SET_RATE_PARENT,
224                                 reg_base + ASS_CLK_GATE, 2, 0, &lock);
225
226         clk_table[EXYNOS_SCLK_I2S] = clk_hw_register_gate(dev, "sclk_i2s",
227                                 "dout_i2s", CLK_SET_RATE_PARENT,
228                                 reg_base + ASS_CLK_GATE, 3, 0, &lock);
229
230         clk_table[EXYNOS_PCM_BUS] = clk_hw_register_gate(dev, "pcm_bus",
231                                  "sclk_pcm", CLK_SET_RATE_PARENT,
232                                 reg_base + ASS_CLK_GATE, 4, 0, &lock);
233
234         sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in");
235         if (!IS_ERR(sclk_pcm_in))
236                 sclk_pcm_p = __clk_get_name(sclk_pcm_in);
237         clk_table[EXYNOS_SCLK_PCM] = clk_hw_register_gate(dev, "sclk_pcm",
238                                 sclk_pcm_p, CLK_SET_RATE_PARENT,
239                                 reg_base + ASS_CLK_GATE, 5, 0, &lock);
240
241         if (variant->has_adma_clk) {
242                 clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma",
243                                 "dout_srp", CLK_SET_RATE_PARENT,
244                                 reg_base + ASS_CLK_GATE, 9, 0, &lock);
245         }
246
247         for (i = 0; i < clk_data->num; i++) {
248                 if (IS_ERR(clk_table[i])) {
249                         dev_err(dev, "failed to register clock %d\n", i);
250                         ret = PTR_ERR(clk_table[i]);
251                         goto unregister;
252                 }
253         }
254
255         ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
256                                      clk_data);
257         if (ret) {
258                 dev_err(dev, "failed to add clock provider\n");
259                 goto unregister;
260         }
261
262         pm_runtime_put_sync(dev);
263
264         return 0;
265
266 unregister:
267         exynos_audss_clk_teardown();
268         pm_runtime_put_sync(dev);
269         pm_runtime_disable(dev);
270
271         if (!IS_ERR(epll))
272                 clk_disable_unprepare(epll);
273
274         return ret;
275 }
276
277 static int exynos_audss_clk_remove(struct platform_device *pdev)
278 {
279         of_clk_del_provider(pdev->dev.of_node);
280
281         exynos_audss_clk_teardown();
282         pm_runtime_disable(&pdev->dev);
283
284         if (!IS_ERR(epll))
285                 clk_disable_unprepare(epll);
286
287         return 0;
288 }
289
290 static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
291         SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume,
292                            NULL)
293         SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
294                                      pm_runtime_force_resume)
295 };
296
297 static struct platform_driver exynos_audss_clk_driver = {
298         .driver = {
299                 .name = "exynos-audss-clk",
300                 .of_match_table = exynos_audss_clk_of_match,
301                 .pm = &exynos_audss_clk_pm_ops,
302         },
303         .probe = exynos_audss_clk_probe,
304         .remove = exynos_audss_clk_remove,
305 };
306
307 module_platform_driver(exynos_audss_clk_driver);
308
309 MODULE_AUTHOR("Padmavathi Venna <padma.v@samsung.com>");
310 MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller");
311 MODULE_LICENSE("GPL v2");
312 MODULE_ALIAS("platform:exynos-audss-clk");