clk: exynos-audss: convert to platform device
[deliverable/linux.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/clkdev.h>
13 #include <linux/io.h>
14 #include <linux/clk-provider.h>
15 #include <linux/of_address.h>
16 #include <linux/syscore_ops.h>
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
19
20 #include <dt-bindings/clk/exynos-audss-clk.h>
21
22 static DEFINE_SPINLOCK(lock);
23 static struct clk **clk_table;
24 static void __iomem *reg_base;
25 static struct clk_onecell_data clk_data;
26
27 #define ASS_CLK_SRC 0x0
28 #define ASS_CLK_DIV 0x4
29 #define ASS_CLK_GATE 0x8
30
31 /* list of all parent clock list */
32 static const char *mout_audss_p[] = { "fin_pll", "fout_epll" };
33 static const char *mout_i2s_p[] = { "mout_audss", "cdclk0", "sclk_audio0" };
34
35 #ifdef CONFIG_PM_SLEEP
36 static unsigned long reg_save[][2] = {
37 {ASS_CLK_SRC, 0},
38 {ASS_CLK_DIV, 0},
39 {ASS_CLK_GATE, 0},
40 };
41
42 static int exynos_audss_clk_suspend(void)
43 {
44 int i;
45
46 for (i = 0; i < ARRAY_SIZE(reg_save); i++)
47 reg_save[i][1] = readl(reg_base + reg_save[i][0]);
48
49 return 0;
50 }
51
52 static void exynos_audss_clk_resume(void)
53 {
54 int i;
55
56 for (i = 0; i < ARRAY_SIZE(reg_save); i++)
57 writel(reg_save[i][1], reg_base + reg_save[i][0]);
58 }
59
60 static struct syscore_ops exynos_audss_clk_syscore_ops = {
61 .suspend = exynos_audss_clk_suspend,
62 .resume = exynos_audss_clk_resume,
63 };
64 #endif /* CONFIG_PM_SLEEP */
65
66 /* register exynos_audss clocks */
67 static int exynos_audss_clk_probe(struct platform_device *pdev)
68 {
69 int i, ret = 0;
70 struct resource *res;
71
72 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
73 reg_base = devm_ioremap_resource(&pdev->dev, res);
74 if (IS_ERR(reg_base)) {
75 dev_err(&pdev->dev, "failed to map audss registers\n");
76 return PTR_ERR(reg_base);
77 }
78
79 clk_table = devm_kzalloc(&pdev->dev,
80 sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
81 GFP_KERNEL);
82 if (!clk_table)
83 return -ENOMEM;
84
85 clk_data.clks = clk_table;
86 clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS;
87
88 clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
89 mout_audss_p, ARRAY_SIZE(mout_audss_p),
90 CLK_SET_RATE_NO_REPARENT,
91 reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
92
93 clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
94 mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
95 CLK_SET_RATE_NO_REPARENT,
96 reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
97
98 clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp",
99 "mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
100 0, &lock);
101
102 clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL,
103 "dout_aud_bus", "dout_srp", 0,
104 reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
105
106 clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s",
107 "mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
108 &lock);
109
110 clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk",
111 "dout_srp", CLK_SET_RATE_PARENT,
112 reg_base + ASS_CLK_GATE, 0, 0, &lock);
113
114 clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus",
115 "dout_aud_bus", CLK_SET_RATE_PARENT,
116 reg_base + ASS_CLK_GATE, 2, 0, &lock);
117
118 clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s",
119 "dout_i2s", CLK_SET_RATE_PARENT,
120 reg_base + ASS_CLK_GATE, 3, 0, &lock);
121
122 clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus",
123 "sclk_pcm", CLK_SET_RATE_PARENT,
124 reg_base + ASS_CLK_GATE, 4, 0, &lock);
125
126 clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
127 "div_pcm0", CLK_SET_RATE_PARENT,
128 reg_base + ASS_CLK_GATE, 5, 0, &lock);
129
130 for (i = 0; i < clk_data.clk_num; i++) {
131 if (IS_ERR(clk_table[i])) {
132 dev_err(&pdev->dev, "failed to register clock %d\n", i);
133 ret = PTR_ERR(clk_table[i]);
134 goto unregister;
135 }
136 }
137
138 ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
139 &clk_data);
140 if (ret) {
141 dev_err(&pdev->dev, "failed to add clock provider\n");
142 goto unregister;
143 }
144
145 #ifdef CONFIG_PM_SLEEP
146 register_syscore_ops(&exynos_audss_clk_syscore_ops);
147 #endif
148
149 dev_info(&pdev->dev, "setup completed\n");
150
151 return 0;
152
153 unregister:
154 for (i = 0; i < clk_data.clk_num; i++) {
155 if (!IS_ERR(clk_table[i]))
156 clk_unregister(clk_table[i]);
157 }
158
159 return ret;
160 }
161
162 static int exynos_audss_clk_remove(struct platform_device *pdev)
163 {
164 int i;
165
166 of_clk_del_provider(pdev->dev.of_node);
167
168 for (i = 0; i < clk_data.clk_num; i++) {
169 if (!IS_ERR(clk_table[i]))
170 clk_unregister(clk_table[i]);
171 }
172
173 return 0;
174 }
175
176 static const struct of_device_id exynos_audss_clk_of_match[] = {
177 { .compatible = "samsung,exynos4210-audss-clock", },
178 { .compatible = "samsung,exynos5250-audss-clock", },
179 {},
180 };
181
182 static struct platform_driver exynos_audss_clk_driver = {
183 .driver = {
184 .name = "exynos-audss-clk",
185 .owner = THIS_MODULE,
186 .of_match_table = exynos_audss_clk_of_match,
187 },
188 .probe = exynos_audss_clk_probe,
189 .remove = exynos_audss_clk_remove,
190 };
191
192 static int __init exynos_audss_clk_init(void)
193 {
194 return platform_driver_register(&exynos_audss_clk_driver);
195 }
196 core_initcall(exynos_audss_clk_init);
197
198 static void __exit exynos_audss_clk_exit(void)
199 {
200 platform_driver_unregister(&exynos_audss_clk_driver);
201 }
202 module_exit(exynos_audss_clk_exit);
203
204 MODULE_AUTHOR("Padmavathi Venna <padma.v@samsung.com>");
205 MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller");
206 MODULE_LICENSE("GPL v2");
207 MODULE_ALIAS("platform:exynos-audss-clk");
This page took 0.036161 seconds and 6 git commands to generate.