xref: /linux/drivers/mfd/arizona-core.c (revision f99fea949736b6a79cddaaaec7e3b8830fd81c93)
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 
13cdd8da8cSSylwester Nawrocki #include <linux/clk.h>
143cc72986SMark Brown #include <linux/delay.h>
1559db9691SMark Brown #include <linux/err.h>
16c1860466SCharles Keepax #include <linux/gpio/consumer.h>
173cc72986SMark Brown #include <linux/interrupt.h>
183cc72986SMark Brown #include <linux/mfd/core.h>
193cc72986SMark Brown #include <linux/module.h>
20d781009cSMark Brown #include <linux/of.h>
21d781009cSMark Brown #include <linux/of_device.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>
27*f99fea94SCharles Keepax #include <linux/ktime.h>
28ae05ea36SRichard Fitzgerald #include <linux/platform_device.h>
293cc72986SMark Brown 
303cc72986SMark Brown #include <linux/mfd/arizona/core.h>
313cc72986SMark Brown #include <linux/mfd/arizona/registers.h>
323cc72986SMark Brown 
333cc72986SMark Brown #include "arizona.h"
343cc72986SMark Brown 
353762aedeSCharles Keepax static const char * const wm5102_core_supplies[] = {
363cc72986SMark Brown 	"AVDD",
373cc72986SMark Brown 	"DBVDD1",
383cc72986SMark Brown };
393cc72986SMark Brown 
403cc72986SMark Brown int arizona_clk32k_enable(struct arizona *arizona)
413cc72986SMark Brown {
423cc72986SMark Brown 	int ret = 0;
433cc72986SMark Brown 
443cc72986SMark Brown 	mutex_lock(&arizona->clk_lock);
453cc72986SMark Brown 
463cc72986SMark Brown 	arizona->clk32k_ref++;
473cc72986SMark Brown 
48247fa192SMark Brown 	if (arizona->clk32k_ref == 1) {
49247fa192SMark Brown 		switch (arizona->pdata.clk32k_src) {
50247fa192SMark Brown 		case ARIZONA_32KZ_MCLK1:
51247fa192SMark Brown 			ret = pm_runtime_get_sync(arizona->dev);
52247fa192SMark Brown 			if (ret != 0)
53cdd8da8cSSylwester Nawrocki 				goto err_ref;
54cdd8da8cSSylwester Nawrocki 			ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]);
55cdd8da8cSSylwester Nawrocki 			if (ret != 0)
56cdd8da8cSSylwester Nawrocki 				goto err_pm;
57cdd8da8cSSylwester Nawrocki 			break;
58cdd8da8cSSylwester Nawrocki 		case ARIZONA_32KZ_MCLK2:
59cdd8da8cSSylwester Nawrocki 			ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK2]);
60cdd8da8cSSylwester Nawrocki 			if (ret != 0)
61cdd8da8cSSylwester Nawrocki 				goto err_ref;
62247fa192SMark Brown 			break;
63247fa192SMark Brown 		}
64247fa192SMark Brown 
653cc72986SMark Brown 		ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
663cc72986SMark Brown 					 ARIZONA_CLK_32K_ENA,
673cc72986SMark Brown 					 ARIZONA_CLK_32K_ENA);
68247fa192SMark Brown 	}
693cc72986SMark Brown 
70cdd8da8cSSylwester Nawrocki err_pm:
71cdd8da8cSSylwester Nawrocki 	pm_runtime_put_sync(arizona->dev);
72cdd8da8cSSylwester Nawrocki err_ref:
733cc72986SMark Brown 	if (ret != 0)
743cc72986SMark Brown 		arizona->clk32k_ref--;
753cc72986SMark Brown 
763cc72986SMark Brown 	mutex_unlock(&arizona->clk_lock);
773cc72986SMark Brown 
783cc72986SMark Brown 	return ret;
793cc72986SMark Brown }
803cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
813cc72986SMark Brown 
823cc72986SMark Brown int arizona_clk32k_disable(struct arizona *arizona)
833cc72986SMark Brown {
843cc72986SMark Brown 	mutex_lock(&arizona->clk_lock);
853cc72986SMark Brown 
863cc72986SMark Brown 	BUG_ON(arizona->clk32k_ref <= 0);
873cc72986SMark Brown 
883cc72986SMark Brown 	arizona->clk32k_ref--;
893cc72986SMark Brown 
90247fa192SMark Brown 	if (arizona->clk32k_ref == 0) {
913cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
923cc72986SMark Brown 				   ARIZONA_CLK_32K_ENA, 0);
933cc72986SMark Brown 
94247fa192SMark Brown 		switch (arizona->pdata.clk32k_src) {
95247fa192SMark Brown 		case ARIZONA_32KZ_MCLK1:
96247fa192SMark Brown 			pm_runtime_put_sync(arizona->dev);
97cdd8da8cSSylwester Nawrocki 			clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK1]);
98cdd8da8cSSylwester Nawrocki 			break;
99cdd8da8cSSylwester Nawrocki 		case ARIZONA_32KZ_MCLK2:
100cdd8da8cSSylwester Nawrocki 			clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK2]);
101247fa192SMark Brown 			break;
102247fa192SMark Brown 		}
103247fa192SMark Brown 	}
104247fa192SMark Brown 
1053cc72986SMark Brown 	mutex_unlock(&arizona->clk_lock);
1063cc72986SMark Brown 
107a260fba1SJavier Martinez Canillas 	return 0;
1083cc72986SMark Brown }
1093cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
1103cc72986SMark Brown 
1113cc72986SMark Brown static irqreturn_t arizona_clkgen_err(int irq, void *data)
1123cc72986SMark Brown {
1133cc72986SMark Brown 	struct arizona *arizona = data;
1143cc72986SMark Brown 
1153cc72986SMark Brown 	dev_err(arizona->dev, "CLKGEN error\n");
1163cc72986SMark Brown 
1173cc72986SMark Brown 	return IRQ_HANDLED;
1183cc72986SMark Brown }
1193cc72986SMark Brown 
1203cc72986SMark Brown static irqreturn_t arizona_underclocked(int irq, void *data)
1213cc72986SMark Brown {
1223cc72986SMark Brown 	struct arizona *arizona = data;
1233cc72986SMark Brown 	unsigned int val;
1243cc72986SMark Brown 	int ret;
1253cc72986SMark Brown 
1263cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8,
1273cc72986SMark Brown 			  &val);
1283cc72986SMark Brown 	if (ret != 0) {
1293cc72986SMark Brown 		dev_err(arizona->dev, "Failed to read underclock status: %d\n",
1303cc72986SMark Brown 			ret);
1313cc72986SMark Brown 		return IRQ_NONE;
1323cc72986SMark Brown 	}
1333cc72986SMark Brown 
1343cc72986SMark Brown 	if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
1353cc72986SMark Brown 		dev_err(arizona->dev, "AIF3 underclocked\n");
1363cc72986SMark Brown 	if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
1373ebef34dSCharles Keepax 		dev_err(arizona->dev, "AIF2 underclocked\n");
1383ebef34dSCharles Keepax 	if (val & ARIZONA_AIF1_UNDERCLOCKED_STS)
1393cc72986SMark Brown 		dev_err(arizona->dev, "AIF1 underclocked\n");
1406e440d27SCharles Keepax 	if (val & ARIZONA_ISRC3_UNDERCLOCKED_STS)
1416e440d27SCharles Keepax 		dev_err(arizona->dev, "ISRC3 underclocked\n");
1423cc72986SMark Brown 	if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
1433cc72986SMark Brown 		dev_err(arizona->dev, "ISRC2 underclocked\n");
1443cc72986SMark Brown 	if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
1453cc72986SMark Brown 		dev_err(arizona->dev, "ISRC1 underclocked\n");
1463cc72986SMark Brown 	if (val & ARIZONA_FX_UNDERCLOCKED_STS)
1473cc72986SMark Brown 		dev_err(arizona->dev, "FX underclocked\n");
1483cc72986SMark Brown 	if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
1493cc72986SMark Brown 		dev_err(arizona->dev, "ASRC underclocked\n");
1503cc72986SMark Brown 	if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
1513cc72986SMark Brown 		dev_err(arizona->dev, "DAC underclocked\n");
1523cc72986SMark Brown 	if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
1533cc72986SMark Brown 		dev_err(arizona->dev, "ADC underclocked\n");
1543cc72986SMark Brown 	if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
155648a9880SMark Brown 		dev_err(arizona->dev, "Mixer dropped sample\n");
1563cc72986SMark Brown 
1573cc72986SMark Brown 	return IRQ_HANDLED;
1583cc72986SMark Brown }
1593cc72986SMark Brown 
1603cc72986SMark Brown static irqreturn_t arizona_overclocked(int irq, void *data)
1613cc72986SMark Brown {
1623cc72986SMark Brown 	struct arizona *arizona = data;
1636887b042SRichard Fitzgerald 	unsigned int val[3];
1643cc72986SMark Brown 	int ret;
1653cc72986SMark Brown 
1663cc72986SMark Brown 	ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
1676887b042SRichard Fitzgerald 			       &val[0], 3);
1683cc72986SMark Brown 	if (ret != 0) {
1693cc72986SMark Brown 		dev_err(arizona->dev, "Failed to read overclock status: %d\n",
1703cc72986SMark Brown 			ret);
1713cc72986SMark Brown 		return IRQ_NONE;
1723cc72986SMark Brown 	}
1733cc72986SMark Brown 
1746887b042SRichard Fitzgerald 	switch (arizona->type) {
1756887b042SRichard Fitzgerald 	case WM8998:
1766887b042SRichard Fitzgerald 	case WM1814:
1776887b042SRichard Fitzgerald 		/* Some bits are shifted on WM8998,
1786887b042SRichard Fitzgerald 		 * rearrange to match the standard bit layout
1796887b042SRichard Fitzgerald 		 */
1806887b042SRichard Fitzgerald 		val[0] = ((val[0] & 0x60e0) >> 1) |
1816887b042SRichard Fitzgerald 			 ((val[0] & 0x1e00) >> 2) |
1826887b042SRichard Fitzgerald 			 (val[0] & 0x000f);
1836887b042SRichard Fitzgerald 		break;
1846887b042SRichard Fitzgerald 	default:
1856887b042SRichard Fitzgerald 		break;
1866887b042SRichard Fitzgerald 	}
1876887b042SRichard Fitzgerald 
1883cc72986SMark Brown 	if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
1893cc72986SMark Brown 		dev_err(arizona->dev, "PWM overclocked\n");
1903cc72986SMark Brown 	if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
1913cc72986SMark Brown 		dev_err(arizona->dev, "FX core overclocked\n");
1923cc72986SMark Brown 	if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
1933cc72986SMark Brown 		dev_err(arizona->dev, "DAC SYS overclocked\n");
1943cc72986SMark Brown 	if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
1953cc72986SMark Brown 		dev_err(arizona->dev, "DAC WARP overclocked\n");
1963cc72986SMark Brown 	if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
1973cc72986SMark Brown 		dev_err(arizona->dev, "ADC overclocked\n");
1983cc72986SMark Brown 	if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
1993cc72986SMark Brown 		dev_err(arizona->dev, "Mixer overclocked\n");
2003cc72986SMark Brown 	if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
2013cc72986SMark Brown 		dev_err(arizona->dev, "AIF3 overclocked\n");
2023cc72986SMark Brown 	if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
2033cc72986SMark Brown 		dev_err(arizona->dev, "AIF2 overclocked\n");
2043cc72986SMark Brown 	if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
2053cc72986SMark Brown 		dev_err(arizona->dev, "AIF1 overclocked\n");
2063cc72986SMark Brown 	if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
2073cc72986SMark Brown 		dev_err(arizona->dev, "Pad control overclocked\n");
2083cc72986SMark Brown 
2093cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
2103cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
2113cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
2123cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus async overclocked\n");
2133cc72986SMark Brown 	if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
2143cc72986SMark Brown 		dev_err(arizona->dev, "Slimbus sync overclocked\n");
2153cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
2163cc72986SMark Brown 		dev_err(arizona->dev, "ASRC async system overclocked\n");
2173cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
2183cc72986SMark Brown 		dev_err(arizona->dev, "ASRC async WARP overclocked\n");
2193cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
2203cc72986SMark Brown 		dev_err(arizona->dev, "ASRC sync system overclocked\n");
2213cc72986SMark Brown 	if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
2223cc72986SMark Brown 		dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
2233cc72986SMark Brown 	if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
2243cc72986SMark Brown 		dev_err(arizona->dev, "DSP1 overclocked\n");
2256e440d27SCharles Keepax 	if (val[1] & ARIZONA_ISRC3_OVERCLOCKED_STS)
2266e440d27SCharles Keepax 		dev_err(arizona->dev, "ISRC3 overclocked\n");
2273cc72986SMark Brown 	if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
2283cc72986SMark Brown 		dev_err(arizona->dev, "ISRC2 overclocked\n");
2293cc72986SMark Brown 	if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
2303cc72986SMark Brown 		dev_err(arizona->dev, "ISRC1 overclocked\n");
2313cc72986SMark Brown 
2326887b042SRichard Fitzgerald 	if (val[2] & ARIZONA_SPDIF_OVERCLOCKED_STS)
2336887b042SRichard Fitzgerald 		dev_err(arizona->dev, "SPDIF overclocked\n");
2346887b042SRichard Fitzgerald 
2353cc72986SMark Brown 	return IRQ_HANDLED;
2363cc72986SMark Brown }
2373cc72986SMark Brown 
238ef84f885SCharles Keepax #define ARIZONA_REG_POLL_DELAY_US 7500
239ef84f885SCharles Keepax 
240*f99fea94SCharles Keepax static inline bool arizona_poll_reg_delay(ktime_t timeout)
241*f99fea94SCharles Keepax {
242*f99fea94SCharles Keepax 	if (ktime_compare(ktime_get(), timeout) > 0)
243*f99fea94SCharles Keepax 		return false;
244*f99fea94SCharles Keepax 
245*f99fea94SCharles Keepax 	usleep_range(ARIZONA_REG_POLL_DELAY_US / 2, ARIZONA_REG_POLL_DELAY_US);
246*f99fea94SCharles Keepax 
247*f99fea94SCharles Keepax 	return true;
248*f99fea94SCharles Keepax }
249*f99fea94SCharles Keepax 
2509d53dfdcSCharles Keepax static int arizona_poll_reg(struct arizona *arizona,
251ef84f885SCharles Keepax 			    int timeout_ms, unsigned int reg,
2529d53dfdcSCharles Keepax 			    unsigned int mask, unsigned int target)
2539d53dfdcSCharles Keepax {
254*f99fea94SCharles Keepax 	ktime_t timeout = ktime_add_us(ktime_get(), timeout_ms * USEC_PER_MSEC);
2559d53dfdcSCharles Keepax 	unsigned int val = 0;
256ef84f885SCharles Keepax 	int ret;
2579d53dfdcSCharles Keepax 
258*f99fea94SCharles Keepax 	do {
259*f99fea94SCharles Keepax 		ret = regmap_read(arizona->regmap, reg, &val);
2609d53dfdcSCharles Keepax 
261*f99fea94SCharles Keepax 		if ((val & mask) == target)
262*f99fea94SCharles Keepax 			return 0;
263*f99fea94SCharles Keepax 	} while (arizona_poll_reg_delay(timeout));
264*f99fea94SCharles Keepax 
265*f99fea94SCharles Keepax 	if (ret) {
266*f99fea94SCharles Keepax 		dev_err(arizona->dev, "Failed polling reg 0x%x: %d\n",
267*f99fea94SCharles Keepax 			reg, ret);
268ef84f885SCharles Keepax 		return ret;
2699d53dfdcSCharles Keepax 	}
2709d53dfdcSCharles Keepax 
271*f99fea94SCharles Keepax 	dev_err(arizona->dev, "Polling reg 0x%x timed out: %x\n", reg, val);
272*f99fea94SCharles Keepax 	return -ETIMEDOUT;
273*f99fea94SCharles Keepax }
274*f99fea94SCharles Keepax 
2753cc72986SMark Brown static int arizona_wait_for_boot(struct arizona *arizona)
2763cc72986SMark Brown {
2779d53dfdcSCharles Keepax 	int ret;
2783cc72986SMark Brown 
2793cc72986SMark Brown 	/*
2803cc72986SMark Brown 	 * We can't use an interrupt as we need to runtime resume to do so,
2813cc72986SMark Brown 	 * we won't race with the interrupt handler as it'll be blocked on
2823cc72986SMark Brown 	 * runtime resume.
2833cc72986SMark Brown 	 */
284ef84f885SCharles Keepax 	ret = arizona_poll_reg(arizona, 30, ARIZONA_INTERRUPT_RAW_STATUS_5,
2859d53dfdcSCharles Keepax 			       ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS);
2863cc72986SMark Brown 
2879d53dfdcSCharles Keepax 	if (!ret)
2883cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
2893cc72986SMark Brown 			     ARIZONA_BOOT_DONE_STS);
2903cc72986SMark Brown 
2913cc72986SMark Brown 	pm_runtime_mark_last_busy(arizona->dev);
2923cc72986SMark Brown 
2939d53dfdcSCharles Keepax 	return ret;
2943cc72986SMark Brown }
2953cc72986SMark Brown 
2962229875dSCharles Keepax static inline void arizona_enable_reset(struct arizona *arizona)
2972229875dSCharles Keepax {
2982229875dSCharles Keepax 	if (arizona->pdata.reset)
299c1860466SCharles Keepax 		gpiod_set_raw_value_cansleep(arizona->pdata.reset, 0);
3002229875dSCharles Keepax }
3012229875dSCharles Keepax 
3022229875dSCharles Keepax static void arizona_disable_reset(struct arizona *arizona)
3032229875dSCharles Keepax {
3042229875dSCharles Keepax 	if (arizona->pdata.reset) {
305121c075cSCharles Keepax 		switch (arizona->type) {
306121c075cSCharles Keepax 		case WM5110:
307121c075cSCharles Keepax 		case WM8280:
308121c075cSCharles Keepax 			/* Meet requirements for minimum reset duration */
309b79a980fSLee Jones 			usleep_range(5000, 10000);
310121c075cSCharles Keepax 			break;
311121c075cSCharles Keepax 		default:
312121c075cSCharles Keepax 			break;
313121c075cSCharles Keepax 		}
314121c075cSCharles Keepax 
315c1860466SCharles Keepax 		gpiod_set_raw_value_cansleep(arizona->pdata.reset, 1);
316b79a980fSLee Jones 		usleep_range(1000, 5000);
3172229875dSCharles Keepax 	}
3182229875dSCharles Keepax }
3192229875dSCharles Keepax 
3203850e3eeSCharles Keepax struct arizona_sysclk_state {
3213850e3eeSCharles Keepax 	unsigned int fll;
3223850e3eeSCharles Keepax 	unsigned int sysclk;
3233850e3eeSCharles Keepax };
3243850e3eeSCharles Keepax 
3253850e3eeSCharles Keepax static int arizona_enable_freerun_sysclk(struct arizona *arizona,
3263850e3eeSCharles Keepax 					 struct arizona_sysclk_state *state)
327e80436bbSCharles Keepax {
328e80436bbSCharles Keepax 	int ret, err;
329e80436bbSCharles Keepax 
330e80436bbSCharles Keepax 	/* Cache existing FLL and SYSCLK settings */
3313850e3eeSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &state->fll);
3320be068a0SCharles Keepax 	if (ret) {
333e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
334e80436bbSCharles Keepax 			ret);
335e80436bbSCharles Keepax 		return ret;
336e80436bbSCharles Keepax 	}
3373850e3eeSCharles Keepax 	ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
3383850e3eeSCharles Keepax 			  &state->sysclk);
3390be068a0SCharles Keepax 	if (ret) {
340e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
341e80436bbSCharles Keepax 			ret);
342e80436bbSCharles Keepax 		return ret;
343e80436bbSCharles Keepax 	}
344e80436bbSCharles Keepax 
345e80436bbSCharles Keepax 	/* Start up SYSCLK using the FLL in free running mode */
346e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
347e80436bbSCharles Keepax 			ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
3480be068a0SCharles Keepax 	if (ret) {
349e80436bbSCharles Keepax 		dev_err(arizona->dev,
350e80436bbSCharles Keepax 			"Failed to start FLL in freerunning mode: %d\n",
351e80436bbSCharles Keepax 			ret);
352e80436bbSCharles Keepax 		return ret;
353e80436bbSCharles Keepax 	}
354ef84f885SCharles Keepax 	ret = arizona_poll_reg(arizona, 180, ARIZONA_INTERRUPT_RAW_STATUS_5,
355e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS,
356e80436bbSCharles Keepax 			       ARIZONA_FLL1_CLOCK_OK_STS);
357de4ea10aSCharles Keepax 	if (ret)
358e80436bbSCharles Keepax 		goto err_fll;
359e80436bbSCharles Keepax 
360e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
3610be068a0SCharles Keepax 	if (ret) {
362e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
363e80436bbSCharles Keepax 		goto err_fll;
364e80436bbSCharles Keepax 	}
365e80436bbSCharles Keepax 
3663850e3eeSCharles Keepax 	return 0;
3673850e3eeSCharles Keepax 
3683850e3eeSCharles Keepax err_fll:
3693850e3eeSCharles Keepax 	err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
3703850e3eeSCharles Keepax 	if (err)
3713850e3eeSCharles Keepax 		dev_err(arizona->dev,
3723850e3eeSCharles Keepax 			"Failed to re-apply old FLL settings: %d\n", err);
3733850e3eeSCharles Keepax 
3743850e3eeSCharles Keepax 	return ret;
3753850e3eeSCharles Keepax }
3763850e3eeSCharles Keepax 
3773850e3eeSCharles Keepax static int arizona_disable_freerun_sysclk(struct arizona *arizona,
3783850e3eeSCharles Keepax 					  struct arizona_sysclk_state *state)
3793850e3eeSCharles Keepax {
3803850e3eeSCharles Keepax 	int ret;
3813850e3eeSCharles Keepax 
3823850e3eeSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
3833850e3eeSCharles Keepax 			   state->sysclk);
3843850e3eeSCharles Keepax 	if (ret) {
3853850e3eeSCharles Keepax 		dev_err(arizona->dev,
3863850e3eeSCharles Keepax 			"Failed to re-apply old SYSCLK settings: %d\n", ret);
3873850e3eeSCharles Keepax 		return ret;
3883850e3eeSCharles Keepax 	}
3893850e3eeSCharles Keepax 
3903850e3eeSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
3913850e3eeSCharles Keepax 	if (ret) {
3923850e3eeSCharles Keepax 		dev_err(arizona->dev,
3933850e3eeSCharles Keepax 			"Failed to re-apply old FLL settings: %d\n", ret);
3943850e3eeSCharles Keepax 		return ret;
3953850e3eeSCharles Keepax 	}
3963850e3eeSCharles Keepax 
3973850e3eeSCharles Keepax 	return 0;
3983850e3eeSCharles Keepax }
3993850e3eeSCharles Keepax 
4003850e3eeSCharles Keepax static int wm5102_apply_hardware_patch(struct arizona *arizona)
4013850e3eeSCharles Keepax {
4023850e3eeSCharles Keepax 	struct arizona_sysclk_state state;
4033850e3eeSCharles Keepax 	int err, ret;
4043850e3eeSCharles Keepax 
4053850e3eeSCharles Keepax 	ret = arizona_enable_freerun_sysclk(arizona, &state);
4063850e3eeSCharles Keepax 	if (ret)
4073850e3eeSCharles Keepax 		return ret;
4083850e3eeSCharles Keepax 
409e80436bbSCharles Keepax 	/* Start the write sequencer and wait for it to finish */
410e80436bbSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
411e80436bbSCharles Keepax 			   ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
4120be068a0SCharles Keepax 	if (ret) {
413e80436bbSCharles Keepax 		dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
414e80436bbSCharles Keepax 			ret);
4153850e3eeSCharles Keepax 		goto err;
416e80436bbSCharles Keepax 	}
4173850e3eeSCharles Keepax 
418ef84f885SCharles Keepax 	ret = arizona_poll_reg(arizona, 30, ARIZONA_WRITE_SEQUENCER_CTRL_1,
419e80436bbSCharles Keepax 			       ARIZONA_WSEQ_BUSY, 0);
420de4ea10aSCharles Keepax 	if (ret)
421e80436bbSCharles Keepax 		regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
422e80436bbSCharles Keepax 			     ARIZONA_WSEQ_ABORT);
423e80436bbSCharles Keepax 
4243850e3eeSCharles Keepax err:
4253850e3eeSCharles Keepax 	err = arizona_disable_freerun_sysclk(arizona, &state);
426e80436bbSCharles Keepax 
4270be068a0SCharles Keepax 	return ret ?: err;
428e80436bbSCharles Keepax }
429e80436bbSCharles Keepax 
430882bc468SCharles Keepax /*
431882bc468SCharles Keepax  * Register patch to some of the CODECs internal write sequences
432882bc468SCharles Keepax  * to ensure a clean exit from the low power sleep state.
433882bc468SCharles Keepax  */
4348019ff6cSNariman Poushin static const struct reg_sequence wm5110_sleep_patch[] = {
435882bc468SCharles Keepax 	{ 0x337A, 0xC100 },
436882bc468SCharles Keepax 	{ 0x337B, 0x0041 },
437882bc468SCharles Keepax 	{ 0x3300, 0xA210 },
438882bc468SCharles Keepax 	{ 0x3301, 0x050C },
439882bc468SCharles Keepax };
440882bc468SCharles Keepax 
441882bc468SCharles Keepax static int wm5110_apply_sleep_patch(struct arizona *arizona)
442882bc468SCharles Keepax {
443882bc468SCharles Keepax 	struct arizona_sysclk_state state;
444882bc468SCharles Keepax 	int err, ret;
445882bc468SCharles Keepax 
446882bc468SCharles Keepax 	ret = arizona_enable_freerun_sysclk(arizona, &state);
447882bc468SCharles Keepax 	if (ret)
448882bc468SCharles Keepax 		return ret;
449882bc468SCharles Keepax 
450882bc468SCharles Keepax 	ret = regmap_multi_reg_write_bypassed(arizona->regmap,
451882bc468SCharles Keepax 					      wm5110_sleep_patch,
452882bc468SCharles Keepax 					      ARRAY_SIZE(wm5110_sleep_patch));
453882bc468SCharles Keepax 
454882bc468SCharles Keepax 	err = arizona_disable_freerun_sysclk(arizona, &state);
455882bc468SCharles Keepax 
456882bc468SCharles Keepax 	return ret ?: err;
457882bc468SCharles Keepax }
458882bc468SCharles Keepax 
4591c1c6bbaSCharles Keepax static int wm5102_clear_write_sequencer(struct arizona *arizona)
4601c1c6bbaSCharles Keepax {
4611c1c6bbaSCharles Keepax 	int ret;
4621c1c6bbaSCharles Keepax 
4631c1c6bbaSCharles Keepax 	ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_3,
4641c1c6bbaSCharles Keepax 			   0x0);
4651c1c6bbaSCharles Keepax 	if (ret) {
4661c1c6bbaSCharles Keepax 		dev_err(arizona->dev,
4671c1c6bbaSCharles Keepax 			"Failed to clear write sequencer state: %d\n", ret);
4681c1c6bbaSCharles Keepax 		return ret;
4691c1c6bbaSCharles Keepax 	}
4701c1c6bbaSCharles Keepax 
4711c1c6bbaSCharles Keepax 	arizona_enable_reset(arizona);
4721c1c6bbaSCharles Keepax 	regulator_disable(arizona->dcvdd);
4731c1c6bbaSCharles Keepax 
4741c1c6bbaSCharles Keepax 	msleep(20);
4751c1c6bbaSCharles Keepax 
4761c1c6bbaSCharles Keepax 	ret = regulator_enable(arizona->dcvdd);
4771c1c6bbaSCharles Keepax 	if (ret) {
4781c1c6bbaSCharles Keepax 		dev_err(arizona->dev, "Failed to re-enable DCVDD: %d\n", ret);
4791c1c6bbaSCharles Keepax 		return ret;
4801c1c6bbaSCharles Keepax 	}
4811c1c6bbaSCharles Keepax 	arizona_disable_reset(arizona);
4821c1c6bbaSCharles Keepax 
4831c1c6bbaSCharles Keepax 	return 0;
4841c1c6bbaSCharles Keepax }
4851c1c6bbaSCharles Keepax 
48648bb9fe4SRafael J. Wysocki #ifdef CONFIG_PM
487e7811147SRichard Fitzgerald static int arizona_isolate_dcvdd(struct arizona *arizona)
488e7811147SRichard Fitzgerald {
489e7811147SRichard Fitzgerald 	int ret;
490e7811147SRichard Fitzgerald 
491e7811147SRichard Fitzgerald 	ret = regmap_update_bits(arizona->regmap,
492e7811147SRichard Fitzgerald 				 ARIZONA_ISOLATION_CONTROL,
493e7811147SRichard Fitzgerald 				 ARIZONA_ISOLATE_DCVDD1,
494e7811147SRichard Fitzgerald 				 ARIZONA_ISOLATE_DCVDD1);
495e7811147SRichard Fitzgerald 	if (ret != 0)
496e7811147SRichard Fitzgerald 		dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n", ret);
497e7811147SRichard Fitzgerald 
498e7811147SRichard Fitzgerald 	return ret;
499e7811147SRichard Fitzgerald }
500e7811147SRichard Fitzgerald 
501e7811147SRichard Fitzgerald static int arizona_connect_dcvdd(struct arizona *arizona)
502e7811147SRichard Fitzgerald {
503e7811147SRichard Fitzgerald 	int ret;
504e7811147SRichard Fitzgerald 
505e7811147SRichard Fitzgerald 	ret = regmap_update_bits(arizona->regmap,
506e7811147SRichard Fitzgerald 				 ARIZONA_ISOLATION_CONTROL,
507e7811147SRichard Fitzgerald 				 ARIZONA_ISOLATE_DCVDD1, 0);
508e7811147SRichard Fitzgerald 	if (ret != 0)
509e7811147SRichard Fitzgerald 		dev_err(arizona->dev, "Failed to connect DCVDD: %d\n", ret);
510e7811147SRichard Fitzgerald 
511e7811147SRichard Fitzgerald 	return ret;
512e7811147SRichard Fitzgerald }
513e7811147SRichard Fitzgerald 
514e3424273SRichard Fitzgerald static int arizona_is_jack_det_active(struct arizona *arizona)
515e3424273SRichard Fitzgerald {
516e3424273SRichard Fitzgerald 	unsigned int val;
517e3424273SRichard Fitzgerald 	int ret;
518e3424273SRichard Fitzgerald 
519e3424273SRichard Fitzgerald 	ret = regmap_read(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, &val);
520e3424273SRichard Fitzgerald 	if (ret) {
521e3424273SRichard Fitzgerald 		dev_err(arizona->dev,
522e3424273SRichard Fitzgerald 			"Failed to check jack det status: %d\n", ret);
523e3424273SRichard Fitzgerald 		return ret;
524e3424273SRichard Fitzgerald 	} else if (val & ARIZONA_JD1_ENA) {
525e3424273SRichard Fitzgerald 		return 1;
526e3424273SRichard Fitzgerald 	} else {
527e3424273SRichard Fitzgerald 		return 0;
528e3424273SRichard Fitzgerald 	}
529e3424273SRichard Fitzgerald }
530e3424273SRichard Fitzgerald 
5313cc72986SMark Brown static int arizona_runtime_resume(struct device *dev)
5323cc72986SMark Brown {
5333cc72986SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
5343cc72986SMark Brown 	int ret;
5353cc72986SMark Brown 
536508c8299SMark Brown 	dev_dbg(arizona->dev, "Leaving AoD mode\n");
537508c8299SMark Brown 
538e6cb7341SCharles Keepax 	if (arizona->has_fully_powered_off) {
539e6cb7341SCharles Keepax 		dev_dbg(arizona->dev, "Re-enabling core supplies\n");
540e6cb7341SCharles Keepax 
541e6cb7341SCharles Keepax 		ret = regulator_bulk_enable(arizona->num_core_supplies,
542e6cb7341SCharles Keepax 					    arizona->core_supplies);
543e6cb7341SCharles Keepax 		if (ret) {
544e6cb7341SCharles Keepax 			dev_err(dev, "Failed to enable core supplies: %d\n",
545e6cb7341SCharles Keepax 				ret);
546e6cb7341SCharles Keepax 			return ret;
547e6cb7341SCharles Keepax 		}
548e6cb7341SCharles Keepax 	}
549e6cb7341SCharles Keepax 
55059db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
55159db9691SMark Brown 	if (ret != 0) {
55259db9691SMark Brown 		dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
553e6cb7341SCharles Keepax 		if (arizona->has_fully_powered_off)
554e6cb7341SCharles Keepax 			regulator_bulk_disable(arizona->num_core_supplies,
555e6cb7341SCharles Keepax 					       arizona->core_supplies);
55659db9691SMark Brown 		return ret;
55759db9691SMark Brown 	}
5583cc72986SMark Brown 
559e6cb7341SCharles Keepax 	if (arizona->has_fully_powered_off) {
560e6cb7341SCharles Keepax 		arizona_disable_reset(arizona);
561e6cb7341SCharles Keepax 		enable_irq(arizona->irq);
562e6cb7341SCharles Keepax 		arizona->has_fully_powered_off = false;
563e6cb7341SCharles Keepax 	}
564e6cb7341SCharles Keepax 
5653cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
5663cc72986SMark Brown 
5674c9bb8bcSCharles Keepax 	switch (arizona->type) {
5684c9bb8bcSCharles Keepax 	case WM5102:
5695927467dSMark Brown 		if (arizona->external_dcvdd) {
570e7811147SRichard Fitzgerald 			ret = arizona_connect_dcvdd(arizona);
571e7811147SRichard Fitzgerald 			if (ret != 0)
5725927467dSMark Brown 				goto err;
5735927467dSMark Brown 		}
5745927467dSMark Brown 
5754c9bb8bcSCharles Keepax 		ret = wm5102_patch(arizona);
5764c9bb8bcSCharles Keepax 		if (ret != 0) {
5774c9bb8bcSCharles Keepax 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
5784c9bb8bcSCharles Keepax 				ret);
5794c9bb8bcSCharles Keepax 			goto err;
5804c9bb8bcSCharles Keepax 		}
581e80436bbSCharles Keepax 
5820be068a0SCharles Keepax 		ret = wm5102_apply_hardware_patch(arizona);
5830be068a0SCharles Keepax 		if (ret) {
584e80436bbSCharles Keepax 			dev_err(arizona->dev,
585e80436bbSCharles Keepax 				"Failed to apply hardware patch: %d\n",
586e80436bbSCharles Keepax 				ret);
587e80436bbSCharles Keepax 			goto err;
588e80436bbSCharles Keepax 		}
589e80436bbSCharles Keepax 		break;
59096129a0eSCharles Keepax 	case WM5110:
59196129a0eSCharles Keepax 	case WM8280:
59296129a0eSCharles Keepax 		ret = arizona_wait_for_boot(arizona);
59396129a0eSCharles Keepax 		if (ret)
59496129a0eSCharles Keepax 			goto err;
59596129a0eSCharles Keepax 
59696129a0eSCharles Keepax 		if (arizona->external_dcvdd) {
597e7811147SRichard Fitzgerald 			ret = arizona_connect_dcvdd(arizona);
598e7811147SRichard Fitzgerald 			if (ret != 0)
59996129a0eSCharles Keepax 				goto err;
60096129a0eSCharles Keepax 		} else {
60196129a0eSCharles Keepax 			/*
60296129a0eSCharles Keepax 			 * As this is only called for the internal regulator
60396129a0eSCharles Keepax 			 * (where we know voltage ranges available) it is ok
60496129a0eSCharles Keepax 			 * to request an exact range.
60596129a0eSCharles Keepax 			 */
60696129a0eSCharles Keepax 			ret = regulator_set_voltage(arizona->dcvdd,
60796129a0eSCharles Keepax 						    1200000, 1200000);
60896129a0eSCharles Keepax 			if (ret < 0) {
60996129a0eSCharles Keepax 				dev_err(arizona->dev,
61096129a0eSCharles Keepax 					"Failed to set resume voltage: %d\n",
61196129a0eSCharles Keepax 					ret);
61296129a0eSCharles Keepax 				goto err;
61396129a0eSCharles Keepax 			}
61496129a0eSCharles Keepax 		}
615e6cb7341SCharles Keepax 
616e6cb7341SCharles Keepax 		ret = wm5110_apply_sleep_patch(arizona);
617e6cb7341SCharles Keepax 		if (ret) {
618e6cb7341SCharles Keepax 			dev_err(arizona->dev,
619e6cb7341SCharles Keepax 				"Failed to re-apply sleep patch: %d\n",
620e6cb7341SCharles Keepax 				ret);
621e6cb7341SCharles Keepax 			goto err;
622e6cb7341SCharles Keepax 		}
62396129a0eSCharles Keepax 		break;
624ea1f3339SRichard Fitzgerald 	case WM1831:
625ea1f3339SRichard Fitzgerald 	case CS47L24:
626ea1f3339SRichard Fitzgerald 		ret = arizona_wait_for_boot(arizona);
627ea1f3339SRichard Fitzgerald 		if (ret != 0)
628ea1f3339SRichard Fitzgerald 			goto err;
629ea1f3339SRichard Fitzgerald 		break;
630e80436bbSCharles Keepax 	default:
63112bb68edSCharles Keepax 		ret = arizona_wait_for_boot(arizona);
6323762aedeSCharles Keepax 		if (ret != 0)
63312bb68edSCharles Keepax 			goto err;
63412bb68edSCharles Keepax 
6355927467dSMark Brown 		if (arizona->external_dcvdd) {
636e7811147SRichard Fitzgerald 			ret = arizona_connect_dcvdd(arizona);
637e7811147SRichard Fitzgerald 			if (ret != 0)
6385927467dSMark Brown 				goto err;
6395927467dSMark Brown 		}
640e80436bbSCharles Keepax 		break;
6414c9bb8bcSCharles Keepax 	}
6424c9bb8bcSCharles Keepax 
6439270bdf5SMark Brown 	ret = regcache_sync(arizona->regmap);
6449270bdf5SMark Brown 	if (ret != 0) {
6459270bdf5SMark Brown 		dev_err(arizona->dev, "Failed to restore register cache\n");
6464816bd1cSMark Brown 		goto err;
6479270bdf5SMark Brown 	}
6483cc72986SMark Brown 
6493cc72986SMark Brown 	return 0;
6504816bd1cSMark Brown 
6514816bd1cSMark Brown err:
6524816bd1cSMark Brown 	regcache_cache_only(arizona->regmap, true);
6534816bd1cSMark Brown 	regulator_disable(arizona->dcvdd);
6544816bd1cSMark Brown 	return ret;
6553cc72986SMark Brown }
6563cc72986SMark Brown 
6573cc72986SMark Brown static int arizona_runtime_suspend(struct device *dev)
6583cc72986SMark Brown {
6593cc72986SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
660a05950a4SDan Carpenter 	int jd_active = 0;
6615927467dSMark Brown 	int ret;
6623cc72986SMark Brown 
663508c8299SMark Brown 	dev_dbg(arizona->dev, "Entering AoD mode\n");
664508c8299SMark Brown 
66596129a0eSCharles Keepax 	switch (arizona->type) {
66696129a0eSCharles Keepax 	case WM5110:
66796129a0eSCharles Keepax 	case WM8280:
668e3424273SRichard Fitzgerald 		jd_active = arizona_is_jack_det_active(arizona);
669e3424273SRichard Fitzgerald 		if (jd_active < 0)
670e3424273SRichard Fitzgerald 			return jd_active;
671e3424273SRichard Fitzgerald 
672e7811147SRichard Fitzgerald 		if (arizona->external_dcvdd) {
673e7811147SRichard Fitzgerald 			ret = arizona_isolate_dcvdd(arizona);
674e7811147SRichard Fitzgerald 			if (ret != 0)
675e7811147SRichard Fitzgerald 				return ret;
676e7811147SRichard Fitzgerald 		} else {
67796129a0eSCharles Keepax 			/*
67896129a0eSCharles Keepax 			 * As this is only called for the internal regulator
67996129a0eSCharles Keepax 			 * (where we know voltage ranges available) it is ok
68096129a0eSCharles Keepax 			 * to request an exact range.
68196129a0eSCharles Keepax 			 */
682e7811147SRichard Fitzgerald 			ret = regulator_set_voltage(arizona->dcvdd,
683e7811147SRichard Fitzgerald 						    1175000, 1175000);
68496129a0eSCharles Keepax 			if (ret < 0) {
68596129a0eSCharles Keepax 				dev_err(arizona->dev,
686e7811147SRichard Fitzgerald 					"Failed to set suspend voltage: %d\n",
687e7811147SRichard Fitzgerald 					ret);
688e6cb7341SCharles Keepax 				return ret;
689e6cb7341SCharles Keepax 			}
690e7811147SRichard Fitzgerald 		}
691e6cb7341SCharles Keepax 		break;
692e6cb7341SCharles Keepax 	case WM5102:
693e3424273SRichard Fitzgerald 		jd_active = arizona_is_jack_det_active(arizona);
694e3424273SRichard Fitzgerald 		if (jd_active < 0)
695e3424273SRichard Fitzgerald 			return jd_active;
696e3424273SRichard Fitzgerald 
697e7811147SRichard Fitzgerald 		if (arizona->external_dcvdd) {
698e7811147SRichard Fitzgerald 			ret = arizona_isolate_dcvdd(arizona);
699e7811147SRichard Fitzgerald 			if (ret != 0)
700e7811147SRichard Fitzgerald 				return ret;
701e7811147SRichard Fitzgerald 		}
702e7811147SRichard Fitzgerald 
703e3424273SRichard Fitzgerald 		if (!jd_active) {
704e6cb7341SCharles Keepax 			ret = regmap_write(arizona->regmap,
705e6cb7341SCharles Keepax 					   ARIZONA_WRITE_SEQUENCER_CTRL_3, 0x0);
706e6cb7341SCharles Keepax 			if (ret) {
707e6cb7341SCharles Keepax 				dev_err(arizona->dev,
708e6cb7341SCharles Keepax 					"Failed to clear write sequencer: %d\n",
70996129a0eSCharles Keepax 					ret);
71096129a0eSCharles Keepax 				return ret;
71196129a0eSCharles Keepax 			}
712e6cb7341SCharles Keepax 		}
71396129a0eSCharles Keepax 		break;
714ea1f3339SRichard Fitzgerald 	case WM1831:
715ea1f3339SRichard Fitzgerald 	case CS47L24:
716ea1f3339SRichard Fitzgerald 		break;
71796129a0eSCharles Keepax 	default:
718e3424273SRichard Fitzgerald 		jd_active = arizona_is_jack_det_active(arizona);
719e3424273SRichard Fitzgerald 		if (jd_active < 0)
720e3424273SRichard Fitzgerald 			return jd_active;
721e3424273SRichard Fitzgerald 
722e7811147SRichard Fitzgerald 		if (arizona->external_dcvdd) {
723e7811147SRichard Fitzgerald 			ret = arizona_isolate_dcvdd(arizona);
724e7811147SRichard Fitzgerald 			if (ret != 0)
725e7811147SRichard Fitzgerald 				return ret;
726e7811147SRichard Fitzgerald 		}
72796129a0eSCharles Keepax 		break;
72896129a0eSCharles Keepax 	}
7295927467dSMark Brown 
7303cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
7313cc72986SMark Brown 	regcache_mark_dirty(arizona->regmap);
732e293e847SCharles Keepax 	regulator_disable(arizona->dcvdd);
7333cc72986SMark Brown 
734e6cb7341SCharles Keepax 	/* Allow us to completely power down if no jack detection */
735e3424273SRichard Fitzgerald 	if (!jd_active) {
736e6cb7341SCharles Keepax 		dev_dbg(arizona->dev, "Fully powering off\n");
737e6cb7341SCharles Keepax 
738e6cb7341SCharles Keepax 		arizona->has_fully_powered_off = true;
739e6cb7341SCharles Keepax 
74011150929SCharles Keepax 		disable_irq_nosync(arizona->irq);
741e6cb7341SCharles Keepax 		arizona_enable_reset(arizona);
742e6cb7341SCharles Keepax 		regulator_bulk_disable(arizona->num_core_supplies,
743e6cb7341SCharles Keepax 				       arizona->core_supplies);
744e6cb7341SCharles Keepax 	}
745e6cb7341SCharles Keepax 
7463cc72986SMark Brown 	return 0;
7473cc72986SMark Brown }
7483cc72986SMark Brown #endif
7493cc72986SMark Brown 
750dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP
75167c99296SMark Brown static int arizona_suspend(struct device *dev)
75267c99296SMark Brown {
75367c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
75467c99296SMark Brown 
75567c99296SMark Brown 	dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
75667c99296SMark Brown 	disable_irq(arizona->irq);
75767c99296SMark Brown 
75867c99296SMark Brown 	return 0;
75967c99296SMark Brown }
76067c99296SMark Brown 
7613612b27cSCharles Keepax static int arizona_suspend_noirq(struct device *dev)
76267c99296SMark Brown {
76367c99296SMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
76467c99296SMark Brown 
76567c99296SMark Brown 	dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
76667c99296SMark Brown 	enable_irq(arizona->irq);
76767c99296SMark Brown 
76867c99296SMark Brown 	return 0;
76967c99296SMark Brown }
77067c99296SMark Brown 
771dc781d0eSMark Brown static int arizona_resume_noirq(struct device *dev)
772dc781d0eSMark Brown {
773dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
774dc781d0eSMark Brown 
775dc781d0eSMark Brown 	dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
776dc781d0eSMark Brown 	disable_irq(arizona->irq);
777dc781d0eSMark Brown 
778dc781d0eSMark Brown 	return 0;
779dc781d0eSMark Brown }
780dc781d0eSMark Brown 
781dc781d0eSMark Brown static int arizona_resume(struct device *dev)
782dc781d0eSMark Brown {
783dc781d0eSMark Brown 	struct arizona *arizona = dev_get_drvdata(dev);
784dc781d0eSMark Brown 
7853612b27cSCharles Keepax 	dev_dbg(arizona->dev, "Resume, reenabling IRQ\n");
786dc781d0eSMark Brown 	enable_irq(arizona->irq);
787dc781d0eSMark Brown 
788dc781d0eSMark Brown 	return 0;
789dc781d0eSMark Brown }
790dc781d0eSMark Brown #endif
791dc781d0eSMark Brown 
7923cc72986SMark Brown const struct dev_pm_ops arizona_pm_ops = {
7933cc72986SMark Brown 	SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
7943cc72986SMark Brown 			   arizona_runtime_resume,
7953cc72986SMark Brown 			   NULL)
79667c99296SMark Brown 	SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
7973612b27cSCharles Keepax 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(arizona_suspend_noirq,
7983612b27cSCharles Keepax 				      arizona_resume_noirq)
7993cc72986SMark Brown };
8003cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_pm_ops);
8013cc72986SMark Brown 
802d781009cSMark Brown #ifdef CONFIG_OF
803942786e6SLee Jones unsigned long arizona_of_get_type(struct device *dev)
804d781009cSMark Brown {
805d781009cSMark Brown 	const struct of_device_id *id = of_match_device(arizona_of_match, dev);
806d781009cSMark Brown 
807d781009cSMark Brown 	if (id)
808942786e6SLee Jones 		return (unsigned long)id->data;
809d781009cSMark Brown 	else
810d781009cSMark Brown 		return 0;
811d781009cSMark Brown }
812d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_get_type);
813d781009cSMark Brown 
814d781009cSMark Brown static int arizona_of_get_core_pdata(struct arizona *arizona)
815d781009cSMark Brown {
816e4fcb1d6SCharles Keepax 	struct arizona_pdata *pdata = &arizona->pdata;
817d781009cSMark Brown 	int ret, i;
818d781009cSMark Brown 
819c1860466SCharles Keepax 	/* Handle old non-standard DT binding */
820c1860466SCharles Keepax 	pdata->reset = devm_gpiod_get_from_of_node(arizona->dev,
821c1860466SCharles Keepax 						   arizona->dev->of_node,
822c1860466SCharles Keepax 						   "wlf,reset", 0,
823c1860466SCharles Keepax 						   GPIOD_OUT_LOW,
824c1860466SCharles Keepax 						   "arizona /RESET");
825c1860466SCharles Keepax 	if (IS_ERR(pdata->reset)) {
826c1860466SCharles Keepax 		ret = PTR_ERR(pdata->reset);
8271961531dSCharles Keepax 
828c1860466SCharles Keepax 		/*
829c1860466SCharles Keepax 		 * Reset missing will be caught when other binding is read
830c1860466SCharles Keepax 		 * but all other errors imply this binding is in use but has
831c1860466SCharles Keepax 		 * encountered a problem so should be handled.
832c1860466SCharles Keepax 		 */
833c1860466SCharles Keepax 		if (ret == -EPROBE_DEFER)
834c1860466SCharles Keepax 			return ret;
835c1860466SCharles Keepax 		else if (ret != -ENOENT && ret != -ENOSYS)
836c1860466SCharles Keepax 			dev_err(arizona->dev, "Reset GPIO malformed: %d\n",
837c1860466SCharles Keepax 				ret);
838c1860466SCharles Keepax 
839c1860466SCharles Keepax 		pdata->reset = NULL;
8401961531dSCharles Keepax 	}
841d781009cSMark Brown 
842d781009cSMark Brown 	ret = of_property_read_u32_array(arizona->dev->of_node,
843d781009cSMark Brown 					 "wlf,gpio-defaults",
8443762aedeSCharles Keepax 					 pdata->gpio_defaults,
8453762aedeSCharles Keepax 					 ARRAY_SIZE(pdata->gpio_defaults));
846d781009cSMark Brown 	if (ret >= 0) {
847d781009cSMark Brown 		/*
848d781009cSMark Brown 		 * All values are literal except out of range values
849d781009cSMark Brown 		 * which are chip default, translate into platform
850d781009cSMark Brown 		 * data which uses 0 as chip default and out of range
851d781009cSMark Brown 		 * as zero.
852d781009cSMark Brown 		 */
8533762aedeSCharles Keepax 		for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
8543762aedeSCharles Keepax 			if (pdata->gpio_defaults[i] > 0xffff)
8553762aedeSCharles Keepax 				pdata->gpio_defaults[i] = 0;
8563762aedeSCharles Keepax 			else if (pdata->gpio_defaults[i] == 0)
8573762aedeSCharles Keepax 				pdata->gpio_defaults[i] = 0x10000;
858d781009cSMark Brown 		}
859d781009cSMark Brown 	} else {
860d781009cSMark Brown 		dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
861d781009cSMark Brown 			ret);
862d781009cSMark Brown 	}
863d781009cSMark Brown 
864d781009cSMark Brown 	return 0;
865d781009cSMark Brown }
866d781009cSMark Brown 
867d781009cSMark Brown const struct of_device_id arizona_of_match[] = {
868d781009cSMark Brown 	{ .compatible = "wlf,wm5102", .data = (void *)WM5102 },
869d781009cSMark Brown 	{ .compatible = "wlf,wm5110", .data = (void *)WM5110 },
870e5d4ef0dSRichard Fitzgerald 	{ .compatible = "wlf,wm8280", .data = (void *)WM8280 },
871dc7d4863SCharles Keepax 	{ .compatible = "wlf,wm8997", .data = (void *)WM8997 },
8726887b042SRichard Fitzgerald 	{ .compatible = "wlf,wm8998", .data = (void *)WM8998 },
8736887b042SRichard Fitzgerald 	{ .compatible = "wlf,wm1814", .data = (void *)WM1814 },
874ea1f3339SRichard Fitzgerald 	{ .compatible = "wlf,wm1831", .data = (void *)WM1831 },
875ea1f3339SRichard Fitzgerald 	{ .compatible = "cirrus,cs47l24", .data = (void *)CS47L24 },
876d781009cSMark Brown 	{},
877d781009cSMark Brown };
878d781009cSMark Brown EXPORT_SYMBOL_GPL(arizona_of_match);
879d781009cSMark Brown #else
880d781009cSMark Brown static inline int arizona_of_get_core_pdata(struct arizona *arizona)
881d781009cSMark Brown {
882d781009cSMark Brown 	return 0;
883d781009cSMark Brown }
884d781009cSMark Brown #endif
885d781009cSMark Brown 
8865ac98553SGeert Uytterhoeven static const struct mfd_cell early_devs[] = {
8873cc72986SMark Brown 	{ .name = "arizona-ldo1" },
8883cc72986SMark Brown };
8893cc72986SMark Brown 
8903762aedeSCharles Keepax static const char * const wm5102_supplies[] = {
8915fc6c396SCharles Keepax 	"MICVDD",
89232dadef2SCharles Keepax 	"DBVDD2",
89332dadef2SCharles Keepax 	"DBVDD3",
89432dadef2SCharles Keepax 	"CPVDD",
89532dadef2SCharles Keepax 	"SPKVDDL",
89632dadef2SCharles Keepax 	"SPKVDDR",
89732dadef2SCharles Keepax };
89832dadef2SCharles Keepax 
8995ac98553SGeert Uytterhoeven static const struct mfd_cell wm5102_devs[] = {
900d7768111SMark Brown 	{ .name = "arizona-micsupp" },
901f83c218cSCharles Keepax 	{ .name = "arizona-gpio" },
9025fc6c396SCharles Keepax 	{
9035fc6c396SCharles Keepax 		.name = "arizona-extcon",
9045fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
9055fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
9065fc6c396SCharles Keepax 	},
907503b1cacSMark Brown 	{ .name = "arizona-haptics" },
9083cc72986SMark Brown 	{ .name = "arizona-pwm" },
90932dadef2SCharles Keepax 	{
91032dadef2SCharles Keepax 		.name = "wm5102-codec",
91132dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
91232dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
91332dadef2SCharles Keepax 	},
9143cc72986SMark Brown };
9153cc72986SMark Brown 
9165ac98553SGeert Uytterhoeven static const struct mfd_cell wm5110_devs[] = {
917d7768111SMark Brown 	{ .name = "arizona-micsupp" },
918f83c218cSCharles Keepax 	{ .name = "arizona-gpio" },
9195fc6c396SCharles Keepax 	{
9205fc6c396SCharles Keepax 		.name = "arizona-extcon",
9215fc6c396SCharles Keepax 		.parent_supplies = wm5102_supplies,
9225fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
9235fc6c396SCharles Keepax 	},
924503b1cacSMark Brown 	{ .name = "arizona-haptics" },
925e102befeSMark Brown 	{ .name = "arizona-pwm" },
92632dadef2SCharles Keepax 	{
92732dadef2SCharles Keepax 		.name = "wm5110-codec",
92832dadef2SCharles Keepax 		.parent_supplies = wm5102_supplies,
92932dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
93032dadef2SCharles Keepax 	},
93132dadef2SCharles Keepax };
93232dadef2SCharles Keepax 
933ea1f3339SRichard Fitzgerald static const char * const cs47l24_supplies[] = {
934ea1f3339SRichard Fitzgerald 	"MICVDD",
935ea1f3339SRichard Fitzgerald 	"CPVDD",
936ea1f3339SRichard Fitzgerald 	"SPKVDD",
937ea1f3339SRichard Fitzgerald };
938ea1f3339SRichard Fitzgerald 
939ea1f3339SRichard Fitzgerald static const struct mfd_cell cs47l24_devs[] = {
940ea1f3339SRichard Fitzgerald 	{ .name = "arizona-gpio" },
941ea1f3339SRichard Fitzgerald 	{ .name = "arizona-haptics" },
942ea1f3339SRichard Fitzgerald 	{ .name = "arizona-pwm" },
943ea1f3339SRichard Fitzgerald 	{
944ea1f3339SRichard Fitzgerald 		.name = "cs47l24-codec",
945ea1f3339SRichard Fitzgerald 		.parent_supplies = cs47l24_supplies,
946ea1f3339SRichard Fitzgerald 		.num_parent_supplies = ARRAY_SIZE(cs47l24_supplies),
947ea1f3339SRichard Fitzgerald 	},
948ea1f3339SRichard Fitzgerald };
949ea1f3339SRichard Fitzgerald 
9503762aedeSCharles Keepax static const char * const wm8997_supplies[] = {
951996c2d4fSCharles Keepax 	"MICVDD",
95232dadef2SCharles Keepax 	"DBVDD2",
95332dadef2SCharles Keepax 	"CPVDD",
95432dadef2SCharles Keepax 	"SPKVDD",
955e102befeSMark Brown };
956e102befeSMark Brown 
9575ac98553SGeert Uytterhoeven static const struct mfd_cell wm8997_devs[] = {
958dc7d4863SCharles Keepax 	{ .name = "arizona-micsupp" },
959f83c218cSCharles Keepax 	{ .name = "arizona-gpio" },
9605fc6c396SCharles Keepax 	{
9615fc6c396SCharles Keepax 		.name = "arizona-extcon",
9625fc6c396SCharles Keepax 		.parent_supplies = wm8997_supplies,
9635fc6c396SCharles Keepax 		.num_parent_supplies = 1, /* We only need MICVDD */
9645fc6c396SCharles Keepax 	},
965dc7d4863SCharles Keepax 	{ .name = "arizona-haptics" },
966dc7d4863SCharles Keepax 	{ .name = "arizona-pwm" },
96732dadef2SCharles Keepax 	{
96832dadef2SCharles Keepax 		.name = "wm8997-codec",
96932dadef2SCharles Keepax 		.parent_supplies = wm8997_supplies,
97032dadef2SCharles Keepax 		.num_parent_supplies = ARRAY_SIZE(wm8997_supplies),
97132dadef2SCharles Keepax 	},
972dc7d4863SCharles Keepax };
973dc7d4863SCharles Keepax 
9746887b042SRichard Fitzgerald static const struct mfd_cell wm8998_devs[] = {
975f83c218cSCharles Keepax 	{ .name = "arizona-micsupp" },
976f83c218cSCharles Keepax 	{ .name = "arizona-gpio" },
9776887b042SRichard Fitzgerald 	{
9786887b042SRichard Fitzgerald 		.name = "arizona-extcon",
9796887b042SRichard Fitzgerald 		.parent_supplies = wm5102_supplies,
9806887b042SRichard Fitzgerald 		.num_parent_supplies = 1, /* We only need MICVDD */
9816887b042SRichard Fitzgerald 	},
9826887b042SRichard Fitzgerald 	{ .name = "arizona-haptics" },
9836887b042SRichard Fitzgerald 	{ .name = "arizona-pwm" },
9846887b042SRichard Fitzgerald 	{
9856887b042SRichard Fitzgerald 		.name = "wm8998-codec",
9866887b042SRichard Fitzgerald 		.parent_supplies = wm5102_supplies,
9876887b042SRichard Fitzgerald 		.num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
9886887b042SRichard Fitzgerald 	},
9896887b042SRichard Fitzgerald };
9906887b042SRichard Fitzgerald 
991f791be49SBill Pemberton int arizona_dev_init(struct arizona *arizona)
9923cc72986SMark Brown {
993cdd8da8cSSylwester Nawrocki 	const char * const mclk_name[] = { "mclk1", "mclk2" };
9943cc72986SMark Brown 	struct device *dev = arizona->dev;
995ea1f3339SRichard Fitzgerald 	const char *type_name = NULL;
9966000c99eSCharles Keepax 	unsigned int reg, val;
99762d62b59SMark Brown 	int (*apply_patch)(struct arizona *) = NULL;
998ae05ea36SRichard Fitzgerald 	const struct mfd_cell *subdevs = NULL;
999ae05ea36SRichard Fitzgerald 	int n_subdevs, ret, i;
10003cc72986SMark Brown 
10013cc72986SMark Brown 	dev_set_drvdata(arizona->dev, arizona);
10023cc72986SMark Brown 	mutex_init(&arizona->clk_lock);
10033cc72986SMark Brown 
1004b8d336edSCharles Keepax 	if (dev_get_platdata(arizona->dev)) {
10053cc72986SMark Brown 		memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
10063cc72986SMark Brown 		       sizeof(arizona->pdata));
1007b8d336edSCharles Keepax 	} else {
1008b8d336edSCharles Keepax 		ret = arizona_of_get_core_pdata(arizona);
1009b8d336edSCharles Keepax 		if (ret < 0)
1010b8d336edSCharles Keepax 			return ret;
1011b8d336edSCharles Keepax 	}
10123cc72986SMark Brown 
1013cdd8da8cSSylwester Nawrocki 	BUILD_BUG_ON(ARRAY_SIZE(arizona->mclk) != ARRAY_SIZE(mclk_name));
1014cdd8da8cSSylwester Nawrocki 	for (i = 0; i < ARRAY_SIZE(arizona->mclk); i++) {
1015cdd8da8cSSylwester Nawrocki 		arizona->mclk[i] = devm_clk_get(arizona->dev, mclk_name[i]);
1016cdd8da8cSSylwester Nawrocki 		if (IS_ERR(arizona->mclk[i])) {
1017cdd8da8cSSylwester Nawrocki 			dev_info(arizona->dev, "Failed to get %s: %ld\n",
1018cdd8da8cSSylwester Nawrocki 				 mclk_name[i], PTR_ERR(arizona->mclk[i]));
1019cdd8da8cSSylwester Nawrocki 			arizona->mclk[i] = NULL;
1020cdd8da8cSSylwester Nawrocki 		}
1021cdd8da8cSSylwester Nawrocki 	}
1022cdd8da8cSSylwester Nawrocki 
10233cc72986SMark Brown 	regcache_cache_only(arizona->regmap, true);
10243cc72986SMark Brown 
10253cc72986SMark Brown 	switch (arizona->type) {
10263cc72986SMark Brown 	case WM5102:
1027e102befeSMark Brown 	case WM5110:
1028e5d4ef0dSRichard Fitzgerald 	case WM8280:
1029dc7d4863SCharles Keepax 	case WM8997:
10306887b042SRichard Fitzgerald 	case WM8998:
10316887b042SRichard Fitzgerald 	case WM1814:
1032ea1f3339SRichard Fitzgerald 	case WM1831:
1033ea1f3339SRichard Fitzgerald 	case CS47L24:
10343cc72986SMark Brown 		for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
10353cc72986SMark Brown 			arizona->core_supplies[i].supply
10363cc72986SMark Brown 				= wm5102_core_supplies[i];
10373cc72986SMark Brown 		arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
10383cc72986SMark Brown 		break;
10393cc72986SMark Brown 	default:
10403cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device type %d\n",
10413cc72986SMark Brown 			arizona->type);
104275d8a2b0SCharles Keepax 		return -ENODEV;
10433cc72986SMark Brown 	}
10443cc72986SMark Brown 
10454a8c475fSCharles Keepax 	/* Mark DCVDD as external, LDO1 driver will clear if internal */
10464a8c475fSCharles Keepax 	arizona->external_dcvdd = true;
10474a8c475fSCharles Keepax 
1048ea1f3339SRichard Fitzgerald 	switch (arizona->type) {
1049ea1f3339SRichard Fitzgerald 	case WM1831:
1050ea1f3339SRichard Fitzgerald 	case CS47L24:
1051ea1f3339SRichard Fitzgerald 		break; /* No LDO1 regulator */
1052ea1f3339SRichard Fitzgerald 	default:
10533cc72986SMark Brown 		ret = mfd_add_devices(arizona->dev, -1, early_devs,
10540848c94fSMark Brown 				      ARRAY_SIZE(early_devs), NULL, 0, NULL);
10553cc72986SMark Brown 		if (ret != 0) {
10563cc72986SMark Brown 			dev_err(dev, "Failed to add early children: %d\n", ret);
10573cc72986SMark Brown 			return ret;
10583cc72986SMark Brown 		}
1059ea1f3339SRichard Fitzgerald 		break;
1060ea1f3339SRichard Fitzgerald 	}
10613cc72986SMark Brown 
10623cc72986SMark Brown 	ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
10633cc72986SMark Brown 				      arizona->core_supplies);
10643cc72986SMark Brown 	if (ret != 0) {
10653cc72986SMark Brown 		dev_err(dev, "Failed to request core supplies: %d\n",
10663cc72986SMark Brown 			ret);
10673cc72986SMark Brown 		goto err_early;
10683cc72986SMark Brown 	}
10693cc72986SMark Brown 
10700c2d0ffbSCharles Keepax 	/**
10710c2d0ffbSCharles Keepax 	 * Don't use devres here because the only device we have to get
10720c2d0ffbSCharles Keepax 	 * against is the MFD device and DCVDD will likely be supplied by
10730c2d0ffbSCharles Keepax 	 * one of its children. Meaning that the regulator will be
10740c2d0ffbSCharles Keepax 	 * destroyed by the time devres calls regulator put.
10750c2d0ffbSCharles Keepax 	 */
1076e6021511SCharles Keepax 	arizona->dcvdd = regulator_get(arizona->dev, "DCVDD");
107759db9691SMark Brown 	if (IS_ERR(arizona->dcvdd)) {
107859db9691SMark Brown 		ret = PTR_ERR(arizona->dcvdd);
107959db9691SMark Brown 		dev_err(dev, "Failed to request DCVDD: %d\n", ret);
108059db9691SMark Brown 		goto err_early;
108159db9691SMark Brown 	}
108259db9691SMark Brown 
1083c1860466SCharles Keepax 	if (!arizona->pdata.reset) {
108487d3af4aSMark Brown 		/* Start out with /RESET low to put the chip into reset */
1085c1860466SCharles Keepax 		arizona->pdata.reset = devm_gpiod_get(arizona->dev, "reset",
1086c1860466SCharles Keepax 						      GPIOD_OUT_LOW);
1087c1860466SCharles Keepax 		if (IS_ERR(arizona->pdata.reset)) {
1088c1860466SCharles Keepax 			ret = PTR_ERR(arizona->pdata.reset);
1089c1860466SCharles Keepax 			if (ret == -EPROBE_DEFER)
1090e6021511SCharles Keepax 				goto err_dcvdd;
1091c1860466SCharles Keepax 
1092c1860466SCharles Keepax 			dev_err(arizona->dev,
1093c1860466SCharles Keepax 				"Reset GPIO missing/malformed: %d\n", ret);
1094c1860466SCharles Keepax 
1095c1860466SCharles Keepax 			arizona->pdata.reset = NULL;
109687d3af4aSMark Brown 		}
109787d3af4aSMark Brown 	}
109887d3af4aSMark Brown 
10993cc72986SMark Brown 	ret = regulator_bulk_enable(arizona->num_core_supplies,
11003cc72986SMark Brown 				    arizona->core_supplies);
11013cc72986SMark Brown 	if (ret != 0) {
11023cc72986SMark Brown 		dev_err(dev, "Failed to enable core supplies: %d\n",
11033cc72986SMark Brown 			ret);
1104e6021511SCharles Keepax 		goto err_dcvdd;
11053cc72986SMark Brown 	}
11063cc72986SMark Brown 
110759db9691SMark Brown 	ret = regulator_enable(arizona->dcvdd);
110859db9691SMark Brown 	if (ret != 0) {
110959db9691SMark Brown 		dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
111059db9691SMark Brown 		goto err_enable;
111159db9691SMark Brown 	}
111259db9691SMark Brown 
11132229875dSCharles Keepax 	arizona_disable_reset(arizona);
11143cc72986SMark Brown 
11153cc72986SMark Brown 	regcache_cache_only(arizona->regmap, false);
11163cc72986SMark Brown 
1117ca76ceb8SMark Brown 	/* Verify that this is a chip we know about */
1118ca76ceb8SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
1119ca76ceb8SMark Brown 	if (ret != 0) {
1120ca76ceb8SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
1121ca76ceb8SMark Brown 		goto err_reset;
1122ca76ceb8SMark Brown 	}
1123ca76ceb8SMark Brown 
1124ca76ceb8SMark Brown 	switch (reg) {
1125ca76ceb8SMark Brown 	case 0x5102:
1126ca76ceb8SMark Brown 	case 0x5110:
11276887b042SRichard Fitzgerald 	case 0x6349:
1128ea1f3339SRichard Fitzgerald 	case 0x6363:
1129dc7d4863SCharles Keepax 	case 0x8997:
1130ca76ceb8SMark Brown 		break;
1131ca76ceb8SMark Brown 	default:
1132ca76ceb8SMark Brown 		dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
113375d8a2b0SCharles Keepax 		ret = -ENODEV;
1134ca76ceb8SMark Brown 		goto err_reset;
1135ca76ceb8SMark Brown 	}
1136ca76ceb8SMark Brown 
1137ca76ceb8SMark Brown 	/* If we have a /RESET GPIO we'll already be reset */
1138ca76ceb8SMark Brown 	if (!arizona->pdata.reset) {
1139ca76ceb8SMark Brown 		ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
1140ca76ceb8SMark Brown 		if (ret != 0) {
1141ca76ceb8SMark Brown 			dev_err(dev, "Failed to reset device: %d\n", ret);
1142ca76ceb8SMark Brown 			goto err_reset;
1143ca76ceb8SMark Brown 		}
1144ca76ceb8SMark Brown 
1145b79a980fSLee Jones 		usleep_range(1000, 5000);
1146ca76ceb8SMark Brown 	}
1147ca76ceb8SMark Brown 
1148ca76ceb8SMark Brown 	/* Ensure device startup is complete */
1149ca76ceb8SMark Brown 	switch (arizona->type) {
1150ca76ceb8SMark Brown 	case WM5102:
115148018943SMark Brown 		ret = regmap_read(arizona->regmap,
115248018943SMark Brown 				  ARIZONA_WRITE_SEQUENCER_CTRL_3, &val);
11531c1c6bbaSCharles Keepax 		if (ret) {
1154ca76ceb8SMark Brown 			dev_err(dev,
1155ca76ceb8SMark Brown 				"Failed to check write sequencer state: %d\n",
1156ca76ceb8SMark Brown 				ret);
11571c1c6bbaSCharles Keepax 		} else if (val & 0x01) {
11581c1c6bbaSCharles Keepax 			ret = wm5102_clear_write_sequencer(arizona);
11591c1c6bbaSCharles Keepax 			if (ret)
11601c1c6bbaSCharles Keepax 				return ret;
1161ca76ceb8SMark Brown 		}
1162ca76ceb8SMark Brown 		break;
11631c1c6bbaSCharles Keepax 	default:
11641c1c6bbaSCharles Keepax 		break;
11651c1c6bbaSCharles Keepax 	}
11661c1c6bbaSCharles Keepax 
11671c1c6bbaSCharles Keepax 	ret = arizona_wait_for_boot(arizona);
11681c1c6bbaSCharles Keepax 	if (ret) {
11691c1c6bbaSCharles Keepax 		dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
11701c1c6bbaSCharles Keepax 		goto err_reset;
1171ca76ceb8SMark Brown 	}
1172ca76ceb8SMark Brown 
1173ca76ceb8SMark Brown 	/* Read the device ID information & do device specific stuff */
11743cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
11753cc72986SMark Brown 	if (ret != 0) {
11763cc72986SMark Brown 		dev_err(dev, "Failed to read ID register: %d\n", ret);
117759db9691SMark Brown 		goto err_reset;
11783cc72986SMark Brown 	}
11793cc72986SMark Brown 
11803cc72986SMark Brown 	ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
11813cc72986SMark Brown 			  &arizona->rev);
11823cc72986SMark Brown 	if (ret != 0) {
11833cc72986SMark Brown 		dev_err(dev, "Failed to read revision register: %d\n", ret);
118459db9691SMark Brown 		goto err_reset;
11853cc72986SMark Brown 	}
11863cc72986SMark Brown 	arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
11873cc72986SMark Brown 
11883cc72986SMark Brown 	switch (reg) {
11893cc72986SMark Brown 	case 0x5102:
1190b61c1ec0SRichard Fitzgerald 		if (IS_ENABLED(CONFIG_MFD_WM5102)) {
11913cc72986SMark Brown 			type_name = "WM5102";
11923cc72986SMark Brown 			if (arizona->type != WM5102) {
1193b61c1ec0SRichard Fitzgerald 				dev_warn(arizona->dev,
1194b61c1ec0SRichard Fitzgerald 					 "WM5102 registered as %d\n",
11953cc72986SMark Brown 					 arizona->type);
11963cc72986SMark Brown 				arizona->type = WM5102;
11973cc72986SMark Brown 			}
1198b61c1ec0SRichard Fitzgerald 
119962d62b59SMark Brown 			apply_patch = wm5102_patch;
1200c6d6bfb1SMark Brown 			arizona->rev &= 0x7;
1201ae05ea36SRichard Fitzgerald 			subdevs = wm5102_devs;
1202ae05ea36SRichard Fitzgerald 			n_subdevs = ARRAY_SIZE(wm5102_devs);
1203b61c1ec0SRichard Fitzgerald 		}
12043cc72986SMark Brown 		break;
1205e102befeSMark Brown 	case 0x5110:
1206b61c1ec0SRichard Fitzgerald 		if (IS_ENABLED(CONFIG_MFD_WM5110)) {
1207e5d4ef0dSRichard Fitzgerald 			switch (arizona->type) {
1208e5d4ef0dSRichard Fitzgerald 			case WM5110:
1209e102befeSMark Brown 				type_name = "WM5110";
1210e5d4ef0dSRichard Fitzgerald 				break;
1211e5d4ef0dSRichard Fitzgerald 			case WM8280:
1212e5d4ef0dSRichard Fitzgerald 				type_name = "WM8280";
1213e5d4ef0dSRichard Fitzgerald 				break;
1214e5d4ef0dSRichard Fitzgerald 			default:
1215e5d4ef0dSRichard Fitzgerald 				type_name = "WM5110";
1216b61c1ec0SRichard Fitzgerald 				dev_warn(arizona->dev,
1217b61c1ec0SRichard Fitzgerald 					 "WM5110 registered as %d\n",
1218e102befeSMark Brown 					 arizona->type);
1219e102befeSMark Brown 				arizona->type = WM5110;
1220e5d4ef0dSRichard Fitzgerald 				break;
1221e102befeSMark Brown 			}
1222b61c1ec0SRichard Fitzgerald 
122362d62b59SMark Brown 			apply_patch = wm5110_patch;
1224ae05ea36SRichard Fitzgerald 			subdevs = wm5110_devs;
1225ae05ea36SRichard Fitzgerald 			n_subdevs = ARRAY_SIZE(wm5110_devs);
1226b61c1ec0SRichard Fitzgerald 		}
1227e102befeSMark Brown 		break;
1228ea1f3339SRichard Fitzgerald 	case 0x6363:
1229ea1f3339SRichard Fitzgerald 		if (IS_ENABLED(CONFIG_MFD_CS47L24)) {
1230ea1f3339SRichard Fitzgerald 			switch (arizona->type) {
1231ea1f3339SRichard Fitzgerald 			case CS47L24:
1232ea1f3339SRichard Fitzgerald 				type_name = "CS47L24";
1233ea1f3339SRichard Fitzgerald 				break;
1234ea1f3339SRichard Fitzgerald 
1235ea1f3339SRichard Fitzgerald 			case WM1831:
1236ea1f3339SRichard Fitzgerald 				type_name = "WM1831";
1237ea1f3339SRichard Fitzgerald 				break;
1238ea1f3339SRichard Fitzgerald 
1239ea1f3339SRichard Fitzgerald 			default:
1240ea1f3339SRichard Fitzgerald 				dev_warn(arizona->dev,
1241ea1f3339SRichard Fitzgerald 					 "CS47L24 registered as %d\n",
1242ea1f3339SRichard Fitzgerald 					 arizona->type);
1243ea1f3339SRichard Fitzgerald 				arizona->type = CS47L24;
1244ea1f3339SRichard Fitzgerald 				break;
1245ea1f3339SRichard Fitzgerald 			}
1246ea1f3339SRichard Fitzgerald 
1247ea1f3339SRichard Fitzgerald 			apply_patch = cs47l24_patch;
1248ea1f3339SRichard Fitzgerald 			subdevs = cs47l24_devs;
1249ea1f3339SRichard Fitzgerald 			n_subdevs = ARRAY_SIZE(cs47l24_devs);
1250ea1f3339SRichard Fitzgerald 		}
1251ea1f3339SRichard Fitzgerald 		break;
1252dc7d4863SCharles Keepax 	case 0x8997:
1253b61c1ec0SRichard Fitzgerald 		if (IS_ENABLED(CONFIG_MFD_WM8997)) {
1254dc7d4863SCharles Keepax 			type_name = "WM8997";
1255dc7d4863SCharles Keepax 			if (arizona->type != WM8997) {
1256b61c1ec0SRichard Fitzgerald 				dev_warn(arizona->dev,
1257b61c1ec0SRichard Fitzgerald 					 "WM8997 registered as %d\n",
1258dc7d4863SCharles Keepax 					 arizona->type);
1259dc7d4863SCharles Keepax 				arizona->type = WM8997;
1260dc7d4863SCharles Keepax 			}
1261b61c1ec0SRichard Fitzgerald 
1262dc7d4863SCharles Keepax 			apply_patch = wm8997_patch;
1263ae05ea36SRichard Fitzgerald 			subdevs = wm8997_devs;
1264ae05ea36SRichard Fitzgerald 			n_subdevs = ARRAY_SIZE(wm8997_devs);
1265b61c1ec0SRichard Fitzgerald 		}
1266dc7d4863SCharles Keepax 		break;
12676887b042SRichard Fitzgerald 	case 0x6349:
1268b61c1ec0SRichard Fitzgerald 		if (IS_ENABLED(CONFIG_MFD_WM8998)) {
12696887b042SRichard Fitzgerald 			switch (arizona->type) {
12706887b042SRichard Fitzgerald 			case WM8998:
12716887b042SRichard Fitzgerald 				type_name = "WM8998";
12726887b042SRichard Fitzgerald 				break;
12736887b042SRichard Fitzgerald 
12746887b042SRichard Fitzgerald 			case WM1814:
12756887b042SRichard Fitzgerald 				type_name = "WM1814";
12766887b042SRichard Fitzgerald 				break;
12776887b042SRichard Fitzgerald 
12786887b042SRichard Fitzgerald 			default:
12796887b042SRichard Fitzgerald 				type_name = "WM8998";
1280b61c1ec0SRichard Fitzgerald 				dev_warn(arizona->dev,
1281b61c1ec0SRichard Fitzgerald 					 "WM8998 registered as %d\n",
12826887b042SRichard Fitzgerald 					 arizona->type);
12836887b042SRichard Fitzgerald 				arizona->type = WM8998;
12846887b042SRichard Fitzgerald 			}
12856887b042SRichard Fitzgerald 
12866887b042SRichard Fitzgerald 			apply_patch = wm8998_patch;
1287ae05ea36SRichard Fitzgerald 			subdevs = wm8998_devs;
1288ae05ea36SRichard Fitzgerald 			n_subdevs = ARRAY_SIZE(wm8998_devs);
1289b61c1ec0SRichard Fitzgerald 		}
12906887b042SRichard Fitzgerald 		break;
12913cc72986SMark Brown 	default:
12923cc72986SMark Brown 		dev_err(arizona->dev, "Unknown device ID %x\n", reg);
129375d8a2b0SCharles Keepax 		ret = -ENODEV;
129459db9691SMark Brown 		goto err_reset;
12953cc72986SMark Brown 	}
12963cc72986SMark Brown 
1297b61c1ec0SRichard Fitzgerald 	if (!subdevs) {
1298b61c1ec0SRichard Fitzgerald 		dev_err(arizona->dev,
1299b61c1ec0SRichard Fitzgerald 			"No kernel support for device ID %x\n", reg);
130075d8a2b0SCharles Keepax 		ret = -ENODEV;
1301b61c1ec0SRichard Fitzgerald 		goto err_reset;
1302b61c1ec0SRichard Fitzgerald 	}
1303b61c1ec0SRichard Fitzgerald 
13043cc72986SMark Brown 	dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
13053cc72986SMark Brown 
130662d62b59SMark Brown 	if (apply_patch) {
130762d62b59SMark Brown 		ret = apply_patch(arizona);
130862d62b59SMark Brown 		if (ret != 0) {
130962d62b59SMark Brown 			dev_err(arizona->dev, "Failed to apply patch: %d\n",
131062d62b59SMark Brown 				ret);
131162d62b59SMark Brown 			goto err_reset;
131262d62b59SMark Brown 		}
1313e80436bbSCharles Keepax 
1314e80436bbSCharles Keepax 		switch (arizona->type) {
1315e80436bbSCharles Keepax 		case WM5102:
13160be068a0SCharles Keepax 			ret = wm5102_apply_hardware_patch(arizona);
13170be068a0SCharles Keepax 			if (ret) {
1318e80436bbSCharles Keepax 				dev_err(arizona->dev,
1319e80436bbSCharles Keepax 					"Failed to apply hardware patch: %d\n",
1320e80436bbSCharles Keepax 					ret);
1321e80436bbSCharles Keepax 				goto err_reset;
1322e80436bbSCharles Keepax 			}
1323e80436bbSCharles Keepax 			break;
1324882bc468SCharles Keepax 		case WM5110:
1325882bc468SCharles Keepax 		case WM8280:
1326882bc468SCharles Keepax 			ret = wm5110_apply_sleep_patch(arizona);
1327882bc468SCharles Keepax 			if (ret) {
1328882bc468SCharles Keepax 				dev_err(arizona->dev,
1329882bc468SCharles Keepax 					"Failed to apply sleep patch: %d\n",
1330882bc468SCharles Keepax 					ret);
1331882bc468SCharles Keepax 				goto err_reset;
1332882bc468SCharles Keepax 			}
1333882bc468SCharles Keepax 			break;
1334e80436bbSCharles Keepax 		default:
1335e80436bbSCharles Keepax 			break;
1336e80436bbSCharles Keepax 		}
133762d62b59SMark Brown 	}
133862d62b59SMark Brown 
13393cc72986SMark Brown 	for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
13403cc72986SMark Brown 		if (!arizona->pdata.gpio_defaults[i])
13413cc72986SMark Brown 			continue;
13423cc72986SMark Brown 
13433cc72986SMark Brown 		regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
13443cc72986SMark Brown 			     arizona->pdata.gpio_defaults[i]);
13453cc72986SMark Brown 	}
13463cc72986SMark Brown 
13473cc72986SMark Brown 	/* Chip default */
13483cc72986SMark Brown 	if (!arizona->pdata.clk32k_src)
13493cc72986SMark Brown 		arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
13503cc72986SMark Brown 
13513cc72986SMark Brown 	switch (arizona->pdata.clk32k_src) {
13523cc72986SMark Brown 	case ARIZONA_32KZ_MCLK1:
13533cc72986SMark Brown 	case ARIZONA_32KZ_MCLK2:
13543cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
13553cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK,
13563cc72986SMark Brown 				   arizona->pdata.clk32k_src - 1);
1357767c6dc0SMark Brown 		arizona_clk32k_enable(arizona);
13583cc72986SMark Brown 		break;
13593cc72986SMark Brown 	case ARIZONA_32KZ_NONE:
13603cc72986SMark Brown 		regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
13613cc72986SMark Brown 				   ARIZONA_CLK_32K_SRC_MASK, 2);
13623cc72986SMark Brown 		break;
13633cc72986SMark Brown 	default:
13643cc72986SMark Brown 		dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
13653cc72986SMark Brown 			arizona->pdata.clk32k_src);
13663cc72986SMark Brown 		ret = -EINVAL;
136759db9691SMark Brown 		goto err_reset;
13683cc72986SMark Brown 	}
13693cc72986SMark Brown 
13703d91f828SMark Brown 	for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
1371544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV &&
1372544c7aadSMark Brown 		    !arizona->pdata.micbias[i].bypass)
13733d91f828SMark Brown 			continue;
13743d91f828SMark Brown 
1375544c7aadSMark Brown 		/* Apply default for bypass mode */
1376544c7aadSMark Brown 		if (!arizona->pdata.micbias[i].mV)
1377544c7aadSMark Brown 			arizona->pdata.micbias[i].mV = 2800;
1378544c7aadSMark Brown 
13793d91f828SMark Brown 		val = (arizona->pdata.micbias[i].mV - 1500) / 100;
1380544c7aadSMark Brown 
13813d91f828SMark Brown 		val <<= ARIZONA_MICB1_LVL_SHIFT;
13823d91f828SMark Brown 
13833d91f828SMark Brown 		if (arizona->pdata.micbias[i].ext_cap)
13843d91f828SMark Brown 			val |= ARIZONA_MICB1_EXT_CAP;
13853d91f828SMark Brown 
13863d91f828SMark Brown 		if (arizona->pdata.micbias[i].discharge)
13873d91f828SMark Brown 			val |= ARIZONA_MICB1_DISCH;
13883d91f828SMark Brown 
1389f773fc6dSCharles Keepax 		if (arizona->pdata.micbias[i].soft_start)
13903d91f828SMark Brown 			val |= ARIZONA_MICB1_RATE;
13913d91f828SMark Brown 
1392544c7aadSMark Brown 		if (arizona->pdata.micbias[i].bypass)
1393544c7aadSMark Brown 			val |= ARIZONA_MICB1_BYPASS;
1394544c7aadSMark Brown 
13953d91f828SMark Brown 		regmap_update_bits(arizona->regmap,
13963d91f828SMark Brown 				   ARIZONA_MIC_BIAS_CTRL_1 + i,
13973d91f828SMark Brown 				   ARIZONA_MICB1_LVL_MASK |
139871d134b9SCharles Keepax 				   ARIZONA_MICB1_EXT_CAP |
13993d91f828SMark Brown 				   ARIZONA_MICB1_DISCH |
1400544c7aadSMark Brown 				   ARIZONA_MICB1_BYPASS |
14013d91f828SMark Brown 				   ARIZONA_MICB1_RATE, val);
14023d91f828SMark Brown 	}
14033d91f828SMark Brown 
140472e43164SCharles Keepax 	pm_runtime_set_active(arizona->dev);
140572e43164SCharles Keepax 	pm_runtime_enable(arizona->dev);
140672e43164SCharles Keepax 
14073cc72986SMark Brown 	/* Set up for interrupts */
14083cc72986SMark Brown 	ret = arizona_irq_init(arizona);
14093cc72986SMark Brown 	if (ret != 0)
1410d347792cSCharles Keepax 		goto err_pm;
14113cc72986SMark Brown 
141272e43164SCharles Keepax 	pm_runtime_set_autosuspend_delay(arizona->dev, 100);
141372e43164SCharles Keepax 	pm_runtime_use_autosuspend(arizona->dev);
141472e43164SCharles Keepax 
14153cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
14163cc72986SMark Brown 			    arizona_clkgen_err, arizona);
14173cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
14183cc72986SMark Brown 			    arizona_overclocked, arizona);
14193cc72986SMark Brown 	arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
14203cc72986SMark Brown 			    arizona_underclocked, arizona);
14213cc72986SMark Brown 
1422ae05ea36SRichard Fitzgerald 	ret = mfd_add_devices(arizona->dev, PLATFORM_DEVID_NONE,
1423ae05ea36SRichard Fitzgerald 			      subdevs, n_subdevs, NULL, 0, NULL);
14243cc72986SMark Brown 
1425ae05ea36SRichard Fitzgerald 	if (ret) {
14263cc72986SMark Brown 		dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
14273cc72986SMark Brown 		goto err_irq;
14283cc72986SMark Brown 	}
14293cc72986SMark Brown 
14303cc72986SMark Brown 	return 0;
14313cc72986SMark Brown 
14323cc72986SMark Brown err_irq:
14333cc72986SMark Brown 	arizona_irq_exit(arizona);
1434d347792cSCharles Keepax err_pm:
1435d347792cSCharles Keepax 	pm_runtime_disable(arizona->dev);
14363cc72986SMark Brown err_reset:
14372229875dSCharles Keepax 	arizona_enable_reset(arizona);
143859db9691SMark Brown 	regulator_disable(arizona->dcvdd);
14393cc72986SMark Brown err_enable:
14403a36a0dbSMark Brown 	regulator_bulk_disable(arizona->num_core_supplies,
14413cc72986SMark Brown 			       arizona->core_supplies);
1442e6021511SCharles Keepax err_dcvdd:
1443e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
14443cc72986SMark Brown err_early:
14453cc72986SMark Brown 	mfd_remove_devices(dev);
14463cc72986SMark Brown 	return ret;
14473cc72986SMark Brown }
14483cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_init);
14493cc72986SMark Brown 
14504740f73fSBill Pemberton int arizona_dev_exit(struct arizona *arizona)
14513cc72986SMark Brown {
1452fb36f77eSCharles Keepax 	disable_irq(arizona->irq);
1453b804020aSCharles Keepax 	pm_runtime_disable(arizona->dev);
1454b804020aSCharles Keepax 
1455df6b3352SCharles Keepax 	regulator_disable(arizona->dcvdd);
1456e6021511SCharles Keepax 	regulator_put(arizona->dcvdd);
1457df6b3352SCharles Keepax 
14583cc72986SMark Brown 	mfd_remove_devices(arizona->dev);
14593cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
14603cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
14613cc72986SMark Brown 	arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
14623cc72986SMark Brown 	arizona_irq_exit(arizona);
14632229875dSCharles Keepax 	arizona_enable_reset(arizona);
1464df6b3352SCharles Keepax 
14654420286eSCharles Keepax 	regulator_bulk_disable(arizona->num_core_supplies,
14661d017b6bSMark Brown 			       arizona->core_supplies);
14673cc72986SMark Brown 	return 0;
14683cc72986SMark Brown }
14693cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_exit);
1470