xref: /linux/drivers/mfd/arizona-core.c (revision 48bb9fe4b3b361570f3619086a22d9bf9dd4c980)
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 
333*48bb9fe4SRafael J. Wysocki #ifdef CONFIG_PM
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 
3969270bdf5SMark Brown 	ret = regcache_sync(arizona->regmap);
3979270bdf5SMark Brown 	if (ret != 0) {
3989270bdf5SMark Brown 		dev_err(arizona->dev, "Failed to restore register cache\n");
3994816bd1cSMark Brown 		goto err;
4009270bdf5SMark Brown 	}
4013cc72986SMark Brown 
4023cc72986SMark Brown 	return 0;
4034816bd1cSMark Brown 
4044816bd1cSMark Brown err:
4054816bd1cSMark Brown 	regcache_cache_only(arizona->regmap, true);
4064816bd1cSMark Brown 	regulator_disable(arizona->dcvdd);
4074816bd1cSMark Brown 	return ret;
4083cc72986SMark Brown }
4093cc72986SMark Brown 
4103cc72986SMark Brown static int arizona_runtime_suspend(struct device *dev)
4113cc72986SMark Brown {
4123cc72986SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
4135927467dSMark Brown 	int ret;
4143cc72986SMark Brown 
415508c8299SMark Brown 	dev_dbg(arizona->dev, "Entering AoD mode\n");
416508c8299SMark Brown 
4175927467dSMark Brown 	if (arizona->external_dcvdd) {
4185927467dSMark Brown 		ret = regmap_update_bits(arizona->regmap,
4195927467dSMark Brown 					 ARIZONA_ISOLATION_CONTROL,
4205927467dSMark Brown 					 ARIZONA_ISOLATE_DCVDD1,
4215927467dSMark Brown 					 ARIZONA_ISOLATE_DCVDD1);
4225927467dSMark Brown 		if (ret != 0) {
4235927467dSMark Brown 			dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n",
4245927467dSMark Brown 				ret);
4255927467dSMark Brown 			return ret;
4265927467dSMark Brown 		}
4275927467dSMark Brown 	}
4285927467dSMark Brown 
4293cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
4303cc72986SMark Brown 	regcache_mark_dirty(arizona->regmap);
431e293e847SCharles Keepax 	regulator_disable(arizona->dcvdd);
4323cc72986SMark Brown 
4333cc72986SMark Brown 	return 0;
4343cc72986SMark Brown }
4353cc72986SMark Brown #endif
4363cc72986SMark Brown 
437dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP
43867c99296SMark Brown static int arizona_suspend(struct device *dev)
43967c99296SMark Brown {
44067c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
44167c99296SMark Brown 
44267c99296SMark Brown 	dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
44367c99296SMark Brown 	disable_irq(arizona->irq);
44467c99296SMark Brown 
44567c99296SMark Brown 	return 0;
44667c99296SMark Brown }
44767c99296SMark Brown 
44867c99296SMark Brown static int arizona_suspend_late(struct device *dev)
44967c99296SMark Brown {
45067c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
45167c99296SMark Brown 
45267c99296SMark Brown 	dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
45367c99296SMark Brown 	enable_irq(arizona->irq);
45467c99296SMark Brown 
45567c99296SMark Brown 	return 0;
45667c99296SMark Brown }
45767c99296SMark Brown 
458dc781d0eSMark Brown static int arizona_resume_noirq(struct device *dev)
459dc781d0eSMark Brown {
460dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
461dc781d0eSMark Brown 
462dc781d0eSMark Brown 	dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
463dc781d0eSMark Brown 	disable_irq(arizona->irq);
464dc781d0eSMark Brown 
465dc781d0eSMark Brown 	return 0;
466dc781d0eSMark Brown }
467dc781d0eSMark Brown 
468dc781d0eSMark Brown static int arizona_resume(struct device *dev)
469dc781d0eSMark Brown {
470dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
471dc781d0eSMark Brown 
472dc781d0eSMark Brown 	dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n");
473dc781d0eSMark Brown 	enable_irq(arizona->irq);
474dc781d0eSMark Brown 
475dc781d0eSMark Brown 	return 0;
476dc781d0eSMark Brown }
477dc781d0eSMark Brown #endif
478dc781d0eSMark Brown 
4793cc72986SMark Brown const struct dev_pm_ops arizona_pm_ops = {
4803cc72986SMark Brown 	SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
4813cc72986SMark Brown 			   arizona_runtime_resume,
4823cc72986SMark Brown 			   NULL)
48367c99296SMark Brown 	SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
484dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP
48567c99296SMark Brown 	.suspend_late = arizona_suspend_late,
486dc781d0eSMark Brown 	.resume_noirq = arizona_resume_noirq,
487dc781d0eSMark Brown #endif
4883cc72986SMark Brown };
4893cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_pm_ops);
4903cc72986SMark Brown 
491d781009cSMark Brown #ifdef CONFIG_OF
492942786e6SLee Jones unsigned long arizona_of_get_type(struct device *dev)
493d781009cSMark Brown {
494d781009cSMark Brown 	const struct of_device_id *id = of_match_device(arizona_of_match, dev);
495d781009cSMark Brown 
496d781009cSMark Brown 	if (id)
497942786e6SLee Jones 		return (unsigned long)id->data;
498d781009cSMark Brown 	else
499d781009cSMark Brown 		return 0;
500d781009cSMark Brown }
501d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_get_type);
502d781009cSMark Brown 
503e4fcb1d6SCharles Keepax int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop,
504e4fcb1d6SCharles Keepax 			      bool mandatory)
505e4fcb1d6SCharles Keepax {
506e4fcb1d6SCharles Keepax 	int gpio;
507e4fcb1d6SCharles Keepax 
508e4fcb1d6SCharles Keepax 	gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0);
509e4fcb1d6SCharles Keepax 	if (gpio < 0) {
510e4fcb1d6SCharles Keepax 		if (mandatory)
511e4fcb1d6SCharles Keepax 			dev_err(arizona->dev,
512e4fcb1d6SCharles Keepax 				"Mandatory DT gpio %s missing/malformed: %d\n",
513e4fcb1d6SCharles Keepax 				prop, gpio);
514e4fcb1d6SCharles Keepax 
515e4fcb1d6SCharles Keepax 		gpio = 0;
516e4fcb1d6SCharles Keepax 	}
517e4fcb1d6SCharles Keepax 
518e4fcb1d6SCharles Keepax 	return gpio;
519e4fcb1d6SCharles Keepax }
520e4fcb1d6SCharles Keepax EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio);
521e4fcb1d6SCharles Keepax 
522d781009cSMark Brown static int arizona_of_get_core_pdata(struct arizona *arizona)
523d781009cSMark Brown {
524e4fcb1d6SCharles Keepax 	struct arizona_pdata *pdata = &arizona->pdata;
525cc47aed9SInha Song 	struct property *prop;
526cc47aed9SInha Song 	const __be32 *cur;
527cc47aed9SInha Song 	u32 val;
528d781009cSMark Brown 	int ret, i;
529cc47aed9SInha Song 	int count = 0;
530d781009cSMark Brown 
531e4fcb1d6SCharles Keepax 	pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true);
532d781009cSMark Brown 
533d781009cSMark Brown 	ret = of_property_read_u32_array(arizona->dev->of_node,
534d781009cSMark Brown 					 "wlf,gpio-defaults",
535d781009cSMark Brown 					 arizona->pdata.gpio_defaults,
536d781009cSMark Brown 					 ARRAY_SIZE(arizona->pdata.gpio_defaults));
537d781009cSMark Brown 	if (ret >= 0) {
538d781009cSMark Brown 		/*
539d781009cSMark Brown 		 * All values are literal except out of range values
540d781009cSMark Brown 		 * which are chip default, translate into platform
541d781009cSMark Brown 		 * data which uses 0 as chip default and out of range
542d781009cSMark Brown 		 * as zero.
543d781009cSMark Brown 		 */
544d781009cSMark Brown 		for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
545d781009cSMark Brown 			if (arizona->pdata.gpio_defaults[i] > 0xffff)
546d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0;
54791c73935SCharles Keepax 			else if (arizona->pdata.gpio_defaults[i] == 0)
548d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0x10000;
549d781009cSMark Brown 		}
550d781009cSMark Brown 	} else {
551d781009cSMark Brown 		dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
552d781009cSMark Brown 			ret);
553d781009cSMark Brown 	}
554d781009cSMark Brown 
555cc47aed9SInha Song 	of_property_for_each_u32(arizona->dev->of_node, "wlf,inmode", prop,
556cc47aed9SInha Song 				 cur, val) {
557cc47aed9SInha Song 		if (count == ARRAY_SIZE(arizona->pdata.inmode))
558cc47aed9SInha Song 			break;
559cc47aed9SInha Song 
560cc47aed9SInha Song 		arizona->pdata.inmode[count] = val;
561cc47aed9SInha Song 		count++;
562cc47aed9SInha Song 	}
563cc47aed9SInha Song 
564d781009cSMark Brown 	return 0;
565d781009cSMark Brown }
566d781009cSMark Brown 
567d781009cSMark Brown const struct of_device_id arizona_of_match[] = {
568d781009cSMark Brown 	{ .compatible = "wlf,wm5102", .data = (void *)WM5102 },
569d781009cSMark Brown 	{ .compatible = "wlf,wm5110", .data = (void *)WM5110 },
570dc7d4863SCharles Keepax 	{ .compatible = "wlf,wm8997", .data = (void *)WM8997 },
571d781009cSMark Brown 	{},
572d781009cSMark Brown };
573d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_match);
574d781009cSMark Brown #else
575d781009cSMark Brown static inline int arizona_of_get_core_pdata(struct arizona *arizona)
576d781009cSMark Brown {
577d781009cSMark Brown 	return 0;
578d781009cSMark Brown }
579d781009cSMark Brown #endif
580d781009cSMark Brown 
5815ac98553SGeert Uytterhoeven static const struct mfd_cell early_devs[] = {
5823cc72986SMark Brown 	{ .name = "arizona-ldo1" },
5833cc72986SMark Brown };
5843cc72986SMark Brown 
58532dadef2SCharles Keepax static const char *wm5102_supplies[] = {
5865fc6c396SCharles Keepax 	"MICVDD",
58732dadef2SCharles Keepax 	"DBVDD2",
58832dadef2SCharles Keepax 	"DBVDD3",
58932dadef2SCharles Keepax 	"CPVDD",
59032dadef2SCharles Keepax 	"SPKVDDL",
59132dadef2SCharles Keepax 	"SPKVDDR",
59232dadef2SCharles Keepax };
59332dadef2SCharles Keepax 
5945ac98553SGeert Uytterhoeven static const struct mfd_cell wm5102_devs[] = {
595d7768111SMark Brown 	{ .name = "arizona-micsupp" },
5965fc6c396SCharles Keepax 	{
5975fc6c396SCharles Keepax 		.name = "arizona-extcon",
5985fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
5995fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6005fc6c396SCharles Keepax 	},
6013cc72986SMark Brown 	{ .name = "arizona-gpio" },
602503b1cacSMark Brown 	{ .name = "arizona-haptics" },
6033cc72986SMark Brown 	{ .name = "arizona-pwm" },
60432dadef2SCharles Keepax 	{
60532dadef2SCharles Keepax 		.name = "wm5102-codec",
60632dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
60732dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
60832dadef2SCharles Keepax 	},
6093cc72986SMark Brown };
6103cc72986SMark Brown 
6115ac98553SGeert Uytterhoeven static const struct mfd_cell wm5110_devs[] = {
612d7768111SMark Brown 	{ .name = "arizona-micsupp" },
6135fc6c396SCharles Keepax 	{
6145fc6c396SCharles Keepax 		.name = "arizona-extcon",
6155fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
6165fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6175fc6c396SCharles Keepax 	},
618e102befeSMark Brown 	{ .name = "arizona-gpio" },
619503b1cacSMark Brown 	{ .name = "arizona-haptics" },
620e102befeSMark Brown 	{ .name = "arizona-pwm" },
62132dadef2SCharles Keepax 	{
62232dadef2SCharles Keepax 		.name = "wm5110-codec",
62332dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
62432dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
62532dadef2SCharles Keepax 	},
62632dadef2SCharles Keepax };
62732dadef2SCharles Keepax 
62832dadef2SCharles Keepax static const char *wm8997_supplies[] = {
629996c2d4fSCharles Keepax 	"MICVDD",
63032dadef2SCharles Keepax 	"DBVDD2",
63132dadef2SCharles Keepax 	"CPVDD",
63232dadef2SCharles Keepax 	"SPKVDD",
633e102befeSMark Brown };
634e102befeSMark Brown 
6355ac98553SGeert Uytterhoeven static const struct mfd_cell wm8997_devs[] = {
636dc7d4863SCharles Keepax 	{ .name = "arizona-micsupp" },
6375fc6c396SCharles Keepax 	{
6385fc6c396SCharles Keepax 		.name = "arizona-extcon",
6395fc6c396SCharles Keepax 		.parent_supplies = wm8997_supplies,
6405fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6415fc6c396SCharles Keepax 	},
642dc7d4863SCharles Keepax 	{ .name = "arizona-gpio" },
643dc7d4863SCharles Keepax 	{ .name = "arizona-haptics" },
644dc7d4863SCharles Keepax 	{ .name = "arizona-pwm" },
64532dadef2SCharles Keepax 	{
64632dadef2SCharles Keepax 		.name = "wm8997-codec",
64732dadef2SCharles Keepax 		.parent_supplies = wm8997_supplies,
64832dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm8997_supplies),
64932dadef2SCharles Keepax 	},
650dc7d4863SCharles Keepax };
651dc7d4863SCharles Keepax 
652f791be49SBill Pemberton int arizona_dev_init(struct arizona *arizona)
6533cc72986SMark Brown {
6543cc72986SMark Brown 	struct device *dev = arizona->dev;
6553cc72986SMark Brown 	const char *type_name;
6563cc72986SMark Brown 	unsigned int reg, val;
65762d62b59SMark Brown 	int (*apply_patch)(struct arizona *) = NULL;
6583cc72986SMark Brown 	int ret, i;
6593cc72986SMark Brown 
6603cc72986SMark Brown 	dev_set_drvdata(arizona->dev, arizona);
6613cc72986SMark Brown 	mutex_init(&arizona->clk_lock);
6623cc72986SMark Brown 
6633cc72986SMark Brown 	if (dev_get_platdata(arizona->dev))
6643cc72986SMark Brown 		memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
6653cc72986SMark Brown 		       sizeof(arizona->pdata));
66622d7dc8aSLee Jones 	else
66722d7dc8aSLee Jones 		arizona_of_get_core_pdata(arizona);
6683cc72986SMark Brown 
6693cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
6703cc72986SMark Brown 
6713cc72986SMark Brown 	switch (arizona->type) {
6723cc72986SMark Brown 	case WM5102:
673e102befeSMark Brown 	case WM5110:
674dc7d4863SCharles Keepax 	case WM8997:
6753cc72986SMark Brown 		for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
6763cc72986SMark Brown 			arizona->core_supplies[i].supply
6773cc72986SMark Brown 				= wm5102_core_supplies[i];
6783cc72986SMark Brown 		arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
6793cc72986SMark Brown 		break;
6803cc72986SMark Brown 	default:
6813cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device type %d\n",
6823cc72986SMark Brown 			arizona->type);
6833cc72986SMark Brown 		return -EINVAL;
6843cc72986SMark Brown 	}
6853cc72986SMark Brown 
6864a8c475fSCharles Keepax 	/* Mark DCVDD as external, LDO1 driver will clear if internal */
6874a8c475fSCharles Keepax 	arizona->external_dcvdd = true;
6884a8c475fSCharles Keepax 
6893cc72986SMark Brown 	ret = mfd_add_devices(arizona->dev, -1, early_devs,
6900848c94fSMark Brown 			      ARRAY_SIZE(early_devs), NULL, 0, NULL);
6913cc72986SMark Brown 	if (ret != 0) {
6923cc72986SMark Brown 		dev_err(dev, "Failed to add early children: %d\n", ret);
6933cc72986SMark Brown 		return ret;
6943cc72986SMark Brown 	}
6953cc72986SMark Brown 
6963cc72986SMark Brown 	ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
6973cc72986SMark Brown 				      arizona->core_supplies);
6983cc72986SMark Brown 	if (ret != 0) {
6993cc72986SMark Brown 		dev_err(dev, "Failed to request core supplies: %d\n",
7003cc72986SMark Brown 			ret);
7013cc72986SMark Brown 		goto err_early;
7023cc72986SMark Brown 	}
7033cc72986SMark Brown 
7040c2d0ffbSCharles Keepax 	/**
7050c2d0ffbSCharles Keepax 	 * Don't use devres here because the only device we have to get
7060c2d0ffbSCharles Keepax 	 * against is the MFD device and DCVDD will likely be supplied by
7070c2d0ffbSCharles Keepax 	 * one of its children. Meaning that the regulator will be
7080c2d0ffbSCharles Keepax 	 * destroyed by the time devres calls regulator put.
7090c2d0ffbSCharles Keepax 	 */
710e6021511SCharles Keepax 	arizona->dcvdd = regulator_get(arizona->dev, "DCVDD");
71159db9691SMark Brown 	if (IS_ERR(arizona->dcvdd)) {
71259db9691SMark Brown 		ret = PTR_ERR(arizona->dcvdd);
71359db9691SMark Brown 		dev_err(dev, "Failed to request DCVDD: %d\n", ret);
71459db9691SMark Brown 		goto err_early;
71559db9691SMark Brown 	}
71659db9691SMark Brown 
71787d3af4aSMark Brown 	if (arizona->pdata.reset) {
71887d3af4aSMark Brown 		/* Start out with /RESET low to put the chip into reset */
71987d3af4aSMark Brown 		ret = gpio_request_one(arizona->pdata.reset,
72087d3af4aSMark Brown 				       GPIOF_DIR_OUT | GPIOF_INIT_LOW,
72187d3af4aSMark Brown 				       "arizona /RESET");
72287d3af4aSMark Brown 		if (ret != 0) {
72387d3af4aSMark Brown 			dev_err(dev, "Failed to request /RESET: %d\n", ret);
724e6021511SCharles Keepax 			goto err_dcvdd;
72587d3af4aSMark Brown 		}
72687d3af4aSMark Brown 	}
72787d3af4aSMark Brown 
7283cc72986SMark Brown 	ret = regulator_bulk_enable(arizona->num_core_supplies,
7293cc72986SMark Brown 				    arizona->core_supplies);
7303cc72986SMark Brown 	if (ret != 0) {
7313cc72986SMark Brown 		dev_err(dev, "Failed to enable core supplies: %d\n",
7323cc72986SMark Brown 			ret);
733e6021511SCharles Keepax 		goto err_dcvdd;
7343cc72986SMark Brown 	}
7353cc72986SMark Brown 
73659db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
73759db9691SMark Brown 	if (ret != 0) {
73859db9691SMark Brown 		dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
73959db9691SMark Brown 		goto err_enable;
74059db9691SMark Brown 	}
74159db9691SMark Brown 
742c25feaa5SCharles Keepax 	if (arizona->pdata.reset) {
7433cc72986SMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 1);
744c25feaa5SCharles Keepax 		msleep(1);
745c25feaa5SCharles Keepax 	}
7463cc72986SMark Brown 
7473cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
7483cc72986SMark Brown 
749ca76ceb8SMark Brown 	/* Verify that this is a chip we know about */
750ca76ceb8SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
751ca76ceb8SMark Brown 	if (ret != 0) {
752ca76ceb8SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
753ca76ceb8SMark Brown 		goto err_reset;
754ca76ceb8SMark Brown 	}
755ca76ceb8SMark Brown 
756ca76ceb8SMark Brown 	switch (reg) {
757ca76ceb8SMark Brown 	case 0x5102:
758ca76ceb8SMark Brown 	case 0x5110:
759dc7d4863SCharles Keepax 	case 0x8997:
760ca76ceb8SMark Brown 		break;
761ca76ceb8SMark Brown 	default:
762ca76ceb8SMark Brown 		dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
763ca76ceb8SMark Brown 		goto err_reset;
764ca76ceb8SMark Brown 	}
765ca76ceb8SMark Brown 
766ca76ceb8SMark Brown 	/* If we have a /RESET GPIO we'll already be reset */
767ca76ceb8SMark Brown 	if (!arizona->pdata.reset) {
768ca76ceb8SMark Brown 		regcache_mark_dirty(arizona->regmap);
769ca76ceb8SMark Brown 
770ca76ceb8SMark Brown 		ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
771ca76ceb8SMark Brown 		if (ret != 0) {
772ca76ceb8SMark Brown 			dev_err(dev, "Failed to reset device: %d\n", ret);
773ca76ceb8SMark Brown 			goto err_reset;
774ca76ceb8SMark Brown 		}
775ca76ceb8SMark Brown 
776ca76ceb8SMark Brown 		msleep(1);
777ca76ceb8SMark Brown 
778ca76ceb8SMark Brown 		ret = regcache_sync(arizona->regmap);
779ca76ceb8SMark Brown 		if (ret != 0) {
780ca76ceb8SMark Brown 			dev_err(dev, "Failed to sync device: %d\n", ret);
781ca76ceb8SMark Brown 			goto err_reset;
782ca76ceb8SMark Brown 		}
783ca76ceb8SMark Brown 	}
784ca76ceb8SMark Brown 
785ca76ceb8SMark Brown 	/* Ensure device startup is complete */
786ca76ceb8SMark Brown 	switch (arizona->type) {
787ca76ceb8SMark Brown 	case WM5102:
78848018943SMark Brown 		ret = regmap_read(arizona->regmap,
78948018943SMark Brown 				  ARIZONA_WRITE_SEQUENCER_CTRL_3, &val);
790ca76ceb8SMark Brown 		if (ret != 0)
791ca76ceb8SMark Brown 			dev_err(dev,
792ca76ceb8SMark Brown 				"Failed to check write sequencer state: %d\n",
793ca76ceb8SMark Brown 				ret);
794ca76ceb8SMark Brown 		else if (val & 0x01)
795ca76ceb8SMark Brown 			break;
796ca76ceb8SMark Brown 		/* Fall through */
797ca76ceb8SMark Brown 	default:
798ca76ceb8SMark Brown 		ret = arizona_wait_for_boot(arizona);
799ca76ceb8SMark Brown 		if (ret != 0) {
800ca76ceb8SMark Brown 			dev_err(arizona->dev,
801ca76ceb8SMark Brown 				"Device failed initial boot: %d\n", ret);
802ca76ceb8SMark Brown 			goto err_reset;
803ca76ceb8SMark Brown 		}
804ca76ceb8SMark Brown 		break;
805ca76ceb8SMark Brown 	}
806ca76ceb8SMark Brown 
807ca76ceb8SMark Brown 	/* Read the device ID information & do device specific stuff */
8083cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
8093cc72986SMark Brown 	if (ret != 0) {
8103cc72986SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
81159db9691SMark Brown 		goto err_reset;
8123cc72986SMark Brown 	}
8133cc72986SMark Brown 
8143cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
8153cc72986SMark Brown 			  &arizona->rev);
8163cc72986SMark Brown 	if (ret != 0) {
8173cc72986SMark Brown 		dev_err(dev, "Failed to read revision register: %d\n", ret);
81859db9691SMark Brown 		goto err_reset;
8193cc72986SMark Brown 	}
8203cc72986SMark Brown 	arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
8213cc72986SMark Brown 
8223cc72986SMark Brown 	switch (reg) {
823863df8d5SMark Brown #ifdef CONFIG_MFD_WM5102
8243cc72986SMark Brown 	case 0x5102:
8253cc72986SMark Brown 		type_name = "WM5102";
8263cc72986SMark Brown 		if (arizona->type != WM5102) {
8273cc72986SMark Brown 			dev_err(arizona->dev, "WM5102 registered as %d\n",
8283cc72986SMark Brown 				arizona->type);
8293cc72986SMark Brown 			arizona->type = WM5102;
8303cc72986SMark Brown 		}
83162d62b59SMark Brown 		apply_patch = wm5102_patch;
832c6d6bfb1SMark Brown 		arizona->rev &= 0x7;
8333cc72986SMark Brown 		break;
834863df8d5SMark Brown #endif
835e102befeSMark Brown #ifdef CONFIG_MFD_WM5110
836e102befeSMark Brown 	case 0x5110:
837e102befeSMark Brown 		type_name = "WM5110";
838e102befeSMark Brown 		if (arizona->type != WM5110) {
839e102befeSMark Brown 			dev_err(arizona->dev, "WM5110 registered as %d\n",
840e102befeSMark Brown 				arizona->type);
841e102befeSMark Brown 			arizona->type = WM5110;
842e102befeSMark Brown 		}
84362d62b59SMark Brown 		apply_patch = wm5110_patch;
844e102befeSMark Brown 		break;
845e102befeSMark Brown #endif
846dc7d4863SCharles Keepax #ifdef CONFIG_MFD_WM8997
847dc7d4863SCharles Keepax 	case 0x8997:
848dc7d4863SCharles Keepax 		type_name = "WM8997";
849dc7d4863SCharles Keepax 		if (arizona->type != WM8997) {
850dc7d4863SCharles Keepax 			dev_err(arizona->dev, "WM8997 registered as %d\n",
851dc7d4863SCharles Keepax 				arizona->type);
852dc7d4863SCharles Keepax 			arizona->type = WM8997;
853dc7d4863SCharles Keepax 		}
854dc7d4863SCharles Keepax 		apply_patch = wm8997_patch;
855dc7d4863SCharles Keepax 		break;
856dc7d4863SCharles Keepax #endif
8573cc72986SMark Brown 	default:
8583cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device ID %x\n", reg);
85959db9691SMark Brown 		goto err_reset;
8603cc72986SMark Brown 	}
8613cc72986SMark Brown 
8623cc72986SMark Brown 	dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
8633cc72986SMark Brown 
86462d62b59SMark Brown 	if (apply_patch) {
86562d62b59SMark Brown 		ret = apply_patch(arizona);
86662d62b59SMark Brown 		if (ret != 0) {
86762d62b59SMark Brown 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
86862d62b59SMark Brown 				ret);
86962d62b59SMark Brown 			goto err_reset;
87062d62b59SMark Brown 		}
871e80436bbSCharles Keepax 
872e80436bbSCharles Keepax 		switch (arizona->type) {
873e80436bbSCharles Keepax 		case WM5102:
874e80436bbSCharles Keepax 			ret = arizona_apply_hardware_patch(arizona);
875e80436bbSCharles Keepax 			if (ret != 0) {
876e80436bbSCharles Keepax 				dev_err(arizona->dev,
877e80436bbSCharles Keepax 					"Failed to apply hardware patch: %d\n",
878e80436bbSCharles Keepax 					ret);
879e80436bbSCharles Keepax 				goto err_reset;
880e80436bbSCharles Keepax 			}
881e80436bbSCharles Keepax 			break;
882e80436bbSCharles Keepax 		default:
883e80436bbSCharles Keepax 			break;
884e80436bbSCharles Keepax 		}
88562d62b59SMark Brown 	}
88662d62b59SMark Brown 
8873cc72986SMark Brown 	for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
8883cc72986SMark Brown 		if (!arizona->pdata.gpio_defaults[i])
8893cc72986SMark Brown 			continue;
8903cc72986SMark Brown 
8913cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
8923cc72986SMark Brown 			     arizona->pdata.gpio_defaults[i]);
8933cc72986SMark Brown 	}
8943cc72986SMark Brown 
8953cc72986SMark Brown 	pm_runtime_set_autosuspend_delay(arizona->dev, 100);
8963cc72986SMark Brown 	pm_runtime_use_autosuspend(arizona->dev);
8973cc72986SMark Brown 	pm_runtime_enable(arizona->dev);
8983cc72986SMark Brown 
8993cc72986SMark Brown 	/* Chip default */
9003cc72986SMark Brown 	if (!arizona->pdata.clk32k_src)
9013cc72986SMark Brown 		arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
9023cc72986SMark Brown 
9033cc72986SMark Brown 	switch (arizona->pdata.clk32k_src) {
9043cc72986SMark Brown 	case ARIZONA_32KZ_MCLK1:
9053cc72986SMark Brown 	case ARIZONA_32KZ_MCLK2:
9063cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
9073cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK,
9083cc72986SMark Brown 				   arizona->pdata.clk32k_src - 1);
909767c6dc0SMark Brown 		arizona_clk32k_enable(arizona);
9103cc72986SMark Brown 		break;
9113cc72986SMark Brown 	case ARIZONA_32KZ_NONE:
9123cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
9133cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK, 2);
9143cc72986SMark Brown 		break;
9153cc72986SMark Brown 	default:
9163cc72986SMark Brown 		dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
9173cc72986SMark Brown 			arizona->pdata.clk32k_src);
9183cc72986SMark Brown 		ret = -EINVAL;
91959db9691SMark Brown 		goto err_reset;
9203cc72986SMark Brown 	}
9213cc72986SMark Brown 
9223d91f828SMark Brown 	for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
923544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV &&
924544c7aadSMark Brown 		    !arizona->pdata.micbias[i].bypass)
9253d91f828SMark Brown 			continue;
9263d91f828SMark Brown 
927544c7aadSMark Brown 		/* Apply default for bypass mode */
928544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV)
929544c7aadSMark Brown 			arizona->pdata.micbias[i].mV = 2800;
930544c7aadSMark Brown 
9313d91f828SMark Brown 		val = (arizona->pdata.micbias[i].mV - 1500) / 100;
932544c7aadSMark Brown 
9333d91f828SMark Brown 		val <<= ARIZONA_MICB1_LVL_SHIFT;
9343d91f828SMark Brown 
9353d91f828SMark Brown 		if (arizona->pdata.micbias[i].ext_cap)
9363d91f828SMark Brown 			val |= ARIZONA_MICB1_EXT_CAP;
9373d91f828SMark Brown 
9383d91f828SMark Brown 		if (arizona->pdata.micbias[i].discharge)
9393d91f828SMark Brown 			val |= ARIZONA_MICB1_DISCH;
9403d91f828SMark Brown 
941f773fc6dSCharles Keepax 		if (arizona->pdata.micbias[i].soft_start)
9423d91f828SMark Brown 			val |= ARIZONA_MICB1_RATE;
9433d91f828SMark Brown 
944544c7aadSMark Brown 		if (arizona->pdata.micbias[i].bypass)
945544c7aadSMark Brown 			val |= ARIZONA_MICB1_BYPASS;
946544c7aadSMark Brown 
9473d91f828SMark Brown 		regmap_update_bits(arizona->regmap,
9483d91f828SMark Brown 				   ARIZONA_MIC_BIAS_CTRL_1 + i,
9493d91f828SMark Brown 				   ARIZONA_MICB1_LVL_MASK |
95071d134b9SCharles Keepax 				   ARIZONA_MICB1_EXT_CAP |
9513d91f828SMark Brown 				   ARIZONA_MICB1_DISCH |
952544c7aadSMark Brown 				   ARIZONA_MICB1_BYPASS |
9533d91f828SMark Brown 				   ARIZONA_MICB1_RATE, val);
9543d91f828SMark Brown 	}
9553d91f828SMark Brown 
9563cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
9573cc72986SMark Brown 		/* Default for both is 0 so noop with defaults */
9583cc72986SMark Brown 		val = arizona->pdata.dmic_ref[i]
9593cc72986SMark Brown 			<< ARIZONA_IN1_DMIC_SUP_SHIFT;
9603cc72986SMark Brown 		val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
9613cc72986SMark Brown 
9623cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9633cc72986SMark Brown 				   ARIZONA_IN1L_CONTROL + (i * 8),
9643cc72986SMark Brown 				   ARIZONA_IN1_DMIC_SUP_MASK |
9653cc72986SMark Brown 				   ARIZONA_IN1_MODE_MASK, val);
9663cc72986SMark Brown 	}
9673cc72986SMark Brown 
9683cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
9693cc72986SMark Brown 		/* Default is 0 so noop with defaults */
9703cc72986SMark Brown 		if (arizona->pdata.out_mono[i])
9713cc72986SMark Brown 			val = ARIZONA_OUT1_MONO;
9723cc72986SMark Brown 		else
9733cc72986SMark Brown 			val = 0;
9743cc72986SMark Brown 
9753cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9763cc72986SMark Brown 				   ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
9773cc72986SMark Brown 				   ARIZONA_OUT1_MONO, val);
9783cc72986SMark Brown 	}
9793cc72986SMark Brown 
9803cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
9813cc72986SMark Brown 		if (arizona->pdata.spk_mute[i])
9823cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
9832a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
9843cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_ENDIAN_MASK |
9853cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_SEQ1_MASK,
9863cc72986SMark Brown 					   arizona->pdata.spk_mute[i]);
9873cc72986SMark Brown 
9883cc72986SMark Brown 		if (arizona->pdata.spk_fmt[i])
9893cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
9902a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
9913cc72986SMark Brown 					   ARIZONA_SPK1_FMT_MASK,
9923cc72986SMark Brown 					   arizona->pdata.spk_fmt[i]);
9933cc72986SMark Brown 	}
9943cc72986SMark Brown 
9953cc72986SMark Brown 	/* Set up for interrupts */
9963cc72986SMark Brown 	ret = arizona_irq_init(arizona);
9973cc72986SMark Brown 	if (ret != 0)
99859db9691SMark Brown 		goto err_reset;
9993cc72986SMark Brown 
10003cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
10013cc72986SMark Brown 			    arizona_clkgen_err, arizona);
10023cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
10033cc72986SMark Brown 			    arizona_overclocked, arizona);
10043cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
10053cc72986SMark Brown 			    arizona_underclocked, arizona);
10063cc72986SMark Brown 
10073cc72986SMark Brown 	switch (arizona->type) {
10083cc72986SMark Brown 	case WM5102:
10093cc72986SMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
10100848c94fSMark Brown 				      ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
10113cc72986SMark Brown 		break;
1012e102befeSMark Brown 	case WM5110:
1013e102befeSMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
101478566afdSCharles Keepax 				      ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
1015e102befeSMark Brown 		break;
1016dc7d4863SCharles Keepax 	case WM8997:
1017dc7d4863SCharles Keepax 		ret = mfd_add_devices(arizona->dev, -1, wm8997_devs,
1018dc7d4863SCharles Keepax 				      ARRAY_SIZE(wm8997_devs), NULL, 0, NULL);
1019dc7d4863SCharles Keepax 		break;
10203cc72986SMark Brown 	}
10213cc72986SMark Brown 
10223cc72986SMark Brown 	if (ret != 0) {
10233cc72986SMark Brown 		dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
10243cc72986SMark Brown 		goto err_irq;
10253cc72986SMark Brown 	}
10263cc72986SMark Brown 
1027*48bb9fe4SRafael J. Wysocki #ifdef CONFIG_PM
102859db9691SMark Brown 	regulator_disable(arizona->dcvdd);
102959db9691SMark Brown #endif
103059db9691SMark Brown 
10313cc72986SMark Brown 	return 0;
10323cc72986SMark Brown 
10333cc72986SMark Brown err_irq:
10343cc72986SMark Brown 	arizona_irq_exit(arizona);
10353cc72986SMark Brown err_reset:
10363cc72986SMark Brown 	if (arizona->pdata.reset) {
103787d3af4aSMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 0);
10383cc72986SMark Brown 		gpio_free(arizona->pdata.reset);
10393cc72986SMark Brown 	}
104059db9691SMark Brown 	regulator_disable(arizona->dcvdd);
10413cc72986SMark Brown err_enable:
10423a36a0dbSMark Brown 	regulator_bulk_disable(arizona->num_core_supplies,
10433cc72986SMark Brown 			       arizona->core_supplies);
1044e6021511SCharles Keepax err_dcvdd:
1045e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
10463cc72986SMark Brown err_early:
10473cc72986SMark Brown 	mfd_remove_devices(dev);
10483cc72986SMark Brown 	return ret;
10493cc72986SMark Brown }
10503cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_init);
10513cc72986SMark Brown 
10524740f73fSBill Pemberton int arizona_dev_exit(struct arizona *arizona)
10533cc72986SMark Brown {
1054b804020aSCharles Keepax 	pm_runtime_disable(arizona->dev);
1055b804020aSCharles Keepax 
1056df6b3352SCharles Keepax 	regulator_disable(arizona->dcvdd);
1057e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
1058df6b3352SCharles Keepax 
10593cc72986SMark Brown 	mfd_remove_devices(arizona->dev);
10603cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
10613cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
10623cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
10633cc72986SMark Brown 	arizona_irq_exit(arizona);
10641d017b6bSMark Brown 	if (arizona->pdata.reset)
10651d017b6bSMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 0);
1066df6b3352SCharles Keepax 
10674420286eSCharles Keepax 	regulator_bulk_disable(arizona->num_core_supplies,
10681d017b6bSMark Brown 			       arizona->core_supplies);
10693cc72986SMark Brown 	return 0;
10703cc72986SMark Brown }
10713cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_exit);
1072