1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Synopsys AXS10X SDP I2S PLL clock driver |
4 | * |
5 | * Copyright (C) 2016 Synopsys |
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> |
13 | #include <linux/io.h> |
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(val: i2s_pll_read(clk, PLL_IDIV_REG)); |
105 | fbdiv = i2s_pll_get_value(val: i2s_pll_read(clk, PLL_FBDIV_REG)); |
106 | odiv = i2s_pll_get_value(val: 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: *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(prate: 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, val: pll_cfg[i].idiv); |
145 | i2s_pll_write(clk, PLL_FBDIV_REG, val: pll_cfg[i].fbdiv); |
146 | i2s_pll_write(clk, PLL_ODIV0_REG, val: pll_cfg[i].odiv0); |
147 | i2s_pll_write(clk, PLL_ODIV1_REG, val: 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; |
172 | |
173 | pll_clk = devm_kzalloc(dev, size: sizeof(*pll_clk), GFP_KERNEL); |
174 | if (!pll_clk) |
175 | return -ENOMEM; |
176 | |
177 | pll_clk->base = devm_platform_ioremap_resource(pdev, index: 0); |
178 | if (IS_ERR(ptr: pll_clk->base)) |
179 | return PTR_ERR(ptr: pll_clk->base); |
180 | |
181 | memset(&init, 0, sizeof(init)); |
182 | clk_name = node->name; |
183 | init.name = clk_name; |
184 | init.ops = &i2s_pll_ops; |
185 | parent_name = of_clk_get_parent_name(np: node, index: 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, hw: &pll_clk->hw); |
192 | if (IS_ERR(ptr: clk)) { |
193 | dev_err(dev, "failed to register %s clock (%ld)\n" , |
194 | clk_name, PTR_ERR(clk)); |
195 | return PTR_ERR(ptr: clk); |
196 | } |
197 | |
198 | return of_clk_add_provider(np: node, clk_src_get: of_clk_src_simple_get, data: clk); |
199 | } |
200 | |
201 | static void i2s_pll_clk_remove(struct platform_device *pdev) |
202 | { |
203 | of_clk_del_provider(np: pdev->dev.of_node); |
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, |
218 | .remove_new = i2s_pll_clk_remove, |
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" ); |
225 | |