1210afeecSPeter Ujfalusi /* 2210afeecSPeter Ujfalusi * MFD driver for TWL6040 audio device 3210afeecSPeter Ujfalusi * 4210afeecSPeter Ujfalusi * Authors: Misael Lopez Cruz <misael.lopez@ti.com> 5210afeecSPeter Ujfalusi * Jorge Eduardo Candelaria <jorge.candelaria@ti.com> 6210afeecSPeter Ujfalusi * Peter Ujfalusi <peter.ujfalusi@ti.com> 7210afeecSPeter Ujfalusi * 8210afeecSPeter Ujfalusi * Copyright: (C) 2011 Texas Instruments, Inc. 9210afeecSPeter Ujfalusi * 10210afeecSPeter Ujfalusi * This program is free software; you can redistribute it and/or modify 11210afeecSPeter Ujfalusi * it under the terms of the GNU General Public License version 2 as 12210afeecSPeter Ujfalusi * published by the Free Software Foundation. 13210afeecSPeter Ujfalusi * 14210afeecSPeter Ujfalusi * This program is distributed in the hope that it will be useful, but 15210afeecSPeter Ujfalusi * WITHOUT ANY WARRANTY; without even the implied warranty of 16210afeecSPeter Ujfalusi * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17210afeecSPeter Ujfalusi * General Public License for more details. 18210afeecSPeter Ujfalusi * 19210afeecSPeter Ujfalusi * You should have received a copy of the GNU General Public License 20210afeecSPeter Ujfalusi * along with this program; if not, write to the Free Software 21210afeecSPeter Ujfalusi * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22210afeecSPeter Ujfalusi * 02110-1301 USA 23210afeecSPeter Ujfalusi * 24210afeecSPeter Ujfalusi */ 25210afeecSPeter Ujfalusi 26210afeecSPeter Ujfalusi #include <linux/module.h> 27210afeecSPeter Ujfalusi #include <linux/types.h> 28210afeecSPeter Ujfalusi #include <linux/slab.h> 29210afeecSPeter Ujfalusi #include <linux/kernel.h> 30210afeecSPeter Ujfalusi #include <linux/err.h> 31210afeecSPeter Ujfalusi #include <linux/platform_device.h> 32210afeecSPeter Ujfalusi #include <linux/of.h> 33210afeecSPeter Ujfalusi #include <linux/of_irq.h> 34210afeecSPeter Ujfalusi #include <linux/of_gpio.h> 35210afeecSPeter Ujfalusi #include <linux/of_platform.h> 36210afeecSPeter Ujfalusi #include <linux/gpio.h> 37210afeecSPeter Ujfalusi #include <linux/delay.h> 38210afeecSPeter Ujfalusi #include <linux/i2c.h> 39210afeecSPeter Ujfalusi #include <linux/regmap.h> 40210afeecSPeter Ujfalusi #include <linux/mfd/core.h> 41210afeecSPeter Ujfalusi #include <linux/mfd/twl6040.h> 42210afeecSPeter Ujfalusi #include <linux/regulator/consumer.h> 43210afeecSPeter Ujfalusi 44210afeecSPeter Ujfalusi #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) 45210afeecSPeter Ujfalusi #define TWL6040_NUM_SUPPLIES (2) 46210afeecSPeter Ujfalusi 47de1e23f8SKrzysztof Kozlowski static const struct reg_default twl6040_defaults[] = { 48c7f9129dSPeter Ujfalusi { 0x01, 0x4B }, /* REG_ASICID (ro) */ 49c7f9129dSPeter Ujfalusi { 0x02, 0x00 }, /* REG_ASICREV (ro) */ 50c7f9129dSPeter Ujfalusi { 0x03, 0x00 }, /* REG_INTID */ 51c7f9129dSPeter Ujfalusi { 0x04, 0x00 }, /* REG_INTMR */ 52c7f9129dSPeter Ujfalusi { 0x05, 0x00 }, /* REG_NCPCTRL */ 53c7f9129dSPeter Ujfalusi { 0x06, 0x00 }, /* REG_LDOCTL */ 54c7f9129dSPeter Ujfalusi { 0x07, 0x60 }, /* REG_HPPLLCTL */ 55c7f9129dSPeter Ujfalusi { 0x08, 0x00 }, /* REG_LPPLLCTL */ 56c7f9129dSPeter Ujfalusi { 0x09, 0x4A }, /* REG_LPPLLDIV */ 57c7f9129dSPeter Ujfalusi { 0x0A, 0x00 }, /* REG_AMICBCTL */ 58c7f9129dSPeter Ujfalusi { 0x0B, 0x00 }, /* REG_DMICBCTL */ 59c7f9129dSPeter Ujfalusi { 0x0C, 0x00 }, /* REG_MICLCTL */ 60c7f9129dSPeter Ujfalusi { 0x0D, 0x00 }, /* REG_MICRCTL */ 61c7f9129dSPeter Ujfalusi { 0x0E, 0x00 }, /* REG_MICGAIN */ 62c7f9129dSPeter Ujfalusi { 0x0F, 0x1B }, /* REG_LINEGAIN */ 63c7f9129dSPeter Ujfalusi { 0x10, 0x00 }, /* REG_HSLCTL */ 64c7f9129dSPeter Ujfalusi { 0x11, 0x00 }, /* REG_HSRCTL */ 65c7f9129dSPeter Ujfalusi { 0x12, 0x00 }, /* REG_HSGAIN */ 66c7f9129dSPeter Ujfalusi { 0x13, 0x00 }, /* REG_EARCTL */ 67c7f9129dSPeter Ujfalusi { 0x14, 0x00 }, /* REG_HFLCTL */ 68c7f9129dSPeter Ujfalusi { 0x15, 0x00 }, /* REG_HFLGAIN */ 69c7f9129dSPeter Ujfalusi { 0x16, 0x00 }, /* REG_HFRCTL */ 70c7f9129dSPeter Ujfalusi { 0x17, 0x00 }, /* REG_HFRGAIN */ 71c7f9129dSPeter Ujfalusi { 0x18, 0x00 }, /* REG_VIBCTLL */ 72c7f9129dSPeter Ujfalusi { 0x19, 0x00 }, /* REG_VIBDATL */ 73c7f9129dSPeter Ujfalusi { 0x1A, 0x00 }, /* REG_VIBCTLR */ 74c7f9129dSPeter Ujfalusi { 0x1B, 0x00 }, /* REG_VIBDATR */ 75c7f9129dSPeter Ujfalusi { 0x1C, 0x00 }, /* REG_HKCTL1 */ 76c7f9129dSPeter Ujfalusi { 0x1D, 0x00 }, /* REG_HKCTL2 */ 77c7f9129dSPeter Ujfalusi { 0x1E, 0x00 }, /* REG_GPOCTL */ 78c7f9129dSPeter Ujfalusi { 0x1F, 0x00 }, /* REG_ALB */ 79c7f9129dSPeter Ujfalusi { 0x20, 0x00 }, /* REG_DLB */ 80c7f9129dSPeter Ujfalusi /* 0x28, REG_TRIM1 */ 81c7f9129dSPeter Ujfalusi /* 0x29, REG_TRIM2 */ 82c7f9129dSPeter Ujfalusi /* 0x2A, REG_TRIM3 */ 83c7f9129dSPeter Ujfalusi /* 0x2B, REG_HSOTRIM */ 84c7f9129dSPeter Ujfalusi /* 0x2C, REG_HFOTRIM */ 85c7f9129dSPeter Ujfalusi { 0x2D, 0x08 }, /* REG_ACCCTL */ 86c7f9129dSPeter Ujfalusi { 0x2E, 0x00 }, /* REG_STATUS (ro) */ 87c7f9129dSPeter Ujfalusi }; 88c7f9129dSPeter Ujfalusi 898019ff6cSNariman Poushin static struct reg_sequence twl6040_patch[] = { 9011e38e11SPeter Ujfalusi /* 9111e38e11SPeter Ujfalusi * Select I2C bus access to dual access registers 9211e38e11SPeter Ujfalusi * Interrupt register is cleared on read 9311e38e11SPeter Ujfalusi * Select fast mode for i2c (400KHz) 9411e38e11SPeter Ujfalusi */ 9511e38e11SPeter Ujfalusi { TWL6040_REG_ACCCTL, 9611e38e11SPeter Ujfalusi TWL6040_I2CSEL | TWL6040_INTCLRMODE | TWL6040_I2CMODE(1) }, 97c7f9129dSPeter Ujfalusi }; 98c7f9129dSPeter Ujfalusi 99c7f9129dSPeter Ujfalusi 100df04b624SPeter Ujfalusi static bool twl6040_has_vibra(struct device_node *node) 101210afeecSPeter Ujfalusi { 102210afeecSPeter Ujfalusi #ifdef CONFIG_OF 103210afeecSPeter Ujfalusi if (of_find_node_by_name(node, "vibra")) 104210afeecSPeter Ujfalusi return true; 105210afeecSPeter Ujfalusi #endif 106210afeecSPeter Ujfalusi return false; 107210afeecSPeter Ujfalusi } 108210afeecSPeter Ujfalusi 109210afeecSPeter Ujfalusi int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) 110210afeecSPeter Ujfalusi { 111210afeecSPeter Ujfalusi int ret; 112210afeecSPeter Ujfalusi unsigned int val; 113210afeecSPeter Ujfalusi 114210afeecSPeter Ujfalusi ret = regmap_read(twl6040->regmap, reg, &val); 115210afeecSPeter Ujfalusi if (ret < 0) 116210afeecSPeter Ujfalusi return ret; 117210afeecSPeter Ujfalusi 118210afeecSPeter Ujfalusi return val; 119210afeecSPeter Ujfalusi } 120210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_read); 121210afeecSPeter Ujfalusi 122210afeecSPeter Ujfalusi int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val) 123210afeecSPeter Ujfalusi { 124210afeecSPeter Ujfalusi int ret; 125210afeecSPeter Ujfalusi 126210afeecSPeter Ujfalusi ret = regmap_write(twl6040->regmap, reg, val); 127210afeecSPeter Ujfalusi 128210afeecSPeter Ujfalusi return ret; 129210afeecSPeter Ujfalusi } 130210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_write); 131210afeecSPeter Ujfalusi 132210afeecSPeter Ujfalusi int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) 133210afeecSPeter Ujfalusi { 134210afeecSPeter Ujfalusi return regmap_update_bits(twl6040->regmap, reg, mask, mask); 135210afeecSPeter Ujfalusi } 136210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_bits); 137210afeecSPeter Ujfalusi 138210afeecSPeter Ujfalusi int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) 139210afeecSPeter Ujfalusi { 140210afeecSPeter Ujfalusi return regmap_update_bits(twl6040->regmap, reg, mask, 0); 141210afeecSPeter Ujfalusi } 142210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_clear_bits); 143210afeecSPeter Ujfalusi 144210afeecSPeter Ujfalusi /* twl6040 codec manual power-up sequence */ 145210afeecSPeter Ujfalusi static int twl6040_power_up_manual(struct twl6040 *twl6040) 146210afeecSPeter Ujfalusi { 147210afeecSPeter Ujfalusi u8 ldoctl, ncpctl, lppllctl; 148210afeecSPeter Ujfalusi int ret; 149210afeecSPeter Ujfalusi 150210afeecSPeter Ujfalusi /* enable high-side LDO, reference system and internal oscillator */ 151210afeecSPeter Ujfalusi ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA; 152210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 153210afeecSPeter Ujfalusi if (ret) 154210afeecSPeter Ujfalusi return ret; 155210afeecSPeter Ujfalusi usleep_range(10000, 10500); 156210afeecSPeter Ujfalusi 157210afeecSPeter Ujfalusi /* enable negative charge pump */ 158210afeecSPeter Ujfalusi ncpctl = TWL6040_NCPENA; 159210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 160210afeecSPeter Ujfalusi if (ret) 161210afeecSPeter Ujfalusi goto ncp_err; 162210afeecSPeter Ujfalusi usleep_range(1000, 1500); 163210afeecSPeter Ujfalusi 164210afeecSPeter Ujfalusi /* enable low-side LDO */ 165210afeecSPeter Ujfalusi ldoctl |= TWL6040_LSLDOENA; 166210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 167210afeecSPeter Ujfalusi if (ret) 168210afeecSPeter Ujfalusi goto lsldo_err; 169210afeecSPeter Ujfalusi usleep_range(1000, 1500); 170210afeecSPeter Ujfalusi 171210afeecSPeter Ujfalusi /* enable low-power PLL */ 172210afeecSPeter Ujfalusi lppllctl = TWL6040_LPLLENA; 173210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 174210afeecSPeter Ujfalusi if (ret) 175210afeecSPeter Ujfalusi goto lppll_err; 176210afeecSPeter Ujfalusi usleep_range(5000, 5500); 177210afeecSPeter Ujfalusi 178210afeecSPeter Ujfalusi /* disable internal oscillator */ 179210afeecSPeter Ujfalusi ldoctl &= ~TWL6040_OSCENA; 180210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 181210afeecSPeter Ujfalusi if (ret) 182210afeecSPeter Ujfalusi goto osc_err; 183210afeecSPeter Ujfalusi 184210afeecSPeter Ujfalusi return 0; 185210afeecSPeter Ujfalusi 186210afeecSPeter Ujfalusi osc_err: 187210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLENA; 188210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 189210afeecSPeter Ujfalusi lppll_err: 190210afeecSPeter Ujfalusi ldoctl &= ~TWL6040_LSLDOENA; 191210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 192210afeecSPeter Ujfalusi lsldo_err: 193210afeecSPeter Ujfalusi ncpctl &= ~TWL6040_NCPENA; 194210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 195210afeecSPeter Ujfalusi ncp_err: 196210afeecSPeter Ujfalusi ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); 197210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 198210afeecSPeter Ujfalusi 199210afeecSPeter Ujfalusi dev_err(twl6040->dev, "manual power-up failed\n"); 200210afeecSPeter Ujfalusi return ret; 201210afeecSPeter Ujfalusi } 202210afeecSPeter Ujfalusi 203210afeecSPeter Ujfalusi /* twl6040 manual power-down sequence */ 204210afeecSPeter Ujfalusi static void twl6040_power_down_manual(struct twl6040 *twl6040) 205210afeecSPeter Ujfalusi { 206210afeecSPeter Ujfalusi u8 ncpctl, ldoctl, lppllctl; 207210afeecSPeter Ujfalusi 208210afeecSPeter Ujfalusi ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL); 209210afeecSPeter Ujfalusi ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL); 210210afeecSPeter Ujfalusi lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); 211210afeecSPeter Ujfalusi 212210afeecSPeter Ujfalusi /* enable internal oscillator */ 213210afeecSPeter Ujfalusi ldoctl |= TWL6040_OSCENA; 214210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 215210afeecSPeter Ujfalusi usleep_range(1000, 1500); 216210afeecSPeter Ujfalusi 217210afeecSPeter Ujfalusi /* disable low-power PLL */ 218210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLENA; 219210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 220210afeecSPeter Ujfalusi 221210afeecSPeter Ujfalusi /* disable low-side LDO */ 222210afeecSPeter Ujfalusi ldoctl &= ~TWL6040_LSLDOENA; 223210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 224210afeecSPeter Ujfalusi 225210afeecSPeter Ujfalusi /* disable negative charge pump */ 226210afeecSPeter Ujfalusi ncpctl &= ~TWL6040_NCPENA; 227210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 228210afeecSPeter Ujfalusi 229210afeecSPeter Ujfalusi /* disable high-side LDO, reference system and internal oscillator */ 230210afeecSPeter Ujfalusi ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); 231210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 232210afeecSPeter Ujfalusi } 233210afeecSPeter Ujfalusi 234210afeecSPeter Ujfalusi static irqreturn_t twl6040_readyint_handler(int irq, void *data) 235210afeecSPeter Ujfalusi { 236210afeecSPeter Ujfalusi struct twl6040 *twl6040 = data; 237210afeecSPeter Ujfalusi 238210afeecSPeter Ujfalusi complete(&twl6040->ready); 239210afeecSPeter Ujfalusi 240210afeecSPeter Ujfalusi return IRQ_HANDLED; 241210afeecSPeter Ujfalusi } 242210afeecSPeter Ujfalusi 243210afeecSPeter Ujfalusi static irqreturn_t twl6040_thint_handler(int irq, void *data) 244210afeecSPeter Ujfalusi { 245210afeecSPeter Ujfalusi struct twl6040 *twl6040 = data; 246210afeecSPeter Ujfalusi u8 status; 247210afeecSPeter Ujfalusi 248210afeecSPeter Ujfalusi status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); 249210afeecSPeter Ujfalusi if (status & TWL6040_TSHUTDET) { 250210afeecSPeter Ujfalusi dev_warn(twl6040->dev, "Thermal shutdown, powering-off"); 251210afeecSPeter Ujfalusi twl6040_power(twl6040, 0); 252210afeecSPeter Ujfalusi } else { 253210afeecSPeter Ujfalusi dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on"); 254210afeecSPeter Ujfalusi twl6040_power(twl6040, 1); 255210afeecSPeter Ujfalusi } 256210afeecSPeter Ujfalusi 257210afeecSPeter Ujfalusi return IRQ_HANDLED; 258210afeecSPeter Ujfalusi } 259210afeecSPeter Ujfalusi 260210afeecSPeter Ujfalusi static int twl6040_power_up_automatic(struct twl6040 *twl6040) 261210afeecSPeter Ujfalusi { 262210afeecSPeter Ujfalusi int time_left; 263210afeecSPeter Ujfalusi 264210afeecSPeter Ujfalusi gpio_set_value(twl6040->audpwron, 1); 265210afeecSPeter Ujfalusi 266210afeecSPeter Ujfalusi time_left = wait_for_completion_timeout(&twl6040->ready, 267210afeecSPeter Ujfalusi msecs_to_jiffies(144)); 268210afeecSPeter Ujfalusi if (!time_left) { 269210afeecSPeter Ujfalusi u8 intid; 270210afeecSPeter Ujfalusi 271210afeecSPeter Ujfalusi dev_warn(twl6040->dev, "timeout waiting for READYINT\n"); 272210afeecSPeter Ujfalusi intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); 273210afeecSPeter Ujfalusi if (!(intid & TWL6040_READYINT)) { 274210afeecSPeter Ujfalusi dev_err(twl6040->dev, "automatic power-up failed\n"); 275210afeecSPeter Ujfalusi gpio_set_value(twl6040->audpwron, 0); 276210afeecSPeter Ujfalusi return -ETIMEDOUT; 277210afeecSPeter Ujfalusi } 278210afeecSPeter Ujfalusi } 279210afeecSPeter Ujfalusi 280210afeecSPeter Ujfalusi return 0; 281210afeecSPeter Ujfalusi } 282210afeecSPeter Ujfalusi 283210afeecSPeter Ujfalusi int twl6040_power(struct twl6040 *twl6040, int on) 284210afeecSPeter Ujfalusi { 285210afeecSPeter Ujfalusi int ret = 0; 286210afeecSPeter Ujfalusi 287210afeecSPeter Ujfalusi mutex_lock(&twl6040->mutex); 288210afeecSPeter Ujfalusi 289210afeecSPeter Ujfalusi if (on) { 290210afeecSPeter Ujfalusi /* already powered-up */ 291210afeecSPeter Ujfalusi if (twl6040->power_count++) 292210afeecSPeter Ujfalusi goto out; 293210afeecSPeter Ujfalusi 29468bab866SPeter Ujfalusi clk_prepare_enable(twl6040->clk32k); 29568bab866SPeter Ujfalusi 296c7f9129dSPeter Ujfalusi /* Allow writes to the chip */ 297c7f9129dSPeter Ujfalusi regcache_cache_only(twl6040->regmap, false); 298c7f9129dSPeter Ujfalusi 299210afeecSPeter Ujfalusi if (gpio_is_valid(twl6040->audpwron)) { 300210afeecSPeter Ujfalusi /* use automatic power-up sequence */ 301210afeecSPeter Ujfalusi ret = twl6040_power_up_automatic(twl6040); 302210afeecSPeter Ujfalusi if (ret) { 303210afeecSPeter Ujfalusi twl6040->power_count = 0; 304210afeecSPeter Ujfalusi goto out; 305210afeecSPeter Ujfalusi } 306210afeecSPeter Ujfalusi } else { 307210afeecSPeter Ujfalusi /* use manual power-up sequence */ 308210afeecSPeter Ujfalusi ret = twl6040_power_up_manual(twl6040); 309210afeecSPeter Ujfalusi if (ret) { 310210afeecSPeter Ujfalusi twl6040->power_count = 0; 311210afeecSPeter Ujfalusi goto out; 312210afeecSPeter Ujfalusi } 313210afeecSPeter Ujfalusi } 314c7f9129dSPeter Ujfalusi 315c7f9129dSPeter Ujfalusi /* Sync with the HW */ 316c7f9129dSPeter Ujfalusi regcache_sync(twl6040->regmap); 317c7f9129dSPeter Ujfalusi 318210afeecSPeter Ujfalusi /* Default PLL configuration after power up */ 319210afeecSPeter Ujfalusi twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; 320210afeecSPeter Ujfalusi twl6040->sysclk = 19200000; 321210afeecSPeter Ujfalusi twl6040->mclk = 32768; 322210afeecSPeter Ujfalusi } else { 323210afeecSPeter Ujfalusi /* already powered-down */ 324210afeecSPeter Ujfalusi if (!twl6040->power_count) { 325210afeecSPeter Ujfalusi dev_err(twl6040->dev, 326210afeecSPeter Ujfalusi "device is already powered-off\n"); 327210afeecSPeter Ujfalusi ret = -EPERM; 328210afeecSPeter Ujfalusi goto out; 329210afeecSPeter Ujfalusi } 330210afeecSPeter Ujfalusi 331210afeecSPeter Ujfalusi if (--twl6040->power_count) 332210afeecSPeter Ujfalusi goto out; 333210afeecSPeter Ujfalusi 334210afeecSPeter Ujfalusi if (gpio_is_valid(twl6040->audpwron)) { 335210afeecSPeter Ujfalusi /* use AUDPWRON line */ 336210afeecSPeter Ujfalusi gpio_set_value(twl6040->audpwron, 0); 337210afeecSPeter Ujfalusi 338210afeecSPeter Ujfalusi /* power-down sequence latency */ 339210afeecSPeter Ujfalusi usleep_range(500, 700); 340210afeecSPeter Ujfalusi } else { 341210afeecSPeter Ujfalusi /* use manual power-down sequence */ 342210afeecSPeter Ujfalusi twl6040_power_down_manual(twl6040); 343210afeecSPeter Ujfalusi } 344c7f9129dSPeter Ujfalusi 345c7f9129dSPeter Ujfalusi /* Set regmap to cache only and mark it as dirty */ 346c7f9129dSPeter Ujfalusi regcache_cache_only(twl6040->regmap, true); 347c7f9129dSPeter Ujfalusi regcache_mark_dirty(twl6040->regmap); 348c7f9129dSPeter Ujfalusi 349210afeecSPeter Ujfalusi twl6040->sysclk = 0; 350210afeecSPeter Ujfalusi twl6040->mclk = 0; 35168bab866SPeter Ujfalusi 35268bab866SPeter Ujfalusi clk_disable_unprepare(twl6040->clk32k); 353210afeecSPeter Ujfalusi } 354210afeecSPeter Ujfalusi 355210afeecSPeter Ujfalusi out: 356210afeecSPeter Ujfalusi mutex_unlock(&twl6040->mutex); 357210afeecSPeter Ujfalusi return ret; 358210afeecSPeter Ujfalusi } 359210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_power); 360210afeecSPeter Ujfalusi 361210afeecSPeter Ujfalusi int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, 362210afeecSPeter Ujfalusi unsigned int freq_in, unsigned int freq_out) 363210afeecSPeter Ujfalusi { 364210afeecSPeter Ujfalusi u8 hppllctl, lppllctl; 365210afeecSPeter Ujfalusi int ret = 0; 366210afeecSPeter Ujfalusi 367210afeecSPeter Ujfalusi mutex_lock(&twl6040->mutex); 368210afeecSPeter Ujfalusi 369210afeecSPeter Ujfalusi hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL); 370210afeecSPeter Ujfalusi lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); 371210afeecSPeter Ujfalusi 372210afeecSPeter Ujfalusi /* Force full reconfiguration when switching between PLL */ 373210afeecSPeter Ujfalusi if (pll_id != twl6040->pll) { 374210afeecSPeter Ujfalusi twl6040->sysclk = 0; 375210afeecSPeter Ujfalusi twl6040->mclk = 0; 376210afeecSPeter Ujfalusi } 377210afeecSPeter Ujfalusi 378210afeecSPeter Ujfalusi switch (pll_id) { 379210afeecSPeter Ujfalusi case TWL6040_SYSCLK_SEL_LPPLL: 380210afeecSPeter Ujfalusi /* low-power PLL divider */ 381210afeecSPeter Ujfalusi /* Change the sysclk configuration only if it has been canged */ 382210afeecSPeter Ujfalusi if (twl6040->sysclk != freq_out) { 383210afeecSPeter Ujfalusi switch (freq_out) { 384210afeecSPeter Ujfalusi case 17640000: 385210afeecSPeter Ujfalusi lppllctl |= TWL6040_LPLLFIN; 386210afeecSPeter Ujfalusi break; 387210afeecSPeter Ujfalusi case 19200000: 388210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLFIN; 389210afeecSPeter Ujfalusi break; 390210afeecSPeter Ujfalusi default: 391210afeecSPeter Ujfalusi dev_err(twl6040->dev, 392210afeecSPeter Ujfalusi "freq_out %d not supported\n", 393210afeecSPeter Ujfalusi freq_out); 394210afeecSPeter Ujfalusi ret = -EINVAL; 395210afeecSPeter Ujfalusi goto pll_out; 396210afeecSPeter Ujfalusi } 397210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 398210afeecSPeter Ujfalusi lppllctl); 399210afeecSPeter Ujfalusi } 400210afeecSPeter Ujfalusi 401210afeecSPeter Ujfalusi /* The PLL in use has not been change, we can exit */ 402210afeecSPeter Ujfalusi if (twl6040->pll == pll_id) 403210afeecSPeter Ujfalusi break; 404210afeecSPeter Ujfalusi 405210afeecSPeter Ujfalusi switch (freq_in) { 406210afeecSPeter Ujfalusi case 32768: 407210afeecSPeter Ujfalusi lppllctl |= TWL6040_LPLLENA; 408210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 409210afeecSPeter Ujfalusi lppllctl); 410210afeecSPeter Ujfalusi mdelay(5); 411210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_HPLLSEL; 412210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 413210afeecSPeter Ujfalusi lppllctl); 414210afeecSPeter Ujfalusi hppllctl &= ~TWL6040_HPLLENA; 415210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, 416210afeecSPeter Ujfalusi hppllctl); 417210afeecSPeter Ujfalusi break; 418210afeecSPeter Ujfalusi default: 419210afeecSPeter Ujfalusi dev_err(twl6040->dev, 420210afeecSPeter Ujfalusi "freq_in %d not supported\n", freq_in); 421210afeecSPeter Ujfalusi ret = -EINVAL; 422210afeecSPeter Ujfalusi goto pll_out; 423210afeecSPeter Ujfalusi } 424210afeecSPeter Ujfalusi break; 425210afeecSPeter Ujfalusi case TWL6040_SYSCLK_SEL_HPPLL: 426210afeecSPeter Ujfalusi /* high-performance PLL can provide only 19.2 MHz */ 427210afeecSPeter Ujfalusi if (freq_out != 19200000) { 428210afeecSPeter Ujfalusi dev_err(twl6040->dev, 429210afeecSPeter Ujfalusi "freq_out %d not supported\n", freq_out); 430210afeecSPeter Ujfalusi ret = -EINVAL; 431210afeecSPeter Ujfalusi goto pll_out; 432210afeecSPeter Ujfalusi } 433210afeecSPeter Ujfalusi 434210afeecSPeter Ujfalusi if (twl6040->mclk != freq_in) { 435210afeecSPeter Ujfalusi hppllctl &= ~TWL6040_MCLK_MSK; 436210afeecSPeter Ujfalusi 437210afeecSPeter Ujfalusi switch (freq_in) { 438210afeecSPeter Ujfalusi case 12000000: 439210afeecSPeter Ujfalusi /* PLL enabled, active mode */ 440210afeecSPeter Ujfalusi hppllctl |= TWL6040_MCLK_12000KHZ | 441210afeecSPeter Ujfalusi TWL6040_HPLLENA; 442210afeecSPeter Ujfalusi break; 443210afeecSPeter Ujfalusi case 19200000: 444ac8320c4SPeter Ujfalusi /* PLL enabled, bypass mode */ 445ac8320c4SPeter Ujfalusi hppllctl |= TWL6040_MCLK_19200KHZ | 446ac8320c4SPeter Ujfalusi TWL6040_HPLLBP | TWL6040_HPLLENA; 447210afeecSPeter Ujfalusi break; 448210afeecSPeter Ujfalusi case 26000000: 449210afeecSPeter Ujfalusi /* PLL enabled, active mode */ 450210afeecSPeter Ujfalusi hppllctl |= TWL6040_MCLK_26000KHZ | 451210afeecSPeter Ujfalusi TWL6040_HPLLENA; 452210afeecSPeter Ujfalusi break; 453210afeecSPeter Ujfalusi case 38400000: 454ac8320c4SPeter Ujfalusi /* PLL enabled, bypass mode */ 455210afeecSPeter Ujfalusi hppllctl |= TWL6040_MCLK_38400KHZ | 456ac8320c4SPeter Ujfalusi TWL6040_HPLLBP | TWL6040_HPLLENA; 457210afeecSPeter Ujfalusi break; 458210afeecSPeter Ujfalusi default: 459210afeecSPeter Ujfalusi dev_err(twl6040->dev, 460210afeecSPeter Ujfalusi "freq_in %d not supported\n", freq_in); 461210afeecSPeter Ujfalusi ret = -EINVAL; 462210afeecSPeter Ujfalusi goto pll_out; 463210afeecSPeter Ujfalusi } 464210afeecSPeter Ujfalusi 465210afeecSPeter Ujfalusi /* 466210afeecSPeter Ujfalusi * enable clock slicer to ensure input waveform is 467210afeecSPeter Ujfalusi * square 468210afeecSPeter Ujfalusi */ 469210afeecSPeter Ujfalusi hppllctl |= TWL6040_HPLLSQRENA; 470210afeecSPeter Ujfalusi 471210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, 472210afeecSPeter Ujfalusi hppllctl); 473210afeecSPeter Ujfalusi usleep_range(500, 700); 474210afeecSPeter Ujfalusi lppllctl |= TWL6040_HPLLSEL; 475210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 476210afeecSPeter Ujfalusi lppllctl); 477210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLENA; 478210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 479210afeecSPeter Ujfalusi lppllctl); 480210afeecSPeter Ujfalusi } 481210afeecSPeter Ujfalusi break; 482210afeecSPeter Ujfalusi default: 483210afeecSPeter Ujfalusi dev_err(twl6040->dev, "unknown pll id %d\n", pll_id); 484210afeecSPeter Ujfalusi ret = -EINVAL; 485210afeecSPeter Ujfalusi goto pll_out; 486210afeecSPeter Ujfalusi } 487210afeecSPeter Ujfalusi 488210afeecSPeter Ujfalusi twl6040->sysclk = freq_out; 489210afeecSPeter Ujfalusi twl6040->mclk = freq_in; 490210afeecSPeter Ujfalusi twl6040->pll = pll_id; 491210afeecSPeter Ujfalusi 492210afeecSPeter Ujfalusi pll_out: 493210afeecSPeter Ujfalusi mutex_unlock(&twl6040->mutex); 494210afeecSPeter Ujfalusi return ret; 495210afeecSPeter Ujfalusi } 496210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_pll); 497210afeecSPeter Ujfalusi 498210afeecSPeter Ujfalusi int twl6040_get_pll(struct twl6040 *twl6040) 499210afeecSPeter Ujfalusi { 500210afeecSPeter Ujfalusi if (twl6040->power_count) 501210afeecSPeter Ujfalusi return twl6040->pll; 502210afeecSPeter Ujfalusi else 503210afeecSPeter Ujfalusi return -ENODEV; 504210afeecSPeter Ujfalusi } 505210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_pll); 506210afeecSPeter Ujfalusi 507210afeecSPeter Ujfalusi unsigned int twl6040_get_sysclk(struct twl6040 *twl6040) 508210afeecSPeter Ujfalusi { 509210afeecSPeter Ujfalusi return twl6040->sysclk; 510210afeecSPeter Ujfalusi } 511210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_sysclk); 512210afeecSPeter Ujfalusi 513210afeecSPeter Ujfalusi /* Get the combined status of the vibra control register */ 514210afeecSPeter Ujfalusi int twl6040_get_vibralr_status(struct twl6040 *twl6040) 515210afeecSPeter Ujfalusi { 516c6f39257SMark Brown unsigned int reg; 517c6f39257SMark Brown int ret; 518210afeecSPeter Ujfalusi u8 status; 519210afeecSPeter Ujfalusi 520c6f39257SMark Brown ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, ®); 521c6f39257SMark Brown if (ret != 0) 522c6f39257SMark Brown return ret; 523c6f39257SMark Brown status = reg; 524c6f39257SMark Brown 525c6f39257SMark Brown ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, ®); 526c6f39257SMark Brown if (ret != 0) 527c6f39257SMark Brown return ret; 528c6f39257SMark Brown status |= reg; 529c6f39257SMark Brown 530210afeecSPeter Ujfalusi status &= (TWL6040_VIBENA | TWL6040_VIBSEL); 531210afeecSPeter Ujfalusi 532210afeecSPeter Ujfalusi return status; 533210afeecSPeter Ujfalusi } 534210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_vibralr_status); 535210afeecSPeter Ujfalusi 536210afeecSPeter Ujfalusi static struct resource twl6040_vibra_rsrc[] = { 537210afeecSPeter Ujfalusi { 538210afeecSPeter Ujfalusi .flags = IORESOURCE_IRQ, 539210afeecSPeter Ujfalusi }, 540210afeecSPeter Ujfalusi }; 541210afeecSPeter Ujfalusi 542210afeecSPeter Ujfalusi static struct resource twl6040_codec_rsrc[] = { 543210afeecSPeter Ujfalusi { 544210afeecSPeter Ujfalusi .flags = IORESOURCE_IRQ, 545210afeecSPeter Ujfalusi }, 546210afeecSPeter Ujfalusi }; 547210afeecSPeter Ujfalusi 548210afeecSPeter Ujfalusi static bool twl6040_readable_reg(struct device *dev, unsigned int reg) 549210afeecSPeter Ujfalusi { 550210afeecSPeter Ujfalusi /* Register 0 is not readable */ 551210afeecSPeter Ujfalusi if (!reg) 552210afeecSPeter Ujfalusi return false; 553210afeecSPeter Ujfalusi return true; 554210afeecSPeter Ujfalusi } 555210afeecSPeter Ujfalusi 556c6f39257SMark Brown static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) 557c6f39257SMark Brown { 558c6f39257SMark Brown switch (reg) { 559c7f9129dSPeter Ujfalusi case TWL6040_REG_ASICID: 560c7f9129dSPeter Ujfalusi case TWL6040_REG_ASICREV: 561c7f9129dSPeter Ujfalusi case TWL6040_REG_INTID: 562c7f9129dSPeter Ujfalusi case TWL6040_REG_LPPLLCTL: 563c7f9129dSPeter Ujfalusi case TWL6040_REG_HPPLLCTL: 564c7f9129dSPeter Ujfalusi case TWL6040_REG_STATUS: 565c7f9129dSPeter Ujfalusi return true; 566c7f9129dSPeter Ujfalusi default: 567c7f9129dSPeter Ujfalusi return false; 568c7f9129dSPeter Ujfalusi } 569c7f9129dSPeter Ujfalusi } 570c7f9129dSPeter Ujfalusi 571c7f9129dSPeter Ujfalusi static bool twl6040_writeable_reg(struct device *dev, unsigned int reg) 572c7f9129dSPeter Ujfalusi { 573c7f9129dSPeter Ujfalusi switch (reg) { 574c7f9129dSPeter Ujfalusi case TWL6040_REG_ASICID: 575c7f9129dSPeter Ujfalusi case TWL6040_REG_ASICREV: 576c7f9129dSPeter Ujfalusi case TWL6040_REG_STATUS: 577c6f39257SMark Brown return false; 578c6f39257SMark Brown default: 579c6f39257SMark Brown return true; 580c6f39257SMark Brown } 581c6f39257SMark Brown } 582c6f39257SMark Brown 583de1e23f8SKrzysztof Kozlowski static const struct regmap_config twl6040_regmap_config = { 584210afeecSPeter Ujfalusi .reg_bits = 8, 585210afeecSPeter Ujfalusi .val_bits = 8, 586c7f9129dSPeter Ujfalusi 587c7f9129dSPeter Ujfalusi .reg_defaults = twl6040_defaults, 588c7f9129dSPeter Ujfalusi .num_reg_defaults = ARRAY_SIZE(twl6040_defaults), 589c7f9129dSPeter Ujfalusi 590210afeecSPeter Ujfalusi .max_register = TWL6040_REG_STATUS, /* 0x2e */ 591210afeecSPeter Ujfalusi 592210afeecSPeter Ujfalusi .readable_reg = twl6040_readable_reg, 593c6f39257SMark Brown .volatile_reg = twl6040_volatile_reg, 594c7f9129dSPeter Ujfalusi .writeable_reg = twl6040_writeable_reg, 595c6f39257SMark Brown 596c6f39257SMark Brown .cache_type = REGCACHE_RBTREE, 597210afeecSPeter Ujfalusi }; 598210afeecSPeter Ujfalusi 599210afeecSPeter Ujfalusi static const struct regmap_irq twl6040_irqs[] = { 600210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_THINT, }, 601210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, }, 602210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_HOOKINT, }, 603210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_HFINT, }, 604210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_VIBINT, }, 605210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_READYINT, }, 606210afeecSPeter Ujfalusi }; 607210afeecSPeter Ujfalusi 608210afeecSPeter Ujfalusi static struct regmap_irq_chip twl6040_irq_chip = { 609210afeecSPeter Ujfalusi .name = "twl6040", 610210afeecSPeter Ujfalusi .irqs = twl6040_irqs, 611210afeecSPeter Ujfalusi .num_irqs = ARRAY_SIZE(twl6040_irqs), 612210afeecSPeter Ujfalusi 613210afeecSPeter Ujfalusi .num_regs = 1, 614210afeecSPeter Ujfalusi .status_base = TWL6040_REG_INTID, 615210afeecSPeter Ujfalusi .mask_base = TWL6040_REG_INTMR, 616210afeecSPeter Ujfalusi }; 617210afeecSPeter Ujfalusi 618612b95cdSGreg Kroah-Hartman static int twl6040_probe(struct i2c_client *client, 619210afeecSPeter Ujfalusi const struct i2c_device_id *id) 620210afeecSPeter Ujfalusi { 621210afeecSPeter Ujfalusi struct device_node *node = client->dev.of_node; 622210afeecSPeter Ujfalusi struct twl6040 *twl6040; 623210afeecSPeter Ujfalusi struct mfd_cell *cell = NULL; 624210afeecSPeter Ujfalusi int irq, ret, children = 0; 625210afeecSPeter Ujfalusi 626df04b624SPeter Ujfalusi if (!node) { 627df04b624SPeter Ujfalusi dev_err(&client->dev, "of node is missing\n"); 628210afeecSPeter Ujfalusi return -EINVAL; 629210afeecSPeter Ujfalusi } 630210afeecSPeter Ujfalusi 631210afeecSPeter Ujfalusi /* In order to operate correctly we need valid interrupt config */ 632210afeecSPeter Ujfalusi if (!client->irq) { 633210afeecSPeter Ujfalusi dev_err(&client->dev, "Invalid IRQ configuration\n"); 634210afeecSPeter Ujfalusi return -EINVAL; 635210afeecSPeter Ujfalusi } 636210afeecSPeter Ujfalusi 637210afeecSPeter Ujfalusi twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040), 638210afeecSPeter Ujfalusi GFP_KERNEL); 639ecc8fa1cSPeter Ujfalusi if (!twl6040) 640ecc8fa1cSPeter Ujfalusi return -ENOMEM; 641210afeecSPeter Ujfalusi 642210afeecSPeter Ujfalusi twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config); 643ecc8fa1cSPeter Ujfalusi if (IS_ERR(twl6040->regmap)) 644ecc8fa1cSPeter Ujfalusi return PTR_ERR(twl6040->regmap); 645210afeecSPeter Ujfalusi 646210afeecSPeter Ujfalusi i2c_set_clientdata(client, twl6040); 647210afeecSPeter Ujfalusi 64868bab866SPeter Ujfalusi twl6040->clk32k = devm_clk_get(&client->dev, "clk32k"); 64968bab866SPeter Ujfalusi if (IS_ERR(twl6040->clk32k)) { 65075c08f17STony Lindgren if (PTR_ERR(twl6040->clk32k) == -EPROBE_DEFER) 65175c08f17STony Lindgren return -EPROBE_DEFER; 65268bab866SPeter Ujfalusi dev_info(&client->dev, "clk32k is not handled\n"); 65368bab866SPeter Ujfalusi twl6040->clk32k = NULL; 65468bab866SPeter Ujfalusi } 65568bab866SPeter Ujfalusi 656210afeecSPeter Ujfalusi twl6040->supplies[0].supply = "vio"; 657210afeecSPeter Ujfalusi twl6040->supplies[1].supply = "v2v1"; 658990810b0SJingoo Han ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, 659210afeecSPeter Ujfalusi twl6040->supplies); 660210afeecSPeter Ujfalusi if (ret != 0) { 661210afeecSPeter Ujfalusi dev_err(&client->dev, "Failed to get supplies: %d\n", ret); 662501d609aSWolfram Sang return ret; 663210afeecSPeter Ujfalusi } 664210afeecSPeter Ujfalusi 665210afeecSPeter Ujfalusi ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies); 666210afeecSPeter Ujfalusi if (ret != 0) { 667210afeecSPeter Ujfalusi dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); 668501d609aSWolfram Sang return ret; 669210afeecSPeter Ujfalusi } 670210afeecSPeter Ujfalusi 671210afeecSPeter Ujfalusi twl6040->dev = &client->dev; 672210afeecSPeter Ujfalusi twl6040->irq = client->irq; 673210afeecSPeter Ujfalusi 674210afeecSPeter Ujfalusi mutex_init(&twl6040->mutex); 675210afeecSPeter Ujfalusi init_completion(&twl6040->ready); 676210afeecSPeter Ujfalusi 677006cea3aSPeter Ujfalusi regmap_register_patch(twl6040->regmap, twl6040_patch, 678006cea3aSPeter Ujfalusi ARRAY_SIZE(twl6040_patch)); 679006cea3aSPeter Ujfalusi 680210afeecSPeter Ujfalusi twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); 68189d68998SFlorian Vaussard if (twl6040->rev < 0) { 68289d68998SFlorian Vaussard dev_err(&client->dev, "Failed to read revision register: %d\n", 68389d68998SFlorian Vaussard twl6040->rev); 684f2b86781SJulia Lawall ret = twl6040->rev; 68589d68998SFlorian Vaussard goto gpio_err; 68689d68998SFlorian Vaussard } 687210afeecSPeter Ujfalusi 688210afeecSPeter Ujfalusi /* ERRATA: Automatic power-up is not possible in ES1.0 */ 689df04b624SPeter Ujfalusi if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) 690210afeecSPeter Ujfalusi twl6040->audpwron = of_get_named_gpio(node, 691210afeecSPeter Ujfalusi "ti,audpwron-gpio", 0); 692df04b624SPeter Ujfalusi else 693210afeecSPeter Ujfalusi twl6040->audpwron = -EINVAL; 694210afeecSPeter Ujfalusi 695210afeecSPeter Ujfalusi if (gpio_is_valid(twl6040->audpwron)) { 696990810b0SJingoo Han ret = devm_gpio_request_one(&client->dev, twl6040->audpwron, 697990810b0SJingoo Han GPIOF_OUT_INIT_LOW, "audpwron"); 698210afeecSPeter Ujfalusi if (ret) 699210afeecSPeter Ujfalusi goto gpio_err; 70002d02728SPeter Ujfalusi 70102d02728SPeter Ujfalusi /* Clear any pending interrupt */ 70202d02728SPeter Ujfalusi twl6040_reg_read(twl6040, TWL6040_REG_INTID); 703210afeecSPeter Ujfalusi } 704210afeecSPeter Ujfalusi 70537aefe9fSPeter Ujfalusi ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT, 70637aefe9fSPeter Ujfalusi 0, &twl6040_irq_chip, &twl6040->irq_data); 707210afeecSPeter Ujfalusi if (ret < 0) 708990810b0SJingoo Han goto gpio_err; 709210afeecSPeter Ujfalusi 710210afeecSPeter Ujfalusi twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data, 711210afeecSPeter Ujfalusi TWL6040_IRQ_READY); 712210afeecSPeter Ujfalusi twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data, 713210afeecSPeter Ujfalusi TWL6040_IRQ_TH); 714210afeecSPeter Ujfalusi 715990810b0SJingoo Han ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL, 716210afeecSPeter Ujfalusi twl6040_readyint_handler, IRQF_ONESHOT, 717210afeecSPeter Ujfalusi "twl6040_irq_ready", twl6040); 718210afeecSPeter Ujfalusi if (ret) { 719210afeecSPeter Ujfalusi dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret); 720210afeecSPeter Ujfalusi goto readyirq_err; 721210afeecSPeter Ujfalusi } 722210afeecSPeter Ujfalusi 723990810b0SJingoo Han ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL, 724210afeecSPeter Ujfalusi twl6040_thint_handler, IRQF_ONESHOT, 725210afeecSPeter Ujfalusi "twl6040_irq_th", twl6040); 726210afeecSPeter Ujfalusi if (ret) { 727210afeecSPeter Ujfalusi dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret); 728fc5ee96fSWei Yongjun goto readyirq_err; 729210afeecSPeter Ujfalusi } 730210afeecSPeter Ujfalusi 731210afeecSPeter Ujfalusi /* 732210afeecSPeter Ujfalusi * The main functionality of twl6040 to provide audio on OMAP4+ systems. 733210afeecSPeter Ujfalusi * We can add the ASoC codec child whenever this driver has been loaded. 734210afeecSPeter Ujfalusi */ 735210afeecSPeter Ujfalusi irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG); 736210afeecSPeter Ujfalusi cell = &twl6040->cells[children]; 737210afeecSPeter Ujfalusi cell->name = "twl6040-codec"; 738210afeecSPeter Ujfalusi twl6040_codec_rsrc[0].start = irq; 739210afeecSPeter Ujfalusi twl6040_codec_rsrc[0].end = irq; 740210afeecSPeter Ujfalusi cell->resources = twl6040_codec_rsrc; 741210afeecSPeter Ujfalusi cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); 742210afeecSPeter Ujfalusi children++; 743210afeecSPeter Ujfalusi 744df04b624SPeter Ujfalusi /* Vibra input driver support */ 745df04b624SPeter Ujfalusi if (twl6040_has_vibra(node)) { 746210afeecSPeter Ujfalusi irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB); 747210afeecSPeter Ujfalusi 748210afeecSPeter Ujfalusi cell = &twl6040->cells[children]; 749210afeecSPeter Ujfalusi cell->name = "twl6040-vibra"; 750210afeecSPeter Ujfalusi twl6040_vibra_rsrc[0].start = irq; 751210afeecSPeter Ujfalusi twl6040_vibra_rsrc[0].end = irq; 752210afeecSPeter Ujfalusi cell->resources = twl6040_vibra_rsrc; 753210afeecSPeter Ujfalusi cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc); 754210afeecSPeter Ujfalusi children++; 755210afeecSPeter Ujfalusi } 756210afeecSPeter Ujfalusi 757df04b624SPeter Ujfalusi /* GPO support */ 758210afeecSPeter Ujfalusi cell = &twl6040->cells[children]; 759210afeecSPeter Ujfalusi cell->name = "twl6040-gpo"; 760210afeecSPeter Ujfalusi children++; 761210afeecSPeter Ujfalusi 762c7f9129dSPeter Ujfalusi /* The chip is powered down so mark regmap to cache only and dirty */ 763c7f9129dSPeter Ujfalusi regcache_cache_only(twl6040->regmap, true); 764c7f9129dSPeter Ujfalusi regcache_mark_dirty(twl6040->regmap); 765c7f9129dSPeter Ujfalusi 766210afeecSPeter Ujfalusi ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, 767210afeecSPeter Ujfalusi NULL, 0, NULL); 768210afeecSPeter Ujfalusi if (ret) 769fc5ee96fSWei Yongjun goto readyirq_err; 770210afeecSPeter Ujfalusi 771210afeecSPeter Ujfalusi return 0; 772210afeecSPeter Ujfalusi 773210afeecSPeter Ujfalusi readyirq_err: 774210afeecSPeter Ujfalusi regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); 775210afeecSPeter Ujfalusi gpio_err: 776210afeecSPeter Ujfalusi regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); 777210afeecSPeter Ujfalusi return ret; 778210afeecSPeter Ujfalusi } 779210afeecSPeter Ujfalusi 780612b95cdSGreg Kroah-Hartman static int twl6040_remove(struct i2c_client *client) 781210afeecSPeter Ujfalusi { 782210afeecSPeter Ujfalusi struct twl6040 *twl6040 = i2c_get_clientdata(client); 783210afeecSPeter Ujfalusi 784210afeecSPeter Ujfalusi if (twl6040->power_count) 785210afeecSPeter Ujfalusi twl6040_power(twl6040, 0); 786210afeecSPeter Ujfalusi 787210afeecSPeter Ujfalusi regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); 788210afeecSPeter Ujfalusi 789210afeecSPeter Ujfalusi mfd_remove_devices(&client->dev); 790210afeecSPeter Ujfalusi 791210afeecSPeter Ujfalusi regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); 792210afeecSPeter Ujfalusi 793210afeecSPeter Ujfalusi return 0; 794210afeecSPeter Ujfalusi } 795210afeecSPeter Ujfalusi 796210afeecSPeter Ujfalusi static const struct i2c_device_id twl6040_i2c_id[] = { 797210afeecSPeter Ujfalusi { "twl6040", 0, }, 798210afeecSPeter Ujfalusi { "twl6041", 0, }, 799210afeecSPeter Ujfalusi { }, 800210afeecSPeter Ujfalusi }; 801210afeecSPeter Ujfalusi MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id); 802210afeecSPeter Ujfalusi 803210afeecSPeter Ujfalusi static struct i2c_driver twl6040_driver = { 804210afeecSPeter Ujfalusi .driver = { 805210afeecSPeter Ujfalusi .name = "twl6040", 806210afeecSPeter Ujfalusi }, 807210afeecSPeter Ujfalusi .probe = twl6040_probe, 808612b95cdSGreg Kroah-Hartman .remove = twl6040_remove, 809210afeecSPeter Ujfalusi .id_table = twl6040_i2c_id, 810210afeecSPeter Ujfalusi }; 811210afeecSPeter Ujfalusi 812210afeecSPeter Ujfalusi module_i2c_driver(twl6040_driver); 813210afeecSPeter Ujfalusi 814210afeecSPeter Ujfalusi MODULE_DESCRIPTION("TWL6040 MFD"); 815210afeecSPeter Ujfalusi MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); 816210afeecSPeter Ujfalusi MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>"); 817210afeecSPeter Ujfalusi MODULE_LICENSE("GPL"); 818