1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Core MFD support for Cirrus Logic Madera codecs |
4 | * |
5 | * Copyright (C) 2015-2018 Cirrus Logic |
6 | */ |
7 | |
8 | #include <linux/device.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/err.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/mfd/core.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mutex.h> |
15 | #include <linux/notifier.h> |
16 | #include <linux/of.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/pm_runtime.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/regulator/consumer.h> |
21 | #include <linux/regulator/machine.h> |
22 | #include <linux/regulator/of_regulator.h> |
23 | |
24 | #include <linux/mfd/madera/core.h> |
25 | #include <linux/mfd/madera/registers.h> |
26 | |
27 | #include "madera.h" |
28 | |
29 | #define CS47L15_SILICON_ID 0x6370 |
30 | #define CS47L35_SILICON_ID 0x6360 |
31 | #define CS47L85_SILICON_ID 0x6338 |
32 | #define CS47L90_SILICON_ID 0x6364 |
33 | #define CS47L92_SILICON_ID 0x6371 |
34 | |
35 | #define MADERA_32KZ_MCLK2 1 |
36 | |
37 | #define MADERA_RESET_MIN_US 2000 |
38 | #define MADERA_RESET_MAX_US 3000 |
39 | |
40 | #define ERRATA_DCVDD_MIN_US 10000 |
41 | #define ERRATA_DCVDD_MAX_US 15000 |
42 | |
43 | static const char * const madera_core_supplies[] = { |
44 | "AVDD" , |
45 | "DBVDD1" , |
46 | }; |
47 | |
48 | static const struct mfd_cell madera_ldo1_devs[] = { |
49 | { |
50 | .name = "madera-ldo1" , |
51 | .level = MFD_DEP_LEVEL_HIGH, |
52 | }, |
53 | }; |
54 | |
55 | static const char * const cs47l15_supplies[] = { |
56 | "MICVDD" , |
57 | "CPVDD1" , |
58 | "SPKVDD" , |
59 | }; |
60 | |
61 | static const struct mfd_cell cs47l15_devs[] = { |
62 | { .name = "madera-pinctrl" , }, |
63 | { .name = "madera-irq" , }, |
64 | { .name = "madera-gpio" , }, |
65 | { |
66 | .name = "madera-extcon" , |
67 | .parent_supplies = cs47l15_supplies, |
68 | .num_parent_supplies = 1, /* We only need MICVDD */ |
69 | }, |
70 | { |
71 | .name = "cs47l15-codec" , |
72 | .parent_supplies = cs47l15_supplies, |
73 | .num_parent_supplies = ARRAY_SIZE(cs47l15_supplies), |
74 | }, |
75 | }; |
76 | |
77 | static const char * const cs47l35_supplies[] = { |
78 | "MICVDD" , |
79 | "DBVDD2" , |
80 | "CPVDD1" , |
81 | "CPVDD2" , |
82 | "SPKVDD" , |
83 | }; |
84 | |
85 | static const struct mfd_cell cs47l35_devs[] = { |
86 | { .name = "madera-pinctrl" , }, |
87 | { .name = "madera-irq" , }, |
88 | { .name = "madera-micsupp" , }, |
89 | { .name = "madera-gpio" , }, |
90 | { |
91 | .name = "madera-extcon" , |
92 | .parent_supplies = cs47l35_supplies, |
93 | .num_parent_supplies = 1, /* We only need MICVDD */ |
94 | }, |
95 | { |
96 | .name = "cs47l35-codec" , |
97 | .parent_supplies = cs47l35_supplies, |
98 | .num_parent_supplies = ARRAY_SIZE(cs47l35_supplies), |
99 | }, |
100 | }; |
101 | |
102 | static const char * const cs47l85_supplies[] = { |
103 | "MICVDD" , |
104 | "DBVDD2" , |
105 | "DBVDD3" , |
106 | "DBVDD4" , |
107 | "CPVDD1" , |
108 | "CPVDD2" , |
109 | "SPKVDDL" , |
110 | "SPKVDDR" , |
111 | }; |
112 | |
113 | static const struct mfd_cell cs47l85_devs[] = { |
114 | { .name = "madera-pinctrl" , }, |
115 | { .name = "madera-irq" , }, |
116 | { .name = "madera-micsupp" , }, |
117 | { .name = "madera-gpio" , }, |
118 | { |
119 | .name = "madera-extcon" , |
120 | .parent_supplies = cs47l85_supplies, |
121 | .num_parent_supplies = 1, /* We only need MICVDD */ |
122 | }, |
123 | { |
124 | .name = "cs47l85-codec" , |
125 | .parent_supplies = cs47l85_supplies, |
126 | .num_parent_supplies = ARRAY_SIZE(cs47l85_supplies), |
127 | }, |
128 | }; |
129 | |
130 | static const char * const cs47l90_supplies[] = { |
131 | "MICVDD" , |
132 | "DBVDD2" , |
133 | "DBVDD3" , |
134 | "DBVDD4" , |
135 | "CPVDD1" , |
136 | "CPVDD2" , |
137 | }; |
138 | |
139 | static const struct mfd_cell cs47l90_devs[] = { |
140 | { .name = "madera-pinctrl" , }, |
141 | { .name = "madera-irq" , }, |
142 | { .name = "madera-micsupp" , }, |
143 | { .name = "madera-gpio" , }, |
144 | { |
145 | .name = "madera-extcon" , |
146 | .parent_supplies = cs47l90_supplies, |
147 | .num_parent_supplies = 1, /* We only need MICVDD */ |
148 | }, |
149 | { |
150 | .name = "cs47l90-codec" , |
151 | .parent_supplies = cs47l90_supplies, |
152 | .num_parent_supplies = ARRAY_SIZE(cs47l90_supplies), |
153 | }, |
154 | }; |
155 | |
156 | static const char * const cs47l92_supplies[] = { |
157 | "MICVDD" , |
158 | "CPVDD1" , |
159 | "CPVDD2" , |
160 | }; |
161 | |
162 | static const struct mfd_cell cs47l92_devs[] = { |
163 | { .name = "madera-pinctrl" , }, |
164 | { .name = "madera-irq" , }, |
165 | { .name = "madera-micsupp" , }, |
166 | { .name = "madera-gpio" , }, |
167 | { |
168 | .name = "madera-extcon" , |
169 | .parent_supplies = cs47l92_supplies, |
170 | .num_parent_supplies = 1, /* We only need MICVDD */ |
171 | }, |
172 | { |
173 | .name = "cs47l92-codec" , |
174 | .parent_supplies = cs47l92_supplies, |
175 | .num_parent_supplies = ARRAY_SIZE(cs47l92_supplies), |
176 | }, |
177 | }; |
178 | |
179 | /* Used by madera-i2c and madera-spi drivers */ |
180 | const char *madera_name_from_type(enum madera_type type) |
181 | { |
182 | switch (type) { |
183 | case CS47L15: |
184 | return "CS47L15" ; |
185 | case CS47L35: |
186 | return "CS47L35" ; |
187 | case CS47L85: |
188 | return "CS47L85" ; |
189 | case CS47L90: |
190 | return "CS47L90" ; |
191 | case CS47L91: |
192 | return "CS47L91" ; |
193 | case CS42L92: |
194 | return "CS42L92" ; |
195 | case CS47L92: |
196 | return "CS47L92" ; |
197 | case CS47L93: |
198 | return "CS47L93" ; |
199 | case WM1840: |
200 | return "WM1840" ; |
201 | default: |
202 | return "Unknown" ; |
203 | } |
204 | } |
205 | EXPORT_SYMBOL_GPL(madera_name_from_type); |
206 | |
207 | #define MADERA_BOOT_POLL_INTERVAL_USEC 5000 |
208 | #define MADERA_BOOT_POLL_TIMEOUT_USEC 25000 |
209 | |
210 | static int madera_wait_for_boot_noack(struct madera *madera) |
211 | { |
212 | ktime_t timeout; |
213 | unsigned int val = 0; |
214 | int ret = 0; |
215 | |
216 | /* |
217 | * We can't use an interrupt as we need to runtime resume to do so, |
218 | * so we poll the status bit. This won't race with the interrupt |
219 | * handler because it will be blocked on runtime resume. |
220 | * The chip could NAK a read request while it is booting so ignore |
221 | * errors from regmap_read. |
222 | */ |
223 | timeout = ktime_add_us(kt: ktime_get(), MADERA_BOOT_POLL_TIMEOUT_USEC); |
224 | regmap_read(map: madera->regmap, MADERA_IRQ1_RAW_STATUS_1, val: &val); |
225 | while (!(val & MADERA_BOOT_DONE_STS1) && |
226 | !ktime_after(cmp1: ktime_get(), cmp2: timeout)) { |
227 | usleep_range(MADERA_BOOT_POLL_INTERVAL_USEC / 2, |
228 | MADERA_BOOT_POLL_INTERVAL_USEC); |
229 | regmap_read(map: madera->regmap, MADERA_IRQ1_RAW_STATUS_1, val: &val); |
230 | } |
231 | |
232 | if (!(val & MADERA_BOOT_DONE_STS1)) { |
233 | dev_err(madera->dev, "Polling BOOT_DONE_STS timed out\n" ); |
234 | ret = -ETIMEDOUT; |
235 | } |
236 | |
237 | return ret; |
238 | } |
239 | |
240 | static int madera_wait_for_boot(struct madera *madera) |
241 | { |
242 | int ret = madera_wait_for_boot_noack(madera); |
243 | |
244 | /* |
245 | * BOOT_DONE defaults to unmasked on boot so we must ack it. |
246 | * Do this even after a timeout to avoid interrupt storms. |
247 | */ |
248 | regmap_write(map: madera->regmap, MADERA_IRQ1_STATUS_1, |
249 | MADERA_BOOT_DONE_EINT1); |
250 | |
251 | pm_runtime_mark_last_busy(dev: madera->dev); |
252 | |
253 | return ret; |
254 | } |
255 | |
256 | static int madera_soft_reset(struct madera *madera) |
257 | { |
258 | int ret; |
259 | |
260 | ret = regmap_write(map: madera->regmap, MADERA_SOFTWARE_RESET, val: 0); |
261 | if (ret != 0) { |
262 | dev_err(madera->dev, "Failed to soft reset device: %d\n" , ret); |
263 | return ret; |
264 | } |
265 | |
266 | /* Allow time for internal clocks to startup after reset */ |
267 | usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US); |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | static void madera_enable_hard_reset(struct madera *madera) |
273 | { |
274 | /* |
275 | * There are many existing out-of-tree users of these codecs that we |
276 | * can't break so preserve the expected behaviour of setting the line |
277 | * low to assert reset. |
278 | */ |
279 | gpiod_set_raw_value_cansleep(desc: madera->pdata.reset, value: 0); |
280 | } |
281 | |
282 | static void madera_disable_hard_reset(struct madera *madera) |
283 | { |
284 | gpiod_set_raw_value_cansleep(desc: madera->pdata.reset, value: 1); |
285 | |
286 | usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US); |
287 | } |
288 | |
289 | static int __maybe_unused madera_runtime_resume(struct device *dev) |
290 | { |
291 | struct madera *madera = dev_get_drvdata(dev); |
292 | int ret; |
293 | |
294 | dev_dbg(dev, "Leaving sleep mode\n" ); |
295 | |
296 | if (!madera->reset_errata) |
297 | madera_enable_hard_reset(madera); |
298 | |
299 | ret = regulator_enable(regulator: madera->dcvdd); |
300 | if (ret) { |
301 | dev_err(dev, "Failed to enable DCVDD: %d\n" , ret); |
302 | return ret; |
303 | } |
304 | |
305 | regcache_cache_only(map: madera->regmap, enable: false); |
306 | regcache_cache_only(map: madera->regmap_32bit, enable: false); |
307 | |
308 | if (madera->reset_errata) |
309 | usleep_range(ERRATA_DCVDD_MIN_US, ERRATA_DCVDD_MAX_US); |
310 | else |
311 | madera_disable_hard_reset(madera); |
312 | |
313 | if (!madera->pdata.reset || madera->reset_errata) { |
314 | ret = madera_wait_for_boot(madera); |
315 | if (ret) |
316 | goto err; |
317 | |
318 | ret = madera_soft_reset(madera); |
319 | if (ret) { |
320 | dev_err(dev, "Failed to reset: %d\n" , ret); |
321 | goto err; |
322 | } |
323 | } |
324 | |
325 | ret = madera_wait_for_boot(madera); |
326 | if (ret) |
327 | goto err; |
328 | |
329 | ret = regcache_sync(map: madera->regmap); |
330 | if (ret) { |
331 | dev_err(dev, "Failed to restore 16-bit register cache\n" ); |
332 | goto err; |
333 | } |
334 | |
335 | ret = regcache_sync(map: madera->regmap_32bit); |
336 | if (ret) { |
337 | dev_err(dev, "Failed to restore 32-bit register cache\n" ); |
338 | goto err; |
339 | } |
340 | |
341 | return 0; |
342 | |
343 | err: |
344 | regcache_cache_only(map: madera->regmap_32bit, enable: true); |
345 | regcache_cache_only(map: madera->regmap, enable: true); |
346 | regulator_disable(regulator: madera->dcvdd); |
347 | |
348 | return ret; |
349 | } |
350 | |
351 | static int __maybe_unused madera_runtime_suspend(struct device *dev) |
352 | { |
353 | struct madera *madera = dev_get_drvdata(dev); |
354 | |
355 | dev_dbg(madera->dev, "Entering sleep mode\n" ); |
356 | |
357 | regcache_cache_only(map: madera->regmap, enable: true); |
358 | regcache_mark_dirty(map: madera->regmap); |
359 | regcache_cache_only(map: madera->regmap_32bit, enable: true); |
360 | regcache_mark_dirty(map: madera->regmap_32bit); |
361 | |
362 | regulator_disable(regulator: madera->dcvdd); |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | const struct dev_pm_ops madera_pm_ops = { |
368 | SET_RUNTIME_PM_OPS(madera_runtime_suspend, |
369 | madera_runtime_resume, |
370 | NULL) |
371 | }; |
372 | EXPORT_SYMBOL_GPL(madera_pm_ops); |
373 | |
374 | const struct of_device_id madera_of_match[] = { |
375 | { .compatible = "cirrus,cs47l15" , .data = (void *)CS47L15 }, |
376 | { .compatible = "cirrus,cs47l35" , .data = (void *)CS47L35 }, |
377 | { .compatible = "cirrus,cs47l85" , .data = (void *)CS47L85 }, |
378 | { .compatible = "cirrus,cs47l90" , .data = (void *)CS47L90 }, |
379 | { .compatible = "cirrus,cs47l91" , .data = (void *)CS47L91 }, |
380 | { .compatible = "cirrus,cs42l92" , .data = (void *)CS42L92 }, |
381 | { .compatible = "cirrus,cs47l92" , .data = (void *)CS47L92 }, |
382 | { .compatible = "cirrus,cs47l93" , .data = (void *)CS47L93 }, |
383 | { .compatible = "cirrus,wm1840" , .data = (void *)WM1840 }, |
384 | {} |
385 | }; |
386 | MODULE_DEVICE_TABLE(of, madera_of_match); |
387 | EXPORT_SYMBOL_GPL(madera_of_match); |
388 | |
389 | static int madera_get_reset_gpio(struct madera *madera) |
390 | { |
391 | struct gpio_desc *reset; |
392 | |
393 | if (madera->pdata.reset) |
394 | return 0; |
395 | |
396 | reset = devm_gpiod_get_optional(dev: madera->dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
397 | if (IS_ERR(ptr: reset)) |
398 | return dev_err_probe(dev: madera->dev, err: PTR_ERR(ptr: reset), |
399 | fmt: "Failed to request /RESET" ); |
400 | |
401 | /* |
402 | * A hard reset is needed for full reset of the chip. We allow running |
403 | * without hard reset only because it can be useful for early |
404 | * prototyping and some debugging, but we need to warn it's not ideal. |
405 | */ |
406 | if (!reset) |
407 | dev_warn(madera->dev, |
408 | "Running without reset GPIO is not recommended\n" ); |
409 | |
410 | madera->pdata.reset = reset; |
411 | |
412 | return 0; |
413 | } |
414 | |
415 | static void madera_set_micbias_info(struct madera *madera) |
416 | { |
417 | /* |
418 | * num_childbias is an array because future codecs can have different |
419 | * childbiases for each micbias. Unspecified values default to 0. |
420 | */ |
421 | switch (madera->type) { |
422 | case CS47L15: |
423 | madera->num_micbias = 1; |
424 | madera->num_childbias[0] = 3; |
425 | return; |
426 | case CS47L35: |
427 | madera->num_micbias = 2; |
428 | madera->num_childbias[0] = 2; |
429 | madera->num_childbias[1] = 2; |
430 | return; |
431 | case CS47L85: |
432 | case WM1840: |
433 | madera->num_micbias = 4; |
434 | /* no child biases */ |
435 | return; |
436 | case CS47L90: |
437 | case CS47L91: |
438 | madera->num_micbias = 2; |
439 | madera->num_childbias[0] = 4; |
440 | madera->num_childbias[1] = 4; |
441 | return; |
442 | case CS42L92: |
443 | case CS47L92: |
444 | case CS47L93: |
445 | madera->num_micbias = 2; |
446 | madera->num_childbias[0] = 4; |
447 | madera->num_childbias[1] = 2; |
448 | return; |
449 | default: |
450 | return; |
451 | } |
452 | } |
453 | |
454 | int madera_dev_init(struct madera *madera) |
455 | { |
456 | struct device *dev = madera->dev; |
457 | unsigned int hwid; |
458 | int (*patch_fn)(struct madera *) = NULL; |
459 | const struct mfd_cell *mfd_devs; |
460 | int n_devs = 0; |
461 | int i, ret; |
462 | |
463 | dev_set_drvdata(dev: madera->dev, data: madera); |
464 | BLOCKING_INIT_NOTIFIER_HEAD(&madera->notifier); |
465 | mutex_init(&madera->dapm_ptr_lock); |
466 | |
467 | madera_set_micbias_info(madera); |
468 | |
469 | /* |
470 | * We need writable hw config info that all children can share. |
471 | * Simplest to take one shared copy of pdata struct. |
472 | */ |
473 | if (dev_get_platdata(dev: madera->dev)) { |
474 | memcpy(&madera->pdata, dev_get_platdata(madera->dev), |
475 | sizeof(madera->pdata)); |
476 | } |
477 | |
478 | madera->mclk[MADERA_MCLK1].id = "mclk1" ; |
479 | madera->mclk[MADERA_MCLK2].id = "mclk2" ; |
480 | madera->mclk[MADERA_MCLK3].id = "mclk3" ; |
481 | |
482 | ret = devm_clk_bulk_get_optional(dev: madera->dev, ARRAY_SIZE(madera->mclk), |
483 | clks: madera->mclk); |
484 | if (ret) { |
485 | dev_err(madera->dev, "Failed to get clocks: %d\n" , ret); |
486 | return ret; |
487 | } |
488 | |
489 | /* Not using devm_clk_get to prevent breakage of existing DTs */ |
490 | if (!madera->mclk[MADERA_MCLK2].clk) |
491 | dev_warn(madera->dev, "Missing MCLK2, requires 32kHz clock\n" ); |
492 | |
493 | ret = madera_get_reset_gpio(madera); |
494 | if (ret) |
495 | return ret; |
496 | |
497 | regcache_cache_only(map: madera->regmap, enable: true); |
498 | regcache_cache_only(map: madera->regmap_32bit, enable: true); |
499 | |
500 | for (i = 0; i < ARRAY_SIZE(madera_core_supplies); i++) |
501 | madera->core_supplies[i].supply = madera_core_supplies[i]; |
502 | |
503 | madera->num_core_supplies = ARRAY_SIZE(madera_core_supplies); |
504 | |
505 | /* |
506 | * On some codecs DCVDD could be supplied by the internal LDO1. |
507 | * For those we must add the LDO1 driver before requesting DCVDD |
508 | * No devm_ because we need to control shutdown order of children. |
509 | */ |
510 | switch (madera->type) { |
511 | case CS47L15: |
512 | madera->reset_errata = true; |
513 | break; |
514 | case CS47L35: |
515 | case CS47L90: |
516 | case CS47L91: |
517 | case CS42L92: |
518 | case CS47L92: |
519 | case CS47L93: |
520 | break; |
521 | case CS47L85: |
522 | case WM1840: |
523 | ret = mfd_add_devices(parent: madera->dev, PLATFORM_DEVID_NONE, |
524 | cells: madera_ldo1_devs, |
525 | ARRAY_SIZE(madera_ldo1_devs), |
526 | NULL, irq_base: 0, NULL); |
527 | if (ret) { |
528 | dev_err(dev, "Failed to add LDO1 child: %d\n" , ret); |
529 | return ret; |
530 | } |
531 | break; |
532 | default: |
533 | /* No point continuing if the type is unknown */ |
534 | dev_err(madera->dev, "Unknown device type %d\n" , madera->type); |
535 | return -ENODEV; |
536 | } |
537 | |
538 | ret = devm_regulator_bulk_get(dev, num_consumers: madera->num_core_supplies, |
539 | consumers: madera->core_supplies); |
540 | if (ret) { |
541 | dev_err(dev, "Failed to request core supplies: %d\n" , ret); |
542 | goto err_devs; |
543 | } |
544 | |
545 | /* |
546 | * Don't use devres here. If the regulator is one of our children it |
547 | * will already have been removed before devres cleanup on this mfd |
548 | * driver tries to call put() on it. We need control of shutdown order. |
549 | */ |
550 | madera->dcvdd = regulator_get(dev: madera->dev, id: "DCVDD" ); |
551 | if (IS_ERR(ptr: madera->dcvdd)) { |
552 | ret = PTR_ERR(ptr: madera->dcvdd); |
553 | dev_err(dev, "Failed to request DCVDD: %d\n" , ret); |
554 | goto err_devs; |
555 | } |
556 | |
557 | ret = regulator_bulk_enable(num_consumers: madera->num_core_supplies, |
558 | consumers: madera->core_supplies); |
559 | if (ret) { |
560 | dev_err(dev, "Failed to enable core supplies: %d\n" , ret); |
561 | goto err_dcvdd; |
562 | } |
563 | |
564 | if (madera->reset_errata) |
565 | madera_disable_hard_reset(madera); |
566 | |
567 | ret = regulator_enable(regulator: madera->dcvdd); |
568 | if (ret) { |
569 | dev_err(dev, "Failed to enable DCVDD: %d\n" , ret); |
570 | goto err_enable; |
571 | } |
572 | |
573 | if (madera->reset_errata) |
574 | usleep_range(ERRATA_DCVDD_MIN_US, ERRATA_DCVDD_MAX_US); |
575 | else |
576 | madera_disable_hard_reset(madera); |
577 | |
578 | regcache_cache_only(map: madera->regmap, enable: false); |
579 | regcache_cache_only(map: madera->regmap_32bit, enable: false); |
580 | |
581 | ret = madera_wait_for_boot_noack(madera); |
582 | if (ret) { |
583 | dev_err(madera->dev, "Device failed initial boot: %d\n" , ret); |
584 | goto err_reset; |
585 | } |
586 | |
587 | /* |
588 | * Now we can power up and verify that this is a chip we know about |
589 | * before we start doing any writes to its registers. |
590 | */ |
591 | ret = regmap_read(map: madera->regmap, MADERA_SOFTWARE_RESET, val: &hwid); |
592 | if (ret) { |
593 | dev_err(dev, "Failed to read ID register: %d\n" , ret); |
594 | goto err_reset; |
595 | } |
596 | |
597 | switch (hwid) { |
598 | case CS47L15_SILICON_ID: |
599 | if (IS_ENABLED(CONFIG_MFD_CS47L15)) { |
600 | switch (madera->type) { |
601 | case CS47L15: |
602 | patch_fn = &cs47l15_patch; |
603 | mfd_devs = cs47l15_devs; |
604 | n_devs = ARRAY_SIZE(cs47l15_devs); |
605 | break; |
606 | default: |
607 | break; |
608 | } |
609 | } |
610 | break; |
611 | case CS47L35_SILICON_ID: |
612 | if (IS_ENABLED(CONFIG_MFD_CS47L35)) { |
613 | switch (madera->type) { |
614 | case CS47L35: |
615 | patch_fn = cs47l35_patch; |
616 | mfd_devs = cs47l35_devs; |
617 | n_devs = ARRAY_SIZE(cs47l35_devs); |
618 | break; |
619 | default: |
620 | break; |
621 | } |
622 | } |
623 | break; |
624 | case CS47L85_SILICON_ID: |
625 | if (IS_ENABLED(CONFIG_MFD_CS47L85)) { |
626 | switch (madera->type) { |
627 | case CS47L85: |
628 | case WM1840: |
629 | patch_fn = cs47l85_patch; |
630 | mfd_devs = cs47l85_devs; |
631 | n_devs = ARRAY_SIZE(cs47l85_devs); |
632 | break; |
633 | default: |
634 | break; |
635 | } |
636 | } |
637 | break; |
638 | case CS47L90_SILICON_ID: |
639 | if (IS_ENABLED(CONFIG_MFD_CS47L90)) { |
640 | switch (madera->type) { |
641 | case CS47L90: |
642 | case CS47L91: |
643 | patch_fn = cs47l90_patch; |
644 | mfd_devs = cs47l90_devs; |
645 | n_devs = ARRAY_SIZE(cs47l90_devs); |
646 | break; |
647 | default: |
648 | break; |
649 | } |
650 | } |
651 | break; |
652 | case CS47L92_SILICON_ID: |
653 | if (IS_ENABLED(CONFIG_MFD_CS47L92)) { |
654 | switch (madera->type) { |
655 | case CS42L92: |
656 | case CS47L92: |
657 | case CS47L93: |
658 | patch_fn = cs47l92_patch; |
659 | mfd_devs = cs47l92_devs; |
660 | n_devs = ARRAY_SIZE(cs47l92_devs); |
661 | break; |
662 | default: |
663 | break; |
664 | } |
665 | } |
666 | break; |
667 | default: |
668 | dev_err(madera->dev, "Unknown device ID: %x\n" , hwid); |
669 | ret = -EINVAL; |
670 | goto err_reset; |
671 | } |
672 | |
673 | if (!n_devs) { |
674 | dev_err(madera->dev, "Device ID 0x%x not a %s\n" , hwid, |
675 | madera->type_name); |
676 | ret = -ENODEV; |
677 | goto err_reset; |
678 | } |
679 | |
680 | /* |
681 | * It looks like a device we support. If we don't have a hard reset |
682 | * we can now attempt a soft reset. |
683 | */ |
684 | if (!madera->pdata.reset || madera->reset_errata) { |
685 | ret = madera_soft_reset(madera); |
686 | if (ret) |
687 | goto err_reset; |
688 | } |
689 | |
690 | ret = madera_wait_for_boot(madera); |
691 | if (ret) { |
692 | dev_err(madera->dev, "Failed to clear boot done: %d\n" , ret); |
693 | goto err_reset; |
694 | } |
695 | |
696 | ret = regmap_read(map: madera->regmap, MADERA_HARDWARE_REVISION, |
697 | val: &madera->rev); |
698 | if (ret) { |
699 | dev_err(dev, "Failed to read revision register: %d\n" , ret); |
700 | goto err_reset; |
701 | } |
702 | madera->rev &= MADERA_HW_REVISION_MASK; |
703 | |
704 | dev_info(dev, "%s silicon revision %d\n" , madera->type_name, |
705 | madera->rev); |
706 | |
707 | /* Apply hardware patch */ |
708 | if (patch_fn) { |
709 | ret = patch_fn(madera); |
710 | if (ret) { |
711 | dev_err(madera->dev, "Failed to apply patch %d\n" , ret); |
712 | goto err_reset; |
713 | } |
714 | } |
715 | |
716 | /* Init 32k clock sourced from MCLK2 */ |
717 | ret = clk_prepare_enable(clk: madera->mclk[MADERA_MCLK2].clk); |
718 | if (ret) { |
719 | dev_err(madera->dev, "Failed to enable 32k clock: %d\n" , ret); |
720 | goto err_reset; |
721 | } |
722 | |
723 | ret = regmap_update_bits(map: madera->regmap, |
724 | MADERA_CLOCK_32K_1, |
725 | MADERA_CLK_32K_ENA_MASK | MADERA_CLK_32K_SRC_MASK, |
726 | MADERA_CLK_32K_ENA | MADERA_32KZ_MCLK2); |
727 | if (ret) { |
728 | dev_err(madera->dev, "Failed to init 32k clock: %d\n" , ret); |
729 | goto err_clock; |
730 | } |
731 | |
732 | pm_runtime_set_active(dev: madera->dev); |
733 | pm_runtime_enable(dev: madera->dev); |
734 | pm_runtime_set_autosuspend_delay(dev: madera->dev, delay: 100); |
735 | pm_runtime_use_autosuspend(dev: madera->dev); |
736 | |
737 | /* No devm_ because we need to control shutdown order of children */ |
738 | ret = mfd_add_devices(parent: madera->dev, PLATFORM_DEVID_NONE, |
739 | cells: mfd_devs, n_devs, |
740 | NULL, irq_base: 0, NULL); |
741 | if (ret) { |
742 | dev_err(madera->dev, "Failed to add subdevices: %d\n" , ret); |
743 | goto err_pm_runtime; |
744 | } |
745 | |
746 | return 0; |
747 | |
748 | err_pm_runtime: |
749 | pm_runtime_disable(dev: madera->dev); |
750 | err_clock: |
751 | clk_disable_unprepare(clk: madera->mclk[MADERA_MCLK2].clk); |
752 | err_reset: |
753 | madera_enable_hard_reset(madera); |
754 | regulator_disable(regulator: madera->dcvdd); |
755 | err_enable: |
756 | regulator_bulk_disable(num_consumers: madera->num_core_supplies, |
757 | consumers: madera->core_supplies); |
758 | err_dcvdd: |
759 | regulator_put(regulator: madera->dcvdd); |
760 | err_devs: |
761 | mfd_remove_devices(parent: dev); |
762 | |
763 | return ret; |
764 | } |
765 | EXPORT_SYMBOL_GPL(madera_dev_init); |
766 | |
767 | int madera_dev_exit(struct madera *madera) |
768 | { |
769 | /* Prevent any IRQs being serviced while we clean up */ |
770 | disable_irq(irq: madera->irq); |
771 | |
772 | pm_runtime_get_sync(dev: madera->dev); |
773 | |
774 | mfd_remove_devices(parent: madera->dev); |
775 | |
776 | pm_runtime_disable(dev: madera->dev); |
777 | |
778 | regulator_disable(regulator: madera->dcvdd); |
779 | regulator_put(regulator: madera->dcvdd); |
780 | |
781 | mfd_remove_devices_late(parent: madera->dev); |
782 | |
783 | pm_runtime_set_suspended(dev: madera->dev); |
784 | pm_runtime_put_noidle(dev: madera->dev); |
785 | |
786 | clk_disable_unprepare(clk: madera->mclk[MADERA_MCLK2].clk); |
787 | |
788 | madera_enable_hard_reset(madera); |
789 | |
790 | regulator_bulk_disable(num_consumers: madera->num_core_supplies, |
791 | consumers: madera->core_supplies); |
792 | return 0; |
793 | } |
794 | EXPORT_SYMBOL_GPL(madera_dev_exit); |
795 | |
796 | MODULE_DESCRIPTION("Madera core MFD driver" ); |
797 | MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>" ); |
798 | MODULE_LICENSE("GPL v2" ); |
799 | |