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 47df04b624SPeter Ujfalusi static bool twl6040_has_vibra(struct device_node *node) 48210afeecSPeter Ujfalusi { 49210afeecSPeter Ujfalusi #ifdef CONFIG_OF 50210afeecSPeter Ujfalusi if (of_find_node_by_name(node, "vibra")) 51210afeecSPeter Ujfalusi return true; 52210afeecSPeter Ujfalusi #endif 53210afeecSPeter Ujfalusi return false; 54210afeecSPeter Ujfalusi } 55210afeecSPeter Ujfalusi 56210afeecSPeter Ujfalusi int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) 57210afeecSPeter Ujfalusi { 58210afeecSPeter Ujfalusi int ret; 59210afeecSPeter Ujfalusi unsigned int val; 60210afeecSPeter Ujfalusi 61210afeecSPeter Ujfalusi ret = regmap_read(twl6040->regmap, reg, &val); 62210afeecSPeter Ujfalusi if (ret < 0) 63210afeecSPeter Ujfalusi return ret; 64210afeecSPeter Ujfalusi 65210afeecSPeter Ujfalusi return val; 66210afeecSPeter Ujfalusi } 67210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_read); 68210afeecSPeter Ujfalusi 69210afeecSPeter Ujfalusi int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val) 70210afeecSPeter Ujfalusi { 71210afeecSPeter Ujfalusi int ret; 72210afeecSPeter Ujfalusi 73210afeecSPeter Ujfalusi ret = regmap_write(twl6040->regmap, reg, val); 74210afeecSPeter Ujfalusi 75210afeecSPeter Ujfalusi return ret; 76210afeecSPeter Ujfalusi } 77210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_reg_write); 78210afeecSPeter Ujfalusi 79210afeecSPeter Ujfalusi int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) 80210afeecSPeter Ujfalusi { 81210afeecSPeter Ujfalusi return regmap_update_bits(twl6040->regmap, reg, mask, mask); 82210afeecSPeter Ujfalusi } 83210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_bits); 84210afeecSPeter Ujfalusi 85210afeecSPeter Ujfalusi int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) 86210afeecSPeter Ujfalusi { 87210afeecSPeter Ujfalusi return regmap_update_bits(twl6040->regmap, reg, mask, 0); 88210afeecSPeter Ujfalusi } 89210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_clear_bits); 90210afeecSPeter Ujfalusi 91210afeecSPeter Ujfalusi /* twl6040 codec manual power-up sequence */ 92210afeecSPeter Ujfalusi static int twl6040_power_up_manual(struct twl6040 *twl6040) 93210afeecSPeter Ujfalusi { 94210afeecSPeter Ujfalusi u8 ldoctl, ncpctl, lppllctl; 95210afeecSPeter Ujfalusi int ret; 96210afeecSPeter Ujfalusi 97210afeecSPeter Ujfalusi /* enable high-side LDO, reference system and internal oscillator */ 98210afeecSPeter Ujfalusi ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA; 99210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 100210afeecSPeter Ujfalusi if (ret) 101210afeecSPeter Ujfalusi return ret; 102210afeecSPeter Ujfalusi usleep_range(10000, 10500); 103210afeecSPeter Ujfalusi 104210afeecSPeter Ujfalusi /* enable negative charge pump */ 105210afeecSPeter Ujfalusi ncpctl = TWL6040_NCPENA; 106210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 107210afeecSPeter Ujfalusi if (ret) 108210afeecSPeter Ujfalusi goto ncp_err; 109210afeecSPeter Ujfalusi usleep_range(1000, 1500); 110210afeecSPeter Ujfalusi 111210afeecSPeter Ujfalusi /* enable low-side LDO */ 112210afeecSPeter Ujfalusi ldoctl |= TWL6040_LSLDOENA; 113210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 114210afeecSPeter Ujfalusi if (ret) 115210afeecSPeter Ujfalusi goto lsldo_err; 116210afeecSPeter Ujfalusi usleep_range(1000, 1500); 117210afeecSPeter Ujfalusi 118210afeecSPeter Ujfalusi /* enable low-power PLL */ 119210afeecSPeter Ujfalusi lppllctl = TWL6040_LPLLENA; 120210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 121210afeecSPeter Ujfalusi if (ret) 122210afeecSPeter Ujfalusi goto lppll_err; 123210afeecSPeter Ujfalusi usleep_range(5000, 5500); 124210afeecSPeter Ujfalusi 125210afeecSPeter Ujfalusi /* disable internal oscillator */ 126210afeecSPeter Ujfalusi ldoctl &= ~TWL6040_OSCENA; 127210afeecSPeter Ujfalusi ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 128210afeecSPeter Ujfalusi if (ret) 129210afeecSPeter Ujfalusi goto osc_err; 130210afeecSPeter Ujfalusi 131210afeecSPeter Ujfalusi return 0; 132210afeecSPeter Ujfalusi 133210afeecSPeter Ujfalusi osc_err: 134210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLENA; 135210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 136210afeecSPeter Ujfalusi lppll_err: 137210afeecSPeter Ujfalusi ldoctl &= ~TWL6040_LSLDOENA; 138210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 139210afeecSPeter Ujfalusi lsldo_err: 140210afeecSPeter Ujfalusi ncpctl &= ~TWL6040_NCPENA; 141210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 142210afeecSPeter Ujfalusi ncp_err: 143210afeecSPeter Ujfalusi ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); 144210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 145210afeecSPeter Ujfalusi 146210afeecSPeter Ujfalusi dev_err(twl6040->dev, "manual power-up failed\n"); 147210afeecSPeter Ujfalusi return ret; 148210afeecSPeter Ujfalusi } 149210afeecSPeter Ujfalusi 150210afeecSPeter Ujfalusi /* twl6040 manual power-down sequence */ 151210afeecSPeter Ujfalusi static void twl6040_power_down_manual(struct twl6040 *twl6040) 152210afeecSPeter Ujfalusi { 153210afeecSPeter Ujfalusi u8 ncpctl, ldoctl, lppllctl; 154210afeecSPeter Ujfalusi 155210afeecSPeter Ujfalusi ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL); 156210afeecSPeter Ujfalusi ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL); 157210afeecSPeter Ujfalusi lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); 158210afeecSPeter Ujfalusi 159210afeecSPeter Ujfalusi /* enable internal oscillator */ 160210afeecSPeter Ujfalusi ldoctl |= TWL6040_OSCENA; 161210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 162210afeecSPeter Ujfalusi usleep_range(1000, 1500); 163210afeecSPeter Ujfalusi 164210afeecSPeter Ujfalusi /* disable low-power PLL */ 165210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLENA; 166210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 167210afeecSPeter Ujfalusi 168210afeecSPeter Ujfalusi /* disable low-side LDO */ 169210afeecSPeter Ujfalusi ldoctl &= ~TWL6040_LSLDOENA; 170210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 171210afeecSPeter Ujfalusi 172210afeecSPeter Ujfalusi /* disable negative charge pump */ 173210afeecSPeter Ujfalusi ncpctl &= ~TWL6040_NCPENA; 174210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 175210afeecSPeter Ujfalusi 176210afeecSPeter Ujfalusi /* disable high-side LDO, reference system and internal oscillator */ 177210afeecSPeter Ujfalusi ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); 178210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 179210afeecSPeter Ujfalusi } 180210afeecSPeter Ujfalusi 181210afeecSPeter Ujfalusi static irqreturn_t twl6040_readyint_handler(int irq, void *data) 182210afeecSPeter Ujfalusi { 183210afeecSPeter Ujfalusi struct twl6040 *twl6040 = data; 184210afeecSPeter Ujfalusi 185210afeecSPeter Ujfalusi complete(&twl6040->ready); 186210afeecSPeter Ujfalusi 187210afeecSPeter Ujfalusi return IRQ_HANDLED; 188210afeecSPeter Ujfalusi } 189210afeecSPeter Ujfalusi 190210afeecSPeter Ujfalusi static irqreturn_t twl6040_thint_handler(int irq, void *data) 191210afeecSPeter Ujfalusi { 192210afeecSPeter Ujfalusi struct twl6040 *twl6040 = data; 193210afeecSPeter Ujfalusi u8 status; 194210afeecSPeter Ujfalusi 195210afeecSPeter Ujfalusi status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); 196210afeecSPeter Ujfalusi if (status & TWL6040_TSHUTDET) { 197210afeecSPeter Ujfalusi dev_warn(twl6040->dev, "Thermal shutdown, powering-off"); 198210afeecSPeter Ujfalusi twl6040_power(twl6040, 0); 199210afeecSPeter Ujfalusi } else { 200210afeecSPeter Ujfalusi dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on"); 201210afeecSPeter Ujfalusi twl6040_power(twl6040, 1); 202210afeecSPeter Ujfalusi } 203210afeecSPeter Ujfalusi 204210afeecSPeter Ujfalusi return IRQ_HANDLED; 205210afeecSPeter Ujfalusi } 206210afeecSPeter Ujfalusi 207210afeecSPeter Ujfalusi static int twl6040_power_up_automatic(struct twl6040 *twl6040) 208210afeecSPeter Ujfalusi { 209210afeecSPeter Ujfalusi int time_left; 210210afeecSPeter Ujfalusi 211210afeecSPeter Ujfalusi gpio_set_value(twl6040->audpwron, 1); 212210afeecSPeter Ujfalusi 213210afeecSPeter Ujfalusi time_left = wait_for_completion_timeout(&twl6040->ready, 214210afeecSPeter Ujfalusi msecs_to_jiffies(144)); 215210afeecSPeter Ujfalusi if (!time_left) { 216210afeecSPeter Ujfalusi u8 intid; 217210afeecSPeter Ujfalusi 218210afeecSPeter Ujfalusi dev_warn(twl6040->dev, "timeout waiting for READYINT\n"); 219210afeecSPeter Ujfalusi intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); 220210afeecSPeter Ujfalusi if (!(intid & TWL6040_READYINT)) { 221210afeecSPeter Ujfalusi dev_err(twl6040->dev, "automatic power-up failed\n"); 222210afeecSPeter Ujfalusi gpio_set_value(twl6040->audpwron, 0); 223210afeecSPeter Ujfalusi return -ETIMEDOUT; 224210afeecSPeter Ujfalusi } 225210afeecSPeter Ujfalusi } 226210afeecSPeter Ujfalusi 227210afeecSPeter Ujfalusi return 0; 228210afeecSPeter Ujfalusi } 229210afeecSPeter Ujfalusi 230210afeecSPeter Ujfalusi int twl6040_power(struct twl6040 *twl6040, int on) 231210afeecSPeter Ujfalusi { 232210afeecSPeter Ujfalusi int ret = 0; 233210afeecSPeter Ujfalusi 234210afeecSPeter Ujfalusi mutex_lock(&twl6040->mutex); 235210afeecSPeter Ujfalusi 236210afeecSPeter Ujfalusi if (on) { 237210afeecSPeter Ujfalusi /* already powered-up */ 238210afeecSPeter Ujfalusi if (twl6040->power_count++) 239210afeecSPeter Ujfalusi goto out; 240210afeecSPeter Ujfalusi 241210afeecSPeter Ujfalusi if (gpio_is_valid(twl6040->audpwron)) { 242210afeecSPeter Ujfalusi /* use automatic power-up sequence */ 243210afeecSPeter Ujfalusi ret = twl6040_power_up_automatic(twl6040); 244210afeecSPeter Ujfalusi if (ret) { 245210afeecSPeter Ujfalusi twl6040->power_count = 0; 246210afeecSPeter Ujfalusi goto out; 247210afeecSPeter Ujfalusi } 248210afeecSPeter Ujfalusi } else { 249210afeecSPeter Ujfalusi /* use manual power-up sequence */ 250210afeecSPeter Ujfalusi ret = twl6040_power_up_manual(twl6040); 251210afeecSPeter Ujfalusi if (ret) { 252210afeecSPeter Ujfalusi twl6040->power_count = 0; 253210afeecSPeter Ujfalusi goto out; 254210afeecSPeter Ujfalusi } 255210afeecSPeter Ujfalusi } 256210afeecSPeter Ujfalusi /* Default PLL configuration after power up */ 257210afeecSPeter Ujfalusi twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; 258210afeecSPeter Ujfalusi twl6040->sysclk = 19200000; 259210afeecSPeter Ujfalusi twl6040->mclk = 32768; 260210afeecSPeter Ujfalusi } else { 261210afeecSPeter Ujfalusi /* already powered-down */ 262210afeecSPeter Ujfalusi if (!twl6040->power_count) { 263210afeecSPeter Ujfalusi dev_err(twl6040->dev, 264210afeecSPeter Ujfalusi "device is already powered-off\n"); 265210afeecSPeter Ujfalusi ret = -EPERM; 266210afeecSPeter Ujfalusi goto out; 267210afeecSPeter Ujfalusi } 268210afeecSPeter Ujfalusi 269210afeecSPeter Ujfalusi if (--twl6040->power_count) 270210afeecSPeter Ujfalusi goto out; 271210afeecSPeter Ujfalusi 272210afeecSPeter Ujfalusi if (gpio_is_valid(twl6040->audpwron)) { 273210afeecSPeter Ujfalusi /* use AUDPWRON line */ 274210afeecSPeter Ujfalusi gpio_set_value(twl6040->audpwron, 0); 275210afeecSPeter Ujfalusi 276210afeecSPeter Ujfalusi /* power-down sequence latency */ 277210afeecSPeter Ujfalusi usleep_range(500, 700); 278210afeecSPeter Ujfalusi } else { 279210afeecSPeter Ujfalusi /* use manual power-down sequence */ 280210afeecSPeter Ujfalusi twl6040_power_down_manual(twl6040); 281210afeecSPeter Ujfalusi } 282210afeecSPeter Ujfalusi twl6040->sysclk = 0; 283210afeecSPeter Ujfalusi twl6040->mclk = 0; 284210afeecSPeter Ujfalusi } 285210afeecSPeter Ujfalusi 286210afeecSPeter Ujfalusi out: 287210afeecSPeter Ujfalusi mutex_unlock(&twl6040->mutex); 288210afeecSPeter Ujfalusi return ret; 289210afeecSPeter Ujfalusi } 290210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_power); 291210afeecSPeter Ujfalusi 292210afeecSPeter Ujfalusi int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, 293210afeecSPeter Ujfalusi unsigned int freq_in, unsigned int freq_out) 294210afeecSPeter Ujfalusi { 295210afeecSPeter Ujfalusi u8 hppllctl, lppllctl; 296210afeecSPeter Ujfalusi int ret = 0; 297210afeecSPeter Ujfalusi 298210afeecSPeter Ujfalusi mutex_lock(&twl6040->mutex); 299210afeecSPeter Ujfalusi 300210afeecSPeter Ujfalusi hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL); 301210afeecSPeter Ujfalusi lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); 302210afeecSPeter Ujfalusi 303210afeecSPeter Ujfalusi /* Force full reconfiguration when switching between PLL */ 304210afeecSPeter Ujfalusi if (pll_id != twl6040->pll) { 305210afeecSPeter Ujfalusi twl6040->sysclk = 0; 306210afeecSPeter Ujfalusi twl6040->mclk = 0; 307210afeecSPeter Ujfalusi } 308210afeecSPeter Ujfalusi 309210afeecSPeter Ujfalusi switch (pll_id) { 310210afeecSPeter Ujfalusi case TWL6040_SYSCLK_SEL_LPPLL: 311210afeecSPeter Ujfalusi /* low-power PLL divider */ 312210afeecSPeter Ujfalusi /* Change the sysclk configuration only if it has been canged */ 313210afeecSPeter Ujfalusi if (twl6040->sysclk != freq_out) { 314210afeecSPeter Ujfalusi switch (freq_out) { 315210afeecSPeter Ujfalusi case 17640000: 316210afeecSPeter Ujfalusi lppllctl |= TWL6040_LPLLFIN; 317210afeecSPeter Ujfalusi break; 318210afeecSPeter Ujfalusi case 19200000: 319210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLFIN; 320210afeecSPeter Ujfalusi break; 321210afeecSPeter Ujfalusi default: 322210afeecSPeter Ujfalusi dev_err(twl6040->dev, 323210afeecSPeter Ujfalusi "freq_out %d not supported\n", 324210afeecSPeter Ujfalusi freq_out); 325210afeecSPeter Ujfalusi ret = -EINVAL; 326210afeecSPeter Ujfalusi goto pll_out; 327210afeecSPeter Ujfalusi } 328210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 329210afeecSPeter Ujfalusi lppllctl); 330210afeecSPeter Ujfalusi } 331210afeecSPeter Ujfalusi 332210afeecSPeter Ujfalusi /* The PLL in use has not been change, we can exit */ 333210afeecSPeter Ujfalusi if (twl6040->pll == pll_id) 334210afeecSPeter Ujfalusi break; 335210afeecSPeter Ujfalusi 336210afeecSPeter Ujfalusi switch (freq_in) { 337210afeecSPeter Ujfalusi case 32768: 338210afeecSPeter Ujfalusi lppllctl |= TWL6040_LPLLENA; 339210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 340210afeecSPeter Ujfalusi lppllctl); 341210afeecSPeter Ujfalusi mdelay(5); 342210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_HPLLSEL; 343210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 344210afeecSPeter Ujfalusi lppllctl); 345210afeecSPeter Ujfalusi hppllctl &= ~TWL6040_HPLLENA; 346210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, 347210afeecSPeter Ujfalusi hppllctl); 348210afeecSPeter Ujfalusi break; 349210afeecSPeter Ujfalusi default: 350210afeecSPeter Ujfalusi dev_err(twl6040->dev, 351210afeecSPeter Ujfalusi "freq_in %d not supported\n", freq_in); 352210afeecSPeter Ujfalusi ret = -EINVAL; 353210afeecSPeter Ujfalusi goto pll_out; 354210afeecSPeter Ujfalusi } 355210afeecSPeter Ujfalusi break; 356210afeecSPeter Ujfalusi case TWL6040_SYSCLK_SEL_HPPLL: 357210afeecSPeter Ujfalusi /* high-performance PLL can provide only 19.2 MHz */ 358210afeecSPeter Ujfalusi if (freq_out != 19200000) { 359210afeecSPeter Ujfalusi dev_err(twl6040->dev, 360210afeecSPeter Ujfalusi "freq_out %d not supported\n", freq_out); 361210afeecSPeter Ujfalusi ret = -EINVAL; 362210afeecSPeter Ujfalusi goto pll_out; 363210afeecSPeter Ujfalusi } 364210afeecSPeter Ujfalusi 365210afeecSPeter Ujfalusi if (twl6040->mclk != freq_in) { 366210afeecSPeter Ujfalusi hppllctl &= ~TWL6040_MCLK_MSK; 367210afeecSPeter Ujfalusi 368210afeecSPeter Ujfalusi switch (freq_in) { 369210afeecSPeter Ujfalusi case 12000000: 370210afeecSPeter Ujfalusi /* PLL enabled, active mode */ 371210afeecSPeter Ujfalusi hppllctl |= TWL6040_MCLK_12000KHZ | 372210afeecSPeter Ujfalusi TWL6040_HPLLENA; 373210afeecSPeter Ujfalusi break; 374210afeecSPeter Ujfalusi case 19200000: 375210afeecSPeter Ujfalusi /* 376210afeecSPeter Ujfalusi * PLL disabled 377210afeecSPeter Ujfalusi * (enable PLL if MCLK jitter quality 378210afeecSPeter Ujfalusi * doesn't meet specification) 379210afeecSPeter Ujfalusi */ 380210afeecSPeter Ujfalusi hppllctl |= TWL6040_MCLK_19200KHZ; 381210afeecSPeter Ujfalusi break; 382210afeecSPeter Ujfalusi case 26000000: 383210afeecSPeter Ujfalusi /* PLL enabled, active mode */ 384210afeecSPeter Ujfalusi hppllctl |= TWL6040_MCLK_26000KHZ | 385210afeecSPeter Ujfalusi TWL6040_HPLLENA; 386210afeecSPeter Ujfalusi break; 387210afeecSPeter Ujfalusi case 38400000: 388210afeecSPeter Ujfalusi /* PLL enabled, active mode */ 389210afeecSPeter Ujfalusi hppllctl |= TWL6040_MCLK_38400KHZ | 390210afeecSPeter Ujfalusi TWL6040_HPLLENA; 391210afeecSPeter Ujfalusi break; 392210afeecSPeter Ujfalusi default: 393210afeecSPeter Ujfalusi dev_err(twl6040->dev, 394210afeecSPeter Ujfalusi "freq_in %d not supported\n", freq_in); 395210afeecSPeter Ujfalusi ret = -EINVAL; 396210afeecSPeter Ujfalusi goto pll_out; 397210afeecSPeter Ujfalusi } 398210afeecSPeter Ujfalusi 399210afeecSPeter Ujfalusi /* 400210afeecSPeter Ujfalusi * enable clock slicer to ensure input waveform is 401210afeecSPeter Ujfalusi * square 402210afeecSPeter Ujfalusi */ 403210afeecSPeter Ujfalusi hppllctl |= TWL6040_HPLLSQRENA; 404210afeecSPeter Ujfalusi 405210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, 406210afeecSPeter Ujfalusi hppllctl); 407210afeecSPeter Ujfalusi usleep_range(500, 700); 408210afeecSPeter Ujfalusi lppllctl |= TWL6040_HPLLSEL; 409210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 410210afeecSPeter Ujfalusi lppllctl); 411210afeecSPeter Ujfalusi lppllctl &= ~TWL6040_LPLLENA; 412210afeecSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 413210afeecSPeter Ujfalusi lppllctl); 414210afeecSPeter Ujfalusi } 415210afeecSPeter Ujfalusi break; 416210afeecSPeter Ujfalusi default: 417210afeecSPeter Ujfalusi dev_err(twl6040->dev, "unknown pll id %d\n", pll_id); 418210afeecSPeter Ujfalusi ret = -EINVAL; 419210afeecSPeter Ujfalusi goto pll_out; 420210afeecSPeter Ujfalusi } 421210afeecSPeter Ujfalusi 422210afeecSPeter Ujfalusi twl6040->sysclk = freq_out; 423210afeecSPeter Ujfalusi twl6040->mclk = freq_in; 424210afeecSPeter Ujfalusi twl6040->pll = pll_id; 425210afeecSPeter Ujfalusi 426210afeecSPeter Ujfalusi pll_out: 427210afeecSPeter Ujfalusi mutex_unlock(&twl6040->mutex); 428210afeecSPeter Ujfalusi return ret; 429210afeecSPeter Ujfalusi } 430210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_set_pll); 431210afeecSPeter Ujfalusi 432210afeecSPeter Ujfalusi int twl6040_get_pll(struct twl6040 *twl6040) 433210afeecSPeter Ujfalusi { 434210afeecSPeter Ujfalusi if (twl6040->power_count) 435210afeecSPeter Ujfalusi return twl6040->pll; 436210afeecSPeter Ujfalusi else 437210afeecSPeter Ujfalusi return -ENODEV; 438210afeecSPeter Ujfalusi } 439210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_pll); 440210afeecSPeter Ujfalusi 441210afeecSPeter Ujfalusi unsigned int twl6040_get_sysclk(struct twl6040 *twl6040) 442210afeecSPeter Ujfalusi { 443210afeecSPeter Ujfalusi return twl6040->sysclk; 444210afeecSPeter Ujfalusi } 445210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_sysclk); 446210afeecSPeter Ujfalusi 447210afeecSPeter Ujfalusi /* Get the combined status of the vibra control register */ 448210afeecSPeter Ujfalusi int twl6040_get_vibralr_status(struct twl6040 *twl6040) 449210afeecSPeter Ujfalusi { 450c6f39257SMark Brown unsigned int reg; 451c6f39257SMark Brown int ret; 452210afeecSPeter Ujfalusi u8 status; 453210afeecSPeter Ujfalusi 454c6f39257SMark Brown ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, ®); 455c6f39257SMark Brown if (ret != 0) 456c6f39257SMark Brown return ret; 457c6f39257SMark Brown status = reg; 458c6f39257SMark Brown 459c6f39257SMark Brown ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, ®); 460c6f39257SMark Brown if (ret != 0) 461c6f39257SMark Brown return ret; 462c6f39257SMark Brown status |= reg; 463c6f39257SMark Brown 464210afeecSPeter Ujfalusi status &= (TWL6040_VIBENA | TWL6040_VIBSEL); 465210afeecSPeter Ujfalusi 466210afeecSPeter Ujfalusi return status; 467210afeecSPeter Ujfalusi } 468210afeecSPeter Ujfalusi EXPORT_SYMBOL(twl6040_get_vibralr_status); 469210afeecSPeter Ujfalusi 470210afeecSPeter Ujfalusi static struct resource twl6040_vibra_rsrc[] = { 471210afeecSPeter Ujfalusi { 472210afeecSPeter Ujfalusi .flags = IORESOURCE_IRQ, 473210afeecSPeter Ujfalusi }, 474210afeecSPeter Ujfalusi }; 475210afeecSPeter Ujfalusi 476210afeecSPeter Ujfalusi static struct resource twl6040_codec_rsrc[] = { 477210afeecSPeter Ujfalusi { 478210afeecSPeter Ujfalusi .flags = IORESOURCE_IRQ, 479210afeecSPeter Ujfalusi }, 480210afeecSPeter Ujfalusi }; 481210afeecSPeter Ujfalusi 482210afeecSPeter Ujfalusi static bool twl6040_readable_reg(struct device *dev, unsigned int reg) 483210afeecSPeter Ujfalusi { 484210afeecSPeter Ujfalusi /* Register 0 is not readable */ 485210afeecSPeter Ujfalusi if (!reg) 486210afeecSPeter Ujfalusi return false; 487210afeecSPeter Ujfalusi return true; 488210afeecSPeter Ujfalusi } 489210afeecSPeter Ujfalusi 490c6f39257SMark Brown static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) 491c6f39257SMark Brown { 492c6f39257SMark Brown switch (reg) { 493c6f39257SMark Brown case TWL6040_REG_VIBCTLL: 494c6f39257SMark Brown case TWL6040_REG_VIBCTLR: 495c6f39257SMark Brown case TWL6040_REG_INTMR: 496c6f39257SMark Brown return false; 497c6f39257SMark Brown default: 498c6f39257SMark Brown return true; 499c6f39257SMark Brown } 500c6f39257SMark Brown } 501c6f39257SMark Brown 502210afeecSPeter Ujfalusi static struct regmap_config twl6040_regmap_config = { 503210afeecSPeter Ujfalusi .reg_bits = 8, 504210afeecSPeter Ujfalusi .val_bits = 8, 505210afeecSPeter Ujfalusi .max_register = TWL6040_REG_STATUS, /* 0x2e */ 506210afeecSPeter Ujfalusi 507210afeecSPeter Ujfalusi .readable_reg = twl6040_readable_reg, 508c6f39257SMark Brown .volatile_reg = twl6040_volatile_reg, 509c6f39257SMark Brown 510c6f39257SMark Brown .cache_type = REGCACHE_RBTREE, 511210afeecSPeter Ujfalusi }; 512210afeecSPeter Ujfalusi 513210afeecSPeter Ujfalusi static const struct regmap_irq twl6040_irqs[] = { 514210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_THINT, }, 515210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, }, 516210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_HOOKINT, }, 517210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_HFINT, }, 518210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_VIBINT, }, 519210afeecSPeter Ujfalusi { .reg_offset = 0, .mask = TWL6040_READYINT, }, 520210afeecSPeter Ujfalusi }; 521210afeecSPeter Ujfalusi 522210afeecSPeter Ujfalusi static struct regmap_irq_chip twl6040_irq_chip = { 523210afeecSPeter Ujfalusi .name = "twl6040", 524210afeecSPeter Ujfalusi .irqs = twl6040_irqs, 525210afeecSPeter Ujfalusi .num_irqs = ARRAY_SIZE(twl6040_irqs), 526210afeecSPeter Ujfalusi 527210afeecSPeter Ujfalusi .num_regs = 1, 528210afeecSPeter Ujfalusi .status_base = TWL6040_REG_INTID, 529210afeecSPeter Ujfalusi .mask_base = TWL6040_REG_INTMR, 530210afeecSPeter Ujfalusi }; 531210afeecSPeter Ujfalusi 532612b95cdSGreg Kroah-Hartman static int twl6040_probe(struct i2c_client *client, 533210afeecSPeter Ujfalusi const struct i2c_device_id *id) 534210afeecSPeter Ujfalusi { 535210afeecSPeter Ujfalusi struct device_node *node = client->dev.of_node; 536210afeecSPeter Ujfalusi struct twl6040 *twl6040; 537210afeecSPeter Ujfalusi struct mfd_cell *cell = NULL; 538210afeecSPeter Ujfalusi int irq, ret, children = 0; 539210afeecSPeter Ujfalusi 540df04b624SPeter Ujfalusi if (!node) { 541df04b624SPeter Ujfalusi dev_err(&client->dev, "of node is missing\n"); 542210afeecSPeter Ujfalusi return -EINVAL; 543210afeecSPeter Ujfalusi } 544210afeecSPeter Ujfalusi 545210afeecSPeter Ujfalusi /* In order to operate correctly we need valid interrupt config */ 546210afeecSPeter Ujfalusi if (!client->irq) { 547210afeecSPeter Ujfalusi dev_err(&client->dev, "Invalid IRQ configuration\n"); 548210afeecSPeter Ujfalusi return -EINVAL; 549210afeecSPeter Ujfalusi } 550210afeecSPeter Ujfalusi 551210afeecSPeter Ujfalusi twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040), 552210afeecSPeter Ujfalusi GFP_KERNEL); 553ecc8fa1cSPeter Ujfalusi if (!twl6040) 554ecc8fa1cSPeter Ujfalusi return -ENOMEM; 555210afeecSPeter Ujfalusi 556210afeecSPeter Ujfalusi twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config); 557ecc8fa1cSPeter Ujfalusi if (IS_ERR(twl6040->regmap)) 558ecc8fa1cSPeter Ujfalusi return PTR_ERR(twl6040->regmap); 559210afeecSPeter Ujfalusi 560210afeecSPeter Ujfalusi i2c_set_clientdata(client, twl6040); 561210afeecSPeter Ujfalusi 562210afeecSPeter Ujfalusi twl6040->supplies[0].supply = "vio"; 563210afeecSPeter Ujfalusi twl6040->supplies[1].supply = "v2v1"; 564990810b0SJingoo Han ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, 565210afeecSPeter Ujfalusi twl6040->supplies); 566210afeecSPeter Ujfalusi if (ret != 0) { 567210afeecSPeter Ujfalusi dev_err(&client->dev, "Failed to get supplies: %d\n", ret); 568210afeecSPeter Ujfalusi goto regulator_get_err; 569210afeecSPeter Ujfalusi } 570210afeecSPeter Ujfalusi 571210afeecSPeter Ujfalusi ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies); 572210afeecSPeter Ujfalusi if (ret != 0) { 573210afeecSPeter Ujfalusi dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); 574990810b0SJingoo Han goto regulator_get_err; 575210afeecSPeter Ujfalusi } 576210afeecSPeter Ujfalusi 577210afeecSPeter Ujfalusi twl6040->dev = &client->dev; 578210afeecSPeter Ujfalusi twl6040->irq = client->irq; 579210afeecSPeter Ujfalusi 580210afeecSPeter Ujfalusi mutex_init(&twl6040->mutex); 581210afeecSPeter Ujfalusi init_completion(&twl6040->ready); 582210afeecSPeter Ujfalusi 583210afeecSPeter Ujfalusi twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); 584210afeecSPeter Ujfalusi 585210afeecSPeter Ujfalusi /* ERRATA: Automatic power-up is not possible in ES1.0 */ 586df04b624SPeter Ujfalusi if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) 587210afeecSPeter Ujfalusi twl6040->audpwron = of_get_named_gpio(node, 588210afeecSPeter Ujfalusi "ti,audpwron-gpio", 0); 589df04b624SPeter Ujfalusi else 590210afeecSPeter Ujfalusi twl6040->audpwron = -EINVAL; 591210afeecSPeter Ujfalusi 592210afeecSPeter Ujfalusi if (gpio_is_valid(twl6040->audpwron)) { 593990810b0SJingoo Han ret = devm_gpio_request_one(&client->dev, twl6040->audpwron, 594990810b0SJingoo Han GPIOF_OUT_INIT_LOW, "audpwron"); 595210afeecSPeter Ujfalusi if (ret) 596210afeecSPeter Ujfalusi goto gpio_err; 597210afeecSPeter Ujfalusi } 598210afeecSPeter Ujfalusi 59937aefe9fSPeter Ujfalusi ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT, 60037aefe9fSPeter Ujfalusi 0, &twl6040_irq_chip,&twl6040->irq_data); 601210afeecSPeter Ujfalusi if (ret < 0) 602990810b0SJingoo Han goto gpio_err; 603210afeecSPeter Ujfalusi 604210afeecSPeter Ujfalusi twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data, 605210afeecSPeter Ujfalusi TWL6040_IRQ_READY); 606210afeecSPeter Ujfalusi twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data, 607210afeecSPeter Ujfalusi TWL6040_IRQ_TH); 608210afeecSPeter Ujfalusi 609990810b0SJingoo Han ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL, 610210afeecSPeter Ujfalusi twl6040_readyint_handler, IRQF_ONESHOT, 611210afeecSPeter Ujfalusi "twl6040_irq_ready", twl6040); 612210afeecSPeter Ujfalusi if (ret) { 613210afeecSPeter Ujfalusi dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret); 614210afeecSPeter Ujfalusi goto readyirq_err; 615210afeecSPeter Ujfalusi } 616210afeecSPeter Ujfalusi 617990810b0SJingoo Han ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL, 618210afeecSPeter Ujfalusi twl6040_thint_handler, IRQF_ONESHOT, 619210afeecSPeter Ujfalusi "twl6040_irq_th", twl6040); 620210afeecSPeter Ujfalusi if (ret) { 621210afeecSPeter Ujfalusi dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret); 622fc5ee96fSWei Yongjun goto readyirq_err; 623210afeecSPeter Ujfalusi } 624210afeecSPeter Ujfalusi 625210afeecSPeter Ujfalusi /* dual-access registers controlled by I2C only */ 626210afeecSPeter Ujfalusi twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); 627210afeecSPeter Ujfalusi 628210afeecSPeter Ujfalusi /* 629210afeecSPeter Ujfalusi * The main functionality of twl6040 to provide audio on OMAP4+ systems. 630210afeecSPeter Ujfalusi * We can add the ASoC codec child whenever this driver has been loaded. 631210afeecSPeter Ujfalusi */ 632210afeecSPeter Ujfalusi irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG); 633210afeecSPeter Ujfalusi cell = &twl6040->cells[children]; 634210afeecSPeter Ujfalusi cell->name = "twl6040-codec"; 635210afeecSPeter Ujfalusi twl6040_codec_rsrc[0].start = irq; 636210afeecSPeter Ujfalusi twl6040_codec_rsrc[0].end = irq; 637210afeecSPeter Ujfalusi cell->resources = twl6040_codec_rsrc; 638210afeecSPeter Ujfalusi cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); 639210afeecSPeter Ujfalusi children++; 640210afeecSPeter Ujfalusi 641df04b624SPeter Ujfalusi /* Vibra input driver support */ 642df04b624SPeter Ujfalusi if (twl6040_has_vibra(node)) { 643210afeecSPeter Ujfalusi irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB); 644210afeecSPeter Ujfalusi 645210afeecSPeter Ujfalusi cell = &twl6040->cells[children]; 646210afeecSPeter Ujfalusi cell->name = "twl6040-vibra"; 647210afeecSPeter Ujfalusi twl6040_vibra_rsrc[0].start = irq; 648210afeecSPeter Ujfalusi twl6040_vibra_rsrc[0].end = irq; 649210afeecSPeter Ujfalusi cell->resources = twl6040_vibra_rsrc; 650210afeecSPeter Ujfalusi cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc); 651210afeecSPeter Ujfalusi children++; 652210afeecSPeter Ujfalusi } 653210afeecSPeter Ujfalusi 654df04b624SPeter Ujfalusi /* GPO support */ 655210afeecSPeter Ujfalusi cell = &twl6040->cells[children]; 656210afeecSPeter Ujfalusi cell->name = "twl6040-gpo"; 657210afeecSPeter Ujfalusi children++; 658210afeecSPeter Ujfalusi 659210afeecSPeter Ujfalusi ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, 660210afeecSPeter Ujfalusi NULL, 0, NULL); 661210afeecSPeter Ujfalusi if (ret) 662fc5ee96fSWei Yongjun goto readyirq_err; 663210afeecSPeter Ujfalusi 664210afeecSPeter Ujfalusi return 0; 665210afeecSPeter Ujfalusi 666210afeecSPeter Ujfalusi readyirq_err: 667210afeecSPeter Ujfalusi regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); 668210afeecSPeter Ujfalusi gpio_err: 669210afeecSPeter Ujfalusi regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); 670210afeecSPeter Ujfalusi regulator_get_err: 671210afeecSPeter Ujfalusi i2c_set_clientdata(client, NULL); 672ecc8fa1cSPeter Ujfalusi 673210afeecSPeter Ujfalusi return ret; 674210afeecSPeter Ujfalusi } 675210afeecSPeter Ujfalusi 676612b95cdSGreg Kroah-Hartman static int twl6040_remove(struct i2c_client *client) 677210afeecSPeter Ujfalusi { 678210afeecSPeter Ujfalusi struct twl6040 *twl6040 = i2c_get_clientdata(client); 679210afeecSPeter Ujfalusi 680210afeecSPeter Ujfalusi if (twl6040->power_count) 681210afeecSPeter Ujfalusi twl6040_power(twl6040, 0); 682210afeecSPeter Ujfalusi 683210afeecSPeter Ujfalusi regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); 684210afeecSPeter Ujfalusi 685210afeecSPeter Ujfalusi mfd_remove_devices(&client->dev); 686210afeecSPeter Ujfalusi i2c_set_clientdata(client, NULL); 687210afeecSPeter Ujfalusi 688210afeecSPeter Ujfalusi regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); 689210afeecSPeter Ujfalusi 690210afeecSPeter Ujfalusi return 0; 691210afeecSPeter Ujfalusi } 692210afeecSPeter Ujfalusi 693210afeecSPeter Ujfalusi static const struct i2c_device_id twl6040_i2c_id[] = { 694210afeecSPeter Ujfalusi { "twl6040", 0, }, 695210afeecSPeter Ujfalusi { "twl6041", 0, }, 696210afeecSPeter Ujfalusi { }, 697210afeecSPeter Ujfalusi }; 698210afeecSPeter Ujfalusi MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id); 699210afeecSPeter Ujfalusi 700210afeecSPeter Ujfalusi static struct i2c_driver twl6040_driver = { 701210afeecSPeter Ujfalusi .driver = { 702210afeecSPeter Ujfalusi .name = "twl6040", 703210afeecSPeter Ujfalusi .owner = THIS_MODULE, 704210afeecSPeter Ujfalusi }, 705210afeecSPeter Ujfalusi .probe = twl6040_probe, 706612b95cdSGreg Kroah-Hartman .remove = twl6040_remove, 707210afeecSPeter Ujfalusi .id_table = twl6040_i2c_id, 708210afeecSPeter Ujfalusi }; 709210afeecSPeter Ujfalusi 710210afeecSPeter Ujfalusi module_i2c_driver(twl6040_driver); 711210afeecSPeter Ujfalusi 712210afeecSPeter Ujfalusi MODULE_DESCRIPTION("TWL6040 MFD"); 713210afeecSPeter Ujfalusi MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); 714210afeecSPeter Ujfalusi MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>"); 715210afeecSPeter Ujfalusi MODULE_LICENSE("GPL"); 716210afeecSPeter Ujfalusi MODULE_ALIAS("platform:twl6040"); 717