1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Skyworks Si521xx PCIe clock generator driver |
4 | * |
5 | * The following series can be supported: |
6 | * - Si52144 - 4x DIFF |
7 | * - Si52146 - 6x DIFF |
8 | * - Si52147 - 9x DIFF |
9 | * Currently tested: |
10 | * - Si52144 |
11 | * |
12 | * Copyright (C) 2022 Marek Vasut <marex@denx.de> |
13 | */ |
14 | |
15 | #include <linux/bitfield.h> |
16 | #include <linux/bitrev.h> |
17 | #include <linux/clk-provider.h> |
18 | #include <linux/i2c.h> |
19 | #include <linux/mod_devicetable.h> |
20 | #include <linux/module.h> |
21 | #include <linux/of.h> |
22 | #include <linux/regmap.h> |
23 | |
24 | /* OE1 and OE2 register */ |
25 | #define SI521XX_REG_OE(n) (((n) & 0x1) + 1) |
26 | #define SI521XX_REG_ID 0x3 |
27 | #define SI521XX_REG_ID_PROG GENMASK(7, 4) |
28 | #define SI521XX_REG_ID_VENDOR GENMASK(3, 0) |
29 | #define SI521XX_REG_BC 0x4 |
30 | #define SI521XX_REG_DA 0x5 |
31 | #define SI521XX_REG_DA_AMP_SEL BIT(7) |
32 | #define SI521XX_REG_DA_AMP_MASK GENMASK(6, 4) |
33 | #define SI521XX_REG_DA_AMP_MIN 300000 |
34 | #define SI521XX_REG_DA_AMP_DEFAULT 800000 |
35 | #define SI521XX_REG_DA_AMP_MAX 1000000 |
36 | #define SI521XX_REG_DA_AMP_STEP 100000 |
37 | #define SI521XX_REG_DA_AMP(UV) \ |
38 | FIELD_PREP(SI521XX_REG_DA_AMP_MASK, \ |
39 | ((UV) - SI521XX_REG_DA_AMP_MIN) / SI521XX_REG_DA_AMP_STEP) |
40 | #define SI521XX_REG_DA_UNKNOWN BIT(3) /* Always set */ |
41 | |
42 | /* Count of populated OE bits in control register ref, 1 and 2 */ |
43 | #define SI521XX_OE_MAP(cr1, cr2) (((cr2) << 8) | (cr1)) |
44 | #define SI521XX_OE_MAP_GET_OE(oe, map) (((map) >> (((oe) - 1) * 8)) & 0xff) |
45 | |
46 | #define SI521XX_DIFF_MULT 4 |
47 | #define SI521XX_DIFF_DIV 1 |
48 | |
49 | /* Supported Skyworks Si521xx models. */ |
50 | enum si521xx_model { |
51 | SI52144 = 0x44, |
52 | SI52146 = 0x46, |
53 | SI52147 = 0x47, |
54 | }; |
55 | |
56 | struct si521xx; |
57 | |
58 | struct si_clk { |
59 | struct clk_hw hw; |
60 | struct si521xx *si; |
61 | u8 reg; |
62 | u8 bit; |
63 | }; |
64 | |
65 | struct si521xx { |
66 | struct i2c_client *client; |
67 | struct regmap *regmap; |
68 | struct si_clk clk_dif[9]; |
69 | u16 chip_info; |
70 | u8 pll_amplitude; |
71 | }; |
72 | |
73 | /* |
74 | * Si521xx i2c regmap |
75 | */ |
76 | static const struct regmap_range si521xx_readable_ranges[] = { |
77 | regmap_reg_range(SI521XX_REG_OE(0), SI521XX_REG_DA), |
78 | }; |
79 | |
80 | static const struct regmap_access_table si521xx_readable_table = { |
81 | .yes_ranges = si521xx_readable_ranges, |
82 | .n_yes_ranges = ARRAY_SIZE(si521xx_readable_ranges), |
83 | }; |
84 | |
85 | static const struct regmap_range si521xx_writeable_ranges[] = { |
86 | regmap_reg_range(SI521XX_REG_OE(0), SI521XX_REG_OE(1)), |
87 | regmap_reg_range(SI521XX_REG_BC, SI521XX_REG_DA), |
88 | }; |
89 | |
90 | static const struct regmap_access_table si521xx_writeable_table = { |
91 | .yes_ranges = si521xx_writeable_ranges, |
92 | .n_yes_ranges = ARRAY_SIZE(si521xx_writeable_ranges), |
93 | }; |
94 | |
95 | static int si521xx_regmap_i2c_write(void *context, unsigned int reg, |
96 | unsigned int val) |
97 | { |
98 | struct i2c_client *i2c = context; |
99 | const u8 data[2] = { reg, val }; |
100 | const int count = ARRAY_SIZE(data); |
101 | int ret; |
102 | |
103 | ret = i2c_master_send(client: i2c, buf: data, count); |
104 | if (ret == count) |
105 | return 0; |
106 | else if (ret < 0) |
107 | return ret; |
108 | else |
109 | return -EIO; |
110 | } |
111 | |
112 | static int si521xx_regmap_i2c_read(void *context, unsigned int reg, |
113 | unsigned int *val) |
114 | { |
115 | struct i2c_client *i2c = context; |
116 | struct i2c_msg xfer[2]; |
117 | u8 txdata = reg; |
118 | u8 rxdata[2]; |
119 | int ret; |
120 | |
121 | xfer[0].addr = i2c->addr; |
122 | xfer[0].flags = 0; |
123 | xfer[0].len = 1; |
124 | xfer[0].buf = (void *)&txdata; |
125 | |
126 | xfer[1].addr = i2c->addr; |
127 | xfer[1].flags = I2C_M_RD; |
128 | xfer[1].len = 2; |
129 | xfer[1].buf = (void *)rxdata; |
130 | |
131 | ret = i2c_transfer(adap: i2c->adapter, msgs: xfer, num: 2); |
132 | if (ret < 0) |
133 | return ret; |
134 | if (ret != 2) |
135 | return -EIO; |
136 | |
137 | /* |
138 | * Byte 0 is transfer length, which is always 1 due |
139 | * to BCP register programming to 1 in si521xx_probe(), |
140 | * ignore it and use data from Byte 1. |
141 | */ |
142 | *val = rxdata[1]; |
143 | return 0; |
144 | } |
145 | |
146 | static const struct regmap_config si521xx_regmap_config = { |
147 | .reg_bits = 8, |
148 | .val_bits = 8, |
149 | .cache_type = REGCACHE_FLAT, |
150 | .max_register = SI521XX_REG_DA, |
151 | .rd_table = &si521xx_readable_table, |
152 | .wr_table = &si521xx_writeable_table, |
153 | .reg_write = si521xx_regmap_i2c_write, |
154 | .reg_read = si521xx_regmap_i2c_read, |
155 | }; |
156 | |
157 | static unsigned long si521xx_diff_recalc_rate(struct clk_hw *hw, |
158 | unsigned long parent_rate) |
159 | { |
160 | unsigned long long rate; |
161 | |
162 | rate = (unsigned long long)parent_rate * SI521XX_DIFF_MULT; |
163 | do_div(rate, SI521XX_DIFF_DIV); |
164 | return (unsigned long)rate; |
165 | } |
166 | |
167 | static long si521xx_diff_round_rate(struct clk_hw *hw, unsigned long rate, |
168 | unsigned long *prate) |
169 | { |
170 | unsigned long best_parent; |
171 | |
172 | best_parent = (rate / SI521XX_DIFF_MULT) * SI521XX_DIFF_DIV; |
173 | *prate = clk_hw_round_rate(hw: clk_hw_get_parent(hw), rate: best_parent); |
174 | |
175 | return (*prate / SI521XX_DIFF_DIV) * SI521XX_DIFF_MULT; |
176 | } |
177 | |
178 | static int si521xx_diff_set_rate(struct clk_hw *hw, unsigned long rate, |
179 | unsigned long parent_rate) |
180 | { |
181 | /* |
182 | * We must report success but we can do so unconditionally because |
183 | * si521xx_diff_round_rate returns values that ensure this call is a |
184 | * nop. |
185 | */ |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | #define to_si521xx_clk(_hw) container_of(_hw, struct si_clk, hw) |
191 | |
192 | static int si521xx_diff_prepare(struct clk_hw *hw) |
193 | { |
194 | struct si_clk *si_clk = to_si521xx_clk(hw); |
195 | struct si521xx *si = si_clk->si; |
196 | |
197 | regmap_set_bits(map: si->regmap, SI521XX_REG_OE(si_clk->reg), bits: si_clk->bit); |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | static void si521xx_diff_unprepare(struct clk_hw *hw) |
203 | { |
204 | struct si_clk *si_clk = to_si521xx_clk(hw); |
205 | struct si521xx *si = si_clk->si; |
206 | |
207 | regmap_clear_bits(map: si->regmap, SI521XX_REG_OE(si_clk->reg), bits: si_clk->bit); |
208 | } |
209 | |
210 | static const struct clk_ops si521xx_diff_clk_ops = { |
211 | .round_rate = si521xx_diff_round_rate, |
212 | .set_rate = si521xx_diff_set_rate, |
213 | .recalc_rate = si521xx_diff_recalc_rate, |
214 | .prepare = si521xx_diff_prepare, |
215 | .unprepare = si521xx_diff_unprepare, |
216 | }; |
217 | |
218 | static int si521xx_get_common_config(struct si521xx *si) |
219 | { |
220 | struct i2c_client *client = si->client; |
221 | struct device_node *np = client->dev.of_node; |
222 | unsigned int amp; |
223 | int ret; |
224 | |
225 | /* Set defaults */ |
226 | si->pll_amplitude = SI521XX_REG_DA_AMP(SI521XX_REG_DA_AMP_DEFAULT); |
227 | |
228 | /* Output clock amplitude */ |
229 | ret = of_property_read_u32(np, propname: "skyworks,out-amplitude-microvolt" , |
230 | out_value: &); |
231 | if (!ret) { |
232 | if (amp < SI521XX_REG_DA_AMP_MIN || amp > SI521XX_REG_DA_AMP_MAX || |
233 | amp % SI521XX_REG_DA_AMP_STEP) { |
234 | return dev_err_probe(dev: &client->dev, err: -EINVAL, |
235 | fmt: "Invalid skyworks,out-amplitude-microvolt value\n" ); |
236 | } |
237 | si->pll_amplitude = SI521XX_REG_DA_AMP(amp); |
238 | } |
239 | |
240 | return 0; |
241 | } |
242 | |
243 | static void si521xx_update_config(struct si521xx *si) |
244 | { |
245 | /* If amplitude is non-default, update it. */ |
246 | if (si->pll_amplitude == SI521XX_REG_DA_AMP(SI521XX_REG_DA_AMP_DEFAULT)) |
247 | return; |
248 | |
249 | regmap_update_bits(map: si->regmap, SI521XX_REG_DA, |
250 | SI521XX_REG_DA_AMP_MASK, val: si->pll_amplitude); |
251 | } |
252 | |
253 | static void si521xx_diff_idx_to_reg_bit(const u16 chip_info, const int idx, |
254 | struct si_clk *clk) |
255 | { |
256 | unsigned long mask; |
257 | int oe, b, ctr = 0; |
258 | |
259 | for (oe = 1; oe <= 2; oe++) { |
260 | mask = bitrev8(SI521XX_OE_MAP_GET_OE(oe, chip_info)); |
261 | for_each_set_bit(b, &mask, 8) { |
262 | if (ctr++ != idx) |
263 | continue; |
264 | clk->reg = SI521XX_REG_OE(oe); |
265 | clk->bit = 7 - b; |
266 | return; |
267 | } |
268 | } |
269 | } |
270 | |
271 | static struct clk_hw * |
272 | si521xx_of_clk_get(struct of_phandle_args *clkspec, void *data) |
273 | { |
274 | struct si521xx *si = data; |
275 | unsigned int idx = clkspec->args[0]; |
276 | |
277 | return &si->clk_dif[idx].hw; |
278 | } |
279 | |
280 | static int si521xx_probe(struct i2c_client *client) |
281 | { |
282 | const u16 chip_info = (u16)(uintptr_t)i2c_get_match_data(client); |
283 | const struct clk_parent_data clk_parent_data = { .index = 0 }; |
284 | const u8 data[3] = { SI521XX_REG_BC, 1, 1 }; |
285 | unsigned char name[16] = "DIFF0" ; |
286 | struct clk_init_data init = {}; |
287 | struct si521xx *si; |
288 | int i, ret; |
289 | |
290 | if (!chip_info) |
291 | return -EINVAL; |
292 | |
293 | si = devm_kzalloc(dev: &client->dev, size: sizeof(*si), GFP_KERNEL); |
294 | if (!si) |
295 | return -ENOMEM; |
296 | |
297 | i2c_set_clientdata(client, data: si); |
298 | si->client = client; |
299 | |
300 | /* Fetch common configuration from DT (if specified) */ |
301 | ret = si521xx_get_common_config(si); |
302 | if (ret) |
303 | return ret; |
304 | |
305 | si->regmap = devm_regmap_init(&client->dev, NULL, client, |
306 | &si521xx_regmap_config); |
307 | if (IS_ERR(ptr: si->regmap)) |
308 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: si->regmap), |
309 | fmt: "Failed to allocate register map\n" ); |
310 | |
311 | /* Always read back 1 Byte via I2C */ |
312 | ret = i2c_master_send(client, buf: data, ARRAY_SIZE(data)); |
313 | if (ret < 0) |
314 | return ret; |
315 | |
316 | /* Register clock */ |
317 | for (i = 0; i < hweight16(chip_info); i++) { |
318 | memset(&init, 0, sizeof(init)); |
319 | snprintf(buf: name, size: sizeof(name), fmt: "DIFF%d" , i); |
320 | init.name = name; |
321 | init.ops = &si521xx_diff_clk_ops; |
322 | init.parent_data = &clk_parent_data; |
323 | init.num_parents = 1; |
324 | init.flags = CLK_SET_RATE_PARENT; |
325 | |
326 | si->clk_dif[i].hw.init = &init; |
327 | si->clk_dif[i].si = si; |
328 | |
329 | si521xx_diff_idx_to_reg_bit(chip_info, idx: i, clk: &si->clk_dif[i]); |
330 | |
331 | ret = devm_clk_hw_register(dev: &client->dev, hw: &si->clk_dif[i].hw); |
332 | if (ret) |
333 | return ret; |
334 | } |
335 | |
336 | ret = devm_of_clk_add_hw_provider(dev: &client->dev, get: si521xx_of_clk_get, data: si); |
337 | if (!ret) |
338 | si521xx_update_config(si); |
339 | |
340 | return ret; |
341 | } |
342 | |
343 | static int __maybe_unused si521xx_suspend(struct device *dev) |
344 | { |
345 | struct si521xx *si = dev_get_drvdata(dev); |
346 | |
347 | regcache_cache_only(map: si->regmap, enable: true); |
348 | regcache_mark_dirty(map: si->regmap); |
349 | |
350 | return 0; |
351 | } |
352 | |
353 | static int __maybe_unused si521xx_resume(struct device *dev) |
354 | { |
355 | struct si521xx *si = dev_get_drvdata(dev); |
356 | int ret; |
357 | |
358 | regcache_cache_only(map: si->regmap, enable: false); |
359 | ret = regcache_sync(map: si->regmap); |
360 | if (ret) |
361 | dev_err(dev, "Failed to restore register map: %d\n" , ret); |
362 | return ret; |
363 | } |
364 | |
365 | static const struct i2c_device_id si521xx_id[] = { |
366 | { "si52144" , .driver_data = SI521XX_OE_MAP(0x5, 0xc0) }, |
367 | { "si52146" , .driver_data = SI521XX_OE_MAP(0x15, 0xe0) }, |
368 | { "si52147" , .driver_data = SI521XX_OE_MAP(0x17, 0xf8) }, |
369 | { } |
370 | }; |
371 | MODULE_DEVICE_TABLE(i2c, si521xx_id); |
372 | |
373 | static const struct of_device_id clk_si521xx_of_match[] = { |
374 | { .compatible = "skyworks,si52144" , .data = (void *)SI521XX_OE_MAP(0x5, 0xc0) }, |
375 | { .compatible = "skyworks,si52146" , .data = (void *)SI521XX_OE_MAP(0x15, 0xe0) }, |
376 | { .compatible = "skyworks,si52147" , .data = (void *)SI521XX_OE_MAP(0x15, 0xf8) }, |
377 | { } |
378 | }; |
379 | MODULE_DEVICE_TABLE(of, clk_si521xx_of_match); |
380 | |
381 | static SIMPLE_DEV_PM_OPS(si521xx_pm_ops, si521xx_suspend, si521xx_resume); |
382 | |
383 | static struct i2c_driver si521xx_driver = { |
384 | .driver = { |
385 | .name = "clk-si521xx" , |
386 | .pm = &si521xx_pm_ops, |
387 | .of_match_table = clk_si521xx_of_match, |
388 | }, |
389 | .probe = si521xx_probe, |
390 | .id_table = si521xx_id, |
391 | }; |
392 | module_i2c_driver(si521xx_driver); |
393 | |
394 | MODULE_AUTHOR("Marek Vasut <marex@denx.de>" ); |
395 | MODULE_DESCRIPTION("Skyworks Si521xx PCIe clock generator driver" ); |
396 | MODULE_LICENSE("GPL" ); |
397 | |