xref: /linux/drivers/mfd/arizona-core.c (revision cc47aed9a1ef8eadd7bf14e32117896777b5cc41)
13cc72986SMark Brown /*
23cc72986SMark Brown  * Arizona core driver
33cc72986SMark Brown  *
43cc72986SMark Brown  * Copyright 2012 Wolfson Microelectronics plc
53cc72986SMark Brown  *
63cc72986SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
73cc72986SMark Brown  *
83cc72986SMark Brown  * This program is free software; you can redistribute it and/or modify
93cc72986SMark Brown  * it under the terms of the GNU General Public License version 2 as
103cc72986SMark Brown  * published by the Free Software Foundation.
113cc72986SMark Brown  */
123cc72986SMark Brown 
133cc72986SMark Brown #include <linux/delay.h>
1459db9691SMark Brown #include <linux/err.h>
153cc72986SMark Brown #include <linux/gpio.h>
163cc72986SMark Brown #include <linux/interrupt.h>
173cc72986SMark Brown #include <linux/mfd/core.h>
183cc72986SMark Brown #include <linux/module.h>
19d781009cSMark Brown #include <linux/of.h>
20d781009cSMark Brown #include <linux/of_device.h>
21d781009cSMark Brown #include <linux/of_gpio.h>
223cc72986SMark Brown #include <linux/pm_runtime.h>
233cc72986SMark Brown #include <linux/regmap.h>
243cc72986SMark Brown #include <linux/regulator/consumer.h>
255927467dSMark Brown #include <linux/regulator/machine.h>
263cc72986SMark Brown #include <linux/slab.h>
273cc72986SMark Brown 
283cc72986SMark Brown #include <linux/mfd/arizona/core.h>
293cc72986SMark Brown #include <linux/mfd/arizona/registers.h>
303cc72986SMark Brown 
313cc72986SMark Brown #include "arizona.h"
323cc72986SMark Brown 
333cc72986SMark Brown static const char *wm5102_core_supplies[] = {
343cc72986SMark Brown 	"AVDD",
353cc72986SMark Brown 	"DBVDD1",
363cc72986SMark Brown };
373cc72986SMark Brown 
383cc72986SMark Brown int arizona_clk32k_enable(struct arizona *arizona)
393cc72986SMark Brown {
403cc72986SMark Brown 	int ret = 0;
413cc72986SMark Brown 
423cc72986SMark Brown 	mutex_lock(&arizona->clk_lock);
433cc72986SMark Brown 
443cc72986SMark Brown 	arizona->clk32k_ref++;
453cc72986SMark Brown 
46247fa192SMark Brown 	if (arizona->clk32k_ref == 1) {
47247fa192SMark Brown 		switch (arizona->pdata.clk32k_src) {
48247fa192SMark Brown 		case ARIZONA_32KZ_MCLK1:
49247fa192SMark Brown 			ret = pm_runtime_get_sync(arizona->dev);
50247fa192SMark Brown 			if (ret != 0)
51247fa192SMark Brown 				goto out;
52247fa192SMark Brown 			break;
53247fa192SMark Brown 		}
54247fa192SMark Brown 
553cc72986SMark Brown 		ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
563cc72986SMark Brown 					 ARIZONA_CLK_32K_ENA,
573cc72986SMark Brown 					 ARIZONA_CLK_32K_ENA);
58247fa192SMark Brown 	}
593cc72986SMark Brown 
60247fa192SMark Brown out:
613cc72986SMark Brown 	if (ret != 0)
623cc72986SMark Brown 		arizona->clk32k_ref--;
633cc72986SMark Brown 
643cc72986SMark Brown 	mutex_unlock(&arizona->clk_lock);
653cc72986SMark Brown 
663cc72986SMark Brown 	return ret;
673cc72986SMark Brown }
683cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
693cc72986SMark Brown 
703cc72986SMark Brown int arizona_clk32k_disable(struct arizona *arizona)
713cc72986SMark Brown {
723cc72986SMark Brown 	int ret = 0;
733cc72986SMark Brown 
743cc72986SMark Brown 	mutex_lock(&arizona->clk_lock);
753cc72986SMark Brown 
763cc72986SMark Brown 	BUG_ON(arizona->clk32k_ref <= 0);
773cc72986SMark Brown 
783cc72986SMark Brown 	arizona->clk32k_ref--;
793cc72986SMark Brown 
80247fa192SMark Brown 	if (arizona->clk32k_ref == 0) {
813cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
823cc72986SMark Brown 				   ARIZONA_CLK_32K_ENA, 0);
833cc72986SMark Brown 
84247fa192SMark Brown 		switch (arizona->pdata.clk32k_src) {
85247fa192SMark Brown 		case ARIZONA_32KZ_MCLK1:
86247fa192SMark Brown 			pm_runtime_put_sync(arizona->dev);
87247fa192SMark Brown 			break;
88247fa192SMark Brown 		}
89247fa192SMark Brown 	}
90247fa192SMark Brown 
913cc72986SMark Brown 	mutex_unlock(&arizona->clk_lock);
923cc72986SMark Brown 
933cc72986SMark Brown 	return ret;
943cc72986SMark Brown }
953cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
963cc72986SMark Brown 
973cc72986SMark Brown static irqreturn_t arizona_clkgen_err(int irq, void *data)
983cc72986SMark Brown {
993cc72986SMark Brown 	struct arizona *arizona = data;
1003cc72986SMark Brown 
1013cc72986SMark Brown 	dev_err(arizona->dev, "CLKGEN error\n");
1023cc72986SMark Brown 
1033cc72986SMark Brown 	return IRQ_HANDLED;
1043cc72986SMark Brown }
1053cc72986SMark Brown 
1063cc72986SMark Brown static irqreturn_t arizona_underclocked(int irq, void *data)
1073cc72986SMark Brown {
1083cc72986SMark Brown 	struct arizona *arizona = data;
1093cc72986SMark Brown 	unsigned int val;
1103cc72986SMark Brown 	int ret;
1113cc72986SMark Brown 
1123cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8,
1133cc72986SMark Brown 			  &val);
1143cc72986SMark Brown 	if (ret != 0) {
1153cc72986SMark Brown 		dev_err(arizona->dev, "Failed to read underclock status: %d\n",
1163cc72986SMark Brown 			ret);
1173cc72986SMark Brown 		return IRQ_NONE;
1183cc72986SMark Brown 	}
1193cc72986SMark Brown 
1203cc72986SMark Brown 	if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
1213cc72986SMark Brown 		dev_err(arizona->dev, "AIF3 underclocked\n");
1223cc72986SMark Brown 	if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
1233ebef34dSCharles Keepax 		dev_err(arizona->dev, "AIF2 underclocked\n");
1243ebef34dSCharles Keepax 	if (val & ARIZONA_AIF1_UNDERCLOCKED_STS)
1253cc72986SMark Brown 		dev_err(arizona->dev, "AIF1 underclocked\n");
1266e440d27SCharles Keepax 	if (val & ARIZONA_ISRC3_UNDERCLOCKED_STS)
1276e440d27SCharles Keepax 		dev_err(arizona->dev, "ISRC3 underclocked\n");
1283cc72986SMark Brown 	if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
1293cc72986SMark Brown 		dev_err(arizona->dev, "ISRC2 underclocked\n");
1303cc72986SMark Brown 	if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
1313cc72986SMark Brown 		dev_err(arizona->dev, "ISRC1 underclocked\n");
1323cc72986SMark Brown 	if (val & ARIZONA_FX_UNDERCLOCKED_STS)
1333cc72986SMark Brown 		dev_err(arizona->dev, "FX underclocked\n");
1343cc72986SMark Brown 	if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
1353cc72986SMark Brown 		dev_err(arizona->dev, "ASRC underclocked\n");
1363cc72986SMark Brown 	if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
1373cc72986SMark Brown 		dev_err(arizona->dev, "DAC underclocked\n");
1383cc72986SMark Brown 	if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
1393cc72986SMark Brown 		dev_err(arizona->dev, "ADC underclocked\n");
1403cc72986SMark Brown 	if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
141648a9880SMark Brown 		dev_err(arizona->dev, "Mixer dropped sample\n");
1423cc72986SMark Brown 
1433cc72986SMark Brown 	return IRQ_HANDLED;
1443cc72986SMark Brown }
1453cc72986SMark Brown 
1463cc72986SMark Brown static irqreturn_t arizona_overclocked(int irq, void *data)
1473cc72986SMark Brown {
1483cc72986SMark Brown 	struct arizona *arizona = data;
1493cc72986SMark Brown 	unsigned int val[2];
1503cc72986SMark Brown 	int ret;
1513cc72986SMark Brown 
1523cc72986SMark Brown 	ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
1533cc72986SMark Brown 			       &val[0], 2);
1543cc72986SMark Brown 	if (ret != 0) {
1553cc72986SMark Brown 		dev_err(arizona->dev, "Failed to read overclock status: %d\n",
1563cc72986SMark Brown 			ret);
1573cc72986SMark Brown 		return IRQ_NONE;
1583cc72986SMark Brown 	}
1593cc72986SMark Brown 
1603cc72986SMark Brown 	if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
1613cc72986SMark Brown 		dev_err(arizona->dev, "PWM overclocked\n");
1623cc72986SMark Brown 	if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
1633cc72986SMark Brown 		dev_err(arizona->dev, "FX core overclocked\n");
1643cc72986SMark Brown 	if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
1653cc72986SMark Brown 		dev_err(arizona->dev, "DAC SYS overclocked\n");
1663cc72986SMark Brown 	if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
1673cc72986SMark Brown 		dev_err(arizona->dev, "DAC WARP overclocked\n");
1683cc72986SMark Brown 	if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
1693cc72986SMark Brown 		dev_err(arizona->dev, "ADC overclocked\n");
1703cc72986SMark Brown 	if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
1713cc72986SMark Brown 		dev_err(arizona->dev, "Mixer overclocked\n");
1723cc72986SMark Brown 	if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
1733cc72986SMark Brown 		dev_err(arizona->dev, "AIF3 overclocked\n");
1743cc72986SMark Brown 	if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
1753cc72986SMark Brown 		dev_err(arizona->dev, "AIF2 overclocked\n");
1763cc72986SMark Brown 	if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
1773cc72986SMark Brown 		dev_err(arizona->dev, "AIF1 overclocked\n");
1783cc72986SMark Brown 	if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
1793cc72986SMark Brown 		dev_err(arizona->dev, "Pad control overclocked\n");
1803cc72986SMark Brown 
1813cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
1823cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
1833cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
1843cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus async overclocked\n");
1853cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
1863cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus sync overclocked\n");
1873cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
1883cc72986SMark Brown 		dev_err(arizona->dev, "ASRC async system overclocked\n");
1893cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
1903cc72986SMark Brown 		dev_err(arizona->dev, "ASRC async WARP overclocked\n");
1913cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
1923cc72986SMark Brown 		dev_err(arizona->dev, "ASRC sync system overclocked\n");
1933cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
1943cc72986SMark Brown 		dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
1953cc72986SMark Brown 	if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
1963cc72986SMark Brown 		dev_err(arizona->dev, "DSP1 overclocked\n");
1976e440d27SCharles Keepax 	if (val[1] & ARIZONA_ISRC3_OVERCLOCKED_STS)
1986e440d27SCharles Keepax 		dev_err(arizona->dev, "ISRC3 overclocked\n");
1993cc72986SMark Brown 	if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
2003cc72986SMark Brown 		dev_err(arizona->dev, "ISRC2 overclocked\n");
2013cc72986SMark Brown 	if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
2023cc72986SMark Brown 		dev_err(arizona->dev, "ISRC1 overclocked\n");
2033cc72986SMark Brown 
2043cc72986SMark Brown 	return IRQ_HANDLED;
2053cc72986SMark Brown }
2063cc72986SMark Brown 
2079d53dfdcSCharles Keepax static int arizona_poll_reg(struct arizona *arizona,
2089d53dfdcSCharles Keepax 			    int timeout, unsigned int reg,
2099d53dfdcSCharles Keepax 			    unsigned int mask, unsigned int target)
2109d53dfdcSCharles Keepax {
2119d53dfdcSCharles Keepax 	unsigned int val = 0;
2129d53dfdcSCharles Keepax 	int ret, i;
2139d53dfdcSCharles Keepax 
2149d53dfdcSCharles Keepax 	for (i = 0; i < timeout; i++) {
2159d53dfdcSCharles Keepax 		ret = regmap_read(arizona->regmap, reg, &val);
2169d53dfdcSCharles Keepax 		if (ret != 0) {
2179d53dfdcSCharles Keepax 			dev_err(arizona->dev, "Failed to read reg %u: %d\n",
2189d53dfdcSCharles Keepax 				reg, ret);
2199d53dfdcSCharles Keepax 			continue;
2209d53dfdcSCharles Keepax 		}
2219d53dfdcSCharles Keepax 
2229d53dfdcSCharles Keepax 		if ((val & mask) == target)
2239d53dfdcSCharles Keepax 			return 0;
2249d53dfdcSCharles Keepax 
2259d53dfdcSCharles Keepax 		msleep(1);
2269d53dfdcSCharles Keepax 	}
2279d53dfdcSCharles Keepax 
2289d53dfdcSCharles Keepax 	dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val);
2299d53dfdcSCharles Keepax 	return -ETIMEDOUT;
2309d53dfdcSCharles Keepax }
2319d53dfdcSCharles Keepax 
2323cc72986SMark Brown static int arizona_wait_for_boot(struct arizona *arizona)
2333cc72986SMark Brown {
2349d53dfdcSCharles Keepax 	int ret;
2353cc72986SMark Brown 
2363cc72986SMark Brown 	/*
2373cc72986SMark Brown 	 * We can't use an interrupt as we need to runtime resume to do so,
2383cc72986SMark Brown 	 * we won't race with the interrupt handler as it'll be blocked on
2393cc72986SMark Brown 	 * runtime resume.
2403cc72986SMark Brown 	 */
2419d53dfdcSCharles Keepax 	ret = arizona_poll_reg(arizona, 5, ARIZONA_INTERRUPT_RAW_STATUS_5,
2429d53dfdcSCharles Keepax 			       ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS);
2433cc72986SMark Brown 
2449d53dfdcSCharles Keepax 	if (!ret)
2453cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
2463cc72986SMark Brown 			     ARIZONA_BOOT_DONE_STS);
2473cc72986SMark Brown 
2483cc72986SMark Brown 	pm_runtime_mark_last_busy(arizona->dev);
2493cc72986SMark Brown 
2509d53dfdcSCharles Keepax 	return ret;
2513cc72986SMark Brown }
2523cc72986SMark Brown 
253e80436bbSCharles Keepax static int arizona_apply_hardware_patch(struct arizona* arizona)
254e80436bbSCharles Keepax {
255e80436bbSCharles Keepax 	unsigned int fll, sysclk;
256e80436bbSCharles Keepax 	int ret, err;
257e80436bbSCharles Keepax 
258e80436bbSCharles Keepax 	/* Cache existing FLL and SYSCLK settings */
259e80436bbSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll);
260e80436bbSCharles Keepax 	if (ret != 0) {
261e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
262e80436bbSCharles Keepax 			ret);
263e80436bbSCharles Keepax 		return ret;
264e80436bbSCharles Keepax 	}
265e80436bbSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk);
266e80436bbSCharles Keepax 	if (ret != 0) {
267e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
268e80436bbSCharles Keepax 			ret);
269e80436bbSCharles Keepax 		return ret;
270e80436bbSCharles Keepax 	}
271e80436bbSCharles Keepax 
272e80436bbSCharles Keepax 	/* Start up SYSCLK using the FLL in free running mode */
273e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
274e80436bbSCharles Keepax 			ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
275e80436bbSCharles Keepax 	if (ret != 0) {
276e80436bbSCharles Keepax 		dev_err(arizona->dev,
277e80436bbSCharles Keepax 			"Failed to start FLL in freerunning mode: %d\n",
278e80436bbSCharles Keepax 			ret);
279e80436bbSCharles Keepax 		return ret;
280e80436bbSCharles Keepax 	}
281e80436bbSCharles Keepax 	ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
282e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS,
283e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS);
284e80436bbSCharles Keepax 	if (ret != 0) {
285e80436bbSCharles Keepax 		ret = -ETIMEDOUT;
286e80436bbSCharles Keepax 		goto err_fll;
287e80436bbSCharles Keepax 	}
288e80436bbSCharles Keepax 
289e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
290e80436bbSCharles Keepax 	if (ret != 0) {
291e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
292e80436bbSCharles Keepax 		goto err_fll;
293e80436bbSCharles Keepax 	}
294e80436bbSCharles Keepax 
295e80436bbSCharles Keepax 	/* Start the write sequencer and wait for it to finish */
296e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
297e80436bbSCharles Keepax 			ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
298e80436bbSCharles Keepax 	if (ret != 0) {
299e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
300e80436bbSCharles Keepax 			ret);
301e80436bbSCharles Keepax 		goto err_sysclk;
302e80436bbSCharles Keepax 	}
303e80436bbSCharles Keepax 	ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
304e80436bbSCharles Keepax 			       ARIZONA_WSEQ_BUSY, 0);
305e80436bbSCharles Keepax 	if (ret != 0) {
306e80436bbSCharles Keepax 		regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
307e80436bbSCharles Keepax 				ARIZONA_WSEQ_ABORT);
308e80436bbSCharles Keepax 		ret = -ETIMEDOUT;
309e80436bbSCharles Keepax 	}
310e80436bbSCharles Keepax 
311e80436bbSCharles Keepax err_sysclk:
312e80436bbSCharles Keepax 	err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk);
313e80436bbSCharles Keepax 	if (err != 0) {
314e80436bbSCharles Keepax 		dev_err(arizona->dev,
315e80436bbSCharles Keepax 			"Failed to re-apply old SYSCLK settings: %d\n",
316e80436bbSCharles Keepax 			err);
317e80436bbSCharles Keepax 	}
318e80436bbSCharles Keepax 
319e80436bbSCharles Keepax err_fll:
320e80436bbSCharles Keepax 	err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll);
321e80436bbSCharles Keepax 	if (err != 0) {
322e80436bbSCharles Keepax 		dev_err(arizona->dev,
323e80436bbSCharles Keepax 			"Failed to re-apply old FLL settings: %d\n",
324e80436bbSCharles Keepax 			err);
325e80436bbSCharles Keepax 	}
326e80436bbSCharles Keepax 
327e80436bbSCharles Keepax 	if (ret != 0)
328e80436bbSCharles Keepax 		return ret;
329e80436bbSCharles Keepax 	else
330e80436bbSCharles Keepax 		return err;
331e80436bbSCharles Keepax }
332e80436bbSCharles Keepax 
3333cc72986SMark Brown #ifdef CONFIG_PM_RUNTIME
3343cc72986SMark Brown static int arizona_runtime_resume(struct device *dev)
3353cc72986SMark Brown {
3363cc72986SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
3373cc72986SMark Brown 	int ret;
3383cc72986SMark Brown 
339508c8299SMark Brown 	dev_dbg(arizona->dev, "Leaving AoD mode\n");
340508c8299SMark Brown 
34159db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
34259db9691SMark Brown 	if (ret != 0) {
34359db9691SMark Brown 		dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
34459db9691SMark Brown 		return ret;
34559db9691SMark Brown 	}
3463cc72986SMark Brown 
3473cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
3483cc72986SMark Brown 
3494c9bb8bcSCharles Keepax 	switch (arizona->type) {
3504c9bb8bcSCharles Keepax 	case WM5102:
3515927467dSMark Brown 		if (arizona->external_dcvdd) {
3525927467dSMark Brown 			ret = regmap_update_bits(arizona->regmap,
3535927467dSMark Brown 						 ARIZONA_ISOLATION_CONTROL,
3545927467dSMark Brown 						 ARIZONA_ISOLATE_DCVDD1, 0);
3555927467dSMark Brown 			if (ret != 0) {
3565927467dSMark Brown 				dev_err(arizona->dev,
3575927467dSMark Brown 					"Failed to connect DCVDD: %d\n", ret);
3585927467dSMark Brown 				goto err;
3595927467dSMark Brown 			}
3605927467dSMark Brown 		}
3615927467dSMark Brown 
3624c9bb8bcSCharles Keepax 		ret = wm5102_patch(arizona);
3634c9bb8bcSCharles Keepax 		if (ret != 0) {
3644c9bb8bcSCharles Keepax 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
3654c9bb8bcSCharles Keepax 				ret);
3664c9bb8bcSCharles Keepax 			goto err;
3674c9bb8bcSCharles Keepax 		}
368e80436bbSCharles Keepax 
369e80436bbSCharles Keepax 		ret = arizona_apply_hardware_patch(arizona);
370e80436bbSCharles Keepax 		if (ret != 0) {
371e80436bbSCharles Keepax 			dev_err(arizona->dev,
372e80436bbSCharles Keepax 				"Failed to apply hardware patch: %d\n",
373e80436bbSCharles Keepax 				ret);
374e80436bbSCharles Keepax 			goto err;
375e80436bbSCharles Keepax 		}
376e80436bbSCharles Keepax 		break;
377e80436bbSCharles Keepax 	default:
37812bb68edSCharles Keepax 		ret = arizona_wait_for_boot(arizona);
37912bb68edSCharles Keepax 		if (ret != 0) {
38012bb68edSCharles Keepax 			goto err;
38112bb68edSCharles Keepax 		}
38212bb68edSCharles Keepax 
3835927467dSMark Brown 		if (arizona->external_dcvdd) {
3845927467dSMark Brown 			ret = regmap_update_bits(arizona->regmap,
3855927467dSMark Brown 						 ARIZONA_ISOLATION_CONTROL,
3865927467dSMark Brown 						 ARIZONA_ISOLATE_DCVDD1, 0);
3875927467dSMark Brown 			if (ret != 0) {
3885927467dSMark Brown 				dev_err(arizona->dev,
3895927467dSMark Brown 					"Failed to connect DCVDD: %d\n", ret);
3905927467dSMark Brown 				goto err;
3915927467dSMark Brown 			}
3925927467dSMark Brown 		}
393e80436bbSCharles Keepax 		break;
3944c9bb8bcSCharles Keepax 	}
3954c9bb8bcSCharles Keepax 
396d9d03496SCharles Keepax 	switch (arizona->type) {
397d9d03496SCharles Keepax 	case WM5102:
398d9d03496SCharles Keepax 		ret = wm5102_patch(arizona);
399d9d03496SCharles Keepax 		if (ret != 0) {
400d9d03496SCharles Keepax 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
401d9d03496SCharles Keepax 				ret);
402d9d03496SCharles Keepax 			goto err;
403d9d03496SCharles Keepax 		}
404d9d03496SCharles Keepax 	default:
405d9d03496SCharles Keepax 		break;
406d9d03496SCharles Keepax 	}
407d9d03496SCharles Keepax 
4089270bdf5SMark Brown 	ret = regcache_sync(arizona->regmap);
4099270bdf5SMark Brown 	if (ret != 0) {
4109270bdf5SMark Brown 		dev_err(arizona->dev, "Failed to restore register cache\n");
4114816bd1cSMark Brown 		goto err;
4129270bdf5SMark Brown 	}
4133cc72986SMark Brown 
4143cc72986SMark Brown 	return 0;
4154816bd1cSMark Brown 
4164816bd1cSMark Brown err:
4174816bd1cSMark Brown 	regcache_cache_only(arizona->regmap, true);
4184816bd1cSMark Brown 	regulator_disable(arizona->dcvdd);
4194816bd1cSMark Brown 	return ret;
4203cc72986SMark Brown }
4213cc72986SMark Brown 
4223cc72986SMark Brown static int arizona_runtime_suspend(struct device *dev)
4233cc72986SMark Brown {
4243cc72986SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
4255927467dSMark Brown 	int ret;
4263cc72986SMark Brown 
427508c8299SMark Brown 	dev_dbg(arizona->dev, "Entering AoD mode\n");
428508c8299SMark Brown 
4295927467dSMark Brown 	if (arizona->external_dcvdd) {
4305927467dSMark Brown 		ret = regmap_update_bits(arizona->regmap,
4315927467dSMark Brown 					 ARIZONA_ISOLATION_CONTROL,
4325927467dSMark Brown 					 ARIZONA_ISOLATE_DCVDD1,
4335927467dSMark Brown 					 ARIZONA_ISOLATE_DCVDD1);
4345927467dSMark Brown 		if (ret != 0) {
4355927467dSMark Brown 			dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n",
4365927467dSMark Brown 				ret);
4375927467dSMark Brown 			return ret;
4385927467dSMark Brown 		}
4395927467dSMark Brown 	}
4405927467dSMark Brown 
4413cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
4423cc72986SMark Brown 	regcache_mark_dirty(arizona->regmap);
443e293e847SCharles Keepax 	regulator_disable(arizona->dcvdd);
4443cc72986SMark Brown 
4453cc72986SMark Brown 	return 0;
4463cc72986SMark Brown }
4473cc72986SMark Brown #endif
4483cc72986SMark Brown 
449dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP
45067c99296SMark Brown static int arizona_suspend(struct device *dev)
45167c99296SMark Brown {
45267c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
45367c99296SMark Brown 
45467c99296SMark Brown 	dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
45567c99296SMark Brown 	disable_irq(arizona->irq);
45667c99296SMark Brown 
45767c99296SMark Brown 	return 0;
45867c99296SMark Brown }
45967c99296SMark Brown 
46067c99296SMark Brown static int arizona_suspend_late(struct device *dev)
46167c99296SMark Brown {
46267c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
46367c99296SMark Brown 
46467c99296SMark Brown 	dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
46567c99296SMark Brown 	enable_irq(arizona->irq);
46667c99296SMark Brown 
46767c99296SMark Brown 	return 0;
46867c99296SMark Brown }
46967c99296SMark Brown 
470dc781d0eSMark Brown static int arizona_resume_noirq(struct device *dev)
471dc781d0eSMark Brown {
472dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
473dc781d0eSMark Brown 
474dc781d0eSMark Brown 	dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
475dc781d0eSMark Brown 	disable_irq(arizona->irq);
476dc781d0eSMark Brown 
477dc781d0eSMark Brown 	return 0;
478dc781d0eSMark Brown }
479dc781d0eSMark Brown 
480dc781d0eSMark Brown static int arizona_resume(struct device *dev)
481dc781d0eSMark Brown {
482dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
483dc781d0eSMark Brown 
484dc781d0eSMark Brown 	dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n");
485dc781d0eSMark Brown 	enable_irq(arizona->irq);
486dc781d0eSMark Brown 
487dc781d0eSMark Brown 	return 0;
488dc781d0eSMark Brown }
489dc781d0eSMark Brown #endif
490dc781d0eSMark Brown 
4913cc72986SMark Brown const struct dev_pm_ops arizona_pm_ops = {
4923cc72986SMark Brown 	SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
4933cc72986SMark Brown 			   arizona_runtime_resume,
4943cc72986SMark Brown 			   NULL)
49567c99296SMark Brown 	SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
496dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP
49767c99296SMark Brown 	.suspend_late = arizona_suspend_late,
498dc781d0eSMark Brown 	.resume_noirq = arizona_resume_noirq,
499dc781d0eSMark Brown #endif
5003cc72986SMark Brown };
5013cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_pm_ops);
5023cc72986SMark Brown 
503d781009cSMark Brown #ifdef CONFIG_OF
504942786e6SLee Jones unsigned long arizona_of_get_type(struct device *dev)
505d781009cSMark Brown {
506d781009cSMark Brown 	const struct of_device_id *id = of_match_device(arizona_of_match, dev);
507d781009cSMark Brown 
508d781009cSMark Brown 	if (id)
509942786e6SLee Jones 		return (unsigned long)id->data;
510d781009cSMark Brown 	else
511d781009cSMark Brown 		return 0;
512d781009cSMark Brown }
513d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_get_type);
514d781009cSMark Brown 
515e4fcb1d6SCharles Keepax int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop,
516e4fcb1d6SCharles Keepax 			      bool mandatory)
517e4fcb1d6SCharles Keepax {
518e4fcb1d6SCharles Keepax 	int gpio;
519e4fcb1d6SCharles Keepax 
520e4fcb1d6SCharles Keepax 	gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0);
521e4fcb1d6SCharles Keepax 	if (gpio < 0) {
522e4fcb1d6SCharles Keepax 		if (mandatory)
523e4fcb1d6SCharles Keepax 			dev_err(arizona->dev,
524e4fcb1d6SCharles Keepax 				"Mandatory DT gpio %s missing/malformed: %d\n",
525e4fcb1d6SCharles Keepax 				prop, gpio);
526e4fcb1d6SCharles Keepax 
527e4fcb1d6SCharles Keepax 		gpio = 0;
528e4fcb1d6SCharles Keepax 	}
529e4fcb1d6SCharles Keepax 
530e4fcb1d6SCharles Keepax 	return gpio;
531e4fcb1d6SCharles Keepax }
532e4fcb1d6SCharles Keepax EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio);
533e4fcb1d6SCharles Keepax 
534d781009cSMark Brown static int arizona_of_get_core_pdata(struct arizona *arizona)
535d781009cSMark Brown {
536e4fcb1d6SCharles Keepax 	struct arizona_pdata *pdata = &arizona->pdata;
537*cc47aed9SInha Song 	struct property *prop;
538*cc47aed9SInha Song 	const __be32 *cur;
539*cc47aed9SInha Song 	u32 val;
540d781009cSMark Brown 	int ret, i;
541*cc47aed9SInha Song 	int count = 0;
542d781009cSMark Brown 
543e4fcb1d6SCharles Keepax 	pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true);
544d781009cSMark Brown 
545d781009cSMark Brown 	ret = of_property_read_u32_array(arizona->dev->of_node,
546d781009cSMark Brown 					 "wlf,gpio-defaults",
547d781009cSMark Brown 					 arizona->pdata.gpio_defaults,
548d781009cSMark Brown 					 ARRAY_SIZE(arizona->pdata.gpio_defaults));
549d781009cSMark Brown 	if (ret >= 0) {
550d781009cSMark Brown 		/*
551d781009cSMark Brown 		 * All values are literal except out of range values
552d781009cSMark Brown 		 * which are chip default, translate into platform
553d781009cSMark Brown 		 * data which uses 0 as chip default and out of range
554d781009cSMark Brown 		 * as zero.
555d781009cSMark Brown 		 */
556d781009cSMark Brown 		for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
557d781009cSMark Brown 			if (arizona->pdata.gpio_defaults[i] > 0xffff)
558d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0;
55991c73935SCharles Keepax 			else if (arizona->pdata.gpio_defaults[i] == 0)
560d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0x10000;
561d781009cSMark Brown 		}
562d781009cSMark Brown 	} else {
563d781009cSMark Brown 		dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
564d781009cSMark Brown 			ret);
565d781009cSMark Brown 	}
566d781009cSMark Brown 
567*cc47aed9SInha Song 	of_property_for_each_u32(arizona->dev->of_node, "wlf,inmode", prop,
568*cc47aed9SInha Song 				 cur, val) {
569*cc47aed9SInha Song 		if (count == ARRAY_SIZE(arizona->pdata.inmode))
570*cc47aed9SInha Song 			break;
571*cc47aed9SInha Song 
572*cc47aed9SInha Song 		arizona->pdata.inmode[count] = val;
573*cc47aed9SInha Song 		count++;
574*cc47aed9SInha Song 	}
575*cc47aed9SInha Song 
576d781009cSMark Brown 	return 0;
577d781009cSMark Brown }
578d781009cSMark Brown 
579d781009cSMark Brown const struct of_device_id arizona_of_match[] = {
580d781009cSMark Brown 	{ .compatible = "wlf,wm5102", .data = (void *)WM5102 },
581d781009cSMark Brown 	{ .compatible = "wlf,wm5110", .data = (void *)WM5110 },
582dc7d4863SCharles Keepax 	{ .compatible = "wlf,wm8997", .data = (void *)WM8997 },
583d781009cSMark Brown 	{},
584d781009cSMark Brown };
585d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_match);
586d781009cSMark Brown #else
587d781009cSMark Brown static inline int arizona_of_get_core_pdata(struct arizona *arizona)
588d781009cSMark Brown {
589d781009cSMark Brown 	return 0;
590d781009cSMark Brown }
591d781009cSMark Brown #endif
592d781009cSMark Brown 
5935ac98553SGeert Uytterhoeven static const struct mfd_cell early_devs[] = {
5943cc72986SMark Brown 	{ .name = "arizona-ldo1" },
5953cc72986SMark Brown };
5963cc72986SMark Brown 
59732dadef2SCharles Keepax static const char *wm5102_supplies[] = {
5985fc6c396SCharles Keepax 	"MICVDD",
59932dadef2SCharles Keepax 	"DBVDD2",
60032dadef2SCharles Keepax 	"DBVDD3",
60132dadef2SCharles Keepax 	"CPVDD",
60232dadef2SCharles Keepax 	"SPKVDDL",
60332dadef2SCharles Keepax 	"SPKVDDR",
60432dadef2SCharles Keepax };
60532dadef2SCharles Keepax 
6065ac98553SGeert Uytterhoeven static const struct mfd_cell wm5102_devs[] = {
607d7768111SMark Brown 	{ .name = "arizona-micsupp" },
6085fc6c396SCharles Keepax 	{
6095fc6c396SCharles Keepax 		.name = "arizona-extcon",
6105fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
6115fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6125fc6c396SCharles Keepax 	},
6133cc72986SMark Brown 	{ .name = "arizona-gpio" },
614503b1cacSMark Brown 	{ .name = "arizona-haptics" },
6153cc72986SMark Brown 	{ .name = "arizona-pwm" },
61632dadef2SCharles Keepax 	{
61732dadef2SCharles Keepax 		.name = "wm5102-codec",
61832dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
61932dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
62032dadef2SCharles Keepax 	},
6213cc72986SMark Brown };
6223cc72986SMark Brown 
6235ac98553SGeert Uytterhoeven static const struct mfd_cell wm5110_devs[] = {
624d7768111SMark Brown 	{ .name = "arizona-micsupp" },
6255fc6c396SCharles Keepax 	{
6265fc6c396SCharles Keepax 		.name = "arizona-extcon",
6275fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
6285fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6295fc6c396SCharles Keepax 	},
630e102befeSMark Brown 	{ .name = "arizona-gpio" },
631503b1cacSMark Brown 	{ .name = "arizona-haptics" },
632e102befeSMark Brown 	{ .name = "arizona-pwm" },
63332dadef2SCharles Keepax 	{
63432dadef2SCharles Keepax 		.name = "wm5110-codec",
63532dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
63632dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
63732dadef2SCharles Keepax 	},
63832dadef2SCharles Keepax };
63932dadef2SCharles Keepax 
64032dadef2SCharles Keepax static const char *wm8997_supplies[] = {
641996c2d4fSCharles Keepax 	"MICVDD",
64232dadef2SCharles Keepax 	"DBVDD2",
64332dadef2SCharles Keepax 	"CPVDD",
64432dadef2SCharles Keepax 	"SPKVDD",
645e102befeSMark Brown };
646e102befeSMark Brown 
6475ac98553SGeert Uytterhoeven static const struct mfd_cell wm8997_devs[] = {
648dc7d4863SCharles Keepax 	{ .name = "arizona-micsupp" },
6495fc6c396SCharles Keepax 	{
6505fc6c396SCharles Keepax 		.name = "arizona-extcon",
6515fc6c396SCharles Keepax 		.parent_supplies = wm8997_supplies,
6525fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6535fc6c396SCharles Keepax 	},
654dc7d4863SCharles Keepax 	{ .name = "arizona-gpio" },
655dc7d4863SCharles Keepax 	{ .name = "arizona-haptics" },
656dc7d4863SCharles Keepax 	{ .name = "arizona-pwm" },
65732dadef2SCharles Keepax 	{
65832dadef2SCharles Keepax 		.name = "wm8997-codec",
65932dadef2SCharles Keepax 		.parent_supplies = wm8997_supplies,
66032dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm8997_supplies),
66132dadef2SCharles Keepax 	},
662dc7d4863SCharles Keepax };
663dc7d4863SCharles Keepax 
664f791be49SBill Pemberton int arizona_dev_init(struct arizona *arizona)
6653cc72986SMark Brown {
6663cc72986SMark Brown 	struct device *dev = arizona->dev;
6673cc72986SMark Brown 	const char *type_name;
6683cc72986SMark Brown 	unsigned int reg, val;
66962d62b59SMark Brown 	int (*apply_patch)(struct arizona *) = NULL;
6703cc72986SMark Brown 	int ret, i;
6713cc72986SMark Brown 
6723cc72986SMark Brown 	dev_set_drvdata(arizona->dev, arizona);
6733cc72986SMark Brown 	mutex_init(&arizona->clk_lock);
6743cc72986SMark Brown 
6753cc72986SMark Brown 	if (dev_get_platdata(arizona->dev))
6763cc72986SMark Brown 		memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
6773cc72986SMark Brown 		       sizeof(arizona->pdata));
67822d7dc8aSLee Jones 	else
67922d7dc8aSLee Jones 		arizona_of_get_core_pdata(arizona);
6803cc72986SMark Brown 
6813cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
6823cc72986SMark Brown 
6833cc72986SMark Brown 	switch (arizona->type) {
6843cc72986SMark Brown 	case WM5102:
685e102befeSMark Brown 	case WM5110:
686dc7d4863SCharles Keepax 	case WM8997:
6873cc72986SMark Brown 		for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
6883cc72986SMark Brown 			arizona->core_supplies[i].supply
6893cc72986SMark Brown 				= wm5102_core_supplies[i];
6903cc72986SMark Brown 		arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
6913cc72986SMark Brown 		break;
6923cc72986SMark Brown 	default:
6933cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device type %d\n",
6943cc72986SMark Brown 			arizona->type);
6953cc72986SMark Brown 		return -EINVAL;
6963cc72986SMark Brown 	}
6973cc72986SMark Brown 
6984a8c475fSCharles Keepax 	/* Mark DCVDD as external, LDO1 driver will clear if internal */
6994a8c475fSCharles Keepax 	arizona->external_dcvdd = true;
7004a8c475fSCharles Keepax 
7013cc72986SMark Brown 	ret = mfd_add_devices(arizona->dev, -1, early_devs,
7020848c94fSMark Brown 			      ARRAY_SIZE(early_devs), NULL, 0, NULL);
7033cc72986SMark Brown 	if (ret != 0) {
7043cc72986SMark Brown 		dev_err(dev, "Failed to add early children: %d\n", ret);
7053cc72986SMark Brown 		return ret;
7063cc72986SMark Brown 	}
7073cc72986SMark Brown 
7083cc72986SMark Brown 	ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
7093cc72986SMark Brown 				      arizona->core_supplies);
7103cc72986SMark Brown 	if (ret != 0) {
7113cc72986SMark Brown 		dev_err(dev, "Failed to request core supplies: %d\n",
7123cc72986SMark Brown 			ret);
7133cc72986SMark Brown 		goto err_early;
7143cc72986SMark Brown 	}
7153cc72986SMark Brown 
7160c2d0ffbSCharles Keepax 	/**
7170c2d0ffbSCharles Keepax 	 * Don't use devres here because the only device we have to get
7180c2d0ffbSCharles Keepax 	 * against is the MFD device and DCVDD will likely be supplied by
7190c2d0ffbSCharles Keepax 	 * one of its children. Meaning that the regulator will be
7200c2d0ffbSCharles Keepax 	 * destroyed by the time devres calls regulator put.
7210c2d0ffbSCharles Keepax 	 */
722e6021511SCharles Keepax 	arizona->dcvdd = regulator_get(arizona->dev, "DCVDD");
72359db9691SMark Brown 	if (IS_ERR(arizona->dcvdd)) {
72459db9691SMark Brown 		ret = PTR_ERR(arizona->dcvdd);
72559db9691SMark Brown 		dev_err(dev, "Failed to request DCVDD: %d\n", ret);
72659db9691SMark Brown 		goto err_early;
72759db9691SMark Brown 	}
72859db9691SMark Brown 
72987d3af4aSMark Brown 	if (arizona->pdata.reset) {
73087d3af4aSMark Brown 		/* Start out with /RESET low to put the chip into reset */
73187d3af4aSMark Brown 		ret = gpio_request_one(arizona->pdata.reset,
73287d3af4aSMark Brown 				       GPIOF_DIR_OUT | GPIOF_INIT_LOW,
73387d3af4aSMark Brown 				       "arizona /RESET");
73487d3af4aSMark Brown 		if (ret != 0) {
73587d3af4aSMark Brown 			dev_err(dev, "Failed to request /RESET: %d\n", ret);
736e6021511SCharles Keepax 			goto err_dcvdd;
73787d3af4aSMark Brown 		}
73887d3af4aSMark Brown 	}
73987d3af4aSMark Brown 
7403cc72986SMark Brown 	ret = regulator_bulk_enable(arizona->num_core_supplies,
7413cc72986SMark Brown 				    arizona->core_supplies);
7423cc72986SMark Brown 	if (ret != 0) {
7433cc72986SMark Brown 		dev_err(dev, "Failed to enable core supplies: %d\n",
7443cc72986SMark Brown 			ret);
745e6021511SCharles Keepax 		goto err_dcvdd;
7463cc72986SMark Brown 	}
7473cc72986SMark Brown 
74859db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
74959db9691SMark Brown 	if (ret != 0) {
75059db9691SMark Brown 		dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
75159db9691SMark Brown 		goto err_enable;
75259db9691SMark Brown 	}
75359db9691SMark Brown 
754c25feaa5SCharles Keepax 	if (arizona->pdata.reset) {
7553cc72986SMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 1);
756c25feaa5SCharles Keepax 		msleep(1);
757c25feaa5SCharles Keepax 	}
7583cc72986SMark Brown 
7593cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
7603cc72986SMark Brown 
761ca76ceb8SMark Brown 	/* Verify that this is a chip we know about */
762ca76ceb8SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
763ca76ceb8SMark Brown 	if (ret != 0) {
764ca76ceb8SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
765ca76ceb8SMark Brown 		goto err_reset;
766ca76ceb8SMark Brown 	}
767ca76ceb8SMark Brown 
768ca76ceb8SMark Brown 	switch (reg) {
769ca76ceb8SMark Brown 	case 0x5102:
770ca76ceb8SMark Brown 	case 0x5110:
771dc7d4863SCharles Keepax 	case 0x8997:
772ca76ceb8SMark Brown 		break;
773ca76ceb8SMark Brown 	default:
774ca76ceb8SMark Brown 		dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
775ca76ceb8SMark Brown 		goto err_reset;
776ca76ceb8SMark Brown 	}
777ca76ceb8SMark Brown 
778ca76ceb8SMark Brown 	/* If we have a /RESET GPIO we'll already be reset */
779ca76ceb8SMark Brown 	if (!arizona->pdata.reset) {
780ca76ceb8SMark Brown 		regcache_mark_dirty(arizona->regmap);
781ca76ceb8SMark Brown 
782ca76ceb8SMark Brown 		ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
783ca76ceb8SMark Brown 		if (ret != 0) {
784ca76ceb8SMark Brown 			dev_err(dev, "Failed to reset device: %d\n", ret);
785ca76ceb8SMark Brown 			goto err_reset;
786ca76ceb8SMark Brown 		}
787ca76ceb8SMark Brown 
788ca76ceb8SMark Brown 		msleep(1);
789ca76ceb8SMark Brown 
790ca76ceb8SMark Brown 		ret = regcache_sync(arizona->regmap);
791ca76ceb8SMark Brown 		if (ret != 0) {
792ca76ceb8SMark Brown 			dev_err(dev, "Failed to sync device: %d\n", ret);
793ca76ceb8SMark Brown 			goto err_reset;
794ca76ceb8SMark Brown 		}
795ca76ceb8SMark Brown 	}
796ca76ceb8SMark Brown 
797ca76ceb8SMark Brown 	/* Ensure device startup is complete */
798ca76ceb8SMark Brown 	switch (arizona->type) {
799ca76ceb8SMark Brown 	case WM5102:
80048018943SMark Brown 		ret = regmap_read(arizona->regmap,
80148018943SMark Brown 				  ARIZONA_WRITE_SEQUENCER_CTRL_3, &val);
802ca76ceb8SMark Brown 		if (ret != 0)
803ca76ceb8SMark Brown 			dev_err(dev,
804ca76ceb8SMark Brown 				"Failed to check write sequencer state: %d\n",
805ca76ceb8SMark Brown 				ret);
806ca76ceb8SMark Brown 		else if (val & 0x01)
807ca76ceb8SMark Brown 			break;
808ca76ceb8SMark Brown 		/* Fall through */
809ca76ceb8SMark Brown 	default:
810ca76ceb8SMark Brown 		ret = arizona_wait_for_boot(arizona);
811ca76ceb8SMark Brown 		if (ret != 0) {
812ca76ceb8SMark Brown 			dev_err(arizona->dev,
813ca76ceb8SMark Brown 				"Device failed initial boot: %d\n", ret);
814ca76ceb8SMark Brown 			goto err_reset;
815ca76ceb8SMark Brown 		}
816ca76ceb8SMark Brown 		break;
817ca76ceb8SMark Brown 	}
818ca76ceb8SMark Brown 
819ca76ceb8SMark Brown 	/* Read the device ID information & do device specific stuff */
8203cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
8213cc72986SMark Brown 	if (ret != 0) {
8223cc72986SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
82359db9691SMark Brown 		goto err_reset;
8243cc72986SMark Brown 	}
8253cc72986SMark Brown 
8263cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
8273cc72986SMark Brown 			  &arizona->rev);
8283cc72986SMark Brown 	if (ret != 0) {
8293cc72986SMark Brown 		dev_err(dev, "Failed to read revision register: %d\n", ret);
83059db9691SMark Brown 		goto err_reset;
8313cc72986SMark Brown 	}
8323cc72986SMark Brown 	arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
8333cc72986SMark Brown 
8343cc72986SMark Brown 	switch (reg) {
835863df8d5SMark Brown #ifdef CONFIG_MFD_WM5102
8363cc72986SMark Brown 	case 0x5102:
8373cc72986SMark Brown 		type_name = "WM5102";
8383cc72986SMark Brown 		if (arizona->type != WM5102) {
8393cc72986SMark Brown 			dev_err(arizona->dev, "WM5102 registered as %d\n",
8403cc72986SMark Brown 				arizona->type);
8413cc72986SMark Brown 			arizona->type = WM5102;
8423cc72986SMark Brown 		}
84362d62b59SMark Brown 		apply_patch = wm5102_patch;
844c6d6bfb1SMark Brown 		arizona->rev &= 0x7;
8453cc72986SMark Brown 		break;
846863df8d5SMark Brown #endif
847e102befeSMark Brown #ifdef CONFIG_MFD_WM5110
848e102befeSMark Brown 	case 0x5110:
849e102befeSMark Brown 		type_name = "WM5110";
850e102befeSMark Brown 		if (arizona->type != WM5110) {
851e102befeSMark Brown 			dev_err(arizona->dev, "WM5110 registered as %d\n",
852e102befeSMark Brown 				arizona->type);
853e102befeSMark Brown 			arizona->type = WM5110;
854e102befeSMark Brown 		}
85562d62b59SMark Brown 		apply_patch = wm5110_patch;
856e102befeSMark Brown 		break;
857e102befeSMark Brown #endif
858dc7d4863SCharles Keepax #ifdef CONFIG_MFD_WM8997
859dc7d4863SCharles Keepax 	case 0x8997:
860dc7d4863SCharles Keepax 		type_name = "WM8997";
861dc7d4863SCharles Keepax 		if (arizona->type != WM8997) {
862dc7d4863SCharles Keepax 			dev_err(arizona->dev, "WM8997 registered as %d\n",
863dc7d4863SCharles Keepax 				arizona->type);
864dc7d4863SCharles Keepax 			arizona->type = WM8997;
865dc7d4863SCharles Keepax 		}
866dc7d4863SCharles Keepax 		apply_patch = wm8997_patch;
867dc7d4863SCharles Keepax 		break;
868dc7d4863SCharles Keepax #endif
8693cc72986SMark Brown 	default:
8703cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device ID %x\n", reg);
87159db9691SMark Brown 		goto err_reset;
8723cc72986SMark Brown 	}
8733cc72986SMark Brown 
8743cc72986SMark Brown 	dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
8753cc72986SMark Brown 
87662d62b59SMark Brown 	if (apply_patch) {
87762d62b59SMark Brown 		ret = apply_patch(arizona);
87862d62b59SMark Brown 		if (ret != 0) {
87962d62b59SMark Brown 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
88062d62b59SMark Brown 				ret);
88162d62b59SMark Brown 			goto err_reset;
88262d62b59SMark Brown 		}
883e80436bbSCharles Keepax 
884e80436bbSCharles Keepax 		switch (arizona->type) {
885e80436bbSCharles Keepax 		case WM5102:
886e80436bbSCharles Keepax 			ret = arizona_apply_hardware_patch(arizona);
887e80436bbSCharles Keepax 			if (ret != 0) {
888e80436bbSCharles Keepax 				dev_err(arizona->dev,
889e80436bbSCharles Keepax 					"Failed to apply hardware patch: %d\n",
890e80436bbSCharles Keepax 					ret);
891e80436bbSCharles Keepax 				goto err_reset;
892e80436bbSCharles Keepax 			}
893e80436bbSCharles Keepax 			break;
894e80436bbSCharles Keepax 		default:
895e80436bbSCharles Keepax 			break;
896e80436bbSCharles Keepax 		}
89762d62b59SMark Brown 	}
89862d62b59SMark Brown 
8993cc72986SMark Brown 	for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
9003cc72986SMark Brown 		if (!arizona->pdata.gpio_defaults[i])
9013cc72986SMark Brown 			continue;
9023cc72986SMark Brown 
9033cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
9043cc72986SMark Brown 			     arizona->pdata.gpio_defaults[i]);
9053cc72986SMark Brown 	}
9063cc72986SMark Brown 
9073cc72986SMark Brown 	pm_runtime_set_autosuspend_delay(arizona->dev, 100);
9083cc72986SMark Brown 	pm_runtime_use_autosuspend(arizona->dev);
9093cc72986SMark Brown 	pm_runtime_enable(arizona->dev);
9103cc72986SMark Brown 
9113cc72986SMark Brown 	/* Chip default */
9123cc72986SMark Brown 	if (!arizona->pdata.clk32k_src)
9133cc72986SMark Brown 		arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
9143cc72986SMark Brown 
9153cc72986SMark Brown 	switch (arizona->pdata.clk32k_src) {
9163cc72986SMark Brown 	case ARIZONA_32KZ_MCLK1:
9173cc72986SMark Brown 	case ARIZONA_32KZ_MCLK2:
9183cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
9193cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK,
9203cc72986SMark Brown 				   arizona->pdata.clk32k_src - 1);
921767c6dc0SMark Brown 		arizona_clk32k_enable(arizona);
9223cc72986SMark Brown 		break;
9233cc72986SMark Brown 	case ARIZONA_32KZ_NONE:
9243cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
9253cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK, 2);
9263cc72986SMark Brown 		break;
9273cc72986SMark Brown 	default:
9283cc72986SMark Brown 		dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
9293cc72986SMark Brown 			arizona->pdata.clk32k_src);
9303cc72986SMark Brown 		ret = -EINVAL;
93159db9691SMark Brown 		goto err_reset;
9323cc72986SMark Brown 	}
9333cc72986SMark Brown 
9343d91f828SMark Brown 	for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
935544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV &&
936544c7aadSMark Brown 		    !arizona->pdata.micbias[i].bypass)
9373d91f828SMark Brown 			continue;
9383d91f828SMark Brown 
939544c7aadSMark Brown 		/* Apply default for bypass mode */
940544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV)
941544c7aadSMark Brown 			arizona->pdata.micbias[i].mV = 2800;
942544c7aadSMark Brown 
9433d91f828SMark Brown 		val = (arizona->pdata.micbias[i].mV - 1500) / 100;
944544c7aadSMark Brown 
9453d91f828SMark Brown 		val <<= ARIZONA_MICB1_LVL_SHIFT;
9463d91f828SMark Brown 
9473d91f828SMark Brown 		if (arizona->pdata.micbias[i].ext_cap)
9483d91f828SMark Brown 			val |= ARIZONA_MICB1_EXT_CAP;
9493d91f828SMark Brown 
9503d91f828SMark Brown 		if (arizona->pdata.micbias[i].discharge)
9513d91f828SMark Brown 			val |= ARIZONA_MICB1_DISCH;
9523d91f828SMark Brown 
953f773fc6dSCharles Keepax 		if (arizona->pdata.micbias[i].soft_start)
9543d91f828SMark Brown 			val |= ARIZONA_MICB1_RATE;
9553d91f828SMark Brown 
956544c7aadSMark Brown 		if (arizona->pdata.micbias[i].bypass)
957544c7aadSMark Brown 			val |= ARIZONA_MICB1_BYPASS;
958544c7aadSMark Brown 
9593d91f828SMark Brown 		regmap_update_bits(arizona->regmap,
9603d91f828SMark Brown 				   ARIZONA_MIC_BIAS_CTRL_1 + i,
9613d91f828SMark Brown 				   ARIZONA_MICB1_LVL_MASK |
9623d91f828SMark Brown 				   ARIZONA_MICB1_DISCH |
963544c7aadSMark Brown 				   ARIZONA_MICB1_BYPASS |
9643d91f828SMark Brown 				   ARIZONA_MICB1_RATE, val);
9653d91f828SMark Brown 	}
9663d91f828SMark Brown 
9673cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
9683cc72986SMark Brown 		/* Default for both is 0 so noop with defaults */
9693cc72986SMark Brown 		val = arizona->pdata.dmic_ref[i]
9703cc72986SMark Brown 			<< ARIZONA_IN1_DMIC_SUP_SHIFT;
9713cc72986SMark Brown 		val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
9723cc72986SMark Brown 
9733cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9743cc72986SMark Brown 				   ARIZONA_IN1L_CONTROL + (i * 8),
9753cc72986SMark Brown 				   ARIZONA_IN1_DMIC_SUP_MASK |
9763cc72986SMark Brown 				   ARIZONA_IN1_MODE_MASK, val);
9773cc72986SMark Brown 	}
9783cc72986SMark Brown 
9793cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
9803cc72986SMark Brown 		/* Default is 0 so noop with defaults */
9813cc72986SMark Brown 		if (arizona->pdata.out_mono[i])
9823cc72986SMark Brown 			val = ARIZONA_OUT1_MONO;
9833cc72986SMark Brown 		else
9843cc72986SMark Brown 			val = 0;
9853cc72986SMark Brown 
9863cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9873cc72986SMark Brown 				   ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
9883cc72986SMark Brown 				   ARIZONA_OUT1_MONO, val);
9893cc72986SMark Brown 	}
9903cc72986SMark Brown 
9913cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
9923cc72986SMark Brown 		if (arizona->pdata.spk_mute[i])
9933cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
9942a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
9953cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_ENDIAN_MASK |
9963cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_SEQ1_MASK,
9973cc72986SMark Brown 					   arizona->pdata.spk_mute[i]);
9983cc72986SMark Brown 
9993cc72986SMark Brown 		if (arizona->pdata.spk_fmt[i])
10003cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
10012a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
10023cc72986SMark Brown 					   ARIZONA_SPK1_FMT_MASK,
10033cc72986SMark Brown 					   arizona->pdata.spk_fmt[i]);
10043cc72986SMark Brown 	}
10053cc72986SMark Brown 
10063cc72986SMark Brown 	/* Set up for interrupts */
10073cc72986SMark Brown 	ret = arizona_irq_init(arizona);
10083cc72986SMark Brown 	if (ret != 0)
100959db9691SMark Brown 		goto err_reset;
10103cc72986SMark Brown 
10113cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
10123cc72986SMark Brown 			    arizona_clkgen_err, arizona);
10133cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
10143cc72986SMark Brown 			    arizona_overclocked, arizona);
10153cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
10163cc72986SMark Brown 			    arizona_underclocked, arizona);
10173cc72986SMark Brown 
10183cc72986SMark Brown 	switch (arizona->type) {
10193cc72986SMark Brown 	case WM5102:
10203cc72986SMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
10210848c94fSMark Brown 				      ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
10223cc72986SMark Brown 		break;
1023e102befeSMark Brown 	case WM5110:
1024e102befeSMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
102578566afdSCharles Keepax 				      ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
1026e102befeSMark Brown 		break;
1027dc7d4863SCharles Keepax 	case WM8997:
1028dc7d4863SCharles Keepax 		ret = mfd_add_devices(arizona->dev, -1, wm8997_devs,
1029dc7d4863SCharles Keepax 				      ARRAY_SIZE(wm8997_devs), NULL, 0, NULL);
1030dc7d4863SCharles Keepax 		break;
10313cc72986SMark Brown 	}
10323cc72986SMark Brown 
10333cc72986SMark Brown 	if (ret != 0) {
10343cc72986SMark Brown 		dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
10353cc72986SMark Brown 		goto err_irq;
10363cc72986SMark Brown 	}
10373cc72986SMark Brown 
103859db9691SMark Brown #ifdef CONFIG_PM_RUNTIME
103959db9691SMark Brown 	regulator_disable(arizona->dcvdd);
104059db9691SMark Brown #endif
104159db9691SMark Brown 
10423cc72986SMark Brown 	return 0;
10433cc72986SMark Brown 
10443cc72986SMark Brown err_irq:
10453cc72986SMark Brown 	arizona_irq_exit(arizona);
10463cc72986SMark Brown err_reset:
10473cc72986SMark Brown 	if (arizona->pdata.reset) {
104887d3af4aSMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 0);
10493cc72986SMark Brown 		gpio_free(arizona->pdata.reset);
10503cc72986SMark Brown 	}
105159db9691SMark Brown 	regulator_disable(arizona->dcvdd);
10523cc72986SMark Brown err_enable:
10533a36a0dbSMark Brown 	regulator_bulk_disable(arizona->num_core_supplies,
10543cc72986SMark Brown 			       arizona->core_supplies);
1055e6021511SCharles Keepax err_dcvdd:
1056e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
10573cc72986SMark Brown err_early:
10583cc72986SMark Brown 	mfd_remove_devices(dev);
10593cc72986SMark Brown 	return ret;
10603cc72986SMark Brown }
10613cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_init);
10623cc72986SMark Brown 
10634740f73fSBill Pemberton int arizona_dev_exit(struct arizona *arizona)
10643cc72986SMark Brown {
1065b804020aSCharles Keepax 	pm_runtime_disable(arizona->dev);
1066b804020aSCharles Keepax 
1067df6b3352SCharles Keepax 	regulator_disable(arizona->dcvdd);
1068e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
1069df6b3352SCharles Keepax 
10703cc72986SMark Brown 	mfd_remove_devices(arizona->dev);
10713cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
10723cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
10733cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
10743cc72986SMark Brown 	arizona_irq_exit(arizona);
10751d017b6bSMark Brown 	if (arizona->pdata.reset)
10761d017b6bSMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 0);
1077df6b3352SCharles Keepax 
10784420286eSCharles Keepax 	regulator_bulk_disable(arizona->num_core_supplies,
10791d017b6bSMark Brown 			       arizona->core_supplies);
10803cc72986SMark Brown 	return 0;
10813cc72986SMark Brown }
10823cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_exit);
1083