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, ®); 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, ®); 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