xref: /linux/drivers/mfd/arizona-core.c (revision 2229875dc00f38b95d04fecfc67858ba2221a563)
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 
253*2229875dSCharles Keepax static inline void arizona_enable_reset(struct arizona *arizona)
254*2229875dSCharles Keepax {
255*2229875dSCharles Keepax 	if (arizona->pdata.reset)
256*2229875dSCharles Keepax 		gpio_set_value_cansleep(arizona->pdata.reset, 0);
257*2229875dSCharles Keepax }
258*2229875dSCharles Keepax 
259*2229875dSCharles Keepax static void arizona_disable_reset(struct arizona *arizona)
260*2229875dSCharles Keepax {
261*2229875dSCharles Keepax 	if (arizona->pdata.reset) {
262*2229875dSCharles Keepax 		gpio_set_value_cansleep(arizona->pdata.reset, 1);
263*2229875dSCharles Keepax 		msleep(1);
264*2229875dSCharles Keepax 	}
265*2229875dSCharles Keepax }
266*2229875dSCharles Keepax 
267e80436bbSCharles Keepax static int arizona_apply_hardware_patch(struct arizona* arizona)
268e80436bbSCharles Keepax {
269e80436bbSCharles Keepax 	unsigned int fll, sysclk;
270e80436bbSCharles Keepax 	int ret, err;
271e80436bbSCharles Keepax 
272e80436bbSCharles Keepax 	/* Cache existing FLL and SYSCLK settings */
273e80436bbSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll);
274e80436bbSCharles Keepax 	if (ret != 0) {
275e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
276e80436bbSCharles Keepax 			ret);
277e80436bbSCharles Keepax 		return ret;
278e80436bbSCharles Keepax 	}
279e80436bbSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk);
280e80436bbSCharles Keepax 	if (ret != 0) {
281e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
282e80436bbSCharles Keepax 			ret);
283e80436bbSCharles Keepax 		return ret;
284e80436bbSCharles Keepax 	}
285e80436bbSCharles Keepax 
286e80436bbSCharles Keepax 	/* Start up SYSCLK using the FLL in free running mode */
287e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
288e80436bbSCharles Keepax 			ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
289e80436bbSCharles Keepax 	if (ret != 0) {
290e80436bbSCharles Keepax 		dev_err(arizona->dev,
291e80436bbSCharles Keepax 			"Failed to start FLL in freerunning mode: %d\n",
292e80436bbSCharles Keepax 			ret);
293e80436bbSCharles Keepax 		return ret;
294e80436bbSCharles Keepax 	}
295e80436bbSCharles Keepax 	ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
296e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS,
297e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS);
298e80436bbSCharles Keepax 	if (ret != 0) {
299e80436bbSCharles Keepax 		ret = -ETIMEDOUT;
300e80436bbSCharles Keepax 		goto err_fll;
301e80436bbSCharles Keepax 	}
302e80436bbSCharles Keepax 
303e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
304e80436bbSCharles Keepax 	if (ret != 0) {
305e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
306e80436bbSCharles Keepax 		goto err_fll;
307e80436bbSCharles Keepax 	}
308e80436bbSCharles Keepax 
309e80436bbSCharles Keepax 	/* Start the write sequencer and wait for it to finish */
310e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
311e80436bbSCharles Keepax 			ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
312e80436bbSCharles Keepax 	if (ret != 0) {
313e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
314e80436bbSCharles Keepax 			ret);
315e80436bbSCharles Keepax 		goto err_sysclk;
316e80436bbSCharles Keepax 	}
317e80436bbSCharles Keepax 	ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
318e80436bbSCharles Keepax 			       ARIZONA_WSEQ_BUSY, 0);
319e80436bbSCharles Keepax 	if (ret != 0) {
320e80436bbSCharles Keepax 		regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
321e80436bbSCharles Keepax 				ARIZONA_WSEQ_ABORT);
322e80436bbSCharles Keepax 		ret = -ETIMEDOUT;
323e80436bbSCharles Keepax 	}
324e80436bbSCharles Keepax 
325e80436bbSCharles Keepax err_sysclk:
326e80436bbSCharles Keepax 	err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk);
327e80436bbSCharles Keepax 	if (err != 0) {
328e80436bbSCharles Keepax 		dev_err(arizona->dev,
329e80436bbSCharles Keepax 			"Failed to re-apply old SYSCLK settings: %d\n",
330e80436bbSCharles Keepax 			err);
331e80436bbSCharles Keepax 	}
332e80436bbSCharles Keepax 
333e80436bbSCharles Keepax err_fll:
334e80436bbSCharles Keepax 	err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll);
335e80436bbSCharles Keepax 	if (err != 0) {
336e80436bbSCharles Keepax 		dev_err(arizona->dev,
337e80436bbSCharles Keepax 			"Failed to re-apply old FLL settings: %d\n",
338e80436bbSCharles Keepax 			err);
339e80436bbSCharles Keepax 	}
340e80436bbSCharles Keepax 
341e80436bbSCharles Keepax 	if (ret != 0)
342e80436bbSCharles Keepax 		return ret;
343e80436bbSCharles Keepax 	else
344e80436bbSCharles Keepax 		return err;
345e80436bbSCharles Keepax }
346e80436bbSCharles Keepax 
34748bb9fe4SRafael J. Wysocki #ifdef CONFIG_PM
3483cc72986SMark Brown static int arizona_runtime_resume(struct device *dev)
3493cc72986SMark Brown {
3503cc72986SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
3513cc72986SMark Brown 	int ret;
3523cc72986SMark Brown 
353508c8299SMark Brown 	dev_dbg(arizona->dev, "Leaving AoD mode\n");
354508c8299SMark Brown 
35559db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
35659db9691SMark Brown 	if (ret != 0) {
35759db9691SMark Brown 		dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
35859db9691SMark Brown 		return ret;
35959db9691SMark Brown 	}
3603cc72986SMark Brown 
3613cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
3623cc72986SMark Brown 
3634c9bb8bcSCharles Keepax 	switch (arizona->type) {
3644c9bb8bcSCharles Keepax 	case WM5102:
3655927467dSMark Brown 		if (arizona->external_dcvdd) {
3665927467dSMark Brown 			ret = regmap_update_bits(arizona->regmap,
3675927467dSMark Brown 						 ARIZONA_ISOLATION_CONTROL,
3685927467dSMark Brown 						 ARIZONA_ISOLATE_DCVDD1, 0);
3695927467dSMark Brown 			if (ret != 0) {
3705927467dSMark Brown 				dev_err(arizona->dev,
3715927467dSMark Brown 					"Failed to connect DCVDD: %d\n", ret);
3725927467dSMark Brown 				goto err;
3735927467dSMark Brown 			}
3745927467dSMark Brown 		}
3755927467dSMark Brown 
3764c9bb8bcSCharles Keepax 		ret = wm5102_patch(arizona);
3774c9bb8bcSCharles Keepax 		if (ret != 0) {
3784c9bb8bcSCharles Keepax 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
3794c9bb8bcSCharles Keepax 				ret);
3804c9bb8bcSCharles Keepax 			goto err;
3814c9bb8bcSCharles Keepax 		}
382e80436bbSCharles Keepax 
383e80436bbSCharles Keepax 		ret = arizona_apply_hardware_patch(arizona);
384e80436bbSCharles Keepax 		if (ret != 0) {
385e80436bbSCharles Keepax 			dev_err(arizona->dev,
386e80436bbSCharles Keepax 				"Failed to apply hardware patch: %d\n",
387e80436bbSCharles Keepax 				ret);
388e80436bbSCharles Keepax 			goto err;
389e80436bbSCharles Keepax 		}
390e80436bbSCharles Keepax 		break;
391e80436bbSCharles Keepax 	default:
39212bb68edSCharles Keepax 		ret = arizona_wait_for_boot(arizona);
39312bb68edSCharles Keepax 		if (ret != 0) {
39412bb68edSCharles Keepax 			goto err;
39512bb68edSCharles Keepax 		}
39612bb68edSCharles Keepax 
3975927467dSMark Brown 		if (arizona->external_dcvdd) {
3985927467dSMark Brown 			ret = regmap_update_bits(arizona->regmap,
3995927467dSMark Brown 						 ARIZONA_ISOLATION_CONTROL,
4005927467dSMark Brown 						 ARIZONA_ISOLATE_DCVDD1, 0);
4015927467dSMark Brown 			if (ret != 0) {
4025927467dSMark Brown 				dev_err(arizona->dev,
4035927467dSMark Brown 					"Failed to connect DCVDD: %d\n", ret);
4045927467dSMark Brown 				goto err;
4055927467dSMark Brown 			}
4065927467dSMark Brown 		}
407e80436bbSCharles Keepax 		break;
4084c9bb8bcSCharles Keepax 	}
4094c9bb8bcSCharles Keepax 
4109270bdf5SMark Brown 	ret = regcache_sync(arizona->regmap);
4119270bdf5SMark Brown 	if (ret != 0) {
4129270bdf5SMark Brown 		dev_err(arizona->dev, "Failed to restore register cache\n");
4134816bd1cSMark Brown 		goto err;
4149270bdf5SMark Brown 	}
4153cc72986SMark Brown 
4163cc72986SMark Brown 	return 0;
4174816bd1cSMark Brown 
4184816bd1cSMark Brown err:
4194816bd1cSMark Brown 	regcache_cache_only(arizona->regmap, true);
4204816bd1cSMark Brown 	regulator_disable(arizona->dcvdd);
4214816bd1cSMark Brown 	return ret;
4223cc72986SMark Brown }
4233cc72986SMark Brown 
4243cc72986SMark Brown static int arizona_runtime_suspend(struct device *dev)
4253cc72986SMark Brown {
4263cc72986SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
4275927467dSMark Brown 	int ret;
4283cc72986SMark Brown 
429508c8299SMark Brown 	dev_dbg(arizona->dev, "Entering AoD mode\n");
430508c8299SMark Brown 
4315927467dSMark Brown 	if (arizona->external_dcvdd) {
4325927467dSMark Brown 		ret = regmap_update_bits(arizona->regmap,
4335927467dSMark Brown 					 ARIZONA_ISOLATION_CONTROL,
4345927467dSMark Brown 					 ARIZONA_ISOLATE_DCVDD1,
4355927467dSMark Brown 					 ARIZONA_ISOLATE_DCVDD1);
4365927467dSMark Brown 		if (ret != 0) {
4375927467dSMark Brown 			dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n",
4385927467dSMark Brown 				ret);
4395927467dSMark Brown 			return ret;
4405927467dSMark Brown 		}
4415927467dSMark Brown 	}
4425927467dSMark Brown 
4433cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
4443cc72986SMark Brown 	regcache_mark_dirty(arizona->regmap);
445e293e847SCharles Keepax 	regulator_disable(arizona->dcvdd);
4463cc72986SMark Brown 
4473cc72986SMark Brown 	return 0;
4483cc72986SMark Brown }
4493cc72986SMark Brown #endif
4503cc72986SMark Brown 
451dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP
45267c99296SMark Brown static int arizona_suspend(struct device *dev)
45367c99296SMark Brown {
45467c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
45567c99296SMark Brown 
45667c99296SMark Brown 	dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
45767c99296SMark Brown 	disable_irq(arizona->irq);
45867c99296SMark Brown 
45967c99296SMark Brown 	return 0;
46067c99296SMark Brown }
46167c99296SMark Brown 
46267c99296SMark Brown static int arizona_suspend_late(struct device *dev)
46367c99296SMark Brown {
46467c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
46567c99296SMark Brown 
46667c99296SMark Brown 	dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
46767c99296SMark Brown 	enable_irq(arizona->irq);
46867c99296SMark Brown 
46967c99296SMark Brown 	return 0;
47067c99296SMark Brown }
47167c99296SMark Brown 
472dc781d0eSMark Brown static int arizona_resume_noirq(struct device *dev)
473dc781d0eSMark Brown {
474dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
475dc781d0eSMark Brown 
476dc781d0eSMark Brown 	dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
477dc781d0eSMark Brown 	disable_irq(arizona->irq);
478dc781d0eSMark Brown 
479dc781d0eSMark Brown 	return 0;
480dc781d0eSMark Brown }
481dc781d0eSMark Brown 
482dc781d0eSMark Brown static int arizona_resume(struct device *dev)
483dc781d0eSMark Brown {
484dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
485dc781d0eSMark Brown 
486dc781d0eSMark Brown 	dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n");
487dc781d0eSMark Brown 	enable_irq(arizona->irq);
488dc781d0eSMark Brown 
489dc781d0eSMark Brown 	return 0;
490dc781d0eSMark Brown }
491dc781d0eSMark Brown #endif
492dc781d0eSMark Brown 
4933cc72986SMark Brown const struct dev_pm_ops arizona_pm_ops = {
4943cc72986SMark Brown 	SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
4953cc72986SMark Brown 			   arizona_runtime_resume,
4963cc72986SMark Brown 			   NULL)
49767c99296SMark Brown 	SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
498dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP
49967c99296SMark Brown 	.suspend_late = arizona_suspend_late,
500dc781d0eSMark Brown 	.resume_noirq = arizona_resume_noirq,
501dc781d0eSMark Brown #endif
5023cc72986SMark Brown };
5033cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_pm_ops);
5043cc72986SMark Brown 
505d781009cSMark Brown #ifdef CONFIG_OF
506942786e6SLee Jones unsigned long arizona_of_get_type(struct device *dev)
507d781009cSMark Brown {
508d781009cSMark Brown 	const struct of_device_id *id = of_match_device(arizona_of_match, dev);
509d781009cSMark Brown 
510d781009cSMark Brown 	if (id)
511942786e6SLee Jones 		return (unsigned long)id->data;
512d781009cSMark Brown 	else
513d781009cSMark Brown 		return 0;
514d781009cSMark Brown }
515d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_get_type);
516d781009cSMark Brown 
517e4fcb1d6SCharles Keepax int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop,
518e4fcb1d6SCharles Keepax 			      bool mandatory)
519e4fcb1d6SCharles Keepax {
520e4fcb1d6SCharles Keepax 	int gpio;
521e4fcb1d6SCharles Keepax 
522e4fcb1d6SCharles Keepax 	gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0);
523e4fcb1d6SCharles Keepax 	if (gpio < 0) {
524e4fcb1d6SCharles Keepax 		if (mandatory)
525e4fcb1d6SCharles Keepax 			dev_err(arizona->dev,
526e4fcb1d6SCharles Keepax 				"Mandatory DT gpio %s missing/malformed: %d\n",
527e4fcb1d6SCharles Keepax 				prop, gpio);
528e4fcb1d6SCharles Keepax 
529e4fcb1d6SCharles Keepax 		gpio = 0;
530e4fcb1d6SCharles Keepax 	}
531e4fcb1d6SCharles Keepax 
532e4fcb1d6SCharles Keepax 	return gpio;
533e4fcb1d6SCharles Keepax }
534e4fcb1d6SCharles Keepax EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio);
535e4fcb1d6SCharles Keepax 
536d781009cSMark Brown static int arizona_of_get_core_pdata(struct arizona *arizona)
537d781009cSMark Brown {
538e4fcb1d6SCharles Keepax 	struct arizona_pdata *pdata = &arizona->pdata;
539cc47aed9SInha Song 	struct property *prop;
540cc47aed9SInha Song 	const __be32 *cur;
541cc47aed9SInha Song 	u32 val;
542d781009cSMark Brown 	int ret, i;
543cc47aed9SInha Song 	int count = 0;
544d781009cSMark Brown 
545e4fcb1d6SCharles Keepax 	pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true);
546d781009cSMark Brown 
547d781009cSMark Brown 	ret = of_property_read_u32_array(arizona->dev->of_node,
548d781009cSMark Brown 					 "wlf,gpio-defaults",
549d781009cSMark Brown 					 arizona->pdata.gpio_defaults,
550d781009cSMark Brown 					 ARRAY_SIZE(arizona->pdata.gpio_defaults));
551d781009cSMark Brown 	if (ret >= 0) {
552d781009cSMark Brown 		/*
553d781009cSMark Brown 		 * All values are literal except out of range values
554d781009cSMark Brown 		 * which are chip default, translate into platform
555d781009cSMark Brown 		 * data which uses 0 as chip default and out of range
556d781009cSMark Brown 		 * as zero.
557d781009cSMark Brown 		 */
558d781009cSMark Brown 		for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
559d781009cSMark Brown 			if (arizona->pdata.gpio_defaults[i] > 0xffff)
560d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0;
56191c73935SCharles Keepax 			else if (arizona->pdata.gpio_defaults[i] == 0)
562d781009cSMark Brown 				arizona->pdata.gpio_defaults[i] = 0x10000;
563d781009cSMark Brown 		}
564d781009cSMark Brown 	} else {
565d781009cSMark Brown 		dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
566d781009cSMark Brown 			ret);
567d781009cSMark Brown 	}
568d781009cSMark Brown 
569cc47aed9SInha Song 	of_property_for_each_u32(arizona->dev->of_node, "wlf,inmode", prop,
570cc47aed9SInha Song 				 cur, val) {
571cc47aed9SInha Song 		if (count == ARRAY_SIZE(arizona->pdata.inmode))
572cc47aed9SInha Song 			break;
573cc47aed9SInha Song 
574cc47aed9SInha Song 		arizona->pdata.inmode[count] = val;
575cc47aed9SInha Song 		count++;
576cc47aed9SInha Song 	}
577cc47aed9SInha Song 
578e7ad27caSCharles Keepax 	count = 0;
579e7ad27caSCharles Keepax 	of_property_for_each_u32(arizona->dev->of_node, "wlf,dmic-ref", prop,
580e7ad27caSCharles Keepax 				 cur, val) {
581e7ad27caSCharles Keepax 		if (count == ARRAY_SIZE(arizona->pdata.dmic_ref))
582e7ad27caSCharles Keepax 			break;
583e7ad27caSCharles Keepax 
584e7ad27caSCharles Keepax 		arizona->pdata.dmic_ref[count] = val;
585e7ad27caSCharles Keepax 		count++;
586e7ad27caSCharles Keepax 	}
587e7ad27caSCharles Keepax 
588d781009cSMark Brown 	return 0;
589d781009cSMark Brown }
590d781009cSMark Brown 
591d781009cSMark Brown const struct of_device_id arizona_of_match[] = {
592d781009cSMark Brown 	{ .compatible = "wlf,wm5102", .data = (void *)WM5102 },
593d781009cSMark Brown 	{ .compatible = "wlf,wm5110", .data = (void *)WM5110 },
594e5d4ef0dSRichard Fitzgerald 	{ .compatible = "wlf,wm8280", .data = (void *)WM8280 },
595dc7d4863SCharles Keepax 	{ .compatible = "wlf,wm8997", .data = (void *)WM8997 },
596d781009cSMark Brown 	{},
597d781009cSMark Brown };
598d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_match);
599d781009cSMark Brown #else
600d781009cSMark Brown static inline int arizona_of_get_core_pdata(struct arizona *arizona)
601d781009cSMark Brown {
602d781009cSMark Brown 	return 0;
603d781009cSMark Brown }
604d781009cSMark Brown #endif
605d781009cSMark Brown 
6065ac98553SGeert Uytterhoeven static const struct mfd_cell early_devs[] = {
6073cc72986SMark Brown 	{ .name = "arizona-ldo1" },
6083cc72986SMark Brown };
6093cc72986SMark Brown 
61032dadef2SCharles Keepax static const char *wm5102_supplies[] = {
6115fc6c396SCharles Keepax 	"MICVDD",
61232dadef2SCharles Keepax 	"DBVDD2",
61332dadef2SCharles Keepax 	"DBVDD3",
61432dadef2SCharles Keepax 	"CPVDD",
61532dadef2SCharles Keepax 	"SPKVDDL",
61632dadef2SCharles Keepax 	"SPKVDDR",
61732dadef2SCharles Keepax };
61832dadef2SCharles Keepax 
6195ac98553SGeert Uytterhoeven static const struct mfd_cell wm5102_devs[] = {
620d7768111SMark Brown 	{ .name = "arizona-micsupp" },
6215fc6c396SCharles Keepax 	{
6225fc6c396SCharles Keepax 		.name = "arizona-extcon",
6235fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
6245fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6255fc6c396SCharles Keepax 	},
6263cc72986SMark Brown 	{ .name = "arizona-gpio" },
627503b1cacSMark Brown 	{ .name = "arizona-haptics" },
6283cc72986SMark Brown 	{ .name = "arizona-pwm" },
62932dadef2SCharles Keepax 	{
63032dadef2SCharles Keepax 		.name = "wm5102-codec",
63132dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
63232dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
63332dadef2SCharles Keepax 	},
6343cc72986SMark Brown };
6353cc72986SMark Brown 
6365ac98553SGeert Uytterhoeven static const struct mfd_cell wm5110_devs[] = {
637d7768111SMark Brown 	{ .name = "arizona-micsupp" },
6385fc6c396SCharles Keepax 	{
6395fc6c396SCharles Keepax 		.name = "arizona-extcon",
6405fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
6415fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6425fc6c396SCharles Keepax 	},
643e102befeSMark Brown 	{ .name = "arizona-gpio" },
644503b1cacSMark Brown 	{ .name = "arizona-haptics" },
645e102befeSMark Brown 	{ .name = "arizona-pwm" },
64632dadef2SCharles Keepax 	{
64732dadef2SCharles Keepax 		.name = "wm5110-codec",
64832dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
64932dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
65032dadef2SCharles Keepax 	},
65132dadef2SCharles Keepax };
65232dadef2SCharles Keepax 
65332dadef2SCharles Keepax static const char *wm8997_supplies[] = {
654996c2d4fSCharles Keepax 	"MICVDD",
65532dadef2SCharles Keepax 	"DBVDD2",
65632dadef2SCharles Keepax 	"CPVDD",
65732dadef2SCharles Keepax 	"SPKVDD",
658e102befeSMark Brown };
659e102befeSMark Brown 
6605ac98553SGeert Uytterhoeven static const struct mfd_cell wm8997_devs[] = {
661dc7d4863SCharles Keepax 	{ .name = "arizona-micsupp" },
6625fc6c396SCharles Keepax 	{
6635fc6c396SCharles Keepax 		.name = "arizona-extcon",
6645fc6c396SCharles Keepax 		.parent_supplies = wm8997_supplies,
6655fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
6665fc6c396SCharles Keepax 	},
667dc7d4863SCharles Keepax 	{ .name = "arizona-gpio" },
668dc7d4863SCharles Keepax 	{ .name = "arizona-haptics" },
669dc7d4863SCharles Keepax 	{ .name = "arizona-pwm" },
67032dadef2SCharles Keepax 	{
67132dadef2SCharles Keepax 		.name = "wm8997-codec",
67232dadef2SCharles Keepax 		.parent_supplies = wm8997_supplies,
67332dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm8997_supplies),
67432dadef2SCharles Keepax 	},
675dc7d4863SCharles Keepax };
676dc7d4863SCharles Keepax 
677f791be49SBill Pemberton int arizona_dev_init(struct arizona *arizona)
6783cc72986SMark Brown {
6793cc72986SMark Brown 	struct device *dev = arizona->dev;
6803cc72986SMark Brown 	const char *type_name;
6813cc72986SMark Brown 	unsigned int reg, val;
68262d62b59SMark Brown 	int (*apply_patch)(struct arizona *) = NULL;
6833cc72986SMark Brown 	int ret, i;
6843cc72986SMark Brown 
6853cc72986SMark Brown 	dev_set_drvdata(arizona->dev, arizona);
6863cc72986SMark Brown 	mutex_init(&arizona->clk_lock);
6873cc72986SMark Brown 
6883cc72986SMark Brown 	if (dev_get_platdata(arizona->dev))
6893cc72986SMark Brown 		memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
6903cc72986SMark Brown 		       sizeof(arizona->pdata));
69122d7dc8aSLee Jones 	else
69222d7dc8aSLee Jones 		arizona_of_get_core_pdata(arizona);
6933cc72986SMark Brown 
6943cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
6953cc72986SMark Brown 
6963cc72986SMark Brown 	switch (arizona->type) {
6973cc72986SMark Brown 	case WM5102:
698e102befeSMark Brown 	case WM5110:
699e5d4ef0dSRichard Fitzgerald 	case WM8280:
700dc7d4863SCharles Keepax 	case WM8997:
7013cc72986SMark Brown 		for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
7023cc72986SMark Brown 			arizona->core_supplies[i].supply
7033cc72986SMark Brown 				= wm5102_core_supplies[i];
7043cc72986SMark Brown 		arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
7053cc72986SMark Brown 		break;
7063cc72986SMark Brown 	default:
7073cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device type %d\n",
7083cc72986SMark Brown 			arizona->type);
7093cc72986SMark Brown 		return -EINVAL;
7103cc72986SMark Brown 	}
7113cc72986SMark Brown 
7124a8c475fSCharles Keepax 	/* Mark DCVDD as external, LDO1 driver will clear if internal */
7134a8c475fSCharles Keepax 	arizona->external_dcvdd = true;
7144a8c475fSCharles Keepax 
7153cc72986SMark Brown 	ret = mfd_add_devices(arizona->dev, -1, early_devs,
7160848c94fSMark Brown 			      ARRAY_SIZE(early_devs), NULL, 0, NULL);
7173cc72986SMark Brown 	if (ret != 0) {
7183cc72986SMark Brown 		dev_err(dev, "Failed to add early children: %d\n", ret);
7193cc72986SMark Brown 		return ret;
7203cc72986SMark Brown 	}
7213cc72986SMark Brown 
7223cc72986SMark Brown 	ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
7233cc72986SMark Brown 				      arizona->core_supplies);
7243cc72986SMark Brown 	if (ret != 0) {
7253cc72986SMark Brown 		dev_err(dev, "Failed to request core supplies: %d\n",
7263cc72986SMark Brown 			ret);
7273cc72986SMark Brown 		goto err_early;
7283cc72986SMark Brown 	}
7293cc72986SMark Brown 
7300c2d0ffbSCharles Keepax 	/**
7310c2d0ffbSCharles Keepax 	 * Don't use devres here because the only device we have to get
7320c2d0ffbSCharles Keepax 	 * against is the MFD device and DCVDD will likely be supplied by
7330c2d0ffbSCharles Keepax 	 * one of its children. Meaning that the regulator will be
7340c2d0ffbSCharles Keepax 	 * destroyed by the time devres calls regulator put.
7350c2d0ffbSCharles Keepax 	 */
736e6021511SCharles Keepax 	arizona->dcvdd = regulator_get(arizona->dev, "DCVDD");
73759db9691SMark Brown 	if (IS_ERR(arizona->dcvdd)) {
73859db9691SMark Brown 		ret = PTR_ERR(arizona->dcvdd);
73959db9691SMark Brown 		dev_err(dev, "Failed to request DCVDD: %d\n", ret);
74059db9691SMark Brown 		goto err_early;
74159db9691SMark Brown 	}
74259db9691SMark Brown 
74387d3af4aSMark Brown 	if (arizona->pdata.reset) {
74487d3af4aSMark Brown 		/* Start out with /RESET low to put the chip into reset */
7455f056bf0SCharles Keepax 		ret = devm_gpio_request_one(arizona->dev, arizona->pdata.reset,
74687d3af4aSMark Brown 					    GPIOF_DIR_OUT | GPIOF_INIT_LOW,
74787d3af4aSMark Brown 					    "arizona /RESET");
74887d3af4aSMark Brown 		if (ret != 0) {
74987d3af4aSMark Brown 			dev_err(dev, "Failed to request /RESET: %d\n", ret);
750e6021511SCharles Keepax 			goto err_dcvdd;
75187d3af4aSMark Brown 		}
75287d3af4aSMark Brown 	}
75387d3af4aSMark Brown 
7543cc72986SMark Brown 	ret = regulator_bulk_enable(arizona->num_core_supplies,
7553cc72986SMark Brown 				    arizona->core_supplies);
7563cc72986SMark Brown 	if (ret != 0) {
7573cc72986SMark Brown 		dev_err(dev, "Failed to enable core supplies: %d\n",
7583cc72986SMark Brown 			ret);
759e6021511SCharles Keepax 		goto err_dcvdd;
7603cc72986SMark Brown 	}
7613cc72986SMark Brown 
76259db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
76359db9691SMark Brown 	if (ret != 0) {
76459db9691SMark Brown 		dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
76559db9691SMark Brown 		goto err_enable;
76659db9691SMark Brown 	}
76759db9691SMark Brown 
768*2229875dSCharles Keepax 	arizona_disable_reset(arizona);
7693cc72986SMark Brown 
7703cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
7713cc72986SMark Brown 
772ca76ceb8SMark Brown 	/* Verify that this is a chip we know about */
773ca76ceb8SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
774ca76ceb8SMark Brown 	if (ret != 0) {
775ca76ceb8SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
776ca76ceb8SMark Brown 		goto err_reset;
777ca76ceb8SMark Brown 	}
778ca76ceb8SMark Brown 
779ca76ceb8SMark Brown 	switch (reg) {
780ca76ceb8SMark Brown 	case 0x5102:
781ca76ceb8SMark Brown 	case 0x5110:
782dc7d4863SCharles Keepax 	case 0x8997:
783ca76ceb8SMark Brown 		break;
784ca76ceb8SMark Brown 	default:
785ca76ceb8SMark Brown 		dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
786ca76ceb8SMark Brown 		goto err_reset;
787ca76ceb8SMark Brown 	}
788ca76ceb8SMark Brown 
789ca76ceb8SMark Brown 	/* If we have a /RESET GPIO we'll already be reset */
790ca76ceb8SMark Brown 	if (!arizona->pdata.reset) {
791ca76ceb8SMark Brown 		ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
792ca76ceb8SMark Brown 		if (ret != 0) {
793ca76ceb8SMark Brown 			dev_err(dev, "Failed to reset device: %d\n", ret);
794ca76ceb8SMark Brown 			goto err_reset;
795ca76ceb8SMark Brown 		}
796ca76ceb8SMark Brown 
797ca76ceb8SMark Brown 		msleep(1);
798ca76ceb8SMark Brown 	}
799ca76ceb8SMark Brown 
800ca76ceb8SMark Brown 	/* Ensure device startup is complete */
801ca76ceb8SMark Brown 	switch (arizona->type) {
802ca76ceb8SMark Brown 	case WM5102:
80348018943SMark Brown 		ret = regmap_read(arizona->regmap,
80448018943SMark Brown 				  ARIZONA_WRITE_SEQUENCER_CTRL_3, &val);
805ca76ceb8SMark Brown 		if (ret != 0)
806ca76ceb8SMark Brown 			dev_err(dev,
807ca76ceb8SMark Brown 				"Failed to check write sequencer state: %d\n",
808ca76ceb8SMark Brown 				ret);
809ca76ceb8SMark Brown 		else if (val & 0x01)
810ca76ceb8SMark Brown 			break;
811ca76ceb8SMark Brown 		/* Fall through */
812ca76ceb8SMark Brown 	default:
813ca76ceb8SMark Brown 		ret = arizona_wait_for_boot(arizona);
814ca76ceb8SMark Brown 		if (ret != 0) {
815ca76ceb8SMark Brown 			dev_err(arizona->dev,
816ca76ceb8SMark Brown 				"Device failed initial boot: %d\n", ret);
817ca76ceb8SMark Brown 			goto err_reset;
818ca76ceb8SMark Brown 		}
819ca76ceb8SMark Brown 		break;
820ca76ceb8SMark Brown 	}
821ca76ceb8SMark Brown 
822ca76ceb8SMark Brown 	/* Read the device ID information & do device specific stuff */
8233cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
8243cc72986SMark Brown 	if (ret != 0) {
8253cc72986SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
82659db9691SMark Brown 		goto err_reset;
8273cc72986SMark Brown 	}
8283cc72986SMark Brown 
8293cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
8303cc72986SMark Brown 			  &arizona->rev);
8313cc72986SMark Brown 	if (ret != 0) {
8323cc72986SMark Brown 		dev_err(dev, "Failed to read revision register: %d\n", ret);
83359db9691SMark Brown 		goto err_reset;
8343cc72986SMark Brown 	}
8353cc72986SMark Brown 	arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
8363cc72986SMark Brown 
8373cc72986SMark Brown 	switch (reg) {
838863df8d5SMark Brown #ifdef CONFIG_MFD_WM5102
8393cc72986SMark Brown 	case 0x5102:
8403cc72986SMark Brown 		type_name = "WM5102";
8413cc72986SMark Brown 		if (arizona->type != WM5102) {
8423cc72986SMark Brown 			dev_err(arizona->dev, "WM5102 registered as %d\n",
8433cc72986SMark Brown 				arizona->type);
8443cc72986SMark Brown 			arizona->type = WM5102;
8453cc72986SMark Brown 		}
84662d62b59SMark Brown 		apply_patch = wm5102_patch;
847c6d6bfb1SMark Brown 		arizona->rev &= 0x7;
8483cc72986SMark Brown 		break;
849863df8d5SMark Brown #endif
850e102befeSMark Brown #ifdef CONFIG_MFD_WM5110
851e102befeSMark Brown 	case 0x5110:
852e5d4ef0dSRichard Fitzgerald 		switch (arizona->type) {
853e5d4ef0dSRichard Fitzgerald 		case WM5110:
854e102befeSMark Brown 			type_name = "WM5110";
855e5d4ef0dSRichard Fitzgerald 			break;
856e5d4ef0dSRichard Fitzgerald 		case WM8280:
857e5d4ef0dSRichard Fitzgerald 			type_name = "WM8280";
858e5d4ef0dSRichard Fitzgerald 			break;
859e5d4ef0dSRichard Fitzgerald 		default:
860e5d4ef0dSRichard Fitzgerald 			type_name = "WM5110";
861e102befeSMark Brown 			dev_err(arizona->dev, "WM5110 registered as %d\n",
862e102befeSMark Brown 				arizona->type);
863e102befeSMark Brown 			arizona->type = WM5110;
864e5d4ef0dSRichard Fitzgerald 			break;
865e102befeSMark Brown 		}
86662d62b59SMark Brown 		apply_patch = wm5110_patch;
867e102befeSMark Brown 		break;
868e102befeSMark Brown #endif
869dc7d4863SCharles Keepax #ifdef CONFIG_MFD_WM8997
870dc7d4863SCharles Keepax 	case 0x8997:
871dc7d4863SCharles Keepax 		type_name = "WM8997";
872dc7d4863SCharles Keepax 		if (arizona->type != WM8997) {
873dc7d4863SCharles Keepax 			dev_err(arizona->dev, "WM8997 registered as %d\n",
874dc7d4863SCharles Keepax 				arizona->type);
875dc7d4863SCharles Keepax 			arizona->type = WM8997;
876dc7d4863SCharles Keepax 		}
877dc7d4863SCharles Keepax 		apply_patch = wm8997_patch;
878dc7d4863SCharles Keepax 		break;
879dc7d4863SCharles Keepax #endif
8803cc72986SMark Brown 	default:
8813cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device ID %x\n", reg);
88259db9691SMark Brown 		goto err_reset;
8833cc72986SMark Brown 	}
8843cc72986SMark Brown 
8853cc72986SMark Brown 	dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
8863cc72986SMark Brown 
88762d62b59SMark Brown 	if (apply_patch) {
88862d62b59SMark Brown 		ret = apply_patch(arizona);
88962d62b59SMark Brown 		if (ret != 0) {
89062d62b59SMark Brown 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
89162d62b59SMark Brown 				ret);
89262d62b59SMark Brown 			goto err_reset;
89362d62b59SMark Brown 		}
894e80436bbSCharles Keepax 
895e80436bbSCharles Keepax 		switch (arizona->type) {
896e80436bbSCharles Keepax 		case WM5102:
897e80436bbSCharles Keepax 			ret = arizona_apply_hardware_patch(arizona);
898e80436bbSCharles Keepax 			if (ret != 0) {
899e80436bbSCharles Keepax 				dev_err(arizona->dev,
900e80436bbSCharles Keepax 					"Failed to apply hardware patch: %d\n",
901e80436bbSCharles Keepax 					ret);
902e80436bbSCharles Keepax 				goto err_reset;
903e80436bbSCharles Keepax 			}
904e80436bbSCharles Keepax 			break;
905e80436bbSCharles Keepax 		default:
906e80436bbSCharles Keepax 			break;
907e80436bbSCharles Keepax 		}
90862d62b59SMark Brown 	}
90962d62b59SMark Brown 
9103cc72986SMark Brown 	for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
9113cc72986SMark Brown 		if (!arizona->pdata.gpio_defaults[i])
9123cc72986SMark Brown 			continue;
9133cc72986SMark Brown 
9143cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
9153cc72986SMark Brown 			     arizona->pdata.gpio_defaults[i]);
9163cc72986SMark Brown 	}
9173cc72986SMark Brown 
9183cc72986SMark Brown 	pm_runtime_set_autosuspend_delay(arizona->dev, 100);
9193cc72986SMark Brown 	pm_runtime_use_autosuspend(arizona->dev);
9203cc72986SMark Brown 	pm_runtime_enable(arizona->dev);
9213cc72986SMark Brown 
9223cc72986SMark Brown 	/* Chip default */
9233cc72986SMark Brown 	if (!arizona->pdata.clk32k_src)
9243cc72986SMark Brown 		arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
9253cc72986SMark Brown 
9263cc72986SMark Brown 	switch (arizona->pdata.clk32k_src) {
9273cc72986SMark Brown 	case ARIZONA_32KZ_MCLK1:
9283cc72986SMark Brown 	case ARIZONA_32KZ_MCLK2:
9293cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
9303cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK,
9313cc72986SMark Brown 				   arizona->pdata.clk32k_src - 1);
932767c6dc0SMark Brown 		arizona_clk32k_enable(arizona);
9333cc72986SMark Brown 		break;
9343cc72986SMark Brown 	case ARIZONA_32KZ_NONE:
9353cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
9363cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK, 2);
9373cc72986SMark Brown 		break;
9383cc72986SMark Brown 	default:
9393cc72986SMark Brown 		dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
9403cc72986SMark Brown 			arizona->pdata.clk32k_src);
9413cc72986SMark Brown 		ret = -EINVAL;
94259db9691SMark Brown 		goto err_reset;
9433cc72986SMark Brown 	}
9443cc72986SMark Brown 
9453d91f828SMark Brown 	for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
946544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV &&
947544c7aadSMark Brown 		    !arizona->pdata.micbias[i].bypass)
9483d91f828SMark Brown 			continue;
9493d91f828SMark Brown 
950544c7aadSMark Brown 		/* Apply default for bypass mode */
951544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV)
952544c7aadSMark Brown 			arizona->pdata.micbias[i].mV = 2800;
953544c7aadSMark Brown 
9543d91f828SMark Brown 		val = (arizona->pdata.micbias[i].mV - 1500) / 100;
955544c7aadSMark Brown 
9563d91f828SMark Brown 		val <<= ARIZONA_MICB1_LVL_SHIFT;
9573d91f828SMark Brown 
9583d91f828SMark Brown 		if (arizona->pdata.micbias[i].ext_cap)
9593d91f828SMark Brown 			val |= ARIZONA_MICB1_EXT_CAP;
9603d91f828SMark Brown 
9613d91f828SMark Brown 		if (arizona->pdata.micbias[i].discharge)
9623d91f828SMark Brown 			val |= ARIZONA_MICB1_DISCH;
9633d91f828SMark Brown 
964f773fc6dSCharles Keepax 		if (arizona->pdata.micbias[i].soft_start)
9653d91f828SMark Brown 			val |= ARIZONA_MICB1_RATE;
9663d91f828SMark Brown 
967544c7aadSMark Brown 		if (arizona->pdata.micbias[i].bypass)
968544c7aadSMark Brown 			val |= ARIZONA_MICB1_BYPASS;
969544c7aadSMark Brown 
9703d91f828SMark Brown 		regmap_update_bits(arizona->regmap,
9713d91f828SMark Brown 				   ARIZONA_MIC_BIAS_CTRL_1 + i,
9723d91f828SMark Brown 				   ARIZONA_MICB1_LVL_MASK |
97371d134b9SCharles Keepax 				   ARIZONA_MICB1_EXT_CAP |
9743d91f828SMark Brown 				   ARIZONA_MICB1_DISCH |
975544c7aadSMark Brown 				   ARIZONA_MICB1_BYPASS |
9763d91f828SMark Brown 				   ARIZONA_MICB1_RATE, val);
9773d91f828SMark Brown 	}
9783d91f828SMark Brown 
9793cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
9803cc72986SMark Brown 		/* Default for both is 0 so noop with defaults */
9813cc72986SMark Brown 		val = arizona->pdata.dmic_ref[i]
9823cc72986SMark Brown 			<< ARIZONA_IN1_DMIC_SUP_SHIFT;
9833cc72986SMark Brown 		val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
9843cc72986SMark Brown 
9853cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9863cc72986SMark Brown 				   ARIZONA_IN1L_CONTROL + (i * 8),
9873cc72986SMark Brown 				   ARIZONA_IN1_DMIC_SUP_MASK |
9883cc72986SMark Brown 				   ARIZONA_IN1_MODE_MASK, val);
9893cc72986SMark Brown 	}
9903cc72986SMark Brown 
9913cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
9923cc72986SMark Brown 		/* Default is 0 so noop with defaults */
9933cc72986SMark Brown 		if (arizona->pdata.out_mono[i])
9943cc72986SMark Brown 			val = ARIZONA_OUT1_MONO;
9953cc72986SMark Brown 		else
9963cc72986SMark Brown 			val = 0;
9973cc72986SMark Brown 
9983cc72986SMark Brown 		regmap_update_bits(arizona->regmap,
9993cc72986SMark Brown 				   ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
10003cc72986SMark Brown 				   ARIZONA_OUT1_MONO, val);
10013cc72986SMark Brown 	}
10023cc72986SMark Brown 
10033cc72986SMark Brown 	for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
10043cc72986SMark Brown 		if (arizona->pdata.spk_mute[i])
10053cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
10062a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
10073cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_ENDIAN_MASK |
10083cc72986SMark Brown 					   ARIZONA_SPK1_MUTE_SEQ1_MASK,
10093cc72986SMark Brown 					   arizona->pdata.spk_mute[i]);
10103cc72986SMark Brown 
10113cc72986SMark Brown 		if (arizona->pdata.spk_fmt[i])
10123cc72986SMark Brown 			regmap_update_bits(arizona->regmap,
10132a51da04SMark Brown 					   ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
10143cc72986SMark Brown 					   ARIZONA_SPK1_FMT_MASK,
10153cc72986SMark Brown 					   arizona->pdata.spk_fmt[i]);
10163cc72986SMark Brown 	}
10173cc72986SMark Brown 
10183cc72986SMark Brown 	/* Set up for interrupts */
10193cc72986SMark Brown 	ret = arizona_irq_init(arizona);
10203cc72986SMark Brown 	if (ret != 0)
102159db9691SMark Brown 		goto err_reset;
10223cc72986SMark Brown 
10233cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
10243cc72986SMark Brown 			    arizona_clkgen_err, arizona);
10253cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
10263cc72986SMark Brown 			    arizona_overclocked, arizona);
10273cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
10283cc72986SMark Brown 			    arizona_underclocked, arizona);
10293cc72986SMark Brown 
10303cc72986SMark Brown 	switch (arizona->type) {
10313cc72986SMark Brown 	case WM5102:
10323cc72986SMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
10330848c94fSMark Brown 				      ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
10343cc72986SMark Brown 		break;
1035e102befeSMark Brown 	case WM5110:
1036e5d4ef0dSRichard Fitzgerald 	case WM8280:
1037e102befeSMark Brown 		ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
103878566afdSCharles Keepax 				      ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
1039e102befeSMark Brown 		break;
1040dc7d4863SCharles Keepax 	case WM8997:
1041dc7d4863SCharles Keepax 		ret = mfd_add_devices(arizona->dev, -1, wm8997_devs,
1042dc7d4863SCharles Keepax 				      ARRAY_SIZE(wm8997_devs), NULL, 0, NULL);
1043dc7d4863SCharles Keepax 		break;
10443cc72986SMark Brown 	}
10453cc72986SMark Brown 
10463cc72986SMark Brown 	if (ret != 0) {
10473cc72986SMark Brown 		dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
10483cc72986SMark Brown 		goto err_irq;
10493cc72986SMark Brown 	}
10503cc72986SMark Brown 
105148bb9fe4SRafael J. Wysocki #ifdef CONFIG_PM
105259db9691SMark Brown 	regulator_disable(arizona->dcvdd);
105359db9691SMark Brown #endif
105459db9691SMark Brown 
10553cc72986SMark Brown 	return 0;
10563cc72986SMark Brown 
10573cc72986SMark Brown err_irq:
10583cc72986SMark Brown 	arizona_irq_exit(arizona);
10593cc72986SMark Brown err_reset:
1060*2229875dSCharles Keepax 	arizona_enable_reset(arizona);
106159db9691SMark Brown 	regulator_disable(arizona->dcvdd);
10623cc72986SMark Brown err_enable:
10633a36a0dbSMark Brown 	regulator_bulk_disable(arizona->num_core_supplies,
10643cc72986SMark Brown 			       arizona->core_supplies);
1065e6021511SCharles Keepax err_dcvdd:
1066e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
10673cc72986SMark Brown err_early:
10683cc72986SMark Brown 	mfd_remove_devices(dev);
10693cc72986SMark Brown 	return ret;
10703cc72986SMark Brown }
10713cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_init);
10723cc72986SMark Brown 
10734740f73fSBill Pemberton int arizona_dev_exit(struct arizona *arizona)
10743cc72986SMark Brown {
1075b804020aSCharles Keepax 	pm_runtime_disable(arizona->dev);
1076b804020aSCharles Keepax 
1077df6b3352SCharles Keepax 	regulator_disable(arizona->dcvdd);
1078e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
1079df6b3352SCharles Keepax 
10803cc72986SMark Brown 	mfd_remove_devices(arizona->dev);
10813cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
10823cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
10833cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
10843cc72986SMark Brown 	arizona_irq_exit(arizona);
1085*2229875dSCharles Keepax 	arizona_enable_reset(arizona);
1086df6b3352SCharles Keepax 
10874420286eSCharles Keepax 	regulator_bulk_disable(arizona->num_core_supplies,
10881d017b6bSMark Brown 			       arizona->core_supplies);
10893cc72986SMark Brown 	return 0;
10903cc72986SMark Brown }
10913cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_exit);
1092