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> 193cc72986SMark Brown #include <linux/pm_runtime.h> 203cc72986SMark Brown #include <linux/regmap.h> 213cc72986SMark Brown #include <linux/regulator/consumer.h> 223cc72986SMark Brown #include <linux/slab.h> 233cc72986SMark Brown 243cc72986SMark Brown #include <linux/mfd/arizona/core.h> 253cc72986SMark Brown #include <linux/mfd/arizona/registers.h> 263cc72986SMark Brown 273cc72986SMark Brown #include "arizona.h" 283cc72986SMark Brown 293cc72986SMark Brown static const char *wm5102_core_supplies[] = { 303cc72986SMark Brown "AVDD", 313cc72986SMark Brown "DBVDD1", 323cc72986SMark Brown }; 333cc72986SMark Brown 343cc72986SMark Brown int arizona_clk32k_enable(struct arizona *arizona) 353cc72986SMark Brown { 363cc72986SMark Brown int ret = 0; 373cc72986SMark Brown 383cc72986SMark Brown mutex_lock(&arizona->clk_lock); 393cc72986SMark Brown 403cc72986SMark Brown arizona->clk32k_ref++; 413cc72986SMark Brown 423cc72986SMark Brown if (arizona->clk32k_ref == 1) 433cc72986SMark Brown ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, 443cc72986SMark Brown ARIZONA_CLK_32K_ENA, 453cc72986SMark Brown ARIZONA_CLK_32K_ENA); 463cc72986SMark Brown 473cc72986SMark Brown if (ret != 0) 483cc72986SMark Brown arizona->clk32k_ref--; 493cc72986SMark Brown 503cc72986SMark Brown mutex_unlock(&arizona->clk_lock); 513cc72986SMark Brown 523cc72986SMark Brown return ret; 533cc72986SMark Brown } 543cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_clk32k_enable); 553cc72986SMark Brown 563cc72986SMark Brown int arizona_clk32k_disable(struct arizona *arizona) 573cc72986SMark Brown { 583cc72986SMark Brown int ret = 0; 593cc72986SMark Brown 603cc72986SMark Brown mutex_lock(&arizona->clk_lock); 613cc72986SMark Brown 623cc72986SMark Brown BUG_ON(arizona->clk32k_ref <= 0); 633cc72986SMark Brown 643cc72986SMark Brown arizona->clk32k_ref--; 653cc72986SMark Brown 663cc72986SMark Brown if (arizona->clk32k_ref == 0) 673cc72986SMark Brown regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, 683cc72986SMark Brown ARIZONA_CLK_32K_ENA, 0); 693cc72986SMark Brown 703cc72986SMark Brown mutex_unlock(&arizona->clk_lock); 713cc72986SMark Brown 723cc72986SMark Brown return ret; 733cc72986SMark Brown } 743cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_clk32k_disable); 753cc72986SMark Brown 763cc72986SMark Brown static irqreturn_t arizona_clkgen_err(int irq, void *data) 773cc72986SMark Brown { 783cc72986SMark Brown struct arizona *arizona = data; 793cc72986SMark Brown 803cc72986SMark Brown dev_err(arizona->dev, "CLKGEN error\n"); 813cc72986SMark Brown 823cc72986SMark Brown return IRQ_HANDLED; 833cc72986SMark Brown } 843cc72986SMark Brown 853cc72986SMark Brown static irqreturn_t arizona_underclocked(int irq, void *data) 863cc72986SMark Brown { 873cc72986SMark Brown struct arizona *arizona = data; 883cc72986SMark Brown unsigned int val; 893cc72986SMark Brown int ret; 903cc72986SMark Brown 913cc72986SMark Brown ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8, 923cc72986SMark Brown &val); 933cc72986SMark Brown if (ret != 0) { 943cc72986SMark Brown dev_err(arizona->dev, "Failed to read underclock status: %d\n", 953cc72986SMark Brown ret); 963cc72986SMark Brown return IRQ_NONE; 973cc72986SMark Brown } 983cc72986SMark Brown 993cc72986SMark Brown if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) 1003cc72986SMark Brown dev_err(arizona->dev, "AIF3 underclocked\n"); 1013cc72986SMark Brown if (val & ARIZONA_AIF2_UNDERCLOCKED_STS) 1023ebef34dSCharles Keepax dev_err(arizona->dev, "AIF2 underclocked\n"); 1033ebef34dSCharles Keepax if (val & ARIZONA_AIF1_UNDERCLOCKED_STS) 1043cc72986SMark Brown dev_err(arizona->dev, "AIF1 underclocked\n"); 1053cc72986SMark Brown if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS) 1063cc72986SMark Brown dev_err(arizona->dev, "ISRC2 underclocked\n"); 1073cc72986SMark Brown if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS) 1083cc72986SMark Brown dev_err(arizona->dev, "ISRC1 underclocked\n"); 1093cc72986SMark Brown if (val & ARIZONA_FX_UNDERCLOCKED_STS) 1103cc72986SMark Brown dev_err(arizona->dev, "FX underclocked\n"); 1113cc72986SMark Brown if (val & ARIZONA_ASRC_UNDERCLOCKED_STS) 1123cc72986SMark Brown dev_err(arizona->dev, "ASRC underclocked\n"); 1133cc72986SMark Brown if (val & ARIZONA_DAC_UNDERCLOCKED_STS) 1143cc72986SMark Brown dev_err(arizona->dev, "DAC underclocked\n"); 1153cc72986SMark Brown if (val & ARIZONA_ADC_UNDERCLOCKED_STS) 1163cc72986SMark Brown dev_err(arizona->dev, "ADC underclocked\n"); 1173cc72986SMark Brown if (val & ARIZONA_MIXER_UNDERCLOCKED_STS) 118*648a9880SMark Brown dev_err(arizona->dev, "Mixer dropped sample\n"); 1193cc72986SMark Brown 1203cc72986SMark Brown return IRQ_HANDLED; 1213cc72986SMark Brown } 1223cc72986SMark Brown 1233cc72986SMark Brown static irqreturn_t arizona_overclocked(int irq, void *data) 1243cc72986SMark Brown { 1253cc72986SMark Brown struct arizona *arizona = data; 1263cc72986SMark Brown unsigned int val[2]; 1273cc72986SMark Brown int ret; 1283cc72986SMark Brown 1293cc72986SMark Brown ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6, 1303cc72986SMark Brown &val[0], 2); 1313cc72986SMark Brown if (ret != 0) { 1323cc72986SMark Brown dev_err(arizona->dev, "Failed to read overclock status: %d\n", 1333cc72986SMark Brown ret); 1343cc72986SMark Brown return IRQ_NONE; 1353cc72986SMark Brown } 1363cc72986SMark Brown 1373cc72986SMark Brown if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS) 1383cc72986SMark Brown dev_err(arizona->dev, "PWM overclocked\n"); 1393cc72986SMark Brown if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS) 1403cc72986SMark Brown dev_err(arizona->dev, "FX core overclocked\n"); 1413cc72986SMark Brown if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS) 1423cc72986SMark Brown dev_err(arizona->dev, "DAC SYS overclocked\n"); 1433cc72986SMark Brown if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS) 1443cc72986SMark Brown dev_err(arizona->dev, "DAC WARP overclocked\n"); 1453cc72986SMark Brown if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS) 1463cc72986SMark Brown dev_err(arizona->dev, "ADC overclocked\n"); 1473cc72986SMark Brown if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS) 1483cc72986SMark Brown dev_err(arizona->dev, "Mixer overclocked\n"); 1493cc72986SMark Brown if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS) 1503cc72986SMark Brown dev_err(arizona->dev, "AIF3 overclocked\n"); 1513cc72986SMark Brown if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS) 1523cc72986SMark Brown dev_err(arizona->dev, "AIF2 overclocked\n"); 1533cc72986SMark Brown if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS) 1543cc72986SMark Brown dev_err(arizona->dev, "AIF1 overclocked\n"); 1553cc72986SMark Brown if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS) 1563cc72986SMark Brown dev_err(arizona->dev, "Pad control overclocked\n"); 1573cc72986SMark Brown 1583cc72986SMark Brown if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS) 1593cc72986SMark Brown dev_err(arizona->dev, "Slimbus subsystem overclocked\n"); 1603cc72986SMark Brown if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS) 1613cc72986SMark Brown dev_err(arizona->dev, "Slimbus async overclocked\n"); 1623cc72986SMark Brown if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS) 1633cc72986SMark Brown dev_err(arizona->dev, "Slimbus sync overclocked\n"); 1643cc72986SMark Brown if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS) 1653cc72986SMark Brown dev_err(arizona->dev, "ASRC async system overclocked\n"); 1663cc72986SMark Brown if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS) 1673cc72986SMark Brown dev_err(arizona->dev, "ASRC async WARP overclocked\n"); 1683cc72986SMark Brown if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS) 1693cc72986SMark Brown dev_err(arizona->dev, "ASRC sync system overclocked\n"); 1703cc72986SMark Brown if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS) 1713cc72986SMark Brown dev_err(arizona->dev, "ASRC sync WARP overclocked\n"); 1723cc72986SMark Brown if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS) 1733cc72986SMark Brown dev_err(arizona->dev, "DSP1 overclocked\n"); 1743cc72986SMark Brown if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS) 1753cc72986SMark Brown dev_err(arizona->dev, "ISRC2 overclocked\n"); 1763cc72986SMark Brown if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS) 1773cc72986SMark Brown dev_err(arizona->dev, "ISRC1 overclocked\n"); 1783cc72986SMark Brown 1793cc72986SMark Brown return IRQ_HANDLED; 1803cc72986SMark Brown } 1813cc72986SMark Brown 1823cc72986SMark Brown static int arizona_wait_for_boot(struct arizona *arizona) 1833cc72986SMark Brown { 1843cc72986SMark Brown unsigned int reg; 1853cc72986SMark Brown int ret, i; 1863cc72986SMark Brown 1873cc72986SMark Brown /* 1883cc72986SMark Brown * We can't use an interrupt as we need to runtime resume to do so, 1893cc72986SMark Brown * we won't race with the interrupt handler as it'll be blocked on 1903cc72986SMark Brown * runtime resume. 1913cc72986SMark Brown */ 1923cc72986SMark Brown for (i = 0; i < 5; i++) { 1933cc72986SMark Brown msleep(1); 1943cc72986SMark Brown 1953cc72986SMark Brown ret = regmap_read(arizona->regmap, 1963cc72986SMark Brown ARIZONA_INTERRUPT_RAW_STATUS_5, ®); 1973cc72986SMark Brown if (ret != 0) { 1983cc72986SMark Brown dev_err(arizona->dev, "Failed to read boot state: %d\n", 1993cc72986SMark Brown ret); 200cfe775ceSMark Brown continue; 2013cc72986SMark Brown } 2023cc72986SMark Brown 2033cc72986SMark Brown if (reg & ARIZONA_BOOT_DONE_STS) 2043cc72986SMark Brown break; 2053cc72986SMark Brown } 2063cc72986SMark Brown 2073cc72986SMark Brown if (reg & ARIZONA_BOOT_DONE_STS) { 2083cc72986SMark Brown regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5, 2093cc72986SMark Brown ARIZONA_BOOT_DONE_STS); 2103cc72986SMark Brown } else { 2113cc72986SMark Brown dev_err(arizona->dev, "Device boot timed out: %x\n", reg); 2123cc72986SMark Brown return -ETIMEDOUT; 2133cc72986SMark Brown } 2143cc72986SMark Brown 2153cc72986SMark Brown pm_runtime_mark_last_busy(arizona->dev); 2163cc72986SMark Brown 2173cc72986SMark Brown return 0; 2183cc72986SMark Brown } 2193cc72986SMark Brown 2203cc72986SMark Brown #ifdef CONFIG_PM_RUNTIME 2213cc72986SMark Brown static int arizona_runtime_resume(struct device *dev) 2223cc72986SMark Brown { 2233cc72986SMark Brown struct arizona *arizona = dev_get_drvdata(dev); 2243cc72986SMark Brown int ret; 2253cc72986SMark Brown 226508c8299SMark Brown dev_dbg(arizona->dev, "Leaving AoD mode\n"); 227508c8299SMark Brown 22859db9691SMark Brown ret = regulator_enable(arizona->dcvdd); 22959db9691SMark Brown if (ret != 0) { 23059db9691SMark Brown dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret); 23159db9691SMark Brown return ret; 23259db9691SMark Brown } 2333cc72986SMark Brown 2343cc72986SMark Brown regcache_cache_only(arizona->regmap, false); 2353cc72986SMark Brown 2363cc72986SMark Brown ret = arizona_wait_for_boot(arizona); 2375879f571SMark Brown if (ret != 0) { 2385879f571SMark Brown regulator_disable(arizona->dcvdd); 2393cc72986SMark Brown return ret; 2405879f571SMark Brown } 2413cc72986SMark Brown 2429270bdf5SMark Brown ret = regcache_sync(arizona->regmap); 2439270bdf5SMark Brown if (ret != 0) { 2449270bdf5SMark Brown dev_err(arizona->dev, "Failed to restore register cache\n"); 2459270bdf5SMark Brown regulator_disable(arizona->dcvdd); 2469270bdf5SMark Brown return ret; 2479270bdf5SMark Brown } 2483cc72986SMark Brown 2493cc72986SMark Brown return 0; 2503cc72986SMark Brown } 2513cc72986SMark Brown 2523cc72986SMark Brown static int arizona_runtime_suspend(struct device *dev) 2533cc72986SMark Brown { 2543cc72986SMark Brown struct arizona *arizona = dev_get_drvdata(dev); 2553cc72986SMark Brown 256508c8299SMark Brown dev_dbg(arizona->dev, "Entering AoD mode\n"); 257508c8299SMark Brown 25859db9691SMark Brown regulator_disable(arizona->dcvdd); 2593cc72986SMark Brown regcache_cache_only(arizona->regmap, true); 2603cc72986SMark Brown regcache_mark_dirty(arizona->regmap); 2613cc72986SMark Brown 2623cc72986SMark Brown return 0; 2633cc72986SMark Brown } 2643cc72986SMark Brown #endif 2653cc72986SMark Brown 266dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP 267dc781d0eSMark Brown static int arizona_resume_noirq(struct device *dev) 268dc781d0eSMark Brown { 269dc781d0eSMark Brown struct arizona *arizona = dev_get_drvdata(dev); 270dc781d0eSMark Brown 271dc781d0eSMark Brown dev_dbg(arizona->dev, "Early resume, disabling IRQ\n"); 272dc781d0eSMark Brown disable_irq(arizona->irq); 273dc781d0eSMark Brown 274dc781d0eSMark Brown return 0; 275dc781d0eSMark Brown } 276dc781d0eSMark Brown 277dc781d0eSMark Brown static int arizona_resume(struct device *dev) 278dc781d0eSMark Brown { 279dc781d0eSMark Brown struct arizona *arizona = dev_get_drvdata(dev); 280dc781d0eSMark Brown 281dc781d0eSMark Brown dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n"); 282dc781d0eSMark Brown enable_irq(arizona->irq); 283dc781d0eSMark Brown 284dc781d0eSMark Brown return 0; 285dc781d0eSMark Brown } 286dc781d0eSMark Brown #endif 287dc781d0eSMark Brown 2883cc72986SMark Brown const struct dev_pm_ops arizona_pm_ops = { 2893cc72986SMark Brown SET_RUNTIME_PM_OPS(arizona_runtime_suspend, 2903cc72986SMark Brown arizona_runtime_resume, 2913cc72986SMark Brown NULL) 292dc781d0eSMark Brown SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume) 293dc781d0eSMark Brown #ifdef CONFIG_PM_SLEEP 294dc781d0eSMark Brown .resume_noirq = arizona_resume_noirq, 295dc781d0eSMark Brown #endif 2963cc72986SMark Brown }; 2973cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_pm_ops); 2983cc72986SMark Brown 2993cc72986SMark Brown static struct mfd_cell early_devs[] = { 3003cc72986SMark Brown { .name = "arizona-ldo1" }, 3013cc72986SMark Brown }; 3023cc72986SMark Brown 3033cc72986SMark Brown static struct mfd_cell wm5102_devs[] = { 304d7768111SMark Brown { .name = "arizona-micsupp" }, 3053cc72986SMark Brown { .name = "arizona-extcon" }, 3063cc72986SMark Brown { .name = "arizona-gpio" }, 307503b1cacSMark Brown { .name = "arizona-haptics" }, 3083cc72986SMark Brown { .name = "arizona-pwm" }, 3093cc72986SMark Brown { .name = "wm5102-codec" }, 3103cc72986SMark Brown }; 3113cc72986SMark Brown 312e102befeSMark Brown static struct mfd_cell wm5110_devs[] = { 313d7768111SMark Brown { .name = "arizona-micsupp" }, 314e102befeSMark Brown { .name = "arizona-extcon" }, 315e102befeSMark Brown { .name = "arizona-gpio" }, 316503b1cacSMark Brown { .name = "arizona-haptics" }, 317e102befeSMark Brown { .name = "arizona-pwm" }, 318e102befeSMark Brown { .name = "wm5110-codec" }, 319e102befeSMark Brown }; 320e102befeSMark Brown 321f791be49SBill Pemberton int arizona_dev_init(struct arizona *arizona) 3223cc72986SMark Brown { 3233cc72986SMark Brown struct device *dev = arizona->dev; 3243cc72986SMark Brown const char *type_name; 3253cc72986SMark Brown unsigned int reg, val; 32662d62b59SMark Brown int (*apply_patch)(struct arizona *) = NULL; 3273cc72986SMark Brown int ret, i; 3283cc72986SMark Brown 3293cc72986SMark Brown dev_set_drvdata(arizona->dev, arizona); 3303cc72986SMark Brown mutex_init(&arizona->clk_lock); 3313cc72986SMark Brown 3323cc72986SMark Brown if (dev_get_platdata(arizona->dev)) 3333cc72986SMark Brown memcpy(&arizona->pdata, dev_get_platdata(arizona->dev), 3343cc72986SMark Brown sizeof(arizona->pdata)); 3353cc72986SMark Brown 3363cc72986SMark Brown regcache_cache_only(arizona->regmap, true); 3373cc72986SMark Brown 3383cc72986SMark Brown switch (arizona->type) { 3393cc72986SMark Brown case WM5102: 340e102befeSMark Brown case WM5110: 3413cc72986SMark Brown for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++) 3423cc72986SMark Brown arizona->core_supplies[i].supply 3433cc72986SMark Brown = wm5102_core_supplies[i]; 3443cc72986SMark Brown arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies); 3453cc72986SMark Brown break; 3463cc72986SMark Brown default: 3473cc72986SMark Brown dev_err(arizona->dev, "Unknown device type %d\n", 3483cc72986SMark Brown arizona->type); 3493cc72986SMark Brown return -EINVAL; 3503cc72986SMark Brown } 3513cc72986SMark Brown 3523cc72986SMark Brown ret = mfd_add_devices(arizona->dev, -1, early_devs, 3530848c94fSMark Brown ARRAY_SIZE(early_devs), NULL, 0, NULL); 3543cc72986SMark Brown if (ret != 0) { 3553cc72986SMark Brown dev_err(dev, "Failed to add early children: %d\n", ret); 3563cc72986SMark Brown return ret; 3573cc72986SMark Brown } 3583cc72986SMark Brown 3593cc72986SMark Brown ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies, 3603cc72986SMark Brown arizona->core_supplies); 3613cc72986SMark Brown if (ret != 0) { 3623cc72986SMark Brown dev_err(dev, "Failed to request core supplies: %d\n", 3633cc72986SMark Brown ret); 3643cc72986SMark Brown goto err_early; 3653cc72986SMark Brown } 3663cc72986SMark Brown 36759db9691SMark Brown arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD"); 36859db9691SMark Brown if (IS_ERR(arizona->dcvdd)) { 36959db9691SMark Brown ret = PTR_ERR(arizona->dcvdd); 37059db9691SMark Brown dev_err(dev, "Failed to request DCVDD: %d\n", ret); 37159db9691SMark Brown goto err_early; 37259db9691SMark Brown } 37359db9691SMark Brown 3743cc72986SMark Brown ret = regulator_bulk_enable(arizona->num_core_supplies, 3753cc72986SMark Brown arizona->core_supplies); 3763cc72986SMark Brown if (ret != 0) { 3773cc72986SMark Brown dev_err(dev, "Failed to enable core supplies: %d\n", 3783cc72986SMark Brown ret); 3793cc72986SMark Brown goto err_early; 3803cc72986SMark Brown } 3813cc72986SMark Brown 38259db9691SMark Brown ret = regulator_enable(arizona->dcvdd); 38359db9691SMark Brown if (ret != 0) { 38459db9691SMark Brown dev_err(dev, "Failed to enable DCVDD: %d\n", ret); 38559db9691SMark Brown goto err_enable; 38659db9691SMark Brown } 38759db9691SMark Brown 3883cc72986SMark Brown if (arizona->pdata.reset) { 3893cc72986SMark Brown /* Start out with /RESET low to put the chip into reset */ 3903cc72986SMark Brown ret = gpio_request_one(arizona->pdata.reset, 3913cc72986SMark Brown GPIOF_DIR_OUT | GPIOF_INIT_LOW, 3923cc72986SMark Brown "arizona /RESET"); 3933cc72986SMark Brown if (ret != 0) { 3943cc72986SMark Brown dev_err(dev, "Failed to request /RESET: %d\n", ret); 39559db9691SMark Brown goto err_dcvdd; 3963cc72986SMark Brown } 3973cc72986SMark Brown 3983cc72986SMark Brown gpio_set_value_cansleep(arizona->pdata.reset, 1); 3993cc72986SMark Brown } 4003cc72986SMark Brown 4013cc72986SMark Brown regcache_cache_only(arizona->regmap, false); 4023cc72986SMark Brown 4033cc72986SMark Brown ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); 4043cc72986SMark Brown if (ret != 0) { 4053cc72986SMark Brown dev_err(dev, "Failed to read ID register: %d\n", ret); 40659db9691SMark Brown goto err_reset; 4073cc72986SMark Brown } 4083cc72986SMark Brown 4093cc72986SMark Brown ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, 4103cc72986SMark Brown &arizona->rev); 4113cc72986SMark Brown if (ret != 0) { 4123cc72986SMark Brown dev_err(dev, "Failed to read revision register: %d\n", ret); 41359db9691SMark Brown goto err_reset; 4143cc72986SMark Brown } 4153cc72986SMark Brown arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; 4163cc72986SMark Brown 4173cc72986SMark Brown switch (reg) { 418863df8d5SMark Brown #ifdef CONFIG_MFD_WM5102 4193cc72986SMark Brown case 0x5102: 4203cc72986SMark Brown type_name = "WM5102"; 4213cc72986SMark Brown if (arizona->type != WM5102) { 4223cc72986SMark Brown dev_err(arizona->dev, "WM5102 registered as %d\n", 4233cc72986SMark Brown arizona->type); 4243cc72986SMark Brown arizona->type = WM5102; 4253cc72986SMark Brown } 42662d62b59SMark Brown apply_patch = wm5102_patch; 4273cc72986SMark Brown break; 428863df8d5SMark Brown #endif 429e102befeSMark Brown #ifdef CONFIG_MFD_WM5110 430e102befeSMark Brown case 0x5110: 431e102befeSMark Brown type_name = "WM5110"; 432e102befeSMark Brown if (arizona->type != WM5110) { 433e102befeSMark Brown dev_err(arizona->dev, "WM5110 registered as %d\n", 434e102befeSMark Brown arizona->type); 435e102befeSMark Brown arizona->type = WM5110; 436e102befeSMark Brown } 43762d62b59SMark Brown apply_patch = wm5110_patch; 438e102befeSMark Brown break; 439e102befeSMark Brown #endif 4403cc72986SMark Brown default: 4413cc72986SMark Brown dev_err(arizona->dev, "Unknown device ID %x\n", reg); 44259db9691SMark Brown goto err_reset; 4433cc72986SMark Brown } 4443cc72986SMark Brown 4453cc72986SMark Brown dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); 4463cc72986SMark Brown 4473cc72986SMark Brown /* If we have a /RESET GPIO we'll already be reset */ 4483cc72986SMark Brown if (!arizona->pdata.reset) { 44946b9d13aSCharles Keepax regcache_mark_dirty(arizona->regmap); 45046b9d13aSCharles Keepax 4513cc72986SMark Brown ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); 4523cc72986SMark Brown if (ret != 0) { 4533cc72986SMark Brown dev_err(dev, "Failed to reset device: %d\n", ret); 45459db9691SMark Brown goto err_reset; 4553cc72986SMark Brown } 45646b9d13aSCharles Keepax 45746b9d13aSCharles Keepax ret = regcache_sync(arizona->regmap); 45846b9d13aSCharles Keepax if (ret != 0) { 45946b9d13aSCharles Keepax dev_err(dev, "Failed to sync device: %d\n", ret); 46046b9d13aSCharles Keepax goto err_reset; 46146b9d13aSCharles Keepax } 4623cc72986SMark Brown } 4633cc72986SMark Brown 464af65a361SMark Brown ret = arizona_wait_for_boot(arizona); 465af65a361SMark Brown if (ret != 0) { 466af65a361SMark Brown dev_err(arizona->dev, "Device failed initial boot: %d\n", ret); 467af65a361SMark Brown goto err_reset; 468af65a361SMark Brown } 4693cc72986SMark Brown 47062d62b59SMark Brown if (apply_patch) { 47162d62b59SMark Brown ret = apply_patch(arizona); 47262d62b59SMark Brown if (ret != 0) { 47362d62b59SMark Brown dev_err(arizona->dev, "Failed to apply patch: %d\n", 47462d62b59SMark Brown ret); 47562d62b59SMark Brown goto err_reset; 47662d62b59SMark Brown } 47762d62b59SMark Brown } 47862d62b59SMark Brown 4793cc72986SMark Brown for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { 4803cc72986SMark Brown if (!arizona->pdata.gpio_defaults[i]) 4813cc72986SMark Brown continue; 4823cc72986SMark Brown 4833cc72986SMark Brown regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i, 4843cc72986SMark Brown arizona->pdata.gpio_defaults[i]); 4853cc72986SMark Brown } 4863cc72986SMark Brown 4873cc72986SMark Brown pm_runtime_set_autosuspend_delay(arizona->dev, 100); 4883cc72986SMark Brown pm_runtime_use_autosuspend(arizona->dev); 4893cc72986SMark Brown pm_runtime_enable(arizona->dev); 4903cc72986SMark Brown 4913cc72986SMark Brown /* Chip default */ 4923cc72986SMark Brown if (!arizona->pdata.clk32k_src) 4933cc72986SMark Brown arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2; 4943cc72986SMark Brown 4953cc72986SMark Brown switch (arizona->pdata.clk32k_src) { 4963cc72986SMark Brown case ARIZONA_32KZ_MCLK1: 4973cc72986SMark Brown case ARIZONA_32KZ_MCLK2: 4983cc72986SMark Brown regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, 4993cc72986SMark Brown ARIZONA_CLK_32K_SRC_MASK, 5003cc72986SMark Brown arizona->pdata.clk32k_src - 1); 5013cc72986SMark Brown break; 5023cc72986SMark Brown case ARIZONA_32KZ_NONE: 5033cc72986SMark Brown regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, 5043cc72986SMark Brown ARIZONA_CLK_32K_SRC_MASK, 2); 5053cc72986SMark Brown break; 5063cc72986SMark Brown default: 5073cc72986SMark Brown dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n", 5083cc72986SMark Brown arizona->pdata.clk32k_src); 5093cc72986SMark Brown ret = -EINVAL; 51059db9691SMark Brown goto err_reset; 5113cc72986SMark Brown } 5123cc72986SMark Brown 5133cc72986SMark Brown for (i = 0; i < ARIZONA_MAX_INPUT; i++) { 5143cc72986SMark Brown /* Default for both is 0 so noop with defaults */ 5153cc72986SMark Brown val = arizona->pdata.dmic_ref[i] 5163cc72986SMark Brown << ARIZONA_IN1_DMIC_SUP_SHIFT; 5173cc72986SMark Brown val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT; 5183cc72986SMark Brown 5193cc72986SMark Brown regmap_update_bits(arizona->regmap, 5203cc72986SMark Brown ARIZONA_IN1L_CONTROL + (i * 8), 5213cc72986SMark Brown ARIZONA_IN1_DMIC_SUP_MASK | 5223cc72986SMark Brown ARIZONA_IN1_MODE_MASK, val); 5233cc72986SMark Brown } 5243cc72986SMark Brown 5253cc72986SMark Brown for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) { 5263cc72986SMark Brown /* Default is 0 so noop with defaults */ 5273cc72986SMark Brown if (arizona->pdata.out_mono[i]) 5283cc72986SMark Brown val = ARIZONA_OUT1_MONO; 5293cc72986SMark Brown else 5303cc72986SMark Brown val = 0; 5313cc72986SMark Brown 5323cc72986SMark Brown regmap_update_bits(arizona->regmap, 5333cc72986SMark Brown ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), 5343cc72986SMark Brown ARIZONA_OUT1_MONO, val); 5353cc72986SMark Brown } 5363cc72986SMark Brown 5373cc72986SMark Brown for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { 5383cc72986SMark Brown if (arizona->pdata.spk_mute[i]) 5393cc72986SMark Brown regmap_update_bits(arizona->regmap, 5402a51da04SMark Brown ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), 5413cc72986SMark Brown ARIZONA_SPK1_MUTE_ENDIAN_MASK | 5423cc72986SMark Brown ARIZONA_SPK1_MUTE_SEQ1_MASK, 5433cc72986SMark Brown arizona->pdata.spk_mute[i]); 5443cc72986SMark Brown 5453cc72986SMark Brown if (arizona->pdata.spk_fmt[i]) 5463cc72986SMark Brown regmap_update_bits(arizona->regmap, 5472a51da04SMark Brown ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), 5483cc72986SMark Brown ARIZONA_SPK1_FMT_MASK, 5493cc72986SMark Brown arizona->pdata.spk_fmt[i]); 5503cc72986SMark Brown } 5513cc72986SMark Brown 5523cc72986SMark Brown /* Set up for interrupts */ 5533cc72986SMark Brown ret = arizona_irq_init(arizona); 5543cc72986SMark Brown if (ret != 0) 55559db9691SMark Brown goto err_reset; 5563cc72986SMark Brown 5573cc72986SMark Brown arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error", 5583cc72986SMark Brown arizona_clkgen_err, arizona); 5593cc72986SMark Brown arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked", 5603cc72986SMark Brown arizona_overclocked, arizona); 5613cc72986SMark Brown arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked", 5623cc72986SMark Brown arizona_underclocked, arizona); 5633cc72986SMark Brown 5643cc72986SMark Brown switch (arizona->type) { 5653cc72986SMark Brown case WM5102: 5663cc72986SMark Brown ret = mfd_add_devices(arizona->dev, -1, wm5102_devs, 5670848c94fSMark Brown ARRAY_SIZE(wm5102_devs), NULL, 0, NULL); 5683cc72986SMark Brown break; 569e102befeSMark Brown case WM5110: 570e102befeSMark Brown ret = mfd_add_devices(arizona->dev, -1, wm5110_devs, 57178566afdSCharles Keepax ARRAY_SIZE(wm5110_devs), NULL, 0, NULL); 572e102befeSMark Brown break; 5733cc72986SMark Brown } 5743cc72986SMark Brown 5753cc72986SMark Brown if (ret != 0) { 5763cc72986SMark Brown dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret); 5773cc72986SMark Brown goto err_irq; 5783cc72986SMark Brown } 5793cc72986SMark Brown 58059db9691SMark Brown #ifdef CONFIG_PM_RUNTIME 58159db9691SMark Brown regulator_disable(arizona->dcvdd); 58259db9691SMark Brown #endif 58359db9691SMark Brown 5843cc72986SMark Brown return 0; 5853cc72986SMark Brown 5863cc72986SMark Brown err_irq: 5873cc72986SMark Brown arizona_irq_exit(arizona); 5883cc72986SMark Brown err_reset: 5893cc72986SMark Brown if (arizona->pdata.reset) { 5903cc72986SMark Brown gpio_set_value_cansleep(arizona->pdata.reset, 1); 5913cc72986SMark Brown gpio_free(arizona->pdata.reset); 5923cc72986SMark Brown } 59359db9691SMark Brown err_dcvdd: 59459db9691SMark Brown regulator_disable(arizona->dcvdd); 5953cc72986SMark Brown err_enable: 5963a36a0dbSMark Brown regulator_bulk_disable(arizona->num_core_supplies, 5973cc72986SMark Brown arizona->core_supplies); 5983cc72986SMark Brown err_early: 5993cc72986SMark Brown mfd_remove_devices(dev); 6003cc72986SMark Brown return ret; 6013cc72986SMark Brown } 6023cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_init); 6033cc72986SMark Brown 6044740f73fSBill Pemberton int arizona_dev_exit(struct arizona *arizona) 6053cc72986SMark Brown { 6063cc72986SMark Brown mfd_remove_devices(arizona->dev); 6073cc72986SMark Brown arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona); 6083cc72986SMark Brown arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona); 6093cc72986SMark Brown arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona); 6103cc72986SMark Brown pm_runtime_disable(arizona->dev); 6113cc72986SMark Brown arizona_irq_exit(arizona); 6123cc72986SMark Brown return 0; 6133cc72986SMark Brown } 6143cc72986SMark Brown EXPORT_SYMBOL_GPL(arizona_dev_exit); 615