xref: /linux/drivers/mfd/arizona-core.c (revision dc7d48635dd3c3fd5360238f7d2c697ff13abe7b)
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");
1263cc72986SMark Brown 	if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
1273cc72986SMark Brown 		dev_err(arizona->dev, "ISRC2 underclocked\n");
1283cc72986SMark Brown 	if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
1293cc72986SMark Brown 		dev_err(arizona->dev, "ISRC1 underclocked\n");
1303cc72986SMark Brown 	if (val & ARIZONA_FX_UNDERCLOCKED_STS)
1313cc72986SMark Brown 		dev_err(arizona->dev, "FX underclocked\n");
1323cc72986SMark Brown 	if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
1333cc72986SMark Brown 		dev_err(arizona->dev, "ASRC underclocked\n");
1343cc72986SMark Brown 	if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
1353cc72986SMark Brown 		dev_err(arizona->dev, "DAC underclocked\n");
1363cc72986SMark Brown 	if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
1373cc72986SMark Brown 		dev_err(arizona->dev, "ADC underclocked\n");
1383cc72986SMark Brown 	if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
139648a9880SMark Brown 		dev_err(arizona->dev, "Mixer dropped sample\n");
1403cc72986SMark Brown 
1413cc72986SMark Brown 	return IRQ_HANDLED;
1423cc72986SMark Brown }
1433cc72986SMark Brown 
1443cc72986SMark Brown static irqreturn_t arizona_overclocked(int irq, void *data)
1453cc72986SMark Brown {
1463cc72986SMark Brown 	struct arizona *arizona = data;
1473cc72986SMark Brown 	unsigned int val[2];
1483cc72986SMark Brown 	int ret;
1493cc72986SMark Brown 
1503cc72986SMark Brown 	ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
1513cc72986SMark Brown 			       &val[0], 2);
1523cc72986SMark Brown 	if (ret != 0) {
1533cc72986SMark Brown 		dev_err(arizona->dev, "Failed to read overclock status: %d\n",
1543cc72986SMark Brown 			ret);
1553cc72986SMark Brown 		return IRQ_NONE;
1563cc72986SMark Brown 	}
1573cc72986SMark Brown 
1583cc72986SMark Brown 	if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
1593cc72986SMark Brown 		dev_err(arizona->dev, "PWM overclocked\n");
1603cc72986SMark Brown 	if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
1613cc72986SMark Brown 		dev_err(arizona->dev, "FX core overclocked\n");
1623cc72986SMark Brown 	if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
1633cc72986SMark Brown 		dev_err(arizona->dev, "DAC SYS overclocked\n");
1643cc72986SMark Brown 	if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
1653cc72986SMark Brown 		dev_err(arizona->dev, "DAC WARP overclocked\n");
1663cc72986SMark Brown 	if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
1673cc72986SMark Brown 		dev_err(arizona->dev, "ADC overclocked\n");
1683cc72986SMark Brown 	if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
1693cc72986SMark Brown 		dev_err(arizona->dev, "Mixer overclocked\n");
1703cc72986SMark Brown 	if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
1713cc72986SMark Brown 		dev_err(arizona->dev, "AIF3 overclocked\n");
1723cc72986SMark Brown 	if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
1733cc72986SMark Brown 		dev_err(arizona->dev, "AIF2 overclocked\n");
1743cc72986SMark Brown 	if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
1753cc72986SMark Brown 		dev_err(arizona->dev, "AIF1 overclocked\n");
1763cc72986SMark Brown 	if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
1773cc72986SMark Brown 		dev_err(arizona->dev, "Pad control overclocked\n");
1783cc72986SMark Brown 
1793cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
1803cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
1813cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
1823cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus async overclocked\n");
1833cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
1843cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus sync overclocked\n");
1853cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
1863cc72986SMark Brown 		dev_err(arizona->dev, "ASRC async system overclocked\n");
1873cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
1883cc72986SMark Brown 		dev_err(arizona->dev, "ASRC async WARP overclocked\n");
1893cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
1903cc72986SMark Brown 		dev_err(arizona->dev, "ASRC sync system overclocked\n");
1913cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
1923cc72986SMark Brown 		dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
1933cc72986SMark Brown 	if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
1943cc72986SMark Brown 		dev_err(arizona->dev, "DSP1 overclocked\n");
1953cc72986SMark Brown 	if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
1963cc72986SMark Brown 		dev_err(arizona->dev, "ISRC2 overclocked\n");
1973cc72986SMark Brown 	if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
1983cc72986SMark Brown 		dev_err(arizona->dev, "ISRC1 overclocked\n");
1993cc72986SMark Brown 
2003cc72986SMark Brown 	return IRQ_HANDLED;
2013cc72986SMark Brown }
2023cc72986SMark Brown 
2039d53dfdcSCharles Keepax static int arizona_poll_reg(struct arizona *arizona,
2049d53dfdcSCharles Keepax 			    int timeout, unsigned int reg,
2059d53dfdcSCharles Keepax 			    unsigned int mask, unsigned int target)
2069d53dfdcSCharles Keepax {
2079d53dfdcSCharles Keepax 	unsigned int val = 0;
2089d53dfdcSCharles Keepax 	int ret, i;
2099d53dfdcSCharles Keepax 
2109d53dfdcSCharles Keepax 	for (i = 0; i < timeout; i++) {
2119d53dfdcSCharles Keepax 		ret = regmap_read(arizona->regmap, reg, &val);
2129d53dfdcSCharles Keepax 		if (ret != 0) {
2139d53dfdcSCharles Keepax 			dev_err(arizona->dev, "Failed to read reg %u: %d\n",
2149d53dfdcSCharles Keepax 				reg, ret);
2159d53dfdcSCharles Keepax 			continue;
2169d53dfdcSCharles Keepax 		}
2179d53dfdcSCharles Keepax 
2189d53dfdcSCharles Keepax 		if ((val & mask) == target)
2199d53dfdcSCharles Keepax 			return 0;
2209d53dfdcSCharles Keepax 
2219d53dfdcSCharles Keepax 		msleep(1);
2229d53dfdcSCharles Keepax 	}
2239d53dfdcSCharles Keepax 
2249d53dfdcSCharles Keepax 	dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val);
2259d53dfdcSCharles Keepax 	return -ETIMEDOUT;
2269d53dfdcSCharles Keepax }
2279d53dfdcSCharles Keepax 
2283cc72986SMark Brown static int arizona_wait_for_boot(struct arizona *arizona)
2293cc72986SMark Brown {
2309d53dfdcSCharles Keepax 	int ret;
2313cc72986SMark Brown 
2323cc72986SMark Brown 	/*
2333cc72986SMark Brown 	 * We can't use an interrupt as we need to runtime resume to do so,
2343cc72986SMark Brown 	 * we won't race with the interrupt handler as it'll be blocked on
2353cc72986SMark Brown 	 * runtime resume.
2363cc72986SMark Brown 	 */
2379d53dfdcSCharles Keepax 	ret = arizona_poll_reg(arizona, 5, ARIZONA_INTERRUPT_RAW_STATUS_5,
2389d53dfdcSCharles Keepax 			       ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS);
2393cc72986SMark Brown 
2409d53dfdcSCharles Keepax 	if (!ret)
2413cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
2423cc72986SMark Brown 			     ARIZONA_BOOT_DONE_STS);
2433cc72986SMark Brown 
2443cc72986SMark Brown 	pm_runtime_mark_last_busy(arizona->dev);
2453cc72986SMark Brown 
2469d53dfdcSCharles Keepax 	return ret;
2473cc72986SMark Brown }
2483cc72986SMark Brown 
249e80436bbSCharles Keepax static int arizona_apply_hardware_patch(struct arizona* arizona)
250e80436bbSCharles Keepax {
251e80436bbSCharles Keepax 	unsigned int fll, sysclk;
252e80436bbSCharles Keepax 	int ret, err;
253e80436bbSCharles Keepax 
254e80436bbSCharles Keepax 	regcache_cache_bypass(arizona->regmap, true);
255e80436bbSCharles Keepax 
256e80436bbSCharles Keepax 	/* Cache existing FLL and SYSCLK settings */
257e80436bbSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll);
258e80436bbSCharles Keepax 	if (ret != 0) {
259e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
260e80436bbSCharles Keepax 			ret);
261e80436bbSCharles Keepax 		return ret;
262e80436bbSCharles Keepax 	}
263e80436bbSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk);
264e80436bbSCharles Keepax 	if (ret != 0) {
265e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
266e80436bbSCharles Keepax 			ret);
267e80436bbSCharles Keepax 		return ret;
268e80436bbSCharles Keepax 	}
269e80436bbSCharles Keepax 
270e80436bbSCharles Keepax 	/* Start up SYSCLK using the FLL in free running mode */
271e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
272e80436bbSCharles Keepax 			ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
273e80436bbSCharles Keepax 	if (ret != 0) {
274e80436bbSCharles Keepax 		dev_err(arizona->dev,
275e80436bbSCharles Keepax 			"Failed to start FLL in freerunning mode: %d\n",
276e80436bbSCharles Keepax 			ret);
277e80436bbSCharles Keepax 		return ret;
278e80436bbSCharles Keepax 	}
279e80436bbSCharles Keepax 	ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
280e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS,
281e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS);
282e80436bbSCharles Keepax 	if (ret != 0) {
283e80436bbSCharles Keepax 		ret = -ETIMEDOUT;
284e80436bbSCharles Keepax 		goto err_fll;
285e80436bbSCharles Keepax 	}
286e80436bbSCharles Keepax 
287e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
288e80436bbSCharles Keepax 	if (ret != 0) {
289e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
290e80436bbSCharles Keepax 		goto err_fll;
291e80436bbSCharles Keepax 	}
292e80436bbSCharles Keepax 
293e80436bbSCharles Keepax 	/* Start the write sequencer and wait for it to finish */
294e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
295e80436bbSCharles Keepax 			ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
296e80436bbSCharles Keepax 	if (ret != 0) {
297e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
298e80436bbSCharles Keepax 			ret);
299e80436bbSCharles Keepax 		goto err_sysclk;
300e80436bbSCharles Keepax 	}
301e80436bbSCharles Keepax 	ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
302e80436bbSCharles Keepax 			       ARIZONA_WSEQ_BUSY, 0);
303e80436bbSCharles Keepax 	if (ret != 0) {
304e80436bbSCharles Keepax 		regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
305e80436bbSCharles Keepax 				ARIZONA_WSEQ_ABORT);
306e80436bbSCharles Keepax 		ret = -ETIMEDOUT;
307e80436bbSCharles Keepax 	}
308e80436bbSCharles Keepax 
309e80436bbSCharles Keepax err_sysclk:
310e80436bbSCharles Keepax 	err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk);
311e80436bbSCharles Keepax 	if (err != 0) {
312e80436bbSCharles Keepax 		dev_err(arizona->dev,
313e80436bbSCharles Keepax 			"Failed to re-apply old SYSCLK settings: %d\n",
314e80436bbSCharles Keepax 			err);
315e80436bbSCharles Keepax 	}
316e80436bbSCharles Keepax 
317e80436bbSCharles Keepax err_fll:
318e80436bbSCharles Keepax 	err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll);
319e80436bbSCharles Keepax 	if (err != 0) {
320e80436bbSCharles Keepax 		dev_err(arizona->dev,
321e80436bbSCharles Keepax 			"Failed to re-apply old FLL settings: %d\n",
322e80436bbSCharles Keepax 			err);
323e80436bbSCharles Keepax 	}
324e80436bbSCharles Keepax 
325e80436bbSCharles Keepax 	regcache_cache_bypass(arizona->regmap, false);
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 
44159db9691SMark Brown 	regulator_disable(arizona->dcvdd);
4423cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
4433cc72986SMark Brown 	regcache_mark_dirty(arizona->regmap);
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
504d781009cSMark Brown int 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)
509d781009cSMark Brown 		return (int)id->data;
510d781009cSMark Brown 	else
511d781009cSMark Brown 		return 0;
512d781009cSMark Brown }
513d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_get_type);
514d781009cSMark Brown 
515d781009cSMark Brown static int arizona_of_get_core_pdata(struct arizona *arizona)
516d781009cSMark Brown {
517d781009cSMark Brown 	int ret, i;
518d781009cSMark Brown 
519d781009cSMark Brown 	arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node,
520d781009cSMark Brown 						 "wlf,reset", 0);
521d781009cSMark Brown 	if (arizona->pdata.reset < 0)
522d781009cSMark Brown 		arizona->pdata.reset = 0;
523d781009cSMark Brown 
524d781009cSMark Brown 	arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node,
525d781009cSMark Brown 						  "wlf,ldoena", 0);
526d781009cSMark Brown 	if (arizona->pdata.ldoena < 0)
527d781009cSMark Brown 		arizona->pdata.ldoena = 0;
528d781009cSMark Brown 
529d781009cSMark Brown 	ret = of_property_read_u32_array(arizona->dev->of_node,
530d781009cSMark Brown 					 "wlf,gpio-defaults",
531d781009cSMark Brown 					 arizona->pdata.gpio_defaults,
532d781009cSMark Brown 					 ARRAY_SIZE(arizona->pdata.gpio_defaults));
533d781009cSMark Brown 	if (ret >= 0) {
534d781009cSMark Brown 		/*
535d781009cSMark Brown 		 * All values are literal except out of range values
536d781009cSMark Brown 		 * which are chip default, translate into platform
537d781009cSMark Brown 		 * data which uses 0 as chip default and out of range
538d781009cSMark Brown 		 * as zero.
539d781009cSMark Brown 		 */
540d781009cSMark Brown 		for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
541d781009cSMark Brown 			if (arizona->pdata.gpio_defaults[i] > 0xffff)
542d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0;
543d781009cSMark Brown 			if (arizona->pdata.gpio_defaults[i] == 0)
544d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0x10000;
545d781009cSMark Brown 		}
546d781009cSMark Brown 	} else {
547d781009cSMark Brown 		dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
548d781009cSMark Brown 			ret);
549d781009cSMark Brown 	}
550d781009cSMark Brown 
551d781009cSMark Brown 	return 0;
552d781009cSMark Brown }
553d781009cSMark Brown 
554d781009cSMark Brown const struct of_device_id arizona_of_match[] = {
555d781009cSMark Brown 	{ .compatible = "wlf,wm5102", .data = (void *)WM5102 },
556d781009cSMark Brown 	{ .compatible = "wlf,wm5110", .data = (void *)WM5110 },
557*dc7d4863SCharles Keepax 	{ .compatible = "wlf,wm8997", .data = (void *)WM8997 },
558d781009cSMark Brown 	{},
559d781009cSMark Brown };
560d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_match);
561d781009cSMark Brown #else
562d781009cSMark Brown static inline int arizona_of_get_core_pdata(struct arizona *arizona)
563d781009cSMark Brown {
564d781009cSMark Brown 	return 0;
565d781009cSMark Brown }
566d781009cSMark Brown #endif
567d781009cSMark Brown 
5683cc72986SMark Brown static struct mfd_cell early_devs[] = {
5693cc72986SMark Brown 	{ .name = "arizona-ldo1" },
5703cc72986SMark Brown };
5713cc72986SMark Brown 
5723cc72986SMark Brown static struct mfd_cell wm5102_devs[] = {
573d7768111SMark Brown 	{ .name = "arizona-micsupp" },
5743cc72986SMark Brown 	{ .name = "arizona-extcon" },
5753cc72986SMark Brown 	{ .name = "arizona-gpio" },
576503b1cacSMark Brown 	{ .name = "arizona-haptics" },
5773cc72986SMark Brown 	{ .name = "arizona-pwm" },
5783cc72986SMark Brown 	{ .name = "wm5102-codec" },
5793cc72986SMark Brown };
5803cc72986SMark Brown 
581e102befeSMark Brown static struct mfd_cell wm5110_devs[] = {
582d7768111SMark Brown 	{ .name = "arizona-micsupp" },
583e102befeSMark Brown 	{ .name = "arizona-extcon" },
584e102befeSMark Brown 	{ .name = "arizona-gpio" },
585503b1cacSMark Brown 	{ .name = "arizona-haptics" },
586e102befeSMark Brown 	{ .name = "arizona-pwm" },
587e102befeSMark Brown 	{ .name = "wm5110-codec" },
588e102befeSMark Brown };
589e102befeSMark Brown 
590*dc7d4863SCharles Keepax static struct mfd_cell wm8997_devs[] = {
591*dc7d4863SCharles Keepax 	{ .name = "arizona-micsupp" },
592*dc7d4863SCharles Keepax 	{ .name = "arizona-extcon" },
593*dc7d4863SCharles Keepax 	{ .name = "arizona-gpio" },
594*dc7d4863SCharles Keepax 	{ .name = "arizona-haptics" },
595*dc7d4863SCharles Keepax 	{ .name = "arizona-pwm" },
596*dc7d4863SCharles Keepax 	{ .name = "wm8997-codec" },
597*dc7d4863SCharles Keepax };
598*dc7d4863SCharles Keepax 
599f791be49SBill Pemberton int arizona_dev_init(struct arizona *arizona)
6003cc72986SMark Brown {
6013cc72986SMark Brown 	struct device *dev = arizona->dev;
6023cc72986SMark Brown 	const char *type_name;
6033cc72986SMark Brown 	unsigned int reg, val;
60462d62b59SMark Brown 	int (*apply_patch)(struct arizona *) = NULL;
6053cc72986SMark Brown 	int ret, i;
6063cc72986SMark Brown 
6073cc72986SMark Brown 	dev_set_drvdata(arizona->dev, arizona);
6083cc72986SMark Brown 	mutex_init(&arizona->clk_lock);
6093cc72986SMark Brown 
610d781009cSMark Brown 	arizona_of_get_core_pdata(arizona);
611d781009cSMark Brown 
6123cc72986SMark Brown 	if (dev_get_platdata(arizona->dev))
6133cc72986SMark Brown 		memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
6143cc72986SMark Brown 		       sizeof(arizona->pdata));
6153cc72986SMark Brown 
6163cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
6173cc72986SMark Brown 
6183cc72986SMark Brown 	switch (arizona->type) {
6193cc72986SMark Brown 	case WM5102:
620e102befeSMark Brown 	case WM5110:
621*dc7d4863SCharles Keepax 	case WM8997:
6223cc72986SMark Brown 		for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
6233cc72986SMark Brown 			arizona->core_supplies[i].supply
6243cc72986SMark Brown 				= wm5102_core_supplies[i];
6253cc72986SMark Brown 		arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
6263cc72986SMark Brown 		break;
6273cc72986SMark Brown 	default:
6283cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device type %d\n",
6293cc72986SMark Brown 			arizona->type);
6303cc72986SMark Brown 		return -EINVAL;
6313cc72986SMark Brown 	}
6323cc72986SMark Brown 
6333cc72986SMark Brown 	ret = mfd_add_devices(arizona->dev, -1, early_devs,
6340848c94fSMark Brown 			      ARRAY_SIZE(early_devs), NULL, 0, NULL);
6353cc72986SMark Brown 	if (ret != 0) {
6363cc72986SMark Brown 		dev_err(dev, "Failed to add early children: %d\n", ret);
6373cc72986SMark Brown 		return ret;
6383cc72986SMark Brown 	}
6393cc72986SMark Brown 
6403cc72986SMark Brown 	ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
6413cc72986SMark Brown 				      arizona->core_supplies);
6423cc72986SMark Brown 	if (ret != 0) {
6433cc72986SMark Brown 		dev_err(dev, "Failed to request core supplies: %d\n",
6443cc72986SMark Brown 			ret);
6453cc72986SMark Brown 		goto err_early;
6463cc72986SMark Brown 	}
6473cc72986SMark Brown 
64859db9691SMark Brown 	arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD");
64959db9691SMark Brown 	if (IS_ERR(arizona->dcvdd)) {
65059db9691SMark Brown 		ret = PTR_ERR(arizona->dcvdd);
65159db9691SMark Brown 		dev_err(dev, "Failed to request DCVDD: %d\n", ret);
65259db9691SMark Brown 		goto err_early;
65359db9691SMark Brown 	}
65459db9691SMark Brown 
65587d3af4aSMark Brown 	if (arizona->pdata.reset) {
65687d3af4aSMark Brown 		/* Start out with /RESET low to put the chip into reset */
65787d3af4aSMark Brown 		ret = gpio_request_one(arizona->pdata.reset,
65887d3af4aSMark Brown 				       GPIOF_DIR_OUT | GPIOF_INIT_LOW,
65987d3af4aSMark Brown 				       "arizona /RESET");
66087d3af4aSMark Brown 		if (ret != 0) {
66187d3af4aSMark Brown 			dev_err(dev, "Failed to request /RESET: %d\n", ret);
66287d3af4aSMark Brown 			goto err_early;
66387d3af4aSMark Brown 		}
66487d3af4aSMark Brown 	}
66587d3af4aSMark Brown 
6663cc72986SMark Brown 	ret = regulator_bulk_enable(arizona->num_core_supplies,
6673cc72986SMark Brown 				    arizona->core_supplies);
6683cc72986SMark Brown 	if (ret != 0) {
6693cc72986SMark Brown 		dev_err(dev, "Failed to enable core supplies: %d\n",
6703cc72986SMark Brown 			ret);
6713cc72986SMark Brown 		goto err_early;
6723cc72986SMark Brown 	}
6733cc72986SMark Brown 
67459db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
67559db9691SMark Brown 	if (ret != 0) {
67659db9691SMark Brown 		dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
67759db9691SMark Brown 		goto err_enable;
67859db9691SMark Brown 	}
67959db9691SMark Brown 
680c25feaa5SCharles Keepax 	if (arizona->pdata.reset) {
6813cc72986SMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 1);
682c25feaa5SCharles Keepax 		msleep(1);
683c25feaa5SCharles Keepax 	}
6843cc72986SMark Brown 
6853cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
6863cc72986SMark Brown 
687ca76ceb8SMark Brown 	/* Verify that this is a chip we know about */
688ca76ceb8SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
689ca76ceb8SMark Brown 	if (ret != 0) {
690ca76ceb8SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
691ca76ceb8SMark Brown 		goto err_reset;
692ca76ceb8SMark Brown 	}
693ca76ceb8SMark Brown 
694ca76ceb8SMark Brown 	switch (reg) {
695ca76ceb8SMark Brown 	case 0x5102:
696ca76ceb8SMark Brown 	case 0x5110:
697*dc7d4863SCharles Keepax 	case 0x8997:
698ca76ceb8SMark Brown 		break;
699ca76ceb8SMark Brown 	default:
700ca76ceb8SMark Brown 		dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
701ca76ceb8SMark Brown 		goto err_reset;
702ca76ceb8SMark Brown 	}
703ca76ceb8SMark Brown 
704ca76ceb8SMark Brown 	/* If we have a /RESET GPIO we'll already be reset */
705ca76ceb8SMark Brown 	if (!arizona->pdata.reset) {
706ca76ceb8SMark Brown 		regcache_mark_dirty(arizona->regmap);
707ca76ceb8SMark Brown 
708ca76ceb8SMark Brown 		ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
709ca76ceb8SMark Brown 		if (ret != 0) {
710ca76ceb8SMark Brown 			dev_err(dev, "Failed to reset device: %d\n", ret);
711ca76ceb8SMark Brown 			goto err_reset;
712ca76ceb8SMark Brown 		}
713ca76ceb8SMark Brown 
714ca76ceb8SMark Brown 		msleep(1);
715ca76ceb8SMark Brown 
716ca76ceb8SMark Brown 		ret = regcache_sync(arizona->regmap);
717ca76ceb8SMark Brown 		if (ret != 0) {
718ca76ceb8SMark Brown 			dev_err(dev, "Failed to sync device: %d\n", ret);
719ca76ceb8SMark Brown 			goto err_reset;
720ca76ceb8SMark Brown 		}
721ca76ceb8SMark Brown 	}
722ca76ceb8SMark Brown 
723ca76ceb8SMark Brown 	/* Ensure device startup is complete */
724ca76ceb8SMark Brown 	switch (arizona->type) {
725ca76ceb8SMark Brown 	case WM5102:
726ca76ceb8SMark Brown 		ret = regmap_read(arizona->regmap, 0x19, &val);
727ca76ceb8SMark Brown 		if (ret != 0)
728ca76ceb8SMark Brown 			dev_err(dev,
729ca76ceb8SMark Brown 				"Failed to check write sequencer state: %d\n",
730ca76ceb8SMark Brown 				ret);
731ca76ceb8SMark Brown 		else if (val & 0x01)
732ca76ceb8SMark Brown 			break;
733ca76ceb8SMark Brown 		/* Fall through */
734ca76ceb8SMark Brown 	default:
735ca76ceb8SMark Brown 		ret = arizona_wait_for_boot(arizona);
736ca76ceb8SMark Brown 		if (ret != 0) {
737ca76ceb8SMark Brown 			dev_err(arizona->dev,
738ca76ceb8SMark Brown 				"Device failed initial boot: %d\n", ret);
739ca76ceb8SMark Brown 			goto err_reset;
740ca76ceb8SMark Brown 		}
741ca76ceb8SMark Brown 		break;
742ca76ceb8SMark Brown 	}
743ca76ceb8SMark Brown 
744ca76ceb8SMark Brown 	/* Read the device ID information & do device specific stuff */
7453cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
7463cc72986SMark Brown 	if (ret != 0) {
7473cc72986SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
74859db9691SMark Brown 		goto err_reset;
7493cc72986SMark Brown 	}
7503cc72986SMark Brown 
7513cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
7523cc72986SMark Brown 			  &arizona->rev);
7533cc72986SMark Brown 	if (ret != 0) {
7543cc72986SMark Brown 		dev_err(dev, "Failed to read revision register: %d\n", ret);
75559db9691SMark Brown 		goto err_reset;
7563cc72986SMark Brown 	}
7573cc72986SMark Brown 	arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
7583cc72986SMark Brown 
7593cc72986SMark Brown 	switch (reg) {
760863df8d5SMark Brown #ifdef CONFIG_MFD_WM5102
7613cc72986SMark Brown 	case 0x5102:
7623cc72986SMark Brown 		type_name = "WM5102";
7633cc72986SMark Brown 		if (arizona->type != WM5102) {
7643cc72986SMark Brown 			dev_err(arizona->dev, "WM5102 registered as %d\n",
7653cc72986SMark Brown 				arizona->type);
7663cc72986SMark Brown 			arizona->type = WM5102;
7673cc72986SMark Brown 		}
76862d62b59SMark Brown 		apply_patch = wm5102_patch;
769c6d6bfb1SMark Brown 		arizona->rev &= 0x7;
7703cc72986SMark Brown 		break;
771863df8d5SMark Brown #endif
772e102befeSMark Brown #ifdef CONFIG_MFD_WM5110
773e102befeSMark Brown 	case 0x5110:
774e102befeSMark Brown 		type_name = "WM5110";
775e102befeSMark Brown 		if (arizona->type != WM5110) {
776e102befeSMark Brown 			dev_err(arizona->dev, "WM5110 registered as %d\n",
777e102befeSMark Brown 				arizona->type);
778e102befeSMark Brown 			arizona->type = WM5110;
779e102befeSMark Brown 		}
78062d62b59SMark Brown 		apply_patch = wm5110_patch;
781e102befeSMark Brown 		break;
782e102befeSMark Brown #endif
783*dc7d4863SCharles Keepax #ifdef CONFIG_MFD_WM8997
784*dc7d4863SCharles Keepax 	case 0x8997:
785*dc7d4863SCharles Keepax 		type_name = "WM8997";
786*dc7d4863SCharles Keepax 		if (arizona->type != WM8997) {
787*dc7d4863SCharles Keepax 			dev_err(arizona->dev, "WM8997 registered as %d\n",
788*dc7d4863SCharles Keepax 				arizona->type);
789*dc7d4863SCharles Keepax 			arizona->type = WM8997;
790*dc7d4863SCharles Keepax 		}
791*dc7d4863SCharles Keepax 		apply_patch = wm8997_patch;
792*dc7d4863SCharles Keepax 		break;
793*dc7d4863SCharles Keepax #endif
7943cc72986SMark Brown 	default:
7953cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device ID %x\n", reg);
79659db9691SMark Brown 		goto err_reset;
7973cc72986SMark Brown 	}
7983cc72986SMark Brown 
7993cc72986SMark Brown 	dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
8003cc72986SMark Brown 
80162d62b59SMark Brown 	if (apply_patch) {
80262d62b59SMark Brown 		ret = apply_patch(arizona);
80362d62b59SMark Brown 		if (ret != 0) {
80462d62b59SMark Brown 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
80562d62b59SMark Brown 				ret);
80662d62b59SMark Brown 			goto err_reset;
80762d62b59SMark Brown 		}
808e80436bbSCharles Keepax 
809e80436bbSCharles Keepax 		switch (arizona->type) {
810e80436bbSCharles Keepax 		case WM5102:
811e80436bbSCharles Keepax 			ret = arizona_apply_hardware_patch(arizona);
812e80436bbSCharles Keepax 			if (ret != 0) {
813e80436bbSCharles Keepax 				dev_err(arizona->dev,
814e80436bbSCharles Keepax 					"Failed to apply hardware patch: %d\n",
815e80436bbSCharles Keepax 					ret);
816e80436bbSCharles Keepax 				goto err_reset;
817e80436bbSCharles Keepax 			}
818e80436bbSCharles Keepax 			break;
819e80436bbSCharles Keepax 		default:
820e80436bbSCharles Keepax 			break;
821e80436bbSCharles Keepax 		}
82262d62b59SMark Brown 	}
82362d62b59SMark Brown 
8243cc72986SMark Brown 	for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
8253cc72986SMark Brown 		if (!arizona->pdata.gpio_defaults[i])
8263cc72986SMark Brown 			continue;
8273cc72986SMark Brown 
8283cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
8293cc72986SMark Brown 			     arizona->pdata.gpio_defaults[i]);
8303cc72986SMark Brown 	}
8313cc72986SMark Brown 
8325927467dSMark Brown 	/*
8335927467dSMark Brown 	 * LDO1 can only be used to supply DCVDD so if it has no
8345927467dSMark Brown 	 * consumers then DCVDD is supplied externally.
8355927467dSMark Brown 	 */
8365927467dSMark Brown 	if (arizona->pdata.ldo1 &&
8375927467dSMark Brown 	    arizona->pdata.ldo1->num_consumer_supplies == 0)
8385927467dSMark Brown 		arizona->external_dcvdd = true;
8395927467dSMark Brown 
8403cc72986SMark Brown 	pm_runtime_set_autosuspend_delay(arizona->dev, 100);
8413cc72986SMark Brown 	pm_runtime_use_autosuspend(arizona->dev);
8423cc72986SMark Brown 	pm_runtime_enable(arizona->dev);
8433cc72986SMark Brown 
8443cc72986SMark Brown 	/* Chip default */
8453cc72986SMark Brown 	if (!arizona->pdata.clk32k_src)
8463cc72986SMark Brown 		arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
8473cc72986SMark Brown 
8483cc72986SMark Brown 	switch (arizona->pdata.clk32k_src) {
8493cc72986SMark Brown 	case ARIZONA_32KZ_MCLK1:
8503cc72986SMark Brown 	case ARIZONA_32KZ_MCLK2:
8513cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
8523cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK,
8533cc72986SMark Brown 				   arizona->pdata.clk32k_src - 1);
854767c6dc0SMark Brown 		arizona_clk32k_enable(arizona);
8553cc72986SMark Brown 		break;
8563cc72986SMark Brown 	case ARIZONA_32KZ_NONE:
8573cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
8583cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK, 2);
8593cc72986SMark Brown 		break;
8603cc72986SMark Brown 	default:
8613cc72986SMark Brown 		dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
8623cc72986SMark Brown 			arizona->pdata.clk32k_src);
8633cc72986SMark Brown 		ret = -EINVAL;
86459db9691SMark Brown 		goto err_reset;
8653cc72986SMark Brown 	}
8663cc72986SMark Brown 
8673d91f828SMark Brown 	for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
868544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV &&
869544c7aadSMark Brown 		    !arizona->pdata.micbias[i].bypass)
8703d91f828SMark Brown 			continue;
8713d91f828SMark Brown 
872544c7aadSMark Brown 		/* Apply default for bypass mode */
873544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV)
874544c7aadSMark Brown 			arizona->pdata.micbias[i].mV = 2800;
875544c7aadSMark Brown 
8763d91f828SMark Brown 		val = (arizona->pdata.micbias[i].mV - 1500) / 100;
877544c7aadSMark Brown 
8783d91f828SMark Brown 		val <<= ARIZONA_MICB1_LVL_SHIFT;
8793d91f828SMark Brown 
8803d91f828SMark Brown 		if (arizona->pdata.micbias[i].ext_cap)
8813d91f828SMark Brown 			val |= ARIZONA_MICB1_EXT_CAP;
8823d91f828SMark Brown 
8833d91f828SMark Brown 		if (arizona->pdata.micbias[i].discharge)
8843d91f828SMark Brown 			val |= ARIZONA_MICB1_DISCH;
8853d91f828SMark Brown 
886f773fc6dSCharles Keepax 		if (arizona->pdata.micbias[i].soft_start)
8873d91f828SMark Brown 			val |= ARIZONA_MICB1_RATE;
8883d91f828SMark Brown 
889544c7aadSMark Brown 		if (arizona->pdata.micbias[i].bypass)
890544c7aadSMark Brown 			val |= ARIZONA_MICB1_BYPASS;
891544c7aadSMark Brown 
8923d91f828SMark Brown 		regmap_update_bits(arizona->regmap,
8933d91f828SMark Brown 				   ARIZONA_MIC_BIAS_CTRL_1 + i,
8943d91f828SMark Brown 				   ARIZONA_MICB1_LVL_MASK |
8953d91f828SMark Brown 				   ARIZONA_MICB1_DISCH |
896544c7aadSMark Brown 				   ARIZONA_MICB1_BYPASS |
8973d91f828SMark Brown 				   ARIZONA_MICB1_RATE, val);
8983d91f828SMark Brown 	}
8993d91f828SMark Brown 
9003cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
9013cc72986SMark Brown 		/* Default for both is 0 so noop with defaults */
9023cc72986SMark Brown 		val = arizona->pdata.dmic_ref[i]
9033cc72986SMark Brown 			<< ARIZONA_IN1_DMIC_SUP_SHIFT;
9043cc72986SMark Brown 		val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
9053cc72986SMark Brown 
9063cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9073cc72986SMark Brown 				   ARIZONA_IN1L_CONTROL + (i * 8),
9083cc72986SMark Brown 				   ARIZONA_IN1_DMIC_SUP_MASK |
9093cc72986SMark Brown 				   ARIZONA_IN1_MODE_MASK, val);
9103cc72986SMark Brown 	}
9113cc72986SMark Brown 
9123cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
9133cc72986SMark Brown 		/* Default is 0 so noop with defaults */
9143cc72986SMark Brown 		if (arizona->pdata.out_mono[i])
9153cc72986SMark Brown 			val = ARIZONA_OUT1_MONO;
9163cc72986SMark Brown 		else
9173cc72986SMark Brown 			val = 0;
9183cc72986SMark Brown 
9193cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9203cc72986SMark Brown 				   ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
9213cc72986SMark Brown 				   ARIZONA_OUT1_MONO, val);
9223cc72986SMark Brown 	}
9233cc72986SMark Brown 
9243cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
9253cc72986SMark Brown 		if (arizona->pdata.spk_mute[i])
9263cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
9272a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
9283cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_ENDIAN_MASK |
9293cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_SEQ1_MASK,
9303cc72986SMark Brown 					   arizona->pdata.spk_mute[i]);
9313cc72986SMark Brown 
9323cc72986SMark Brown 		if (arizona->pdata.spk_fmt[i])
9333cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
9342a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
9353cc72986SMark Brown 					   ARIZONA_SPK1_FMT_MASK,
9363cc72986SMark Brown 					   arizona->pdata.spk_fmt[i]);
9373cc72986SMark Brown 	}
9383cc72986SMark Brown 
9393cc72986SMark Brown 	/* Set up for interrupts */
9403cc72986SMark Brown 	ret = arizona_irq_init(arizona);
9413cc72986SMark Brown 	if (ret != 0)
94259db9691SMark Brown 		goto err_reset;
9433cc72986SMark Brown 
9443cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
9453cc72986SMark Brown 			    arizona_clkgen_err, arizona);
9463cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
9473cc72986SMark Brown 			    arizona_overclocked, arizona);
9483cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
9493cc72986SMark Brown 			    arizona_underclocked, arizona);
9503cc72986SMark Brown 
9513cc72986SMark Brown 	switch (arizona->type) {
9523cc72986SMark Brown 	case WM5102:
9533cc72986SMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
9540848c94fSMark Brown 				      ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
9553cc72986SMark Brown 		break;
956e102befeSMark Brown 	case WM5110:
957e102befeSMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
95878566afdSCharles Keepax 				      ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
959e102befeSMark Brown 		break;
960*dc7d4863SCharles Keepax 	case WM8997:
961*dc7d4863SCharles Keepax 		ret = mfd_add_devices(arizona->dev, -1, wm8997_devs,
962*dc7d4863SCharles Keepax 				      ARRAY_SIZE(wm8997_devs), NULL, 0, NULL);
963*dc7d4863SCharles Keepax 		break;
9643cc72986SMark Brown 	}
9653cc72986SMark Brown 
9663cc72986SMark Brown 	if (ret != 0) {
9673cc72986SMark Brown 		dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
9683cc72986SMark Brown 		goto err_irq;
9693cc72986SMark Brown 	}
9703cc72986SMark Brown 
97159db9691SMark Brown #ifdef CONFIG_PM_RUNTIME
97259db9691SMark Brown 	regulator_disable(arizona->dcvdd);
97359db9691SMark Brown #endif
97459db9691SMark Brown 
9753cc72986SMark Brown 	return 0;
9763cc72986SMark Brown 
9773cc72986SMark Brown err_irq:
9783cc72986SMark Brown 	arizona_irq_exit(arizona);
9793cc72986SMark Brown err_reset:
9803cc72986SMark Brown 	if (arizona->pdata.reset) {
98187d3af4aSMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 0);
9823cc72986SMark Brown 		gpio_free(arizona->pdata.reset);
9833cc72986SMark Brown 	}
98459db9691SMark Brown 	regulator_disable(arizona->dcvdd);
9853cc72986SMark Brown err_enable:
9863a36a0dbSMark Brown 	regulator_bulk_disable(arizona->num_core_supplies,
9873cc72986SMark Brown 			       arizona->core_supplies);
9883cc72986SMark Brown err_early:
9893cc72986SMark Brown 	mfd_remove_devices(dev);
9903cc72986SMark Brown 	return ret;
9913cc72986SMark Brown }
9923cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_init);
9933cc72986SMark Brown 
9944740f73fSBill Pemberton int arizona_dev_exit(struct arizona *arizona)
9953cc72986SMark Brown {
9963cc72986SMark Brown 	mfd_remove_devices(arizona->dev);
9973cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
9983cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
9993cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
10003cc72986SMark Brown 	pm_runtime_disable(arizona->dev);
10013cc72986SMark Brown 	arizona_irq_exit(arizona);
10021d017b6bSMark Brown 	if (arizona->pdata.reset)
10031d017b6bSMark Brown 		gpio_set_value_cansleep(arizona->pdata.reset, 0);
10041d017b6bSMark Brown 	regulator_disable(arizona->dcvdd);
10051d017b6bSMark Brown 	regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies),
10061d017b6bSMark Brown 			       arizona->core_supplies);
10073cc72986SMark Brown 	return 0;
10083cc72986SMark Brown }
10093cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_exit);
1010