Commit | Line | Data |
---|---|---|
3bb16560 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
923587aa JA |
2 | /* |
3 | * Synopsys AXS10X SDP I2S PLL clock driver | |
4 | * | |
5 | * Copyright (C) 2016 Synopsys | |
923587aa JA |
6 | */ |
7 | ||
8 | #include <linux/platform_device.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/clk-provider.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/device.h> | |
62e59c4e | 13 | #include <linux/io.h> |
923587aa JA |
14 | #include <linux/of_address.h> |
15 | #include <linux/slab.h> | |
16 | #include <linux/of.h> | |
17 | ||
18 | /* PLL registers addresses */ | |
19 | #define PLL_IDIV_REG 0x0 | |
20 | #define PLL_FBDIV_REG 0x4 | |
21 | #define PLL_ODIV0_REG 0x8 | |
22 | #define PLL_ODIV1_REG 0xC | |
23 | ||
24 | struct i2s_pll_cfg { | |
25 | unsigned int rate; | |
26 | unsigned int idiv; | |
27 | unsigned int fbdiv; | |
28 | unsigned int odiv0; | |
29 | unsigned int odiv1; | |
30 | }; | |
31 | ||
32 | static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = { | |
33 | /* 27 Mhz */ | |
34 | { 1024000, 0x104, 0x451, 0x10E38, 0x2000 }, | |
35 | { 1411200, 0x104, 0x596, 0x10D35, 0x2000 }, | |
36 | { 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 }, | |
37 | { 2048000, 0x82, 0x451, 0x10E38, 0x2000 }, | |
38 | { 2822400, 0x82, 0x596, 0x10D35, 0x2000 }, | |
39 | { 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 }, | |
40 | { 2116800, 0x82, 0x3CF, 0x10C30, 0x2000 }, | |
41 | { 2304000, 0x104, 0x79E, 0x10B2C, 0x2000 }, | |
42 | { 0, 0, 0, 0, 0 }, | |
43 | }; | |
44 | ||
45 | static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = { | |
46 | /* 28.224 Mhz */ | |
47 | { 1024000, 0x82, 0x105, 0x107DF, 0x2000 }, | |
48 | { 1411200, 0x28A, 0x1, 0x10001, 0x2000 }, | |
49 | { 1536000, 0xA28, 0x187, 0x10042, 0x2000 }, | |
50 | { 2048000, 0x41, 0x105, 0x107DF, 0x2000 }, | |
51 | { 2822400, 0x145, 0x1, 0x10001, 0x2000 }, | |
52 | { 3072000, 0x514, 0x187, 0x10042, 0x2000 }, | |
53 | { 2116800, 0x514, 0x42, 0x10001, 0x2000 }, | |
54 | { 2304000, 0x619, 0x82, 0x10001, 0x2000 }, | |
55 | { 0, 0, 0, 0, 0 }, | |
56 | }; | |
57 | ||
58 | struct i2s_pll_clk { | |
59 | void __iomem *base; | |
60 | struct clk_hw hw; | |
61 | struct device *dev; | |
62 | }; | |
63 | ||
64 | static inline void i2s_pll_write(struct i2s_pll_clk *clk, unsigned int reg, | |
65 | unsigned int val) | |
66 | { | |
67 | writel_relaxed(val, clk->base + reg); | |
68 | } | |
69 | ||
70 | static inline unsigned int i2s_pll_read(struct i2s_pll_clk *clk, | |
71 | unsigned int reg) | |
72 | { | |
73 | return readl_relaxed(clk->base + reg); | |
74 | } | |
75 | ||
76 | static inline struct i2s_pll_clk *to_i2s_pll_clk(struct clk_hw *hw) | |
77 | { | |
78 | return container_of(hw, struct i2s_pll_clk, hw); | |
79 | } | |
80 | ||
81 | static inline unsigned int i2s_pll_get_value(unsigned int val) | |
82 | { | |
83 | return (val & 0x3F) + ((val >> 6) & 0x3F); | |
84 | } | |
85 | ||
86 | static const struct i2s_pll_cfg *i2s_pll_get_cfg(unsigned long prate) | |
87 | { | |
88 | switch (prate) { | |
89 | case 27000000: | |
90 | return i2s_pll_cfg_27m; | |
91 | case 28224000: | |
92 | return i2s_pll_cfg_28m; | |
93 | default: | |
94 | return NULL; | |
95 | } | |
96 | } | |
97 | ||
98 | static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw, | |
99 | unsigned long parent_rate) | |
100 | { | |
101 | struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); | |
102 | unsigned int idiv, fbdiv, odiv; | |
103 | ||
104 | idiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_IDIV_REG)); | |
105 | fbdiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_FBDIV_REG)); | |
106 | odiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_ODIV0_REG)); | |
107 | ||
108 | return ((parent_rate / idiv) * fbdiv) / odiv; | |
109 | } | |
110 | ||
111 | static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate, | |
112 | unsigned long *prate) | |
113 | { | |
114 | struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); | |
115 | const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(*prate); | |
116 | int i; | |
117 | ||
118 | if (!pll_cfg) { | |
119 | dev_err(clk->dev, "invalid parent rate=%ld\n", *prate); | |
120 | return -EINVAL; | |
121 | } | |
122 | ||
123 | for (i = 0; pll_cfg[i].rate != 0; i++) | |
124 | if (pll_cfg[i].rate == rate) | |
125 | return rate; | |
126 | ||
127 | return -EINVAL; | |
128 | } | |
129 | ||
130 | static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate, | |
131 | unsigned long parent_rate) | |
132 | { | |
133 | struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); | |
134 | const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(parent_rate); | |
135 | int i; | |
136 | ||
137 | if (!pll_cfg) { | |
138 | dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate); | |
139 | return -EINVAL; | |
140 | } | |
141 | ||
142 | for (i = 0; pll_cfg[i].rate != 0; i++) { | |
143 | if (pll_cfg[i].rate == rate) { | |
144 | i2s_pll_write(clk, PLL_IDIV_REG, pll_cfg[i].idiv); | |
145 | i2s_pll_write(clk, PLL_FBDIV_REG, pll_cfg[i].fbdiv); | |
146 | i2s_pll_write(clk, PLL_ODIV0_REG, pll_cfg[i].odiv0); | |
147 | i2s_pll_write(clk, PLL_ODIV1_REG, pll_cfg[i].odiv1); | |
148 | return 0; | |
149 | } | |
150 | } | |
151 | ||
152 | dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, | |
153 | parent_rate); | |
154 | return -EINVAL; | |
155 | } | |
156 | ||
157 | static const struct clk_ops i2s_pll_ops = { | |
158 | .recalc_rate = i2s_pll_recalc_rate, | |
159 | .round_rate = i2s_pll_round_rate, | |
160 | .set_rate = i2s_pll_set_rate, | |
161 | }; | |
162 | ||
163 | static int i2s_pll_clk_probe(struct platform_device *pdev) | |
164 | { | |
165 | struct device *dev = &pdev->dev; | |
166 | struct device_node *node = dev->of_node; | |
167 | const char *clk_name; | |
168 | const char *parent_name; | |
169 | struct clk *clk; | |
170 | struct i2s_pll_clk *pll_clk; | |
171 | struct clk_init_data init; | |
923587aa JA |
172 | |
173 | pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); | |
174 | if (!pll_clk) | |
175 | return -ENOMEM; | |
176 | ||
21ec8679 | 177 | pll_clk->base = devm_platform_ioremap_resource(pdev, 0); |
923587aa JA |
178 | if (IS_ERR(pll_clk->base)) |
179 | return PTR_ERR(pll_clk->base); | |
180 | ||
6205406c | 181 | memset(&init, 0, sizeof(init)); |
923587aa JA |
182 | clk_name = node->name; |
183 | init.name = clk_name; | |
184 | init.ops = &i2s_pll_ops; | |
185 | parent_name = of_clk_get_parent_name(node, 0); | |
186 | init.parent_names = &parent_name; | |
187 | init.num_parents = 1; | |
188 | pll_clk->hw.init = &init; | |
189 | pll_clk->dev = dev; | |
190 | ||
191 | clk = devm_clk_register(dev, &pll_clk->hw); | |
192 | if (IS_ERR(clk)) { | |
193 | dev_err(dev, "failed to register %s clock (%ld)\n", | |
194 | clk_name, PTR_ERR(clk)); | |
195 | return PTR_ERR(clk); | |
196 | } | |
197 | ||
198 | return of_clk_add_provider(node, of_clk_src_simple_get, clk); | |
199 | } | |
200 | ||
b3438f55 | 201 | static void i2s_pll_clk_remove(struct platform_device *pdev) |
923587aa JA |
202 | { |
203 | of_clk_del_provider(pdev->dev.of_node); | |
923587aa JA |
204 | } |
205 | ||
206 | static const struct of_device_id i2s_pll_clk_id[] = { | |
207 | { .compatible = "snps,axs10x-i2s-pll-clock", }, | |
208 | { }, | |
209 | }; | |
210 | MODULE_DEVICE_TABLE(of, i2s_pll_clk_id); | |
211 | ||
212 | static struct platform_driver i2s_pll_clk_driver = { | |
213 | .driver = { | |
214 | .name = "axs10x-i2s-pll-clock", | |
215 | .of_match_table = i2s_pll_clk_id, | |
216 | }, | |
217 | .probe = i2s_pll_clk_probe, | |
b3438f55 | 218 | .remove_new = i2s_pll_clk_remove, |
923587aa JA |
219 | }; |
220 | module_platform_driver(i2s_pll_clk_driver); | |
221 | ||
222 | MODULE_AUTHOR("Jose Abreu <joabreu@synopsys.com>"); | |
223 | MODULE_DESCRIPTION("Synopsys AXS10X SDP I2S PLL Clock Driver"); | |
224 | MODULE_LICENSE("GPL v2"); |