]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Aug 2011 00:31:44 +0000 (14:31 -1000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Aug 2011 00:31:44 +0000 (14:31 -1000)
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (46 commits)
  mfd: Fix mismatch in twl4030 mutex lock-unlock
  mfd: twl6030-pwm.c needs MODULE_LICENSE
  mfd: Fix the omap-usb-host clock API usage on usbhs_disable()
  mfd: Acknowledge WM8994 IRQs before reporting
  mfd: Acknowlege all WM831x IRQs before we handle them
  mfd: Avoid two assignments if failures happen in tps65910_i2c_probe
  regulator: Storing tps65912 error codes in u8
  mfd: Don't leak init_data in tps65910_i2c_probe
  regulator: aat2870: Add AAT2870 regulator driver
  backlight: Add AAT2870 backlight driver
  mfd: Add AAT2870 mfd driver
  mfd: Remove dead code from max8997-irq
  mfd: Move TPS55910 Kconfig option
  mfd: Fix missing stmpe kerneldoc
  mfd: Fix off-by-one value range checking for tps65912_i2c_write
  mfd: Add devices for WM831x clocking module
  mfd: Remove comp{1,2}_threshold sysfs entries in tps65911_comparator_remove
  mfd: Don't ask about the TPS65912 core driver in Kconfig
  mfd: Fix off by one in WM831x IRQ code
  mfd: Add tps65921 support from twl-core
  ...

44 files changed:
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-tps65912.c [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/aat2870-core.c [new file with mode: 0644]
drivers/mfd/ab3550-core.c
drivers/mfd/ab8500-core.c
drivers/mfd/ab8500-debugfs.c
drivers/mfd/jz4740-adc.c
drivers/mfd/lpc_sch.c
drivers/mfd/max8997-irq.c
drivers/mfd/omap-usb-host.c
drivers/mfd/stmpe.c
drivers/mfd/stmpe.h
drivers/mfd/tps65910.c
drivers/mfd/tps65911-comparator.c
drivers/mfd/tps65912-core.c [new file with mode: 0644]
drivers/mfd/tps65912-i2c.c [new file with mode: 0644]
drivers/mfd/tps65912-irq.c [new file with mode: 0644]
drivers/mfd/tps65912-spi.c [new file with mode: 0644]
drivers/mfd/twl-core.c
drivers/mfd/twl4030-madc.c
drivers/mfd/twl6030-pwm.c
drivers/mfd/wm831x-auxadc.c [new file with mode: 0644]
drivers/mfd/wm831x-core.c
drivers/mfd/wm831x-irq.c
drivers/mfd/wm8350-irq.c
drivers/mfd/wm8994-core.c
drivers/mfd/wm8994-irq.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/aat2870-regulator.c [new file with mode: 0644]
drivers/regulator/tps65912-regulator.c [new file with mode: 0644]
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/aat2870_bl.c [new file with mode: 0644]
include/linux/mfd/aat2870.h [new file with mode: 0644]
include/linux/mfd/ab8500.h
include/linux/mfd/stmpe.h
include/linux/mfd/tps65910.h
include/linux/mfd/tps65912.h [new file with mode: 0644]
include/linux/mfd/wm831x/core.h
include/linux/mfd/wm831x/pdata.h

index 363498697c2c15d2f7fa4fec367ff75a1bfa090a..6778f56a4c64ac2fe03d228f405ae107b3fdf5ab 100644 (file)
@@ -280,6 +280,12 @@ config GPIO_TC3589X
          This enables support for the GPIOs found on the TC3589X
          I/O Expander.
 
+config GPIO_TPS65912
+       tristate "TI TPS65912 GPIO"
+       depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
+       help
+         This driver supports TPS65912 gpio chip
+
 config GPIO_TWL4030
        tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
        depends on TWL4030_CORE
index 7207112513913a457b8b9b8e45c5ede7ae0453b9..4b81d4e1e709d67aea78ff31ae4b71c22f385892 100644 (file)
@@ -48,6 +48,7 @@ obj-$(CONFIG_GPIO_TC3589X)    += gpio-tc3589x.o
 obj-$(CONFIG_ARCH_TEGRA)       += gpio-tegra.o
 obj-$(CONFIG_GPIO_TIMBERDALE)  += gpio-timberdale.o
 obj-$(CONFIG_GPIO_TPS65910)    += gpio-tps65910.o
+obj-$(CONFIG_GPIO_TPS65912)    += gpio-tps65912.o
 obj-$(CONFIG_GPIO_TWL4030)     += gpio-twl4030.o
 obj-$(CONFIG_MACH_U300)                += gpio-u300.o
 obj-$(CONFIG_GPIO_UCB1400)     += gpio-ucb1400.o
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
new file mode 100644 (file)
index 0000000..79e66c0
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ * This driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/mfd/tps65912.h>
+
+struct tps65912_gpio_data {
+       struct tps65912 *tps65912;
+       struct gpio_chip gpio_chip;
+};
+
+static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+       int val;
+
+       val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
+
+       if (val & GPIO_STS_MASK)
+               return 1;
+
+       return 0;
+}
+
+static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
+                             int value)
+{
+       struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+       if (value)
+               tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+                                                       GPIO_SET_MASK);
+       else
+               tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+                                                               GPIO_SET_MASK);
+}
+
+static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset,
+                               int value)
+{
+       struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+       /* Set the initial value */
+       tps65912_gpio_set(gc, offset, value);
+
+       return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+                                                               GPIO_CFG_MASK);
+}
+
+static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+       return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+                                                               GPIO_CFG_MASK);
+
+}
+
+static struct gpio_chip template_chip = {
+       .label                  = "tps65912",
+       .owner                  = THIS_MODULE,
+       .direction_input        = tps65912_gpio_input,
+       .direction_output       = tps65912_gpio_output,
+       .get                    = tps65912_gpio_get,
+       .set                    = tps65912_gpio_set,
+       .can_sleep              = 1,
+       .ngpio                  = 5,
+       .base                   = -1,
+};
+
+static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
+{
+       struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
+       struct tps65912_board *pdata = tps65912->dev->platform_data;
+       struct tps65912_gpio_data *tps65912_gpio;
+       int ret;
+
+       tps65912_gpio = kzalloc(sizeof(*tps65912_gpio), GFP_KERNEL);
+       if (tps65912_gpio == NULL)
+               return -ENOMEM;
+
+       tps65912_gpio->tps65912 = tps65912;
+       tps65912_gpio->gpio_chip = template_chip;
+       tps65912_gpio->gpio_chip.dev = &pdev->dev;
+       if (pdata && pdata->gpio_base)
+               tps65912_gpio->gpio_chip.base = pdata->gpio_base;
+
+       ret = gpiochip_add(&tps65912_gpio->gpio_chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, tps65912_gpio);
+
+       return ret;
+
+err:
+       kfree(tps65912_gpio);
+       return ret;
+}
+
+static int __devexit tps65912_gpio_remove(struct platform_device *pdev)
+{
+       struct tps65912_gpio_data  *tps65912_gpio = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = gpiochip_remove(&tps65912_gpio->gpio_chip);
+       if (ret == 0)
+               kfree(tps65912_gpio);
+
+       return ret;
+}
+
+static struct platform_driver tps65912_gpio_driver = {
+       .driver = {
+               .name = "tps65912-gpio",
+               .owner = THIS_MODULE,
+       },
+       .probe = tps65912_gpio_probe,
+       .remove = __devexit_p(tps65912_gpio_remove),
+};
+
+static int __init tps65912_gpio_init(void)
+{
+       return platform_driver_register(&tps65912_gpio_driver);
+}
+subsys_initcall(tps65912_gpio_init);
+
+static void __exit tps65912_gpio_exit(void)
+{
+       platform_driver_unregister(&tps65912_gpio_driver);
+}
+module_exit(tps65912_gpio_exit);
+
+MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65912-gpio");
index 37b83eb6d703fd782d9f24f9b4763b9a3be683c8..21574bdf485f3f422ca354e1bedc790a511de148 100644 (file)
@@ -171,6 +171,37 @@ config MFD_TPS6586X
          This driver can also be built as a module.  If so, the module
          will be called tps6586x.
 
+config MFD_TPS65910
+       bool "TPS65910 Power Management chip"
+       depends on I2C=y && GPIOLIB
+       select MFD_CORE
+       select GPIO_TPS65910
+       help
+         if you say yes here you get support for the TPS65910 series of
+         Power Management chips.
+
+config MFD_TPS65912
+       bool
+       depends on GPIOLIB
+
+config MFD_TPS65912_I2C
+       bool "TPS95612 Power Management chip with I2C"
+       select MFD_CORE
+       select MFD_TPS65912
+       depends on I2C=y && GPIOLIB
+       help
+         If you say yes here you get support for the TPS65912 series of
+         PM chips with I2C interface.
+
+config MFD_TPS65912_SPI
+       bool "TPS65912 Power Management chip with SPI"
+       select MFD_CORE
+       select MFD_TPS65912
+       depends on SPI_MASTER && GPIOLIB
+       help
+         If you say yes here you get support for the TPS65912 series of
+         PM chips with SPI interface.
+
 config MENELAUS
        bool "Texas Instruments TWL92330/Menelaus PM chip"
        depends on I2C=y && ARCH_OMAP2
@@ -662,8 +693,9 @@ config MFD_JANZ_CMODIO
          CAN and GPIO controllers.
 
 config MFD_JZ4740_ADC
-       tristate "Support for the JZ4740 SoC ADC core"
+       bool "Support for the JZ4740 SoC ADC core"
        select MFD_CORE
+       select GENERIC_IRQ_CHIP
        depends on MACH_JZ4740
        help
          Say yes here if you want support for the ADC unit in the JZ4740 SoC.
@@ -725,18 +757,19 @@ config MFD_PM8XXX_IRQ
          This is required to use certain other PM 8xxx features, such as GPIO
          and MPP.
 
-config MFD_TPS65910
-       bool "TPS65910 Power Management chip"
-       depends on I2C=y && GPIOLIB
-       select MFD_CORE
-       select GPIO_TPS65910
-       help
-         if you say yes here you get support for the TPS65910 series of
-         Power Management chips.
-
 config TPS65911_COMPARATOR
        tristate
 
+config MFD_AAT2870_CORE
+       bool "Support for the AnalogicTech AAT2870"
+       select MFD_CORE
+       depends on I2C=y && GPIOLIB
+       help
+         If you say yes here you get support for the AAT2870.
+         This driver provides common support for accessing the device,
+         additional drivers must be enabled in order to use the
+         functionality of the device.
+
 endif # MFD_SUPPORT
 
 menu "Multimedia Capabilities Port drivers"
index 22a280fcb7059436966400a4a71caaecfd390ece..c58020303d184922dce43b36845413e3d798ff8d 100644 (file)
@@ -23,6 +23,7 @@ obj-$(CONFIG_MFD_TC6393XB)    += tc6393xb.o tmio_core.o
 
 obj-$(CONFIG_MFD_WM8400)       += wm8400-core.o
 wm831x-objs                    := wm831x-core.o wm831x-irq.o wm831x-otp.o
+wm831x-objs                    += wm831x-auxadc.o
 obj-$(CONFIG_MFD_WM831X)       += wm831x.o
 obj-$(CONFIG_MFD_WM831X_I2C)   += wm831x-i2c.o
 obj-$(CONFIG_MFD_WM831X_SPI)   += wm831x-spi.o
@@ -35,6 +36,11 @@ obj-$(CONFIG_MFD_WM8994)     += wm8994-core.o wm8994-irq.o
 obj-$(CONFIG_TPS6105X)         += tps6105x.o
 obj-$(CONFIG_TPS65010)         += tps65010.o
 obj-$(CONFIG_TPS6507X)         += tps6507x.o
+obj-$(CONFIG_MFD_TPS65910)     += tps65910.o tps65910-irq.o
+tps65912-objs                   := tps65912-core.o tps65912-irq.o
+obj-$(CONFIG_MFD_TPS65912)     += tps65912.o
+obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
+obj-$(CONFIG_MFD_TPS65912_SPI)  += tps65912-spi.o
 obj-$(CONFIG_MENELAUS)         += menelaus.o
 
 obj-$(CONFIG_TWL4030_CORE)     += twl-core.o twl4030-irq.o twl6030-irq.o
@@ -94,5 +100,5 @@ obj-$(CONFIG_MFD_CS5535)     += cs5535-mfd.o
 obj-$(CONFIG_MFD_OMAP_USB_HOST)        += omap-usb-host.o
 obj-$(CONFIG_MFD_PM8921_CORE)  += pm8921-core.o
 obj-$(CONFIG_MFD_PM8XXX_IRQ)   += pm8xxx-irq.o
-obj-$(CONFIG_MFD_TPS65910)     += tps65910.o tps65910-irq.o
 obj-$(CONFIG_TPS65911_COMPARATOR)      += tps65911-comparator.o
+obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
new file mode 100644 (file)
index 0000000..345dc65
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ * linux/drivers/mfd/aat2870-core.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/aat2870.h>
+#include <linux/regulator/machine.h>
+
+static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = {
+       /* readable, writeable, value */
+       { 0, 1, 0x00 }, /* 0x00 AAT2870_BL_CH_EN */
+       { 0, 1, 0x16 }, /* 0x01 AAT2870_BLM */
+       { 0, 1, 0x16 }, /* 0x02 AAT2870_BLS */
+       { 0, 1, 0x56 }, /* 0x03 AAT2870_BL1 */
+       { 0, 1, 0x56 }, /* 0x04 AAT2870_BL2 */
+       { 0, 1, 0x56 }, /* 0x05 AAT2870_BL3 */
+       { 0, 1, 0x56 }, /* 0x06 AAT2870_BL4 */
+       { 0, 1, 0x56 }, /* 0x07 AAT2870_BL5 */
+       { 0, 1, 0x56 }, /* 0x08 AAT2870_BL6 */
+       { 0, 1, 0x56 }, /* 0x09 AAT2870_BL7 */
+       { 0, 1, 0x56 }, /* 0x0A AAT2870_BL8 */
+       { 0, 1, 0x00 }, /* 0x0B AAT2870_FLR */
+       { 0, 1, 0x03 }, /* 0x0C AAT2870_FM */
+       { 0, 1, 0x03 }, /* 0x0D AAT2870_FS */
+       { 0, 1, 0x10 }, /* 0x0E AAT2870_ALS_CFG0 */
+       { 0, 1, 0x06 }, /* 0x0F AAT2870_ALS_CFG1 */
+       { 0, 1, 0x00 }, /* 0x10 AAT2870_ALS_CFG2 */
+       { 1, 0, 0x00 }, /* 0x11 AAT2870_AMB */
+       { 0, 1, 0x00 }, /* 0x12 AAT2870_ALS0 */
+       { 0, 1, 0x00 }, /* 0x13 AAT2870_ALS1 */
+       { 0, 1, 0x00 }, /* 0x14 AAT2870_ALS2 */
+       { 0, 1, 0x00 }, /* 0x15 AAT2870_ALS3 */
+       { 0, 1, 0x00 }, /* 0x16 AAT2870_ALS4 */
+       { 0, 1, 0x00 }, /* 0x17 AAT2870_ALS5 */
+       { 0, 1, 0x00 }, /* 0x18 AAT2870_ALS6 */
+       { 0, 1, 0x00 }, /* 0x19 AAT2870_ALS7 */
+       { 0, 1, 0x00 }, /* 0x1A AAT2870_ALS8 */
+       { 0, 1, 0x00 }, /* 0x1B AAT2870_ALS9 */
+       { 0, 1, 0x00 }, /* 0x1C AAT2870_ALSA */
+       { 0, 1, 0x00 }, /* 0x1D AAT2870_ALSB */
+       { 0, 1, 0x00 }, /* 0x1E AAT2870_ALSC */
+       { 0, 1, 0x00 }, /* 0x1F AAT2870_ALSD */
+       { 0, 1, 0x00 }, /* 0x20 AAT2870_ALSE */
+       { 0, 1, 0x00 }, /* 0x21 AAT2870_ALSF */
+       { 0, 1, 0x00 }, /* 0x22 AAT2870_SUB_SET */
+       { 0, 1, 0x00 }, /* 0x23 AAT2870_SUB_CTRL */
+       { 0, 1, 0x00 }, /* 0x24 AAT2870_LDO_AB */
+       { 0, 1, 0x00 }, /* 0x25 AAT2870_LDO_CD */
+       { 0, 1, 0x00 }, /* 0x26 AAT2870_LDO_EN */
+};
+
+static struct mfd_cell aat2870_devs[] = {
+       {
+               .name = "aat2870-backlight",
+               .id = AAT2870_ID_BL,
+               .pdata_size = sizeof(struct aat2870_bl_platform_data),
+       },
+       {
+               .name = "aat2870-regulator",
+               .id = AAT2870_ID_LDOA,
+               .pdata_size = sizeof(struct regulator_init_data),
+       },
+       {
+               .name = "aat2870-regulator",
+               .id = AAT2870_ID_LDOB,
+               .pdata_size = sizeof(struct regulator_init_data),
+       },
+       {
+               .name = "aat2870-regulator",
+               .id = AAT2870_ID_LDOC,
+               .pdata_size = sizeof(struct regulator_init_data),
+       },
+       {
+               .name = "aat2870-regulator",
+               .id = AAT2870_ID_LDOD,
+               .pdata_size = sizeof(struct regulator_init_data),
+       },
+};
+
+static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+       int ret;
+
+       if (addr >= AAT2870_REG_NUM) {
+               dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+               return -EINVAL;
+       }
+
+       if (!aat2870->reg_cache[addr].readable) {
+               *val = aat2870->reg_cache[addr].value;
+               goto out;
+       }
+
+       ret = i2c_master_send(aat2870->client, &addr, 1);
+       if (ret < 0)
+               return ret;
+       if (ret != 1)
+               return -EIO;
+
+       ret = i2c_master_recv(aat2870->client, val, 1);
+       if (ret < 0)
+               return ret;
+       if (ret != 1)
+               return -EIO;
+
+out:
+       dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val);
+       return 0;
+}
+
+static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+       u8 msg[2];
+       int ret;
+
+       if (addr >= AAT2870_REG_NUM) {
+               dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+               return -EINVAL;
+       }
+
+       if (!aat2870->reg_cache[addr].writeable) {
+               dev_err(aat2870->dev, "Address 0x%02x is not writeable\n",
+                       addr);
+               return -EINVAL;
+       }
+
+       msg[0] = addr;
+       msg[1] = val;
+       ret = i2c_master_send(aat2870->client, msg, 2);
+       if (ret < 0)
+               return ret;
+       if (ret != 2)
+               return -EIO;
+
+       aat2870->reg_cache[addr].value = val;
+
+       dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val);
+       return 0;
+}
+
+static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+       int ret;
+
+       mutex_lock(&aat2870->io_lock);
+       ret = __aat2870_read(aat2870, addr, val);
+       mutex_unlock(&aat2870->io_lock);
+
+       return ret;
+}
+
+static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+       int ret;
+
+       mutex_lock(&aat2870->io_lock);
+       ret = __aat2870_write(aat2870, addr, val);
+       mutex_unlock(&aat2870->io_lock);
+
+       return ret;
+}
+
+static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask,
+                         u8 val)
+{
+       int change;
+       u8 old_val, new_val;
+       int ret;
+
+       mutex_lock(&aat2870->io_lock);
+
+       ret = __aat2870_read(aat2870, addr, &old_val);
+       if (ret)
+               goto out_unlock;
+
+       new_val = (old_val & ~mask) | (val & mask);
+       change = old_val != new_val;
+       if (change)
+               ret = __aat2870_write(aat2870, addr, new_val);
+
+out_unlock:
+       mutex_unlock(&aat2870->io_lock);
+
+       return ret;
+}
+
+static inline void aat2870_enable(struct aat2870_data *aat2870)
+{
+       if (aat2870->en_pin >= 0)
+               gpio_set_value(aat2870->en_pin, 1);
+
+       aat2870->is_enable = 1;
+}
+
+static inline void aat2870_disable(struct aat2870_data *aat2870)
+{
+       if (aat2870->en_pin >= 0)
+               gpio_set_value(aat2870->en_pin, 0);
+
+       aat2870->is_enable = 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)
+{
+       u8 addr, val;
+       ssize_t count = 0;
+       int ret;
+
+       count += sprintf(buf, "aat2870 registers\n");
+       for (addr = 0; addr < AAT2870_REG_NUM; addr++) {
+               count += sprintf(buf + count, "0x%02x: ", addr);
+               if (count >= PAGE_SIZE - 1)
+                       break;
+
+               ret = aat2870->read(aat2870, addr, &val);
+               if (ret == 0)
+                       count += snprintf(buf + count, PAGE_SIZE - count,
+                                         "0x%02x", val);
+               else
+                       count += snprintf(buf + count, PAGE_SIZE - count,
+                                         "<read fail: %d>", ret);
+
+               if (count >= PAGE_SIZE - 1)
+                       break;
+
+               count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+               if (count >= PAGE_SIZE - 1)
+                       break;
+       }
+
+       /* Truncate count; min() would cause a warning */
+       if (count >= PAGE_SIZE)
+               count = PAGE_SIZE - 1;
+
+       return count;
+}
+
+static int aat2870_reg_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+
+       return 0;
+}
+
+static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct aat2870_data *aat2870 = file->private_data;
+       char *buf;
+       ssize_t ret;
+
+       buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = aat2870_dump_reg(aat2870, buf);
+       if (ret >= 0)
+               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+       kfree(buf);
+
+       return ret;
+}
+
+static ssize_t aat2870_reg_write_file(struct file *file,
+                                     const char __user *user_buf, size_t count,
+                                     loff_t *ppos)
+{
+       struct aat2870_data *aat2870 = file->private_data;
+       char buf[32];
+       int buf_size;
+       char *start = buf;
+       unsigned long addr, val;
+       int ret;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size)) {
+               dev_err(aat2870->dev, "Failed to copy from user\n");
+               return -EFAULT;
+       }
+       buf[buf_size] = 0;
+
+       while (*start == ' ')
+               start++;
+
+       addr = simple_strtoul(start, &start, 16);
+       if (addr >= AAT2870_REG_NUM) {
+               dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr);
+               return -EINVAL;
+       }
+
+       while (*start == ' ')
+               start++;
+
+       if (strict_strtoul(start, 16, &val))
+               return -EINVAL;
+
+       ret = aat2870->write(aat2870, (u8)addr, (u8)val);
+       if (ret)
+               return ret;
+
+       return buf_size;
+}
+
+static const struct file_operations aat2870_reg_fops = {
+       .open = aat2870_reg_open_file,
+       .read = aat2870_reg_read_file,
+       .write = aat2870_reg_write_file,
+};
+
+static void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+       aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);
+       if (!aat2870->dentry_root) {
+               dev_warn(aat2870->dev,
+                        "Failed to create debugfs root directory\n");
+               return;
+       }
+
+       aat2870->dentry_reg = debugfs_create_file("regs", 0644,
+                                                 aat2870->dentry_root,
+                                                 aat2870, &aat2870_reg_fops);
+       if (!aat2870->dentry_reg)
+               dev_warn(aat2870->dev,
+                        "Failed to create debugfs register file\n");
+}
+
+static void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+       debugfs_remove_recursive(aat2870->dentry_root);
+}
+#else
+static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+}
+
+static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int aat2870_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct aat2870_platform_data *pdata = client->dev.platform_data;
+       struct aat2870_data *aat2870;
+       int i, j;
+       int ret = 0;
+
+       aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL);
+       if (!aat2870) {
+               dev_err(&client->dev,
+                       "Failed to allocate memory for aat2870\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       aat2870->dev = &client->dev;
+       dev_set_drvdata(aat2870->dev, aat2870);
+
+       aat2870->client = client;
+       i2c_set_clientdata(client, aat2870);
+
+       aat2870->reg_cache = aat2870_regs;
+
+       if (pdata->en_pin < 0)
+               aat2870->en_pin = -1;
+       else
+               aat2870->en_pin = pdata->en_pin;
+
+       aat2870->init = pdata->init;
+       aat2870->uninit = pdata->uninit;
+       aat2870->read = aat2870_read;
+       aat2870->write = aat2870_write;
+       aat2870->update = aat2870_update;
+
+       mutex_init(&aat2870->io_lock);
+
+       if (aat2870->init)
+               aat2870->init(aat2870);
+
+       if (aat2870->en_pin >= 0) {
+               ret = gpio_request(aat2870->en_pin, "aat2870-en");
+               if (ret < 0) {
+                       dev_err(&client->dev,
+                               "Failed to request GPIO %d\n", aat2870->en_pin);
+                       goto out_kfree;
+               }
+               gpio_direction_output(aat2870->en_pin, 1);
+       }
+
+       aat2870_enable(aat2870);
+
+       for (i = 0; i < pdata->num_subdevs; i++) {
+               for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) {
+                       if ((pdata->subdevs[i].id == aat2870_devs[j].id) &&
+                                       !strcmp(pdata->subdevs[i].name,
+                                               aat2870_devs[j].name)) {
+                               aat2870_devs[j].platform_data =
+                                       pdata->subdevs[i].platform_data;
+                               break;
+                       }
+               }
+       }
+
+       ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
+                             ARRAY_SIZE(aat2870_devs), NULL, 0);
+       if (ret != 0) {
+               dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);
+               goto out_disable;
+       }
+
+       aat2870_init_debugfs(aat2870);
+
+       return 0;
+
+out_disable:
+       aat2870_disable(aat2870);
+       if (aat2870->en_pin >= 0)
+               gpio_free(aat2870->en_pin);
+out_kfree:
+       kfree(aat2870);
+out:
+       return ret;
+}
+
+static int aat2870_i2c_remove(struct i2c_client *client)
+{
+       struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+       aat2870_uninit_debugfs(aat2870);
+
+       mfd_remove_devices(aat2870->dev);
+       aat2870_disable(aat2870);
+       if (aat2870->en_pin >= 0)
+               gpio_free(aat2870->en_pin);
+       if (aat2870->uninit)
+               aat2870->uninit(aat2870);
+       kfree(aat2870);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+       struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+       aat2870_disable(aat2870);
+
+       return 0;
+}
+
+static int aat2870_i2c_resume(struct i2c_client *client)
+{
+       struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+       struct aat2870_register *reg = NULL;
+       int i;
+
+       aat2870_enable(aat2870);
+
+       /* restore registers */
+       for (i = 0; i < AAT2870_REG_NUM; i++) {
+               reg = &aat2870->reg_cache[i];
+               if (reg->writeable)
+                       aat2870->write(aat2870, i, reg->value);
+       }
+
+       return 0;
+}
+#else
+#define aat2870_i2c_suspend    NULL
+#define aat2870_i2c_resume     NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_device_id aat2870_i2c_id_table[] = {
+       { "aat2870", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table);
+
+static struct i2c_driver aat2870_i2c_driver = {
+       .driver = {
+               .name   = "aat2870",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = aat2870_i2c_probe,
+       .remove         = aat2870_i2c_remove,
+       .suspend        = aat2870_i2c_suspend,
+       .resume         = aat2870_i2c_resume,
+       .id_table       = aat2870_i2c_id_table,
+};
+
+static int __init aat2870_init(void)
+{
+       return i2c_add_driver(&aat2870_i2c_driver);
+}
+subsys_initcall(aat2870_init);
+
+static void __exit aat2870_exit(void)
+{
+       i2c_del_driver(&aat2870_i2c_driver);
+}
+module_exit(aat2870_exit);
+
+MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
index 3d7dce671b936a1affb6429c2469d56a436e8cf2..56ba1943c91d71a0c3d0ff8cba69d9c9c56a85a1 100644 (file)
@@ -879,20 +879,13 @@ static ssize_t ab3550_bank_write(struct file *file,
        size_t count, loff_t *ppos)
 {
        struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_bank;
        int err;
 
        /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf) - 1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_bank);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
        if (err)
-               return -EINVAL;
+               return err;
 
        if (user_bank >= AB3550_NUM_BANKS) {
                dev_err(&ab->i2c_client[0]->dev,
@@ -902,7 +895,7 @@ static ssize_t ab3550_bank_write(struct file *file,
 
        ab->debug_bank = user_bank;
 
-       return buf_size;
+       return count;
 }
 
 static int ab3550_address_print(struct seq_file *s, void *p)
@@ -923,27 +916,21 @@ static ssize_t ab3550_address_write(struct file *file,
        size_t count, loff_t *ppos)
 {
        struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_address;
        int err;
 
        /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf) - 1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_address);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_address);
        if (err)
-               return -EINVAL;
+               return err;
+
        if (user_address > 0xff) {
                dev_err(&ab->i2c_client[0]->dev,
                        "debugfs error input > 0xff\n");
                return -EINVAL;
        }
        ab->debug_address = user_address;
-       return buf_size;
+       return count;
 }
 
 static int ab3550_val_print(struct seq_file *s, void *p)
@@ -971,21 +958,15 @@ static ssize_t ab3550_val_write(struct file *file,
        size_t count, loff_t *ppos)
 {
        struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_val;
        int err;
        u8 regvalue;
 
        /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf)-1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_val);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_val);
        if (err)
-               return -EINVAL;
+               return err;
+
        if (user_val > 0xff) {
                dev_err(&ab->i2c_client[0]->dev,
                        "debugfs error input > 0xff\n");
@@ -1002,7 +983,7 @@ static ssize_t ab3550_val_write(struct file *file,
        if (err)
                return -EINVAL;
 
-       return buf_size;
+       return count;
 }
 
 static const struct file_operations ab3550_bank_fops = {
index fc0c1af1566e08d01b7351de9e1a00466d2c0614..387705e494b963a3b6fa9ded763cf2cc6d8f90a7 100644 (file)
@@ -363,7 +363,7 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
        }
 }
 
-static struct resource ab8500_gpio_resources[] = {
+static struct resource __devinitdata ab8500_gpio_resources[] = {
        {
                .name   = "GPIO_INT6",
                .start  = AB8500_INT_GPIO6R,
@@ -372,7 +372,7 @@ static struct resource ab8500_gpio_resources[] = {
        }
 };
 
-static struct resource ab8500_gpadc_resources[] = {
+static struct resource __devinitdata ab8500_gpadc_resources[] = {
        {
                .name   = "HW_CONV_END",
                .start  = AB8500_INT_GP_HW_ADC_CONV_END,
@@ -387,7 +387,7 @@ static struct resource ab8500_gpadc_resources[] = {
        },
 };
 
-static struct resource ab8500_rtc_resources[] = {
+static struct resource __devinitdata ab8500_rtc_resources[] = {
        {
                .name   = "60S",
                .start  = AB8500_INT_RTC_60S,
@@ -402,7 +402,7 @@ static struct resource ab8500_rtc_resources[] = {
        },
 };
 
-static struct resource ab8500_poweronkey_db_resources[] = {
+static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
        {
                .name   = "ONKEY_DBF",
                .start  = AB8500_INT_PON_KEY1DB_F,
@@ -417,19 +417,46 @@ static struct resource ab8500_poweronkey_db_resources[] = {
        },
 };
 
-static struct resource ab8500_bm_resources[] = {
+static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
        {
-               .name = "MAIN_EXT_CH_NOT_OK",
-               .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
-               .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
-               .flags = IORESOURCE_IRQ,
+              .name = "ACC_DETECT_1DB_F",
+              .start = AB8500_INT_ACC_DETECT_1DB_F,
+              .end = AB8500_INT_ACC_DETECT_1DB_F,
+              .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "BATT_OVV",
-               .start = AB8500_INT_BATT_OVV,
-               .end = AB8500_INT_BATT_OVV,
-               .flags = IORESOURCE_IRQ,
+              .name = "ACC_DETECT_1DB_R",
+              .start = AB8500_INT_ACC_DETECT_1DB_R,
+              .end = AB8500_INT_ACC_DETECT_1DB_R,
+              .flags = IORESOURCE_IRQ,
+       },
+       {
+              .name = "ACC_DETECT_21DB_F",
+              .start = AB8500_INT_ACC_DETECT_21DB_F,
+              .end = AB8500_INT_ACC_DETECT_21DB_F,
+              .flags = IORESOURCE_IRQ,
+       },
+       {
+              .name = "ACC_DETECT_21DB_R",
+              .start = AB8500_INT_ACC_DETECT_21DB_R,
+              .end = AB8500_INT_ACC_DETECT_21DB_R,
+              .flags = IORESOURCE_IRQ,
+       },
+       {
+              .name = "ACC_DETECT_22DB_F",
+              .start = AB8500_INT_ACC_DETECT_22DB_F,
+              .end = AB8500_INT_ACC_DETECT_22DB_F,
+              .flags = IORESOURCE_IRQ,
        },
+       {
+              .name = "ACC_DETECT_22DB_R",
+              .start = AB8500_INT_ACC_DETECT_22DB_R,
+              .end = AB8500_INT_ACC_DETECT_22DB_R,
+              .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct resource __devinitdata ab8500_charger_resources[] = {
        {
                .name = "MAIN_CH_UNPLUG_DET",
                .start = AB8500_INT_MAIN_CH_UNPLUG_DET,
@@ -442,12 +469,6 @@ static struct resource ab8500_bm_resources[] = {
                .end = AB8500_INT_MAIN_CH_PLUG_DET,
                .flags = IORESOURCE_IRQ,
        },
-       {
-               .name = "VBUS_DET_F",
-               .start = AB8500_INT_VBUS_DET_F,
-               .end = AB8500_INT_VBUS_DET_F,
-               .flags = IORESOURCE_IRQ,
-       },
        {
                .name = "VBUS_DET_R",
                .start = AB8500_INT_VBUS_DET_R,
@@ -455,15 +476,21 @@ static struct resource ab8500_bm_resources[] = {
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "BAT_CTRL_INDB",
-               .start = AB8500_INT_BAT_CTRL_INDB,
-               .end = AB8500_INT_BAT_CTRL_INDB,
+               .name = "VBUS_DET_F",
+               .start = AB8500_INT_VBUS_DET_F,
+               .end = AB8500_INT_VBUS_DET_F,
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "CH_WD_EXP",
-               .start = AB8500_INT_CH_WD_EXP,
-               .end = AB8500_INT_CH_WD_EXP,
+               .name = "USB_LINK_STATUS",
+               .start = AB8500_INT_USB_LINK_STATUS,
+               .end = AB8500_INT_USB_LINK_STATUS,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "USB_CHARGE_DET_DONE",
+               .start = AB8500_INT_USB_CHG_DET_DONE,
+               .end = AB8500_INT_USB_CHG_DET_DONE,
                .flags = IORESOURCE_IRQ,
        },
        {
@@ -473,21 +500,60 @@ static struct resource ab8500_bm_resources[] = {
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "NCONV_ACCU",
-               .start = AB8500_INT_CCN_CONV_ACC,
-               .end = AB8500_INT_CCN_CONV_ACC,
+               .name = "USB_CH_TH_PROT_R",
+               .start = AB8500_INT_USB_CH_TH_PROT_R,
+               .end = AB8500_INT_USB_CH_TH_PROT_R,
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "LOW_BAT_F",
-               .start = AB8500_INT_LOW_BAT_F,
-               .end = AB8500_INT_LOW_BAT_F,
+               .name = "USB_CH_TH_PROT_F",
+               .start = AB8500_INT_USB_CH_TH_PROT_F,
+               .end = AB8500_INT_USB_CH_TH_PROT_F,
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "LOW_BAT_R",
-               .start = AB8500_INT_LOW_BAT_R,
-               .end = AB8500_INT_LOW_BAT_R,
+               .name = "MAIN_EXT_CH_NOT_OK",
+               .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+               .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "MAIN_CH_TH_PROT_R",
+               .start = AB8500_INT_MAIN_CH_TH_PROT_R,
+               .end = AB8500_INT_MAIN_CH_TH_PROT_R,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "MAIN_CH_TH_PROT_F",
+               .start = AB8500_INT_MAIN_CH_TH_PROT_F,
+               .end = AB8500_INT_MAIN_CH_TH_PROT_F,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "USB_CHARGER_NOT_OKR",
+               .start = AB8500_INT_USB_CHARGER_NOT_OK,
+               .end = AB8500_INT_USB_CHARGER_NOT_OK,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "USB_CHARGER_NOT_OKF",
+               .start = AB8500_INT_USB_CHARGER_NOT_OKF,
+               .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "CH_WD_EXP",
+               .start = AB8500_INT_CH_WD_EXP,
+               .end = AB8500_INT_CH_WD_EXP,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct resource __devinitdata ab8500_btemp_resources[] = {
+       {
+               .name = "BAT_CTRL_INDB",
+               .start = AB8500_INT_BAT_CTRL_INDB,
+               .end = AB8500_INT_BAT_CTRL_INDB,
                .flags = IORESOURCE_IRQ,
        },
        {
@@ -503,38 +569,55 @@ static struct resource ab8500_bm_resources[] = {
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "USB_CHARGER_NOT_OKR",
-               .start = AB8500_INT_USB_CHARGER_NOT_OK,
-               .end = AB8500_INT_USB_CHARGER_NOT_OK,
+               .name = "BTEMP_LOW_MEDIUM",
+               .start = AB8500_INT_BTEMP_LOW_MEDIUM,
+               .end = AB8500_INT_BTEMP_LOW_MEDIUM,
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "USB_CHARGE_DET_DONE",
-               .start = AB8500_INT_USB_CHG_DET_DONE,
-               .end = AB8500_INT_USB_CHG_DET_DONE,
+               .name = "BTEMP_MEDIUM_HIGH",
+               .start = AB8500_INT_BTEMP_MEDIUM_HIGH,
+               .end = AB8500_INT_BTEMP_MEDIUM_HIGH,
                .flags = IORESOURCE_IRQ,
        },
+};
+
+static struct resource __devinitdata ab8500_fg_resources[] = {
        {
-               .name = "USB_CH_TH_PROT_R",
-               .start = AB8500_INT_USB_CH_TH_PROT_R,
-               .end = AB8500_INT_USB_CH_TH_PROT_R,
+               .name = "NCONV_ACCU",
+               .start = AB8500_INT_CCN_CONV_ACC,
+               .end = AB8500_INT_CCN_CONV_ACC,
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "MAIN_CH_TH_PROT_R",
-               .start = AB8500_INT_MAIN_CH_TH_PROT_R,
-               .end = AB8500_INT_MAIN_CH_TH_PROT_R,
+               .name = "BATT_OVV",
+               .start = AB8500_INT_BATT_OVV,
+               .end = AB8500_INT_BATT_OVV,
                .flags = IORESOURCE_IRQ,
        },
        {
-               .name = "USB_CHARGER_NOT_OKF",
-               .start = AB8500_INT_USB_CHARGER_NOT_OKF,
-               .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+               .name = "LOW_BAT_F",
+               .start = AB8500_INT_LOW_BAT_F,
+               .end = AB8500_INT_LOW_BAT_F,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "LOW_BAT_R",
+               .start = AB8500_INT_LOW_BAT_R,
+               .end = AB8500_INT_LOW_BAT_R,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "CC_INT_CALIB",
+               .start = AB8500_INT_CC_INT_CALIB,
+               .end = AB8500_INT_CC_INT_CALIB,
                .flags = IORESOURCE_IRQ,
        },
 };
 
-static struct resource ab8500_debug_resources[] = {
+static struct resource __devinitdata ab8500_chargalg_resources[] = {};
+
+static struct resource __devinitdata ab8500_debug_resources[] = {
        {
                .name   = "IRQ_FIRST",
                .start  = AB8500_INT_MAIN_EXT_CH_NOT_OK,
@@ -549,7 +632,7 @@ static struct resource ab8500_debug_resources[] = {
        },
 };
 
-static struct resource ab8500_usb_resources[] = {
+static struct resource __devinitdata ab8500_usb_resources[] = {
        {
                .name = "ID_WAKEUP_R",
                .start = AB8500_INT_ID_WAKEUP_R,
@@ -580,9 +663,21 @@ static struct resource ab8500_usb_resources[] = {
                .end = AB8500_INT_USB_LINK_STATUS,
                .flags = IORESOURCE_IRQ,
        },
+       {
+               .name = "USB_ADP_PROBE_PLUG",
+               .start = AB8500_INT_ADP_PROBE_PLUG,
+               .end = AB8500_INT_ADP_PROBE_PLUG,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "USB_ADP_PROBE_UNPLUG",
+               .start = AB8500_INT_ADP_PROBE_UNPLUG,
+               .end = AB8500_INT_ADP_PROBE_UNPLUG,
+               .flags = IORESOURCE_IRQ,
+       },
 };
 
-static struct resource ab8500_temp_resources[] = {
+static struct resource __devinitdata ab8500_temp_resources[] = {
        {
                .name  = "AB8500_TEMP_WARM",
                .start = AB8500_INT_TEMP_WARM,
@@ -591,7 +686,7 @@ static struct resource ab8500_temp_resources[] = {
        },
 };
 
-static struct mfd_cell ab8500_devs[] = {
+static struct mfd_cell __devinitdata ab8500_devs[] = {
 #ifdef CONFIG_DEBUG_FS
        {
                .name = "ab8500-debug",
@@ -621,11 +716,33 @@ static struct mfd_cell ab8500_devs[] = {
                .resources = ab8500_rtc_resources,
        },
        {
-               .name = "ab8500-bm",
-               .num_resources = ARRAY_SIZE(ab8500_bm_resources),
-               .resources = ab8500_bm_resources,
+               .name = "ab8500-charger",
+               .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+               .resources = ab8500_charger_resources,
+       },
+       {
+               .name = "ab8500-btemp",
+               .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+               .resources = ab8500_btemp_resources,
+       },
+       {
+               .name = "ab8500-fg",
+               .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+               .resources = ab8500_fg_resources,
+       },
+       {
+               .name = "ab8500-chargalg",
+               .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+               .resources = ab8500_chargalg_resources,
+       },
+       {
+               .name = "ab8500-acc-det",
+               .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+               .resources = ab8500_av_acc_detect_resources,
+       },
+       {
+               .name = "ab8500-codec",
        },
-       { .name = "ab8500-codec", },
        {
                .name = "ab8500-usb",
                .num_resources = ARRAY_SIZE(ab8500_usb_resources),
index 64748e42ac039c101431782547194834cd4ca69b..64bdeeb1c11a4c18f236a764e2e863cd4c1920c4 100644 (file)
@@ -419,20 +419,13 @@ static ssize_t ab8500_bank_write(struct file *file,
        size_t count, loff_t *ppos)
 {
        struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_bank;
        int err;
 
        /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf) - 1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_bank);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
        if (err)
-               return -EINVAL;
+               return err;
 
        if (user_bank >= AB8500_NUM_BANKS) {
                dev_err(dev, "debugfs error input > number of banks\n");
@@ -441,7 +434,7 @@ static ssize_t ab8500_bank_write(struct file *file,
 
        debug_bank = user_bank;
 
-       return buf_size;
+       return count;
 }
 
 static int ab8500_address_print(struct seq_file *s, void *p)
@@ -459,26 +452,20 @@ static ssize_t ab8500_address_write(struct file *file,
        size_t count, loff_t *ppos)
 {
        struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_address;
        int err;
 
        /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf) - 1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_address);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_address);
        if (err)
-               return -EINVAL;
+               return err;
+
        if (user_address > 0xff) {
                dev_err(dev, "debugfs error input > 0xff\n");
                return -EINVAL;
        }
        debug_address = user_address;
-       return buf_size;
+       return count;
 }
 
 static int ab8500_val_print(struct seq_file *s, void *p)
@@ -509,20 +496,14 @@ static ssize_t ab8500_val_write(struct file *file,
        size_t count, loff_t *ppos)
 {
        struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_val;
        int err;
 
        /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf)-1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_val);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_val);
        if (err)
-               return -EINVAL;
+               return err;
+
        if (user_val > 0xff) {
                dev_err(dev, "debugfs error input > 0xff\n");
                return -EINVAL;
@@ -534,7 +515,7 @@ static ssize_t ab8500_val_write(struct file *file,
                return -EINVAL;
        }
 
-       return buf_size;
+       return count;
 }
 
 static const struct file_operations ab8500_bank_fops = {
index a0bd0cf05af3912b1c0c7d490419a3e6eab9d102..21131c7b0f1e7395573d174efb7e73f889b66a45 100644 (file)
@@ -56,7 +56,7 @@ struct jz4740_adc {
        void __iomem *base;
 
        int irq;
-       int irq_base;
+       struct irq_chip_generic *gc;
 
        struct clk *clk;
        atomic_t clk_ref;
@@ -64,63 +64,17 @@ struct jz4740_adc {
        spinlock_t lock;
 };
 
-static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq,
-       bool masked)
-{
-       unsigned long flags;
-       uint8_t val;
-
-       irq -= adc->irq_base;
-
-       spin_lock_irqsave(&adc->lock, flags);
-
-       val = readb(adc->base + JZ_REG_ADC_CTRL);
-       if (masked)
-               val |= BIT(irq);
-       else
-               val &= ~BIT(irq);
-       writeb(val, adc->base + JZ_REG_ADC_CTRL);
-
-       spin_unlock_irqrestore(&adc->lock, flags);
-}
-
-static void jz4740_adc_irq_mask(struct irq_data *data)
-{
-       struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
-       jz4740_adc_irq_set_masked(adc, data->irq, true);
-}
-
-static void jz4740_adc_irq_unmask(struct irq_data *data)
-{
-       struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
-       jz4740_adc_irq_set_masked(adc, data->irq, false);
-}
-
-static void jz4740_adc_irq_ack(struct irq_data *data)
-{
-       struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
-       unsigned int irq = data->irq - adc->irq_base;
-       writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS);
-}
-
-static struct irq_chip jz4740_adc_irq_chip = {
-       .name = "jz4740-adc",
-       .irq_mask = jz4740_adc_irq_mask,
-       .irq_unmask = jz4740_adc_irq_unmask,
-       .irq_ack = jz4740_adc_irq_ack,
-};
-
 static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
 {
-       struct jz4740_adc *adc = irq_desc_get_handler_data(desc);
+       struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
        uint8_t status;
        unsigned int i;
 
-       status = readb(adc->base + JZ_REG_ADC_STATUS);
+       status = readb(gc->reg_base + JZ_REG_ADC_STATUS);
 
        for (i = 0; i < 5; ++i) {
                if (status & BIT(i))
-                       generic_handle_irq(adc->irq_base + i);
+                       generic_handle_irq(gc->irq_base + i);
        }
 }
 
@@ -249,10 +203,12 @@ const struct mfd_cell jz4740_adc_cells[] = {
 
 static int __devinit jz4740_adc_probe(struct platform_device *pdev)
 {
-       int ret;
+       struct irq_chip_generic *gc;
+       struct irq_chip_type *ct;
        struct jz4740_adc *adc;
        struct resource *mem_base;
-       int irq;
+       int ret;
+       int irq_base;
 
        adc = kmalloc(sizeof(*adc), GFP_KERNEL);
        if (!adc) {
@@ -267,9 +223,9 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
                goto err_free;
        }
 
-       adc->irq_base = platform_get_irq(pdev, 1);
-       if (adc->irq_base < 0) {
-               ret = adc->irq_base;
+       irq_base = platform_get_irq(pdev, 1);
+       if (irq_base < 0) {
+               ret = irq_base;
                dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
                goto err_free;
        }
@@ -309,20 +265,28 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, adc);
 
-       for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) {
-               irq_set_chip_data(irq, adc);
-               irq_set_chip_and_handler(irq, &jz4740_adc_irq_chip,
-                                        handle_level_irq);
-       }
+       gc = irq_alloc_generic_chip("INTC", 1, irq_base, adc->base,
+               handle_level_irq);
+
+       ct = gc->chip_types;
+       ct->regs.mask = JZ_REG_ADC_CTRL;
+       ct->regs.ack = JZ_REG_ADC_STATUS;
+       ct->chip.irq_mask = irq_gc_mask_set_bit;
+       ct->chip.irq_unmask = irq_gc_mask_clr_bit;
+       ct->chip.irq_ack = irq_gc_ack;
+
+       irq_setup_generic_chip(gc, IRQ_MSK(5), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
+
+       adc->gc = gc;
 
-       irq_set_handler_data(adc->irq, adc);
+       irq_set_handler_data(adc->irq, gc);
        irq_set_chained_handler(adc->irq, jz4740_adc_irq_demux);
 
        writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
        writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
 
        ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
-               ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base);
+               ARRAY_SIZE(jz4740_adc_cells), mem_base, irq_base);
        if (ret < 0)
                goto err_clk_put;
 
@@ -347,6 +311,8 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev)
 
        mfd_remove_devices(&pdev->dev);
 
+       irq_remove_generic_chip(adc->gc, IRQ_MSK(5), IRQ_NOPROBE | IRQ_LEVEL, 0);
+       kfree(adc->gc);
        irq_set_handler_data(adc->irq, NULL);
        irq_set_chained_handler(adc->irq, NULL);
 
index ea3f52c07ef7fd3436cee6a078f011961bdb92eb..ea1169b04779597cb4bf8964b26d7f861ba75afa 100644 (file)
@@ -37,6 +37,9 @@
 #define GPIOBASE       0x44
 #define GPIO_IO_SIZE   64
 
+#define WDTBASE                0x84
+#define WDT_IO_SIZE    64
+
 static struct resource smbus_sch_resource = {
                .flags = IORESOURCE_IO,
 };
@@ -59,6 +62,18 @@ static struct mfd_cell lpc_sch_cells[] = {
        },
 };
 
+static struct resource wdt_sch_resource = {
+               .flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell tunnelcreek_cells[] = {
+       {
+               .name = "tunnelcreek_wdt",
+               .num_resources = 1,
+               .resources = &wdt_sch_resource,
+       },
+};
+
 static struct pci_device_id lpc_sch_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
@@ -72,6 +87,7 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
        unsigned int base_addr_cfg;
        unsigned short base_addr;
        int i;
+       int ret;
 
        pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
        if (!(base_addr_cfg & (1 << 31))) {
@@ -104,8 +120,39 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
        for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
                lpc_sch_cells[i].id = id->device;
 
-       return mfd_add_devices(&dev->dev, 0,
+       ret = mfd_add_devices(&dev->dev, 0,
                        lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+       if (ret)
+               goto out_dev;
+
+       if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
+               pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
+               if (!(base_addr_cfg & (1 << 31))) {
+                       dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
+                       ret = -ENODEV;
+                       goto out_dev;
+               }
+               base_addr = (unsigned short)base_addr_cfg;
+               if (base_addr == 0) {
+                       dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
+                       ret = -ENODEV;
+                       goto out_dev;
+               }
+
+               wdt_sch_resource.start = base_addr;
+               wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
+
+               for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
+                       tunnelcreek_cells[i].id = id->device;
+
+               ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
+                       ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
+       }
+
+       return ret;
+out_dev:
+       mfd_remove_devices(&dev->dev);
+       return ret;
 }
 
 static void __devexit lpc_sch_remove(struct pci_dev *dev)
index 638bf7e4d3b38b613a188d08e655d09389516626..09274cf7c33bd0bd7d21ca2ee4cd12f78330bcfb 100644 (file)
@@ -58,8 +58,6 @@ static struct i2c_client *get_i2c(struct max8997_dev *max8997,
        default:
                return ERR_PTR(-EINVAL);
        }
-
-       return ERR_PTR(-EINVAL);
 }
 
 struct max8997_irq_data {
index 1717144fe7f4f220f6cd79ccea856f39f87ecac3..29601e7d606dc425591205597817cc19935066a4 100644 (file)
@@ -998,9 +998,9 @@ static void usbhs_disable(struct device *dev)
 
        if (is_omap_usbhs_rev2(omap)) {
                if (is_ehci_tll_mode(pdata->port_mode[0]))
-                       clk_enable(omap->usbtll_p1_fck);
+                       clk_disable(omap->usbtll_p1_fck);
                if (is_ehci_tll_mode(pdata->port_mode[1]))
-                       clk_enable(omap->usbtll_p2_fck);
+                       clk_disable(omap->usbtll_p2_fck);
                clk_disable(omap->utmi_p2_fck);
                clk_disable(omap->utmi_p1_fck);
        }
index 7ab7746631d4e58a9e2fd0611c13da211ce54463..2963689cf45c2c93dcd56e56fea26b0b4debdf92 100644 (file)
@@ -228,7 +228,7 @@ int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
 EXPORT_SYMBOL_GPL(stmpe_block_write);
 
 /**
- * stmpe_set_altfunc: set the alternate function for STMPE pins
+ * stmpe_set_altfunc()- set the alternate function for STMPE pins
  * @stmpe:     Device to configure
  * @pins:      Bitmask of pins to affect
  * @block:     block to enable alternate functions for
index 0dbdc4e8cd77037ac0d8b2b6c17164af04495773..e4ee38956583069fbf912ac78e59279dbd6615e2 100644 (file)
@@ -42,6 +42,7 @@ struct stmpe_variant_block {
  * @id_mask:   bits valid in CHIPID register for comparison with id_val
  * @num_gpios: number of GPIOS
  * @af_bits:   number of bits used to specify the alternate function
+ * @regs: variant specific registers.
  * @blocks:    list of blocks present on this device
  * @num_blocks:        number of blocks present on this device
  * @num_irqs:  number of internal IRQs available on this device
index 2229e66d80db8e4d7b11afe823295d69162f1f00..6f5b8cf2f652b8edf6accf9db107d8f63ea72c23 100644 (file)
@@ -147,12 +147,11 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
        if (init_data == NULL)
                return -ENOMEM;
 
-       init_data->irq = pmic_plat_data->irq;
-       init_data->irq_base = pmic_plat_data->irq;
-
        tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
-       if (tps65910 == NULL)
+       if (tps65910 == NULL) {
+               kfree(init_data);
                return -ENOMEM;
+       }
 
        i2c_set_clientdata(i2c, tps65910);
        tps65910->dev = &i2c->dev;
@@ -168,17 +167,22 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
        if (ret < 0)
                goto err;
 
+       init_data->irq = pmic_plat_data->irq;
+       init_data->irq_base = pmic_plat_data->irq;
+
        tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
 
        ret = tps65910_irq_init(tps65910, init_data->irq, init_data);
        if (ret < 0)
                goto err;
 
+       kfree(init_data);
        return ret;
 
 err:
        mfd_remove_devices(tps65910->dev);
        kfree(tps65910);
+       kfree(init_data);
        return ret;
 }
 
@@ -187,6 +191,7 @@ static int tps65910_i2c_remove(struct i2c_client *i2c)
        struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
 
        mfd_remove_devices(tps65910->dev);
+       tps65910_irq_exit(tps65910);
        kfree(tps65910);
 
        return 0;
index 283ac67597575831b778ad9653420b6750aab763..e7ff783aa31ec2a214d691cde38902105bebfa49 100644 (file)
@@ -157,6 +157,8 @@ static __devexit int tps65911_comparator_remove(struct platform_device *pdev)
        struct tps65910 *tps65910;
 
        tps65910 = dev_get_drvdata(pdev->dev.parent);
+       device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
+       device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
 
        return 0;
 }
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
new file mode 100644 (file)
index 0000000..955bc00
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * tps65912-core.c  --  TI TPS65912x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static struct mfd_cell tps65912s[] = {
+       {
+               .name = "tps65912-pmic",
+       },
+};
+
+int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+       u8 data;
+       int err;
+
+       mutex_lock(&tps65912->io_mutex);
+
+       err = tps65912->read(tps65912, reg, 1, &data);
+       if (err) {
+               dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+               goto out;
+       }
+
+       data |= mask;
+       err = tps65912->write(tps65912, reg, 1, &data);
+       if (err)
+               dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+       mutex_unlock(&tps65912->io_mutex);
+       return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_set_bits);
+
+int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+       u8 data;
+       int err;
+
+       mutex_lock(&tps65912->io_mutex);
+       err = tps65912->read(tps65912, reg, 1, &data);
+       if (err) {
+               dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+               goto out;
+       }
+
+       data &= ~mask;
+       err = tps65912->write(tps65912, reg, 1, &data);
+       if (err)
+               dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+       mutex_unlock(&tps65912->io_mutex);
+       return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_clear_bits);
+
+static inline int tps65912_read(struct tps65912 *tps65912, u8 reg)
+{
+       u8 val;
+       int err;
+
+       err = tps65912->read(tps65912, reg, 1, &val);
+       if (err < 0)
+               return err;
+
+       return val;
+}
+
+static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+       return tps65912->write(tps65912, reg, 1, &val);
+}
+
+int tps65912_reg_read(struct tps65912 *tps65912, u8 reg)
+{
+       int data;
+
+       mutex_lock(&tps65912->io_mutex);
+
+       data = tps65912_read(tps65912, reg);
+       if (data < 0)
+               dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+
+       mutex_unlock(&tps65912->io_mutex);
+       return data;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_read);
+
+int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+       int err;
+
+       mutex_lock(&tps65912->io_mutex);
+
+       err = tps65912_write(tps65912, reg, val);
+       if (err < 0)
+               dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg);
+
+       mutex_unlock(&tps65912->io_mutex);
+       return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_write);
+
+int tps65912_device_init(struct tps65912 *tps65912)
+{
+       struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
+       struct tps65912_platform_data *init_data;
+       int ret, dcdc_avs, value;
+
+       init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL);
+       if (init_data == NULL)
+               return -ENOMEM;
+
+       init_data->irq = pmic_plat_data->irq;
+       init_data->irq_base = pmic_plat_data->irq;
+
+       mutex_init(&tps65912->io_mutex);
+       dev_set_drvdata(tps65912->dev, tps65912);
+
+       dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 |
+                       pmic_plat_data->is_dcdc2_avs  << 1 |
+                               pmic_plat_data->is_dcdc3_avs << 2 |
+                                       pmic_plat_data->is_dcdc4_avs << 3);
+       if (dcdc_avs) {
+               tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value);
+               dcdc_avs |= value;
+               tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs);
+       }
+
+       ret = mfd_add_devices(tps65912->dev, -1,
+                             tps65912s, ARRAY_SIZE(tps65912s),
+                             NULL, 0);
+       if (ret < 0)
+               goto err;
+
+       ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
+       if (ret < 0)
+               goto err;
+
+       return ret;
+
+err:
+       kfree(init_data);
+       mfd_remove_devices(tps65912->dev);
+       kfree(tps65912);
+       return ret;
+}
+
+void tps65912_device_exit(struct tps65912 *tps65912)
+{
+       mfd_remove_devices(tps65912->dev);
+       kfree(tps65912);
+}
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65912x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c
new file mode 100644 (file)
index 0000000..c041f2c
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * tps65912-i2c.c  --  I2C access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg,
+                                 int bytes, void *dest)
+{
+       struct i2c_client *i2c = tps65912->control_data;
+       struct i2c_msg xfer[2];
+       int ret;
+
+       /* Write register */
+       xfer[0].addr = i2c->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+
+       /* Read data */
+       xfer[1].addr = i2c->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = bytes;
+       xfer[1].buf = dest;
+
+       ret = i2c_transfer(i2c->adapter, xfer, 2);
+       if (ret == 2)
+               ret = 0;
+       else if (ret >= 0)
+               ret = -EIO;
+       return ret;
+}
+
+static int tps65912_i2c_write(struct tps65912 *tps65912, u8 reg,
+                                  int bytes, void *src)
+{
+       struct i2c_client *i2c = tps65912->control_data;
+       /* we add 1 byte for device register */
+       u8 msg[TPS6591X_MAX_REGISTER + 1];
+       int ret;
+
+       if (bytes > TPS6591X_MAX_REGISTER)
+               return -EINVAL;
+
+       msg[0] = reg;
+       memcpy(&msg[1], src, bytes);
+
+       ret = i2c_master_send(i2c, msg, bytes + 1);
+       if (ret < 0)
+               return ret;
+       if (ret != bytes + 1)
+               return -EIO;
+
+       return 0;
+}
+
+static int tps65912_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct tps65912 *tps65912;
+
+       tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+       if (tps65912 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, tps65912);
+       tps65912->dev = &i2c->dev;
+       tps65912->control_data = i2c;
+       tps65912->read = tps65912_i2c_read;
+       tps65912->write = tps65912_i2c_write;
+
+       return tps65912_device_init(tps65912);
+}
+
+static int tps65912_i2c_remove(struct i2c_client *i2c)
+{
+       struct tps65912 *tps65912 = i2c_get_clientdata(i2c);
+
+       tps65912_device_exit(tps65912);
+
+       return 0;
+}
+
+static const struct i2c_device_id tps65912_i2c_id[] = {
+       {"tps65912", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id);
+
+static struct i2c_driver tps65912_i2c_driver = {
+       .driver = {
+                  .name = "tps65912",
+                  .owner = THIS_MODULE,
+       },
+       .probe = tps65912_i2c_probe,
+       .remove = tps65912_i2c_remove,
+       .id_table = tps65912_i2c_id,
+};
+
+static int __init tps65912_i2c_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&tps65912_i2c_driver);
+       if (ret != 0)
+               pr_err("Failed to register TPS65912 I2C driver: %d\n", ret);
+
+       return ret;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_i2c_init);
+
+static void __exit tps65912_i2c_exit(void)
+{
+       i2c_del_driver(&tps65912_i2c_driver);
+}
+module_exit(tps65912_i2c_exit);
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c
new file mode 100644 (file)
index 0000000..d360a83
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * tps65912-irq.c  --  TI TPS6591x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ * This driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65912.h>
+
+static inline int irq_to_tps65912_irq(struct tps65912 *tps65912,
+                                                       int irq)
+{
+       return irq - tps65912->irq_base;
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI.  Since the
+ * IRQ handler explicitly clears the IRQ it handles the IRQ line
+ * will be reasserted and the physical IRQ will be handled again if
+ * another interrupt is asserted while we run - in the normal course
+ * of events this is a rare occurrence so we save I2C/SPI reads. We're
+ * also assuming that it's rare to get lots of interrupts firing
+ * simultaneously so try to minimise I/O.
+ */
+static irqreturn_t tps65912_irq(int irq, void *irq_data)
+{
+       struct tps65912 *tps65912 = irq_data;
+       u32 irq_sts;
+       u32 irq_mask;
+       u8 reg;
+       int i;
+
+
+       tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
+       irq_sts = reg;
+       tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
+       irq_sts |= reg << 8;
+       tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
+       irq_sts |= reg << 16;
+       tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
+       irq_sts |= reg << 24;
+
+       tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
+       irq_mask = reg;
+       tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
+       irq_mask |= reg << 8;
+       tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
+       irq_mask |= reg << 16;
+       tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
+       irq_mask |= reg << 24;
+
+       irq_sts &= ~irq_mask;
+       if (!irq_sts)
+               return IRQ_NONE;
+
+       for (i = 0; i < tps65912->irq_num; i++) {
+               if (!(irq_sts & (1 << i)))
+                       continue;
+
+               handle_nested_irq(tps65912->irq_base + i);
+       }
+
+       /* Write the STS register back to clear IRQs we handled */
+       reg = irq_sts & 0xFF;
+       irq_sts >>= 8;
+       if (reg)
+               tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
+       reg = irq_sts & 0xFF;
+       irq_sts >>= 8;
+       if (reg)
+               tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
+       reg = irq_sts & 0xFF;
+       irq_sts >>= 8;
+       if (reg)
+               tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
+       reg = irq_sts & 0xFF;
+       if (reg)
+               tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
+
+       return IRQ_HANDLED;
+}
+
+static void tps65912_irq_lock(struct irq_data *data)
+{
+       struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_sync_unlock(struct irq_data *data)
+{
+       struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+       u32 reg_mask;
+       u8 reg;
+
+       tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
+       reg_mask = reg;
+       tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
+       reg_mask |= reg << 8;
+       tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
+       reg_mask |= reg << 16;
+       tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
+       reg_mask |= reg << 24;
+
+       if (tps65912->irq_mask != reg_mask) {
+               reg = tps65912->irq_mask & 0xFF;
+               tps65912->write(tps65912, TPS65912_INT_MSK, 1, &reg);
+               reg = tps65912->irq_mask >> 8 & 0xFF;
+               tps65912->write(tps65912, TPS65912_INT_MSK2, 1, &reg);
+               reg = tps65912->irq_mask >> 16 & 0xFF;
+               tps65912->write(tps65912, TPS65912_INT_MSK3, 1, &reg);
+               reg = tps65912->irq_mask >> 24 & 0xFF;
+               tps65912->write(tps65912, TPS65912_INT_MSK4, 1, &reg);
+       }
+
+       mutex_unlock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_enable(struct irq_data *data)
+{
+       struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+       tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static void tps65912_irq_disable(struct irq_data *data)
+{
+       struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+       tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static struct irq_chip tps65912_irq_chip = {
+       .name = "tps65912",
+       .irq_bus_lock = tps65912_irq_lock,
+       .irq_bus_sync_unlock = tps65912_irq_sync_unlock,
+       .irq_disable = tps65912_irq_disable,
+       .irq_enable = tps65912_irq_enable,
+};
+
+int tps65912_irq_init(struct tps65912 *tps65912, int irq,
+                           struct tps65912_platform_data *pdata)
+{
+       int ret, cur_irq;
+       int flags = IRQF_ONESHOT;
+       u8 reg;
+
+       if (!irq) {
+               dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n");
+               return 0;
+       }
+
+       if (!pdata || !pdata->irq_base) {
+               dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n");
+               return 0;
+       }
+
+       /* Clear unattended interrupts */
+       tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
+       tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
+       tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
+       tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
+       tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
+       tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
+       tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
+       tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
+
+       /* Mask top level interrupts */
+       tps65912->irq_mask = 0xFFFFFFFF;
+
+       mutex_init(&tps65912->irq_lock);
+       tps65912->chip_irq = irq;
+       tps65912->irq_base = pdata->irq_base;
+
+       tps65912->irq_num = TPS65912_NUM_IRQ;
+
+       /* Register with genirq */
+       for (cur_irq = tps65912->irq_base;
+            cur_irq < tps65912->irq_num + tps65912->irq_base;
+            cur_irq++) {
+               irq_set_chip_data(cur_irq, tps65912);
+               irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip,
+                                        handle_edge_irq);
+               irq_set_nested_thread(cur_irq, 1);
+               /* ARM needs us to explicitly flag the IRQ as valid
+                * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+               set_irq_flags(cur_irq, IRQF_VALID);
+#else
+               irq_set_noprobe(cur_irq);
+#endif
+       }
+
+       ret = request_threaded_irq(irq, NULL, tps65912_irq, flags,
+                                  "tps65912", tps65912);
+
+       irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+       if (ret != 0)
+               dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret);
+
+       return ret;
+}
+
+int tps65912_irq_exit(struct tps65912 *tps65912)
+{
+       free_irq(tps65912->chip_irq, tps65912);
+       return 0;
+}
diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c
new file mode 100644 (file)
index 0000000..6d71e0d
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * tps65912-spi.c  --  SPI access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_spi_write(struct tps65912 *tps65912, u8 addr,
+                                                       int bytes, void *src)
+{
+       struct spi_device *spi = tps65912->control_data;
+       u8 *data = (u8 *) src;
+       int ret;
+       /* bit 23 is the read/write bit */
+       unsigned long spi_data = 1 << 23 | addr << 15 | *data;
+       struct spi_transfer xfer;
+       struct spi_message msg;
+       u32 tx_buf, rx_buf;
+
+       tx_buf = spi_data;
+       rx_buf = 0;
+
+       xfer.tx_buf     = &tx_buf;
+       xfer.rx_buf     = NULL;
+       xfer.len        = sizeof(unsigned long);
+       xfer.bits_per_word = 24;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+
+       ret = spi_sync(spi, &msg);
+       return ret;
+}
+
+static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr,
+                                                       int bytes, void *dest)
+{
+       struct spi_device *spi = tps65912->control_data;
+       /* bit 23 is the read/write bit */
+       unsigned long spi_data = 0 << 23 | addr << 15;
+       struct spi_transfer xfer;
+       struct spi_message msg;
+       int ret;
+       u8 *data = (u8 *) dest;
+       u32 tx_buf, rx_buf;
+
+       tx_buf = spi_data;
+       rx_buf = 0;
+
+       xfer.tx_buf     = &tx_buf;
+       xfer.rx_buf     = &rx_buf;
+       xfer.len        = sizeof(unsigned long);
+       xfer.bits_per_word = 24;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+
+       if (spi == NULL)
+               return 0;
+
+       ret = spi_sync(spi, &msg);
+       if (ret == 0)
+               *data = (u8) (rx_buf & 0xFF);
+       return ret;
+}
+
+static int __devinit tps65912_spi_probe(struct spi_device *spi)
+{
+       struct tps65912 *tps65912;
+
+       tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+       if (tps65912 == NULL)
+               return -ENOMEM;
+
+       tps65912->dev = &spi->dev;
+       tps65912->control_data = spi;
+       tps65912->read = tps65912_spi_read;
+       tps65912->write = tps65912_spi_write;
+
+       spi_set_drvdata(spi, tps65912);
+
+       return tps65912_device_init(tps65912);
+}
+
+static int __devexit tps65912_spi_remove(struct spi_device *spi)
+{
+       struct tps65912 *tps65912 = spi_get_drvdata(spi);
+
+       tps65912_device_exit(tps65912);
+
+       return 0;
+}
+
+static struct spi_driver tps65912_spi_driver = {
+       .driver = {
+               .name = "tps65912",
+               .bus = &spi_bus_type,
+               .owner = THIS_MODULE,
+       },
+       .probe  = tps65912_spi_probe,
+       .remove = __devexit_p(tps65912_spi_remove),
+};
+
+static int __init tps65912_spi_init(void)
+{
+       int ret;
+
+       ret = spi_register_driver(&tps65912_spi_driver);
+       if (ret != 0)
+               pr_err("Failed to register TPS65912 SPI driver: %d\n", ret);
+
+       return 0;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_spi_init);
+
+static void __exit tps65912_spi_exit(void)
+{
+       spi_unregister_driver(&tps65912_spi_driver);
+}
+module_exit(tps65912_spi_exit);
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd");
+MODULE_LICENSE("GPL");
index a2eddc70995cdfc789651548ad58fde1659c4488..01ecfeee6524697d26a08634f705bc28131e41bc 100644 (file)
@@ -1283,6 +1283,8 @@ static const struct i2c_device_id twl_ids[] = {
        { "tps65950", 0 },              /* catalog version of twl5030 */
        { "tps65930", TPS_SUBSET },     /* fewer LDOs and DACs; no charger */
        { "tps65920", TPS_SUBSET },     /* fewer LDOs; no codec or charger */
+       { "tps65921", TPS_SUBSET },     /* fewer LDOs; no codec, no LED
+                                          and vibrator. Charger in USB module*/
        { "twl6030", TWL6030_CLASS },   /* "Phoenix power chip" */
        { "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
        { /* end of list */ },
index 3941ddcf15feff4bc45403569fa2296bd760ec36..b5d598c3aa71b356f40fb9a5f89738eed16c5350 100644 (file)
@@ -530,13 +530,13 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
        if (ret) {
                dev_err(twl4030_madc->dev,
                        "unable to write sel register 0x%X\n", method->sel + 1);
-               return ret;
+               goto out;
        }
        ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
        if (ret) {
                dev_err(twl4030_madc->dev,
                        "unable to write sel register 0x%X\n", method->sel + 1);
-               return ret;
+               goto out;
        }
        /* Select averaging for all channels if do_avg is set */
        if (req->do_avg) {
@@ -546,7 +546,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
                        dev_err(twl4030_madc->dev,
                                "unable to write avg register 0x%X\n",
                                method->avg + 1);
-                       return ret;
+                       goto out;
                }
                ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
                                       ch_lsb, method->avg);
@@ -554,7 +554,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
                        dev_err(twl4030_madc->dev,
                                "unable to write sel reg 0x%X\n",
                                method->sel + 1);
-                       return ret;
+                       goto out;
                }
        }
        if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
index 5d25bdc78424392d4168ff3065a6eb2f43407c69..e8fee147678d31e57e4d6c9d7272a998ba95029b 100644 (file)
@@ -161,3 +161,5 @@ void pwm_free(struct pwm_device *pwm)
        kfree(pwm);
 }
 EXPORT_SYMBOL(pwm_free);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c
new file mode 100644 (file)
index 0000000..8721095
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * wm831x-auxadc.c  --  AUXADC for Wolfson WM831x PMICs
+ *
+ * Copyright 2009-2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/regulator.h>
+
+struct wm831x_auxadc_req {
+       struct list_head list;
+       enum wm831x_auxadc input;
+       int val;
+       struct completion done;
+};
+
+static int wm831x_auxadc_read_irq(struct wm831x *wm831x,
+                                 enum wm831x_auxadc input)
+{
+       struct wm831x_auxadc_req *req;
+       int ret;
+       bool ena = false;
+
+       req = kzalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       init_completion(&req->done);
+       req->input = input;
+       req->val = -ETIMEDOUT;
+
+       mutex_lock(&wm831x->auxadc_lock);
+
+       /* Enqueue the request */
+       list_add(&req->list, &wm831x->auxadc_pending);
+
+       ena = !wm831x->auxadc_active;
+
+       if (ena) {
+               ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+                                     WM831X_AUX_ENA, WM831X_AUX_ENA);
+               if (ret != 0) {
+                       dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n",
+                               ret);
+                       goto out;
+               }
+       }
+
+       /* Enable the conversion if not already running */
+       if (!(wm831x->auxadc_active & (1 << input))) {
+               ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+                                     1 << input, 1 << input);
+               if (ret != 0) {
+                       dev_err(wm831x->dev,
+                               "Failed to set AUXADC source: %d\n", ret);
+                       goto out;
+               }
+
+               wm831x->auxadc_active |= 1 << input;
+       }
+
+       /* We convert at the fastest rate possible */
+       if (ena) {
+               ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+                                     WM831X_AUX_CVT_ENA |
+                                     WM831X_AUX_RATE_MASK,
+                                     WM831X_AUX_CVT_ENA |
+                                     WM831X_AUX_RATE_MASK);
+               if (ret != 0) {
+                       dev_err(wm831x->dev, "Failed to start AUXADC: %d\n",
+                               ret);
+                       goto out;
+               }
+       }
+
+       mutex_unlock(&wm831x->auxadc_lock);
+
+       /* Wait for an interrupt */
+       wait_for_completion_timeout(&req->done, msecs_to_jiffies(500));
+
+       mutex_lock(&wm831x->auxadc_lock);
+
+       list_del(&req->list);
+       ret = req->val;
+
+out:
+       mutex_unlock(&wm831x->auxadc_lock);
+
+       kfree(req);
+
+       return ret;
+}
+
+static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
+{
+       struct wm831x *wm831x = irq_data;
+       struct wm831x_auxadc_req *req;
+       int ret, input, val;
+
+       ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+       if (ret < 0) {
+               dev_err(wm831x->dev,
+                       "Failed to read AUXADC data: %d\n", ret);
+               return IRQ_NONE;
+       }
+
+       input = ((ret & WM831X_AUX_DATA_SRC_MASK)
+                >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+       if (input == 14)
+               input = WM831X_AUX_CAL;
+
+       val = ret & WM831X_AUX_DATA_MASK;
+
+       mutex_lock(&wm831x->auxadc_lock);
+
+       /* Disable this conversion, we're about to complete all users */
+       wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+                       1 << input, 0);
+       wm831x->auxadc_active &= ~(1 << input);
+
+       /* Turn off the entire convertor if idle */
+       if (!wm831x->auxadc_active)
+               wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0);
+
+       /* Wake up any threads waiting for this request */
+       list_for_each_entry(req, &wm831x->auxadc_pending, list) {
+               if (req->input == input) {
+                       req->val = val;
+                       complete(&req->done);
+               }
+       }
+
+       mutex_unlock(&wm831x->auxadc_lock);
+
+       return IRQ_HANDLED;
+}
+
+static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
+                                    enum wm831x_auxadc input)
+{
+       int ret, src, timeout;
+
+       mutex_lock(&wm831x->auxadc_lock);
+
+       ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+                             WM831X_AUX_ENA, WM831X_AUX_ENA);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
+               goto out;
+       }
+
+       /* We force a single source at present */
+       src = input;
+       ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
+                              1 << src);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
+               goto out;
+       }
+
+       ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+                             WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
+               goto disable;
+       }
+
+       /* If we're not using interrupts then poll the
+        * interrupt status register */
+       timeout = 5;
+       while (timeout) {
+               msleep(1);
+
+               ret = wm831x_reg_read(wm831x,
+                                     WM831X_INTERRUPT_STATUS_1);
+               if (ret < 0) {
+                       dev_err(wm831x->dev,
+                               "ISR 1 read failed: %d\n", ret);
+                       goto disable;
+               }
+
+               /* Did it complete? */
+               if (ret & WM831X_AUXADC_DATA_EINT) {
+                       wm831x_reg_write(wm831x,
+                                        WM831X_INTERRUPT_STATUS_1,
+                                        WM831X_AUXADC_DATA_EINT);
+                       break;
+               } else {
+                       dev_err(wm831x->dev,
+                               "AUXADC conversion timeout\n");
+                       ret = -EBUSY;
+                       goto disable;
+               }
+       }
+
+       ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+       if (ret < 0) {
+               dev_err(wm831x->dev,
+                       "Failed to read AUXADC data: %d\n", ret);
+               goto disable;
+       }
+
+       src = ((ret & WM831X_AUX_DATA_SRC_MASK)
+              >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+       if (src == 14)
+               src = WM831X_AUX_CAL;
+
+       if (src != input) {
+               dev_err(wm831x->dev, "Data from source %d not %d\n",
+                       src, input);
+               ret = -EINVAL;
+       } else {
+               ret &= WM831X_AUX_DATA_MASK;
+       }
+
+disable:
+       wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
+out:
+       mutex_unlock(&wm831x->auxadc_lock);
+       return ret;
+}
+
+/**
+ * wm831x_auxadc_read: Read a value from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+       return wm831x->auxadc_read(wm831x, input);
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
+
+/**
+ * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+       int ret;
+
+       ret = wm831x_auxadc_read(wm831x, input);
+       if (ret < 0)
+               return ret;
+
+       ret *= 1465;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
+
+void wm831x_auxadc_init(struct wm831x *wm831x)
+{
+       int ret;
+
+       mutex_init(&wm831x->auxadc_lock);
+       INIT_LIST_HEAD(&wm831x->auxadc_pending);
+
+       if (wm831x->irq && wm831x->irq_base) {
+               wm831x->auxadc_read = wm831x_auxadc_read_irq;
+
+               ret = request_threaded_irq(wm831x->irq_base +
+                                          WM831X_IRQ_AUXADC_DATA,
+                                          NULL, wm831x_auxadc_irq, 0,
+                                          "auxadc", wm831x);
+               if (ret < 0) {
+                       dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
+                               ret);
+                       wm831x->auxadc_read = NULL;
+               }
+       }
+
+       if (!wm831x->auxadc_read)
+               wm831x->auxadc_read = wm831x_auxadc_read_polled;
+}
index 265f75fc6a25f404a60d0954f2228585bb149938..282e76ab678f15c4cb3d792f571f47e04f6fd872 100644 (file)
@@ -295,7 +295,7 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
                goto out;
 
        r &= ~mask;
-       r |= val;
+       r |= val & mask;
 
        ret = wm831x_write(wm831x, reg, 2, &r);
 
@@ -306,146 +306,6 @@ out:
 }
 EXPORT_SYMBOL_GPL(wm831x_set_bits);
 
-/**
- * wm831x_auxadc_read: Read a value from the WM831x AUXADC
- *
- * @wm831x: Device to read from.
- * @input: AUXADC input to read.
- */
-int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
-{
-       int ret, src, irq_masked, timeout;
-
-       /* Are we using the interrupt? */
-       irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
-       irq_masked &= WM831X_AUXADC_DATA_EINT;
-
-       mutex_lock(&wm831x->auxadc_lock);
-
-       ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
-                             WM831X_AUX_ENA, WM831X_AUX_ENA);
-       if (ret < 0) {
-               dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
-               goto out;
-       }
-
-       /* We force a single source at present */
-       src = input;
-       ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
-                              1 << src);
-       if (ret < 0) {
-               dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
-               goto out;
-       }
-
-       /* Clear any notification from a very late arriving interrupt */
-       try_wait_for_completion(&wm831x->auxadc_done);
-
-       ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
-                             WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
-       if (ret < 0) {
-               dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
-               goto disable;
-       }
-
-       if (irq_masked) {
-               /* If we're not using interrupts then poll the
-                * interrupt status register */
-               timeout = 5;
-               while (timeout) {
-                       msleep(1);
-
-                       ret = wm831x_reg_read(wm831x,
-                                             WM831X_INTERRUPT_STATUS_1);
-                       if (ret < 0) {
-                               dev_err(wm831x->dev,
-                                       "ISR 1 read failed: %d\n", ret);
-                               goto disable;
-                       }
-
-                       /* Did it complete? */
-                       if (ret & WM831X_AUXADC_DATA_EINT) {
-                               wm831x_reg_write(wm831x,
-                                                WM831X_INTERRUPT_STATUS_1,
-                                                WM831X_AUXADC_DATA_EINT);
-                               break;
-                       } else {
-                               dev_err(wm831x->dev,
-                                       "AUXADC conversion timeout\n");
-                               ret = -EBUSY;
-                               goto disable;
-                       }
-               }
-       } else {
-               /* If we are using interrupts then wait for the
-                * interrupt to complete.  Use an extremely long
-                * timeout to handle situations with heavy load where
-                * the notification of the interrupt may be delayed by
-                * threaded IRQ handling. */
-               if (!wait_for_completion_timeout(&wm831x->auxadc_done,
-                                                msecs_to_jiffies(500))) {
-                       dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
-                       ret = -EBUSY;
-                       goto disable;
-               }
-       }
-
-       ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
-       if (ret < 0) {
-               dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret);
-       } else {
-               src = ((ret & WM831X_AUX_DATA_SRC_MASK)
-                      >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
-
-               if (src == 14)
-                       src = WM831X_AUX_CAL;
-
-               if (src != input) {
-                       dev_err(wm831x->dev, "Data from source %d not %d\n",
-                               src, input);
-                       ret = -EINVAL;
-               } else {
-                       ret &= WM831X_AUX_DATA_MASK;
-               }
-       }
-
-disable:
-       wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
-out:
-       mutex_unlock(&wm831x->auxadc_lock);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
-
-static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
-{
-       struct wm831x *wm831x = irq_data;
-
-       complete(&wm831x->auxadc_done);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
- *
- * @wm831x: Device to read from.
- * @input: AUXADC input to read.
- */
-int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
-{
-       int ret;
-
-       ret = wm831x_auxadc_read(wm831x, input);
-       if (ret < 0)
-               return ret;
-
-       ret *= 1465;
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
-
 static struct resource wm831x_dcdc1_resources[] = {
        {
                .start = WM831X_DC1_CONTROL_1,
@@ -871,6 +731,9 @@ static struct mfd_cell wm8310_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
                .resources = wm831x_dcdc4_resources,
        },
+       {
+               .name = "wm831x-clk",
+       },
        {
                .name = "wm831x-epe",
                .id = 1,
@@ -975,11 +838,6 @@ static struct mfd_cell wm8310_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_power_resources),
                .resources = wm831x_power_resources,
        },
-       {
-               .name = "wm831x-rtc",
-               .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
-               .resources = wm831x_rtc_resources,
-       },
        {
                .name = "wm831x-status",
                .id = 1,
@@ -1027,6 +885,9 @@ static struct mfd_cell wm8311_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
                .resources = wm831x_dcdc4_resources,
        },
+       {
+               .name = "wm831x-clk",
+       },
        {
                .name = "wm831x-epe",
                .id = 1,
@@ -1107,11 +968,6 @@ static struct mfd_cell wm8311_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_power_resources),
                .resources = wm831x_power_resources,
        },
-       {
-               .name = "wm831x-rtc",
-               .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
-               .resources = wm831x_rtc_resources,
-       },
        {
                .name = "wm831x-status",
                .id = 1,
@@ -1124,11 +980,6 @@ static struct mfd_cell wm8311_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_status2_resources),
                .resources = wm831x_status2_resources,
        },
-       {
-               .name = "wm831x-touch",
-               .num_resources = ARRAY_SIZE(wm831x_touch_resources),
-               .resources = wm831x_touch_resources,
-       },
        {
                .name = "wm831x-watchdog",
                .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
@@ -1164,6 +1015,9 @@ static struct mfd_cell wm8312_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
                .resources = wm831x_dcdc4_resources,
        },
+       {
+               .name = "wm831x-clk",
+       },
        {
                .name = "wm831x-epe",
                .id = 1,
@@ -1268,11 +1122,6 @@ static struct mfd_cell wm8312_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_power_resources),
                .resources = wm831x_power_resources,
        },
-       {
-               .name = "wm831x-rtc",
-               .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
-               .resources = wm831x_rtc_resources,
-       },
        {
                .name = "wm831x-status",
                .id = 1,
@@ -1285,11 +1134,6 @@ static struct mfd_cell wm8312_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_status2_resources),
                .resources = wm831x_status2_resources,
        },
-       {
-               .name = "wm831x-touch",
-               .num_resources = ARRAY_SIZE(wm831x_touch_resources),
-               .resources = wm831x_touch_resources,
-       },
        {
                .name = "wm831x-watchdog",
                .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
@@ -1325,6 +1169,9 @@ static struct mfd_cell wm8320_devs[] = {
                .num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
                .resources = wm8320_dcdc4_buck_resources,
        },
+       {
+               .name = "wm831x-clk",
+       },
        {
                .name = "wm831x-gpio",
                .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
@@ -1404,11 +1251,6 @@ static struct mfd_cell wm8320_devs[] = {
                .num_resources = ARRAY_SIZE(wm831x_on_resources),
                .resources = wm831x_on_resources,
        },
-       {
-               .name = "wm831x-rtc",
-               .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
-               .resources = wm831x_rtc_resources,
-       },
        {
                .name = "wm831x-status",
                .id = 1,
@@ -1428,6 +1270,22 @@ static struct mfd_cell wm8320_devs[] = {
        },
 };
 
+static struct mfd_cell touch_devs[] = {
+       {
+               .name = "wm831x-touch",
+               .num_resources = ARRAY_SIZE(wm831x_touch_resources),
+               .resources = wm831x_touch_resources,
+       },
+};
+
+static struct mfd_cell rtc_devs[] = {
+       {
+               .name = "wm831x-rtc",
+               .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+               .resources = wm831x_rtc_resources,
+       },
+};
+
 static struct mfd_cell backlight_devs[] = {
        {
                .name = "wm831x-backlight",
@@ -1440,14 +1298,12 @@ static struct mfd_cell backlight_devs[] = {
 int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
 {
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
-       int rev;
+       int rev, wm831x_num;
        enum wm831x_parent parent;
        int ret, i;
 
        mutex_init(&wm831x->io_lock);
        mutex_init(&wm831x->key_lock);
-       mutex_init(&wm831x->auxadc_lock);
-       init_completion(&wm831x->auxadc_done);
        dev_set_drvdata(wm831x->dev, wm831x);
 
        ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
@@ -1592,45 +1448,51 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                }
        }
 
+       /* Multiply by 10 as we have many subdevices of the same type */
+       if (pdata && pdata->wm831x_num)
+               wm831x_num = pdata->wm831x_num * 10;
+       else
+               wm831x_num = -1;
+
        ret = wm831x_irq_init(wm831x, irq);
        if (ret != 0)
                goto err;
 
-       if (wm831x->irq_base) {
-               ret = request_threaded_irq(wm831x->irq_base +
-                                          WM831X_IRQ_AUXADC_DATA,
-                                          NULL, wm831x_auxadc_irq, 0,
-                                          "auxadc", wm831x);
-               if (ret < 0)
-                       dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
-                               ret);
-       }
+       wm831x_auxadc_init(wm831x);
 
        /* The core device is up, instantiate the subdevices. */
        switch (parent) {
        case WM8310:
-               ret = mfd_add_devices(wm831x->dev, -1,
+               ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8310_devs, ARRAY_SIZE(wm8310_devs),
                                      NULL, wm831x->irq_base);
                break;
 
        case WM8311:
-               ret = mfd_add_devices(wm831x->dev, -1,
+               ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8311_devs, ARRAY_SIZE(wm8311_devs),
                                      NULL, wm831x->irq_base);
+               if (!pdata || !pdata->disable_touch)
+                       mfd_add_devices(wm831x->dev, wm831x_num,
+                                       touch_devs, ARRAY_SIZE(touch_devs),
+                                       NULL, wm831x->irq_base);
                break;
 
        case WM8312:
-               ret = mfd_add_devices(wm831x->dev, -1,
+               ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8312_devs, ARRAY_SIZE(wm8312_devs),
                                      NULL, wm831x->irq_base);
+               if (!pdata || !pdata->disable_touch)
+                       mfd_add_devices(wm831x->dev, wm831x_num,
+                                       touch_devs, ARRAY_SIZE(touch_devs),
+                                       NULL, wm831x->irq_base);
                break;
 
        case WM8320:
        case WM8321:
        case WM8325:
        case WM8326:
-               ret = mfd_add_devices(wm831x->dev, -1,
+               ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8320_devs, ARRAY_SIZE(wm8320_devs),
                                      NULL, wm831x->irq_base);
                break;
@@ -1645,9 +1507,30 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                goto err_irq;
        }
 
+       /* The RTC can only be used if the 32.768kHz crystal is
+        * enabled; this can't be controlled by software at runtime.
+        */
+       ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret);
+               goto err_irq;
+       }
+
+       if (ret & WM831X_XTAL_ENA) {
+               ret = mfd_add_devices(wm831x->dev, wm831x_num,
+                                     rtc_devs, ARRAY_SIZE(rtc_devs),
+                                     NULL, wm831x->irq_base);
+               if (ret != 0) {
+                       dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
+                       goto err_irq;
+               }
+       } else {
+               dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n");
+       }
+
        if (pdata && pdata->backlight) {
                /* Treat errors as non-critical */
-               ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
+               ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
                                      ARRAY_SIZE(backlight_devs), NULL,
                                      wm831x->irq_base);
                if (ret < 0)
index 42b928ec891e6b4b2a60a6323ed5d6265659e978..ada1835a54557b31424dc1f93297ffdb45b71a9a 100644 (file)
@@ -348,6 +348,15 @@ static void wm831x_irq_sync_unlock(struct irq_data *data)
        struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
        int i;
 
+       for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) {
+               if (wm831x->gpio_update[i]) {
+                       wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i,
+                                       WM831X_GPN_INT_MODE | WM831X_GPN_POL,
+                                       wm831x->gpio_update[i]);
+                       wm831x->gpio_update[i] = 0;
+               }
+       }
+
        for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
                /* If there's been a change in the mask write it back
                 * to the hardware. */
@@ -387,7 +396,7 @@ static void wm831x_irq_disable(struct irq_data *data)
 static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
 {
        struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
-       int val, irq;
+       int irq;
 
        irq = data->irq - wm831x->irq_base;
 
@@ -399,22 +408,30 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
                        return -EINVAL;
        }
 
+       /* Rebase the IRQ into the GPIO range so we've got a sensible array
+        * index.
+        */
+       irq -= WM831X_IRQ_GPIO_1;
+
+       /* We set the high bit to flag that we need an update; don't
+        * do the update here as we can be called with the bus lock
+        * held.
+        */
        switch (type) {
        case IRQ_TYPE_EDGE_BOTH:
-               val = WM831X_GPN_INT_MODE;
+               wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
                break;
        case IRQ_TYPE_EDGE_RISING:
-               val = WM831X_GPN_POL;
+               wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
                break;
        case IRQ_TYPE_EDGE_FALLING:
-               val = 0;
+               wm831x->gpio_update[irq] = 0x10000;
                break;
        default:
                return -EINVAL;
        }
 
-       return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
-                              WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
+       return 0;
 }
 
 static struct irq_chip wm831x_irq_chip = {
@@ -432,7 +449,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
 {
        struct wm831x *wm831x = data;
        unsigned int i;
-       int primary;
+       int primary, status_addr;
        int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
        int read[WM831X_NUM_IRQ_REGS] = { 0 };
        int *status;
@@ -467,8 +484,9 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
                /* Hopefully there should only be one register to read
                 * each time otherwise we ought to do a block read. */
                if (!read[offset]) {
-                       *status = wm831x_reg_read(wm831x,
-                                    irq_data_to_status_reg(&wm831x_irqs[i]));
+                       status_addr = irq_data_to_status_reg(&wm831x_irqs[i]);
+
+                       *status = wm831x_reg_read(wm831x, status_addr);
                        if (*status < 0) {
                                dev_err(wm831x->dev,
                                        "Failed to read IRQ status: %d\n",
@@ -477,26 +495,21 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
                        }
 
                        read[offset] = 1;
+
+                       /* Ignore any bits that we don't think are masked */
+                       *status &= ~wm831x->irq_masks_cur[offset];
+
+                       /* Acknowledge now so we don't miss
+                        * notifications while we handle.
+                        */
+                       wm831x_reg_write(wm831x, status_addr, *status);
                }
 
-               /* Report it if it isn't masked, or forget the status. */
-               if ((*status & ~wm831x->irq_masks_cur[offset])
-                   & wm831x_irqs[i].mask)
+               if (*status & wm831x_irqs[i].mask)
                        handle_nested_irq(wm831x->irq_base + i);
-               else
-                       *status &= ~wm831x_irqs[i].mask;
        }
 
 out:
-       /* Touchscreen interrupts are handled specially in the driver */
-       status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
-
-       for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
-               if (status_regs[i])
-                       wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
-                                        status_regs[i]);
-       }
-
        return IRQ_HANDLED;
 }
 
@@ -515,13 +528,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
                                 0xffff);
        }
 
-       if (!pdata || !pdata->irq_base) {
-               dev_err(wm831x->dev,
-                       "No interrupt base specified, no interrupts\n");
+       /* Try to dynamically allocate IRQs if no base is specified */
+       if (!pdata || !pdata->irq_base)
+               wm831x->irq_base = -1;
+       else
+               wm831x->irq_base = pdata->irq_base;
+
+       wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
+                                          WM831X_NUM_IRQS, 0);
+       if (wm831x->irq_base < 0) {
+               dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
+                        wm831x->irq_base);
+               wm831x->irq_base = 0;
                return 0;
        }
 
-       if (pdata->irq_cmos)
+       if (pdata && pdata->irq_cmos)
                i = 0;
        else
                i = WM831X_IRQ_OD;
@@ -541,7 +563,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
        }
 
        wm831x->irq = irq;
-       wm831x->irq_base = pdata->irq_base;
 
        /* Register them with genirq */
        for (cur_irq = wm831x->irq_base;
index ed4b22a167b30104d3628bed7d3ee34753dd867b..8a1fafd0bf7d129184e8910b73a0848954dcf42c 100644 (file)
@@ -473,17 +473,13 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
 {
        int ret, cur_irq, i;
        int flags = IRQF_ONESHOT;
+       int irq_base = -1;
 
        if (!irq) {
                dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n");
                return 0;
        }
 
-       if (!pdata || !pdata->irq_base) {
-               dev_warn(wm8350->dev, "No interrupt support, no IRQ base\n");
-               return 0;
-       }
-
        /* Mask top level interrupts */
        wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
 
@@ -502,7 +498,17 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
        wm8350->chip_irq = irq;
        wm8350->irq_base = pdata->irq_base;
 
-       if (pdata->irq_high) {
+       if (pdata && pdata->irq_base > 0)
+               irq_base = pdata->irq_base;
+
+       wm8350->irq_base = irq_alloc_descs(irq_base, 0, ARRAY_SIZE(wm8350_irqs), 0);
+       if (wm8350->irq_base < 0) {
+               dev_warn(wm8350->dev, "Allocating irqs failed with %d\n",
+                       wm8350->irq_base);
+               return 0;
+       }
+
+       if (pdata && pdata->irq_high) {
                flags |= IRQF_TRIGGER_HIGH;
 
                wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
index e198d40292e7f5cb9c1ed5e78c4d3f41e30f1bc5..96479c9b1728918e8672a6aa9d03847365594263 100644 (file)
@@ -316,7 +316,7 @@ static int wm8994_suspend(struct device *dev)
 static int wm8994_resume(struct device *dev)
 {
        struct wm8994 *wm8994 = dev_get_drvdata(dev);
-       int ret;
+       int ret, i;
 
        /* We may have lied to the PM core about suspending */
        if (!wm8994->suspended)
@@ -329,10 +329,16 @@ static int wm8994_resume(struct device *dev)
                return ret;
        }
 
-       ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK,
-                          WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur);
-       if (ret < 0)
-               dev_err(dev, "Failed to restore interrupt masks: %d\n", ret);
+       /* Write register at a time as we use the cache on the CPU so store
+        * it in native endian.
+        */
+       for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
+               ret = wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK
+                                      + i, wm8994->irq_masks_cur[i]);
+               if (ret < 0)
+                       dev_err(dev, "Failed to restore interrupt masks: %d\n",
+                               ret);
+       }
 
        ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
                           &wm8994->ldo_regs);
@@ -403,7 +409,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
                break;
        default:
                BUG();
-               return -EINVAL;
+               goto err;
        }
 
        wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
@@ -425,7 +431,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
                break;
        default:
                BUG();
-               return -EINVAL;
+               goto err;
        }
                
        ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
@@ -476,13 +482,18 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
                goto err_enable;
        }
 
-       switch (ret) {
-       case 0:
-       case 1:
-               if (wm8994->type == WM8994)
+       switch (wm8994->type) {
+       case WM8994:
+               switch (ret) {
+               case 0:
+               case 1:
                        dev_warn(wm8994->dev,
                                 "revision %c not fully supported\n",
                                 'A' + ret);
+                       break;
+               default:
+                       break;
+               }
                break;
        default:
                break;
index 71c6e8f9aedb47af0fb1ab5d72a48bca872ad6ef..d682f7bd112cecf45519ceb178404c958c0f4747 100644 (file)
@@ -231,12 +231,6 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
                status[i] &= ~wm8994->irq_masks_cur[i];
        }
 
-       /* Report */
-       for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
-               if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
-                       handle_nested_irq(wm8994->irq_base + i);
-       }
-
        /* Ack any unmasked IRQs */
        for (i = 0; i < ARRAY_SIZE(status); i++) {
                if (status[i])
@@ -244,6 +238,12 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
                                         status[i]);
        }
 
+       /* Report */
+       for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
+               if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
+                       handle_nested_irq(wm8994->irq_base + i);
+       }
+
        return IRQ_HANDLED;
 }
 
index 118eb213eb3a7ea8e5731fdf7004f2b50b672b82..c7fd2c0e3f2bd212ebe607224fb9dd4943fa8546 100644 (file)
@@ -249,6 +249,12 @@ config REGULATOR_TPS6507X
          three step-down converters and two general-purpose LDO voltage regulators.
          It supports TI's software based Class-2 SmartReflex implementation.
 
+config REGULATOR_TPS65912
+       tristate "TI TPS65912 Power regulator"
+       depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
+       help
+           This driver supports TPS65912 voltage regulator chip.
+
 config REGULATOR_88PM8607
        bool "Marvell 88PM8607 Power regulators"
        depends on MFD_88PM860X=y
@@ -304,5 +310,12 @@ config REGULATOR_TPS65910
        help
          This driver supports TPS65910 voltage regulator chips.
 
+config REGULATOR_AAT2870
+       tristate "AnalogicTech AAT2870 Regulators"
+       depends on MFD_AAT2870_CORE
+       help
+         If you have a AnalogicTech AAT2870 say Y to enable the
+         regulator driver.
+
 endif
 
index 3932d2ec38f36f08ea209dfd50f0a32ba1b75121..040d5aa63535bb33d2c0e946d7878bfc7418fad8 100644 (file)
@@ -38,10 +38,12 @@ obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
 obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
 obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
 obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
 obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
 obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
+obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
new file mode 100644 (file)
index 0000000..cd41045
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * linux/drivers/regulator/aat2870-regulator.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/aat2870.h>
+
+struct aat2870_regulator {
+       struct platform_device *pdev;
+       struct regulator_desc desc;
+
+       const int *voltages; /* uV */
+
+       int min_uV;
+       int max_uV;
+
+       u8 enable_addr;
+       u8 enable_shift;
+       u8 enable_mask;
+
+       u8 voltage_addr;
+       u8 voltage_shift;
+       u8 voltage_mask;
+};
+
+static int aat2870_ldo_list_voltage(struct regulator_dev *rdev,
+                                   unsigned selector)
+{
+       struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+
+       return ri->voltages[selector];
+}
+
+static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev,
+                                      unsigned selector)
+{
+       struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+       struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+
+       return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask,
+                       (selector << ri->voltage_shift) & ri->voltage_mask);
+}
+
+static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev)
+{
+       struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+       struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+       u8 val;
+       int ret;
+
+       ret = aat2870->read(aat2870, ri->voltage_addr, &val);
+       if (ret)
+               return ret;
+
+       return (val & ri->voltage_mask) >> ri->voltage_shift;
+}
+
+static int aat2870_ldo_enable(struct regulator_dev *rdev)
+{
+       struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+       struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+
+       return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask,
+                              ri->enable_mask);
+}
+
+static int aat2870_ldo_disable(struct regulator_dev *rdev)
+{
+       struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+       struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+
+       return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0);
+}
+
+static int aat2870_ldo_is_enabled(struct regulator_dev *rdev)
+{
+       struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+       struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+       u8 val;
+       int ret;
+
+       ret = aat2870->read(aat2870, ri->enable_addr, &val);
+       if (ret)
+               return ret;
+
+       return val & ri->enable_mask ? 1 : 0;
+}
+
+static struct regulator_ops aat2870_ldo_ops = {
+       .list_voltage = aat2870_ldo_list_voltage,
+       .set_voltage_sel = aat2870_ldo_set_voltage_sel,
+       .get_voltage_sel = aat2870_ldo_get_voltage_sel,
+       .enable = aat2870_ldo_enable,
+       .disable = aat2870_ldo_disable,
+       .is_enabled = aat2870_ldo_is_enabled,
+};
+
+static const int aat2870_ldo_voltages[] = {
+       1200000, 1300000, 1500000, 1600000,
+       1800000, 2000000, 2200000, 2500000,
+       2600000, 2700000, 2800000, 2900000,
+       3000000, 3100000, 3200000, 3300000,
+};
+
+#define AAT2870_LDO(ids)                               \
+       {                                               \
+               .desc = {                               \
+                       .name = #ids,                   \
+                       .id = AAT2870_ID_##ids,         \
+                       .n_voltages = ARRAY_SIZE(aat2870_ldo_voltages), \
+                       .ops = &aat2870_ldo_ops,        \
+                       .type = REGULATOR_VOLTAGE,      \
+                       .owner = THIS_MODULE,           \
+               },                                      \
+               .voltages = aat2870_ldo_voltages,       \
+               .min_uV = 1200000,                      \
+               .max_uV = 3300000,                      \
+       }
+
+static struct aat2870_regulator aat2870_regulators[] = {
+       AAT2870_LDO(LDOA),
+       AAT2870_LDO(LDOB),
+       AAT2870_LDO(LDOC),
+       AAT2870_LDO(LDOD),
+};
+
+static struct aat2870_regulator *aat2870_get_regulator(int id)
+{
+       struct aat2870_regulator *ri = NULL;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aat2870_regulators); i++) {
+               ri = &aat2870_regulators[i];
+               if (ri->desc.id == id)
+                       break;
+       }
+
+       if (!ri)
+               return NULL;
+
+       ri->enable_addr = AAT2870_LDO_EN;
+       ri->enable_shift = id - AAT2870_ID_LDOA;
+       ri->enable_mask = 0x1 << ri->enable_shift;
+
+       ri->voltage_addr = (id - AAT2870_ID_LDOA) / 2 ?
+                          AAT2870_LDO_CD : AAT2870_LDO_AB;
+       ri->voltage_shift = (id - AAT2870_ID_LDOA) % 2 ? 0 : 4;
+       ri->voltage_mask = 0xF << ri->voltage_shift;
+
+       return ri;
+}
+
+static int aat2870_regulator_probe(struct platform_device *pdev)
+{
+       struct aat2870_regulator *ri;
+       struct regulator_dev *rdev;
+
+       ri = aat2870_get_regulator(pdev->id);
+       if (!ri) {
+               dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
+               return -EINVAL;
+       }
+       ri->pdev = pdev;
+
+       rdev = regulator_register(&ri->desc, &pdev->dev,
+                                 pdev->dev.platform_data, ri);
+       if (IS_ERR(rdev)) {
+               dev_err(&pdev->dev, "Failed to register regulator %s\n",
+                       ri->desc.name);
+               return PTR_ERR(rdev);
+       }
+       platform_set_drvdata(pdev, rdev);
+
+       return 0;
+}
+
+static int __devexit aat2870_regulator_remove(struct platform_device *pdev)
+{
+       struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+       regulator_unregister(rdev);
+       return 0;
+}
+
+static struct platform_driver aat2870_regulator_driver = {
+       .driver = {
+               .name   = "aat2870-regulator",
+               .owner  = THIS_MODULE,
+       },
+       .probe  = aat2870_regulator_probe,
+       .remove = __devexit_p(aat2870_regulator_remove),
+};
+
+static int __init aat2870_regulator_init(void)
+{
+       return platform_driver_register(&aat2870_regulator_driver);
+}
+subsys_initcall(aat2870_regulator_init);
+
+static void __exit aat2870_regulator_exit(void)
+{
+       platform_driver_unregister(&aat2870_regulator_driver);
+}
+module_exit(aat2870_regulator_exit);
+
+MODULE_DESCRIPTION("AnalogicTech AAT2870 Regulator");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c
new file mode 100644 (file)
index 0000000..3a9313e
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ * tps65912.c  --  TI tps65912
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ * This driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65912.h>
+
+/* DCDC's */
+#define TPS65912_REG_DCDC1     0
+#define TPS65912_REG_DCDC2     1
+#define TPS65912_REG_DCDC3     2
+#define TPS65912_REG_DCDC4     3
+
+/* LDOs */
+#define TPS65912_REG_LDO1      4
+#define TPS65912_REG_LDO2      5
+#define TPS65912_REG_LDO3      6
+#define TPS65912_REG_LDO4      7
+#define TPS65912_REG_LDO5      8
+#define TPS65912_REG_LDO6      9
+#define TPS65912_REG_LDO7      10
+#define TPS65912_REG_LDO8      11
+#define TPS65912_REG_LDO9      12
+#define TPS65912_REG_LDO10     13
+
+#define TPS65912_MAX_REG_ID    TPS65912_REG_LDO_10
+
+/* Number of step-down converters available */
+#define TPS65912_NUM_DCDC      4
+
+/* Number of LDO voltage regulators  available */
+#define TPS65912_NUM_LDO       10
+
+/* Number of total regulators available */
+#define TPS65912_NUM_REGULATOR         (TPS65912_NUM_DCDC + TPS65912_NUM_LDO)
+
+#define TPS65912_REG_ENABLED   0x80
+#define OP_SELREG_MASK         0x40
+#define OP_SELREG_SHIFT                6
+
+struct tps_info {
+       const char *name;
+};
+
+static struct tps_info tps65912_regs[] = {
+       {
+               .name = "DCDC1",
+       },
+       {
+               .name = "DCDC2",
+       },
+       {
+               .name = "DCDC3",
+       },
+       {
+               .name = "DCDC4",
+       },
+       {
+               .name = "LDO1",
+       },
+       {
+               .name = "LDO2",
+       },
+       {
+               .name = "LDO3",
+       },
+       {
+               .name = "LDO4",
+       },
+       {
+               .name = "LDO5",
+       },
+       {
+               .name = "LDO6",
+       },
+       {
+               .name = "LDO7",
+       },
+       {
+               .name = "LDO8",
+       },
+       {
+               .name = "LDO9",
+       },
+       {
+               .name = "LDO10",
+       },
+};
+
+struct tps65912_reg {
+       struct regulator_desc desc[TPS65912_NUM_REGULATOR];
+       struct tps65912 *mfd;
+       struct regulator_dev *rdev[TPS65912_NUM_REGULATOR];
+       struct tps_info *info[TPS65912_NUM_REGULATOR];
+       /* for read/write access */
+       struct mutex io_lock;
+       int mode;
+       int (*get_ctrl_reg)(int);
+       int dcdc1_range;
+       int dcdc2_range;
+       int dcdc3_range;
+       int dcdc4_range;
+       int pwm_mode_reg;
+       int eco_reg;
+};
+
+static int tps65912_get_range(struct tps65912_reg *pmic, int id)
+{
+       struct tps65912 *mfd = pmic->mfd;
+
+       if (id > TPS65912_REG_DCDC4)
+               return 0;
+
+       switch (id) {
+       case TPS65912_REG_DCDC1:
+               pmic->dcdc1_range = tps65912_reg_read(mfd,
+                                                       TPS65912_DCDC1_LIMIT);
+               if (pmic->dcdc1_range < 0)
+                       return pmic->dcdc1_range;
+               pmic->dcdc1_range = (pmic->dcdc1_range &
+                       DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+               return pmic->dcdc1_range;
+       case TPS65912_REG_DCDC2:
+               pmic->dcdc2_range = tps65912_reg_read(mfd,
+                                                       TPS65912_DCDC2_LIMIT);
+               if (pmic->dcdc2_range < 0)
+                       return pmic->dcdc2_range;
+               pmic->dcdc2_range = (pmic->dcdc2_range &
+                       DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+               return pmic->dcdc2_range;
+       case TPS65912_REG_DCDC3:
+               pmic->dcdc3_range = tps65912_reg_read(mfd,
+                                                       TPS65912_DCDC3_LIMIT);
+               if (pmic->dcdc3_range < 0)
+                       return pmic->dcdc3_range;
+               pmic->dcdc3_range = (pmic->dcdc3_range &
+                       DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+               return pmic->dcdc3_range;
+       case TPS65912_REG_DCDC4:
+               pmic->dcdc4_range = tps65912_reg_read(mfd,
+                                                       TPS65912_DCDC4_LIMIT);
+               if (pmic->dcdc4_range < 0)
+                       return pmic->dcdc4_range;
+               pmic->dcdc4_range = (pmic->dcdc4_range &
+                       DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+               return pmic->dcdc4_range;
+       default:
+               return 0;
+       }
+}
+
+static unsigned long tps65912_vsel_to_uv_range0(u8 vsel)
+{
+       unsigned long uv;
+
+       uv = ((vsel * 12500) + 500000);
+       return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_range1(u8 vsel)
+{
+       unsigned long uv;
+
+        uv = ((vsel * 12500) + 700000);
+       return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_range2(u8 vsel)
+{
+       unsigned long uv;
+
+       uv = ((vsel * 25000) + 500000);
+       return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_range3(u8 vsel)
+{
+       unsigned long uv;
+
+       if (vsel == 0x3f)
+               uv = 3800000;
+       else
+               uv = ((vsel * 50000) + 500000);
+
+       return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel)
+{
+       unsigned long uv = 0;
+
+       if (vsel <= 32)
+               uv = ((vsel * 25000) + 800000);
+       else if (vsel > 32 && vsel <= 60)
+               uv = (((vsel - 32) * 50000) + 1600000);
+       else if (vsel > 60)
+               uv = (((vsel - 60) * 100000) + 3000000);
+
+       return uv;
+}
+
+static int tps65912_get_ctrl_register(int id)
+{
+       switch (id) {
+       case TPS65912_REG_DCDC1:
+               return TPS65912_DCDC1_AVS;
+       case TPS65912_REG_DCDC2:
+               return TPS65912_DCDC2_AVS;
+       case TPS65912_REG_DCDC3:
+               return TPS65912_DCDC3_AVS;
+       case TPS65912_REG_DCDC4:
+               return TPS65912_DCDC4_AVS;
+       case TPS65912_REG_LDO1:
+               return TPS65912_LDO1_AVS;
+       case TPS65912_REG_LDO2:
+               return TPS65912_LDO2_AVS;
+       case TPS65912_REG_LDO3:
+               return TPS65912_LDO3_AVS;
+       case TPS65912_REG_LDO4:
+               return TPS65912_LDO4_AVS;
+       case TPS65912_REG_LDO5:
+               return TPS65912_LDO5;
+       case TPS65912_REG_LDO6:
+               return TPS65912_LDO6;
+       case TPS65912_REG_LDO7:
+               return TPS65912_LDO7;
+       case TPS65912_REG_LDO8:
+               return TPS65912_LDO8;
+       case TPS65912_REG_LDO9:
+               return TPS65912_LDO9;
+       case TPS65912_REG_LDO10:
+               return TPS65912_LDO10;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int tps65912_get_dcdc_sel_register(struct tps65912_reg *pmic, int id)
+{
+       struct tps65912 *mfd = pmic->mfd;
+       int opvsel = 0, sr = 0;
+       u8 reg = 0;
+
+       if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_DCDC4)
+               return -EINVAL;
+
+       switch (id) {
+       case TPS65912_REG_DCDC1:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP);
+               sr = ((opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT);
+               if (sr)
+                       reg = TPS65912_DCDC1_AVS;
+               else
+                       reg = TPS65912_DCDC1_OP;
+               break;
+       case TPS65912_REG_DCDC2:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP);
+               sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+               if (sr)
+                       reg = TPS65912_DCDC2_AVS;
+               else
+                       reg = TPS65912_DCDC2_OP;
+               break;
+       case TPS65912_REG_DCDC3:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP);
+               sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+               if (sr)
+                       reg = TPS65912_DCDC3_AVS;
+               else
+                       reg = TPS65912_DCDC3_OP;
+               break;
+       case TPS65912_REG_DCDC4:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP);
+               sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+               if (sr)
+                       reg = TPS65912_DCDC4_AVS;
+               else
+                       reg = TPS65912_DCDC4_OP;
+               break;
+       }
+       return reg;
+}
+
+static int tps65912_get_ldo_sel_register(struct tps65912_reg *pmic, int id)
+{
+       struct tps65912 *mfd = pmic->mfd;
+       int opvsel = 0, sr = 0;
+       u8 reg = 0;
+
+       if (id < TPS65912_REG_LDO1 || id > TPS65912_REG_LDO10)
+               return -EINVAL;
+
+       switch (id) {
+       case TPS65912_REG_LDO1:
+               opvsel = tps65912_reg_read(mfd, TPS65912_LDO1_OP);
+               sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+               if (sr)
+                       reg = TPS65912_LDO1_AVS;
+               else
+                       reg = TPS65912_LDO1_OP;
+               break;
+       case TPS65912_REG_LDO2:
+               opvsel = tps65912_reg_read(mfd, TPS65912_LDO2_OP);
+               sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+               if (sr)
+                       reg = TPS65912_LDO2_AVS;
+               else
+                       reg = TPS65912_LDO2_OP;
+               break;
+       case TPS65912_REG_LDO3:
+               opvsel = tps65912_reg_read(mfd, TPS65912_LDO3_OP);
+               sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+               if (sr)
+                       reg = TPS65912_LDO3_AVS;
+               else
+                       reg = TPS65912_LDO3_OP;
+               break;
+       case TPS65912_REG_LDO4:
+               opvsel = tps65912_reg_read(mfd, TPS65912_LDO4_OP);
+               sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+               if (sr)
+                       reg = TPS65912_LDO4_AVS;
+               else
+                       reg = TPS65912_LDO4_OP;
+               break;
+       case TPS65912_REG_LDO5:
+               reg = TPS65912_LDO5;
+               break;
+       case TPS65912_REG_LDO6:
+               reg = TPS65912_LDO6;
+               break;
+       case TPS65912_REG_LDO7:
+               reg = TPS65912_LDO7;
+               break;
+       case TPS65912_REG_LDO8:
+               reg = TPS65912_LDO8;
+               break;
+       case TPS65912_REG_LDO9:
+               reg = TPS65912_LDO9;
+               break;
+       case TPS65912_REG_LDO10:
+               reg = TPS65912_LDO10;
+               break;
+       }
+
+       return reg;
+}
+
+static int tps65912_get_mode_regiters(struct tps65912_reg *pmic, int id)
+{
+       switch (id) {
+       case TPS65912_REG_DCDC1:
+               pmic->pwm_mode_reg = TPS65912_DCDC1_CTRL;
+               pmic->eco_reg = TPS65912_DCDC1_AVS;
+               break;
+       case TPS65912_REG_DCDC2:
+               pmic->pwm_mode_reg = TPS65912_DCDC2_CTRL;
+               pmic->eco_reg = TPS65912_DCDC2_AVS;
+               break;
+       case TPS65912_REG_DCDC3:
+               pmic->pwm_mode_reg = TPS65912_DCDC3_CTRL;
+               pmic->eco_reg = TPS65912_DCDC3_AVS;
+               break;
+       case TPS65912_REG_DCDC4:
+               pmic->pwm_mode_reg = TPS65912_DCDC4_CTRL;
+               pmic->eco_reg = TPS65912_DCDC4_AVS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int tps65912_reg_is_enabled(struct regulator_dev *dev)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int reg, value, id = rdev_get_id(dev);
+
+       if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10)
+               return -EINVAL;
+
+       reg = pmic->get_ctrl_reg(id);
+       if (reg < 0)
+               return reg;
+
+       value = tps65912_reg_read(mfd, reg);
+       if (value < 0)
+               return value;
+
+       return value & TPS65912_REG_ENABLED;
+}
+
+static int tps65912_reg_enable(struct regulator_dev *dev)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int id = rdev_get_id(dev);
+       int reg;
+
+       if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10)
+               return -EINVAL;
+
+       reg = pmic->get_ctrl_reg(id);
+       if (reg < 0)
+               return reg;
+
+       return tps65912_set_bits(mfd, reg, TPS65912_REG_ENABLED);
+}
+
+static int tps65912_reg_disable(struct regulator_dev *dev)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int id = rdev_get_id(dev), reg;
+
+       reg = pmic->get_ctrl_reg(id);
+       if (reg < 0)
+               return reg;
+
+       return tps65912_clear_bits(mfd, reg, TPS65912_REG_ENABLED);
+}
+
+static int tps65912_set_mode(struct regulator_dev *dev, unsigned int mode)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int pwm_mode, eco, id = rdev_get_id(dev);
+
+       tps65912_get_mode_regiters(pmic, id);
+
+       pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg);
+       eco = tps65912_reg_read(mfd, pmic->eco_reg);
+
+       pwm_mode &= DCDCCTRL_DCDC_MODE_MASK;
+       eco &= DCDC_AVS_ECO_MASK;
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               /* Verify if mode alredy set */
+               if (pwm_mode && !eco)
+                       break;
+               tps65912_set_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
+               tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
+               break;
+       case REGULATOR_MODE_NORMAL:
+       case REGULATOR_MODE_IDLE:
+               if (!pwm_mode && !eco)
+                       break;
+               tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
+               tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
+               break;
+       case REGULATOR_MODE_STANDBY:
+               if (!pwm_mode && eco)
+                       break;
+               tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
+               tps65912_set_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static unsigned int tps65912_get_mode(struct regulator_dev *dev)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int pwm_mode, eco, mode = 0, id = rdev_get_id(dev);
+
+       tps65912_get_mode_regiters(pmic, id);
+
+       pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg);
+       eco = tps65912_reg_read(mfd, pmic->eco_reg);
+
+       pwm_mode &= DCDCCTRL_DCDC_MODE_MASK;
+       eco &= DCDC_AVS_ECO_MASK;
+
+       if (pwm_mode && !eco)
+               mode = REGULATOR_MODE_FAST;
+       else if (!pwm_mode && !eco)
+               mode = REGULATOR_MODE_NORMAL;
+       else if (!pwm_mode && eco)
+               mode = REGULATOR_MODE_STANDBY;
+
+       return mode;
+}
+
+static int tps65912_get_voltage_dcdc(struct regulator_dev *dev)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int id = rdev_get_id(dev), voltage = 0, range;
+       int opvsel = 0, avsel = 0, sr, vsel;
+
+       switch (id) {
+       case TPS65912_REG_DCDC1:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP);
+               avsel = tps65912_reg_read(mfd, TPS65912_DCDC1_AVS);
+               range = pmic->dcdc1_range;
+               break;
+       case TPS65912_REG_DCDC2:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP);
+               avsel = tps65912_reg_read(mfd, TPS65912_DCDC2_AVS);
+               range = pmic->dcdc2_range;
+               break;
+       case TPS65912_REG_DCDC3:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP);
+               avsel = tps65912_reg_read(mfd, TPS65912_DCDC3_AVS);
+               range = pmic->dcdc3_range;
+               break;
+       case TPS65912_REG_DCDC4:
+               opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP);
+               avsel = tps65912_reg_read(mfd, TPS65912_DCDC4_AVS);
+               range = pmic->dcdc4_range;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+       if (sr)
+               vsel = avsel;
+       else
+               vsel = opvsel;
+       vsel &= 0x3F;
+
+       switch (range) {
+       case 0:
+               /* 0.5 - 1.2875V in 12.5mV steps */
+               voltage = tps65912_vsel_to_uv_range0(vsel);
+               break;
+       case 1:
+               /* 0.7 - 1.4875V in 12.5mV steps */
+               voltage = tps65912_vsel_to_uv_range1(vsel);
+               break;
+       case 2:
+               /* 0.5 - 2.075V in 25mV steps */
+               voltage = tps65912_vsel_to_uv_range2(vsel);
+               break;
+       case 3:
+               /* 0.5 - 3.8V in 50mV steps */
+               voltage = tps65912_vsel_to_uv_range3(vsel);
+               break;
+       }
+       return voltage;
+}
+
+static int tps65912_set_voltage_dcdc(struct regulator_dev *dev,
+                                               unsigned selector)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int id = rdev_get_id(dev);
+       int value;
+       u8 reg;
+
+       reg = tps65912_get_dcdc_sel_register(pmic, id);
+       value = tps65912_reg_read(mfd, reg);
+       value &= 0xC0;
+       return tps65912_reg_write(mfd, reg, selector | value);
+}
+
+static int tps65912_get_voltage_ldo(struct regulator_dev *dev)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int id = rdev_get_id(dev);
+       int vsel = 0;
+       u8 reg;
+
+       reg = tps65912_get_ldo_sel_register(pmic, id);
+       vsel = tps65912_reg_read(mfd, reg);
+       vsel &= 0x3F;
+
+       return tps65912_vsel_to_uv_ldo(vsel);
+}
+
+static int tps65912_set_voltage_ldo(struct regulator_dev *dev,
+                                               unsigned selector)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       struct tps65912 *mfd = pmic->mfd;
+       int id = rdev_get_id(dev), reg, value;
+
+       reg = tps65912_get_ldo_sel_register(pmic, id);
+       value = tps65912_reg_read(mfd, reg);
+       value &= 0xC0;
+       return tps65912_reg_write(mfd, reg, selector | value);
+}
+
+static int tps65912_list_voltage_dcdc(struct regulator_dev *dev,
+                                       unsigned selector)
+{
+       struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+       int range, voltage = 0, id = rdev_get_id(dev);
+
+       switch (id) {
+       case TPS65912_REG_DCDC1:
+               range = pmic->dcdc1_range;
+               break;
+       case TPS65912_REG_DCDC2:
+               range = pmic->dcdc2_range;
+               break;
+       case TPS65912_REG_DCDC3:
+               range = pmic->dcdc3_range;
+               break;
+       case TPS65912_REG_DCDC4:
+               range = pmic->dcdc4_range;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (range) {
+       case 0:
+               /* 0.5 - 1.2875V in 12.5mV steps */
+               voltage = tps65912_vsel_to_uv_range0(selector);
+               break;
+       case 1:
+               /* 0.7 - 1.4875V in 12.5mV steps */
+               voltage = tps65912_vsel_to_uv_range1(selector);
+               break;
+       case 2:
+               /* 0.5 - 2.075V in 25mV steps */
+               voltage = tps65912_vsel_to_uv_range2(selector);
+               break;
+       case 3:
+               /* 0.5 - 3.8V in 50mV steps */
+               voltage = tps65912_vsel_to_uv_range3(selector);
+               break;
+       }
+       return voltage;
+}
+
+static int tps65912_list_voltage_ldo(struct regulator_dev *dev,
+                                       unsigned selector)
+{
+       int ldo = rdev_get_id(dev);
+
+       if (ldo < TPS65912_REG_LDO1 || ldo > TPS65912_REG_LDO10)
+               return -EINVAL;
+
+       return tps65912_vsel_to_uv_ldo(selector);
+}
+
+/* Operations permitted on DCDCx */
+static struct regulator_ops tps65912_ops_dcdc = {
+       .is_enabled = tps65912_reg_is_enabled,
+       .enable = tps65912_reg_enable,
+       .disable = tps65912_reg_disable,
+       .set_mode = tps65912_set_mode,
+       .get_mode = tps65912_get_mode,
+       .get_voltage = tps65912_get_voltage_dcdc,
+       .set_voltage_sel = tps65912_set_voltage_dcdc,
+       .list_voltage = tps65912_list_voltage_dcdc,
+};
+
+/* Operations permitted on LDOx */
+static struct regulator_ops tps65912_ops_ldo = {
+       .is_enabled = tps65912_reg_is_enabled,
+       .enable = tps65912_reg_enable,
+       .disable = tps65912_reg_disable,
+       .get_voltage = tps65912_get_voltage_ldo,
+       .set_voltage_sel = tps65912_set_voltage_ldo,
+       .list_voltage = tps65912_list_voltage_ldo,
+};
+
+static __devinit int tps65912_probe(struct platform_device *pdev)
+{
+       struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
+       struct tps_info *info;
+       struct regulator_init_data *reg_data;
+       struct regulator_dev *rdev;
+       struct tps65912_reg *pmic;
+       struct tps65912_board *pmic_plat_data;
+       int i, err;
+
+       pmic_plat_data = dev_get_platdata(tps65912->dev);
+       if (!pmic_plat_data)
+               return -EINVAL;
+
+       reg_data = pmic_plat_data->tps65912_pmic_init_data;
+
+       pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
+       if (!pmic)
+               return -ENOMEM;
+
+       mutex_init(&pmic->io_lock);
+       pmic->mfd = tps65912;
+       platform_set_drvdata(pdev, pmic);
+
+       pmic->get_ctrl_reg = &tps65912_get_ctrl_register;
+       info = tps65912_regs;
+
+       for (i = 0; i < TPS65912_NUM_REGULATOR; i++, info++, reg_data++) {
+               int range = 0;
+               /* Register the regulators */
+               pmic->info[i] = info;
+
+               pmic->desc[i].name = info->name;
+               pmic->desc[i].id = i;
+               pmic->desc[i].n_voltages = 64;
+               pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ?
+                       &tps65912_ops_ldo : &tps65912_ops_dcdc);
+               pmic->desc[i].type = REGULATOR_VOLTAGE;
+               pmic->desc[i].owner = THIS_MODULE;
+               range = tps65912_get_range(pmic, i);
+               rdev = regulator_register(&pmic->desc[i],
+                                       tps65912->dev, reg_data, pmic);
+               if (IS_ERR(rdev)) {
+                       dev_err(tps65912->dev,
+                               "failed to register %s regulator\n",
+                               pdev->name);
+                       err = PTR_ERR(rdev);
+                       goto err;
+               }
+
+               /* Save regulator for cleanup */
+               pmic->rdev[i] = rdev;
+       }
+       return 0;
+
+err:
+       while (--i >= 0)
+               regulator_unregister(pmic->rdev[i]);
+
+       kfree(pmic);
+       return err;
+}
+
+static int __devexit tps65912_remove(struct platform_device *pdev)
+{
+       struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < TPS65912_NUM_REGULATOR; i++)
+               regulator_unregister(tps65912_reg->rdev[i]);
+
+       kfree(tps65912_reg);
+       return 0;
+}
+
+static struct platform_driver tps65912_driver = {
+       .driver = {
+               .name = "tps65912-pmic",
+               .owner = THIS_MODULE,
+       },
+       .probe = tps65912_probe,
+       .remove = __devexit_p(tps65912_remove),
+};
+
+/**
+ * tps65912_init
+ *
+ * Module init function
+ */
+static int __init tps65912_init(void)
+{
+       return platform_driver_register(&tps65912_driver);
+}
+subsys_initcall(tps65912_init);
+
+/**
+ * tps65912_cleanup
+ *
+ * Module exit function
+ */
+static void __exit tps65912_cleanup(void)
+{
+       platform_driver_unregister(&tps65912_driver);
+}
+module_exit(tps65912_cleanup);
+
+MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65912 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65912-pmic");
index 1e54b8b7f698e464d909de332bc5c7efe594ae4f..69407e72aac1a9291d2ceb3dec812da2063cf94b 100644 (file)
@@ -335,6 +335,13 @@ config BACKLIGHT_PCF50633
          If you have a backlight driven by a NXP PCF50633 MFD, say Y here to
          enable its driver.
 
+config BACKLIGHT_AAT2870
+       bool "AnalogicTech AAT2870 Backlight"
+       depends on BACKLIGHT_CLASS_DEVICE && MFD_AAT2870_CORE
+       help
+         If you have a AnalogicTech AAT2870 say Y to enable the
+         backlight driver.
+
 endif # BACKLIGHT_CLASS_DEVICE
 
 endif # BACKLIGHT_LCD_SUPPORT
index bf1dd92b75272e16dea09fc65a67bfd7a346d53d..fdd1fc4b277062333846f4f9cd3ba75855d394a7 100644 (file)
@@ -38,4 +38,5 @@ obj-$(CONFIG_BACKLIGHT_ADP8860)       += adp8860_bl.o
 obj-$(CONFIG_BACKLIGHT_ADP8870)        += adp8870_bl.o
 obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
 obj-$(CONFIG_BACKLIGHT_PCF50633)       += pcf50633-backlight.o
+obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o
 
diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c
new file mode 100644 (file)
index 0000000..4952a61
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * linux/drivers/video/backlight/aat2870_bl.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/mfd/aat2870.h>
+
+struct aat2870_bl_driver_data {
+       struct platform_device *pdev;
+       struct backlight_device *bd;
+
+       int channels;
+       int max_current;
+       int brightness; /* current brightness */
+};
+
+static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl,
+                                    int brightness)
+{
+       struct backlight_device *bd = aat2870_bl->bd;
+       int val;
+
+       val = brightness * aat2870_bl->max_current;
+       val /= bd->props.max_brightness;
+
+       return val;
+}
+
+static inline int aat2870_bl_enable(struct aat2870_bl_driver_data *aat2870_bl)
+{
+       struct aat2870_data *aat2870
+                       = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+
+       return aat2870->write(aat2870, AAT2870_BL_CH_EN,
+                             (u8)aat2870_bl->channels);
+}
+
+static inline int aat2870_bl_disable(struct aat2870_bl_driver_data *aat2870_bl)
+{
+       struct aat2870_data *aat2870
+                       = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+
+       return aat2870->write(aat2870, AAT2870_BL_CH_EN, 0x0);
+}
+
+static int aat2870_bl_get_brightness(struct backlight_device *bd)
+{
+       return bd->props.brightness;
+}
+
+static int aat2870_bl_update_status(struct backlight_device *bd)
+{
+       struct aat2870_bl_driver_data *aat2870_bl = dev_get_drvdata(&bd->dev);
+       struct aat2870_data *aat2870 =
+                       dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+       int brightness = bd->props.brightness;
+       int ret;
+
+       if ((brightness < 0) || (bd->props.max_brightness < brightness)) {
+               dev_err(&bd->dev, "invalid brightness, %d\n", brightness);
+               return -EINVAL;
+       }
+
+       dev_dbg(&bd->dev, "brightness=%d, power=%d, state=%d\n",
+                bd->props.brightness, bd->props.power, bd->props.state);
+
+       if ((bd->props.power != FB_BLANK_UNBLANK) ||
+                       (bd->props.state & BL_CORE_FBBLANK) ||
+                       (bd->props.state & BL_CORE_SUSPENDED))
+               brightness = 0;
+
+       ret = aat2870->write(aat2870, AAT2870_BLM,
+                            (u8)aat2870_brightness(aat2870_bl, brightness));
+       if (ret < 0)
+               return ret;
+
+       if (brightness == 0) {
+               ret = aat2870_bl_disable(aat2870_bl);
+               if (ret < 0)
+                       return ret;
+       } else if (aat2870_bl->brightness == 0) {
+               ret = aat2870_bl_enable(aat2870_bl);
+               if (ret < 0)
+                       return ret;
+       }
+
+       aat2870_bl->brightness = brightness;
+
+       return 0;
+}
+
+static int aat2870_bl_check_fb(struct backlight_device *bd, struct fb_info *fi)
+{
+       return 1;
+}
+
+static const struct backlight_ops aat2870_bl_ops = {
+       .options = BL_CORE_SUSPENDRESUME,
+       .get_brightness = aat2870_bl_get_brightness,
+       .update_status = aat2870_bl_update_status,
+       .check_fb = aat2870_bl_check_fb,
+};
+
+static int aat2870_bl_probe(struct platform_device *pdev)
+{
+       struct aat2870_bl_platform_data *pdata = pdev->dev.platform_data;
+       struct aat2870_bl_driver_data *aat2870_bl;
+       struct backlight_device *bd;
+       struct backlight_properties props;
+       int ret = 0;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "No platform data\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       if (pdev->id != AAT2870_ID_BL) {
+               dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       aat2870_bl = kzalloc(sizeof(struct aat2870_bl_driver_data), GFP_KERNEL);
+       if (!aat2870_bl) {
+               dev_err(&pdev->dev,
+                       "Failed to allocate memory for aat2870 backlight\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+
+       props.type = BACKLIGHT_RAW;
+       bd = backlight_device_register("aat2870-backlight", &pdev->dev,
+                                      aat2870_bl, &aat2870_bl_ops, &props);
+       if (!bd) {
+               dev_err(&pdev->dev,
+                       "Failed allocate memory for backlight device\n");
+               ret = -ENOMEM;
+               goto out_kfree;
+       }
+
+       aat2870_bl->pdev = pdev;
+       platform_set_drvdata(pdev, aat2870_bl);
+
+       aat2870_bl->bd = bd;
+
+       if (pdata->channels > 0)
+               aat2870_bl->channels = pdata->channels;
+       else
+               aat2870_bl->channels = AAT2870_BL_CH_ALL;
+
+       if (pdata->max_brightness > 0)
+               aat2870_bl->max_current = pdata->max_current;
+       else
+               aat2870_bl->max_current = AAT2870_CURRENT_27_9;
+
+       if (pdata->max_brightness > 0)
+               bd->props.max_brightness = pdata->max_brightness;
+       else
+               bd->props.max_brightness = 255;
+
+       aat2870_bl->brightness = 0;
+       bd->props.power = FB_BLANK_UNBLANK;
+       bd->props.brightness = bd->props.max_brightness;
+
+       ret = aat2870_bl_update_status(bd);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to initialize\n");
+               goto out_bl_dev_unregister;
+       }
+
+       return 0;
+
+out_bl_dev_unregister:
+       backlight_device_unregister(bd);
+out_kfree:
+       kfree(aat2870_bl);
+out:
+       return ret;
+}
+
+static int aat2870_bl_remove(struct platform_device *pdev)
+{
+       struct aat2870_bl_driver_data *aat2870_bl = platform_get_drvdata(pdev);
+       struct backlight_device *bd = aat2870_bl->bd;
+
+       bd->props.power = FB_BLANK_POWERDOWN;
+       bd->props.brightness = 0;
+       backlight_update_status(bd);
+
+       backlight_device_unregister(bd);
+       kfree(aat2870_bl);
+
+       return 0;
+}
+
+static struct platform_driver aat2870_bl_driver = {
+       .driver = {
+               .name   = "aat2870-backlight",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = aat2870_bl_probe,
+       .remove         = aat2870_bl_remove,
+};
+
+static int __init aat2870_bl_init(void)
+{
+       return platform_driver_register(&aat2870_bl_driver);
+}
+subsys_initcall(aat2870_bl_init);
+
+static void __exit aat2870_bl_exit(void)
+{
+       platform_driver_unregister(&aat2870_bl_driver);
+}
+module_exit(aat2870_bl_exit);
+
+MODULE_DESCRIPTION("AnalogicTech AAT2870 Backlight");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/include/linux/mfd/aat2870.h b/include/linux/mfd/aat2870.h
new file mode 100644 (file)
index 0000000..89212df
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * linux/include/linux/mfd/aat2870.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_MFD_AAT2870_H
+#define __LINUX_MFD_AAT2870_H
+
+#include <linux/debugfs.h>
+#include <linux/i2c.h>
+
+/* Register offsets */
+#define AAT2870_BL_CH_EN       0x00
+#define AAT2870_BLM            0x01
+#define AAT2870_BLS            0x02
+#define AAT2870_BL1            0x03
+#define AAT2870_BL2            0x04
+#define AAT2870_BL3            0x05
+#define AAT2870_BL4            0x06
+#define AAT2870_BL5            0x07
+#define AAT2870_BL6            0x08
+#define AAT2870_BL7            0x09
+#define AAT2870_BL8            0x0A
+#define AAT2870_FLR            0x0B
+#define AAT2870_FM             0x0C
+#define AAT2870_FS             0x0D
+#define AAT2870_ALS_CFG0       0x0E
+#define AAT2870_ALS_CFG1       0x0F
+#define AAT2870_ALS_CFG2       0x10
+#define AAT2870_AMB            0x11
+#define AAT2870_ALS0           0x12
+#define AAT2870_ALS1           0x13
+#define AAT2870_ALS2           0x14
+#define AAT2870_ALS3           0x15
+#define AAT2870_ALS4           0x16
+#define AAT2870_ALS5           0x17
+#define AAT2870_ALS6           0x18
+#define AAT2870_ALS7           0x19
+#define AAT2870_ALS8           0x1A
+#define AAT2870_ALS9           0x1B
+#define AAT2870_ALSA           0x1C
+#define AAT2870_ALSB           0x1D
+#define AAT2870_ALSC           0x1E
+#define AAT2870_ALSD           0x1F
+#define AAT2870_ALSE           0x20
+#define AAT2870_ALSF           0x21
+#define AAT2870_SUB_SET                0x22
+#define AAT2870_SUB_CTRL       0x23
+#define AAT2870_LDO_AB         0x24
+#define AAT2870_LDO_CD         0x25
+#define AAT2870_LDO_EN         0x26
+#define AAT2870_REG_NUM                0x27
+
+/* Device IDs */
+enum aat2870_id {
+       AAT2870_ID_BL,
+       AAT2870_ID_LDOA,
+       AAT2870_ID_LDOB,
+       AAT2870_ID_LDOC,
+       AAT2870_ID_LDOD
+};
+
+/* Backlight channels */
+#define AAT2870_BL_CH1         0x01
+#define AAT2870_BL_CH2         0x02
+#define AAT2870_BL_CH3         0x04
+#define AAT2870_BL_CH4         0x08
+#define AAT2870_BL_CH5         0x10
+#define AAT2870_BL_CH6         0x20
+#define AAT2870_BL_CH7         0x40
+#define AAT2870_BL_CH8         0x80
+#define AAT2870_BL_CH_ALL      0xFF
+
+/* Backlight current magnitude (mA) */
+enum aat2870_current {
+       AAT2870_CURRENT_0_45,
+       AAT2870_CURRENT_0_90,
+       AAT2870_CURRENT_1_80,
+       AAT2870_CURRENT_2_70,
+       AAT2870_CURRENT_3_60,
+       AAT2870_CURRENT_4_50,
+       AAT2870_CURRENT_5_40,
+       AAT2870_CURRENT_6_30,
+       AAT2870_CURRENT_7_20,
+       AAT2870_CURRENT_8_10,
+       AAT2870_CURRENT_9_00,
+       AAT2870_CURRENT_9_90,
+       AAT2870_CURRENT_10_8,
+       AAT2870_CURRENT_11_7,
+       AAT2870_CURRENT_12_6,
+       AAT2870_CURRENT_13_5,
+       AAT2870_CURRENT_14_4,
+       AAT2870_CURRENT_15_3,
+       AAT2870_CURRENT_16_2,
+       AAT2870_CURRENT_17_1,
+       AAT2870_CURRENT_18_0,
+       AAT2870_CURRENT_18_9,
+       AAT2870_CURRENT_19_8,
+       AAT2870_CURRENT_20_7,
+       AAT2870_CURRENT_21_6,
+       AAT2870_CURRENT_22_5,
+       AAT2870_CURRENT_23_4,
+       AAT2870_CURRENT_24_3,
+       AAT2870_CURRENT_25_2,
+       AAT2870_CURRENT_26_1,
+       AAT2870_CURRENT_27_0,
+       AAT2870_CURRENT_27_9
+};
+
+struct aat2870_register {
+       bool readable;
+       bool writeable;
+       u8 value;
+};
+
+struct aat2870_data {
+       struct device *dev;
+       struct i2c_client *client;
+
+       struct mutex io_lock;
+       struct aat2870_register *reg_cache; /* register cache */
+       int en_pin; /* enable GPIO pin (if < 0, ignore this value) */
+       bool is_enable;
+
+       /* init and uninit for platform specified */
+       int (*init)(struct aat2870_data *aat2870);
+       void (*uninit)(struct aat2870_data *aat2870);
+
+       /* i2c io funcntions */
+       int (*read)(struct aat2870_data *aat2870, u8 addr, u8 *val);
+       int (*write)(struct aat2870_data *aat2870, u8 addr, u8 val);
+       int (*update)(struct aat2870_data *aat2870, u8 addr, u8 mask, u8 val);
+
+       /* for debugfs */
+       struct dentry *dentry_root;
+       struct dentry *dentry_reg;
+};
+
+struct aat2870_subdev_info {
+       int id;
+       const char *name;
+       void *platform_data;
+};
+
+struct aat2870_platform_data {
+       int en_pin; /* enable GPIO pin (if < 0, ignore this value) */
+
+       struct aat2870_subdev_info *subdevs;
+       int num_subdevs;
+
+       /* init and uninit for platform specified */
+       int (*init)(struct aat2870_data *aat2870);
+       void (*uninit)(struct aat2870_data *aat2870);
+};
+
+struct aat2870_bl_platform_data {
+       /* backlight channels, default is AAT2870_BL_CH_ALL */
+       int channels;
+       /* backlight current magnitude, default is AAT2870_CURRENT_27_9 */
+       int max_current;
+       /* maximum brightness, default is 255 */
+       int max_brightness;
+};
+
+#endif /* __LINUX_MFD_AAT2870_H */
index b318430751983cf284069b99e6f2f4fa387deb94..838c6b487cc533193f60b7372f85a5080c89c7a5 100644 (file)
@@ -28,6 +28,7 @@
 #define AB8500_INTERRUPT       0xE
 #define AB8500_RTC             0xF
 #define AB8500_MISC            0x10
+#define AB8500_DEVELOPMENT     0x11
 #define AB8500_DEBUG           0x12
 #define AB8500_PROD_TEST       0x13
 #define AB8500_OTP_EMUL                0x15
 #define AB8500_INT_ACC_DETECT_21DB_F   37
 #define AB8500_INT_ACC_DETECT_21DB_R   38
 #define AB8500_INT_GP_SW_ADC_CONV_END  39
-#define AB8500_INT_ACC_DETECT_1DB_F    33
-#define AB8500_INT_ACC_DETECT_1DB_R    34
-#define AB8500_INT_ACC_DETECT_22DB_F   35
-#define AB8500_INT_ACC_DETECT_22DB_R   36
-#define AB8500_INT_ACC_DETECT_21DB_F   37
-#define AB8500_INT_ACC_DETECT_21DB_R   38
-#define AB8500_INT_GP_SW_ADC_CONV_END  39
 #define AB8500_INT_GPIO6R              40
 #define AB8500_INT_GPIO7R              41
 #define AB8500_INT_GPIO8R              42
index e762c270d8d45aece64e91fc46f0ec16ec773968..be1af7c42e576ecd9881276401df8ee5a49d694f 100644 (file)
@@ -57,6 +57,7 @@ struct stmpe_variant_info;
  * @irq_lock: IRQ bus lock
  * @dev: device, mostly for dev_dbg()
  * @i2c: i2c client
+ * @partnum: part number
  * @variant: the detected STMPE model number
  * @regs: list of addresses of registers which are at different addresses on
  *       different variants.  Indexed by one of STMPE_IDX_*.
@@ -121,6 +122,8 @@ struct stmpe_keypad_platform_data {
  * @norequest_mask: bitmask specifying which GPIOs should _not_ be
  *                 requestable due to different usage (e.g. touch, keypad)
  *                 STMPE_GPIO_NOREQ_* macros can be used here.
+ * @setup: board specific setup callback.
+ * @remove: board specific remove callback
  */
 struct stmpe_gpio_platform_data {
        int gpio_base;
index 73572c65d04f17e8bb6729689035bbe721c516f8..82b4c8801a4fc673fc4c7c0451bdb11330c821c7 100644 (file)
@@ -791,6 +791,7 @@ int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask);
 void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base);
 int tps65910_irq_init(struct tps65910 *tps65910, int irq,
                struct tps65910_platform_data *pdata);
+int tps65910_irq_exit(struct tps65910 *tps65910);
 
 static inline int tps65910_chip_id(struct tps65910 *tps65910)
 {
diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h
new file mode 100644 (file)
index 0000000..aaceab4
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * tps65912.h  --  TI TPS6591x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_TPS65912_H
+#define __LINUX_MFD_TPS65912_H
+
+/* TPS regulator type list */
+#define REGULATOR_LDO          0
+#define REGULATOR_DCDC         1
+
+/*
+ * List of registers for TPS65912
+ */
+
+#define TPS65912_DCDC1_CTRL            0x00
+#define TPS65912_DCDC2_CTRL            0x01
+#define TPS65912_DCDC3_CTRL            0x02
+#define TPS65912_DCDC4_CTRL            0x03
+#define TPS65912_DCDC1_OP              0x04
+#define TPS65912_DCDC1_AVS             0x05
+#define TPS65912_DCDC1_LIMIT           0x06
+#define TPS65912_DCDC2_OP              0x07
+#define TPS65912_DCDC2_AVS             0x08
+#define TPS65912_DCDC2_LIMIT           0x09
+#define TPS65912_DCDC3_OP              0x0A
+#define TPS65912_DCDC3_AVS             0x0B
+#define TPS65912_DCDC3_LIMIT           0x0C
+#define TPS65912_DCDC4_OP              0x0D
+#define TPS65912_DCDC4_AVS             0x0E
+#define TPS65912_DCDC4_LIMIT           0x0F
+#define TPS65912_LDO1_OP               0x10
+#define TPS65912_LDO1_AVS              0x11
+#define TPS65912_LDO1_LIMIT            0x12
+#define TPS65912_LDO2_OP               0x13
+#define TPS65912_LDO2_AVS              0x14
+#define TPS65912_LDO2_LIMIT            0x15
+#define TPS65912_LDO3_OP               0x16
+#define TPS65912_LDO3_AVS              0x17
+#define TPS65912_LDO3_LIMIT            0x18
+#define TPS65912_LDO4_OP               0x19
+#define TPS65912_LDO4_AVS              0x1A
+#define TPS65912_LDO4_LIMIT            0x1B
+#define TPS65912_LDO5                  0x1C
+#define TPS65912_LDO6                  0x1D
+#define TPS65912_LDO7                  0x1E
+#define TPS65912_LDO8                  0x1F
+#define TPS65912_LDO9                  0x20
+#define TPS65912_LDO10                 0x21
+#define TPS65912_THRM                  0x22
+#define TPS65912_CLK32OUT              0x23
+#define TPS65912_DEVCTRL               0x24
+#define TPS65912_DEVCTRL2              0x25
+#define TPS65912_I2C_SPI_CFG           0x26
+#define TPS65912_KEEP_ON               0x27
+#define TPS65912_KEEP_ON2              0x28
+#define TPS65912_SET_OFF1              0x29
+#define TPS65912_SET_OFF2              0x2A
+#define TPS65912_DEF_VOLT              0x2B
+#define TPS65912_DEF_VOLT_MAPPING      0x2C
+#define TPS65912_DISCHARGE             0x2D
+#define TPS65912_DISCHARGE2            0x2E
+#define TPS65912_EN1_SET1              0x2F
+#define TPS65912_EN1_SET2              0x30
+#define TPS65912_EN2_SET1              0x31
+#define TPS65912_EN2_SET2              0x32
+#define TPS65912_EN3_SET1              0x33
+#define TPS65912_EN3_SET2              0x34
+#define TPS65912_EN4_SET1              0x35
+#define TPS65912_EN4_SET2              0x36
+#define TPS65912_PGOOD                 0x37
+#define TPS65912_PGOOD2                        0x38
+#define TPS65912_INT_STS               0x39
+#define TPS65912_INT_MSK               0x3A
+#define TPS65912_INT_STS2              0x3B
+#define TPS65912_INT_MSK2              0x3C
+#define TPS65912_INT_STS3              0x3D
+#define TPS65912_INT_MSK3              0x3E
+#define TPS65912_INT_STS4              0x3F
+#define TPS65912_INT_MSK4              0x40
+#define TPS65912_GPIO1                 0x41
+#define TPS65912_GPIO2                 0x42
+#define TPS65912_GPIO3                 0x43
+#define TPS65912_GPIO4                 0x44
+#define TPS65912_GPIO5                 0x45
+#define TPS65912_VMON                  0x46
+#define TPS65912_LEDA_CTRL1            0x47
+#define TPS65912_LEDA_CTRL2            0x48
+#define TPS65912_LEDA_CTRL3            0x49
+#define TPS65912_LEDA_CTRL4            0x4A
+#define TPS65912_LEDA_CTRL5            0x4B
+#define TPS65912_LEDA_CTRL6            0x4C
+#define TPS65912_LEDA_CTRL7            0x4D
+#define TPS65912_LEDA_CTRL8            0x4E
+#define TPS65912_LEDB_CTRL1            0x4F
+#define TPS65912_LEDB_CTRL2            0x50
+#define TPS65912_LEDB_CTRL3            0x51
+#define TPS65912_LEDB_CTRL4            0x52
+#define TPS65912_LEDB_CTRL5            0x53
+#define TPS65912_LEDB_CTRL6            0x54
+#define TPS65912_LEDB_CTRL7            0x55
+#define TPS65912_LEDB_CTRL8            0x56
+#define TPS65912_LEDC_CTRL1            0x57
+#define TPS65912_LEDC_CTRL2            0x58
+#define TPS65912_LEDC_CTRL3            0x59
+#define TPS65912_LEDC_CTRL4            0x5A
+#define TPS65912_LEDC_CTRL5            0x5B
+#define TPS65912_LEDC_CTRL6            0x5C
+#define TPS65912_LEDC_CTRL7            0x5D
+#define TPS65912_LEDC_CTRL8            0x5E
+#define TPS65912_LED_RAMP_UP_TIME      0x5F
+#define TPS65912_LED_RAMP_DOWN_TIME    0x60
+#define TPS65912_LED_SEQ_EN            0x61
+#define TPS65912_LOADSWITCH            0x62
+#define TPS65912_SPARE                 0x63
+#define TPS65912_VERNUM                        0x64
+#define TPS6591X_MAX_REGISTER          0x64
+
+/* IRQ Definitions */
+#define TPS65912_IRQ_PWRHOLD_F         0
+#define TPS65912_IRQ_VMON              1
+#define TPS65912_IRQ_PWRON             2
+#define TPS65912_IRQ_PWRON_LP          3
+#define TPS65912_IRQ_PWRHOLD_R         4
+#define TPS65912_IRQ_HOTDIE            5
+#define TPS65912_IRQ_GPIO1_R           6
+#define TPS65912_IRQ_GPIO1_F           7
+#define TPS65912_IRQ_GPIO2_R           8
+#define TPS65912_IRQ_GPIO2_F           9
+#define TPS65912_IRQ_GPIO3_R           10
+#define TPS65912_IRQ_GPIO3_F           11
+#define TPS65912_IRQ_GPIO4_R           12
+#define TPS65912_IRQ_GPIO4_F           13
+#define TPS65912_IRQ_GPIO5_R           14
+#define TPS65912_IRQ_GPIO5_F           15
+#define TPS65912_IRQ_PGOOD_DCDC1       16
+#define TPS65912_IRQ_PGOOD_DCDC2       17
+#define TPS65912_IRQ_PGOOD_DCDC3       18
+#define TPS65912_IRQ_PGOOD_DCDC4       19
+#define TPS65912_IRQ_PGOOD_LDO1                20
+#define TPS65912_IRQ_PGOOD_LDO2                21
+#define TPS65912_IRQ_PGOOD_LDO3                22
+#define TPS65912_IRQ_PGOOD_LDO4                23
+#define TPS65912_IRQ_PGOOD_LDO5                24
+#define TPS65912_IRQ_PGOOD_LDO6                25
+#define TPS65912_IRQ_PGOOD_LDO7                26
+#define TPS65912_IRQ_PGOOD_LD08                27
+#define TPS65912_IRQ_PGOOD_LDO9                28
+#define TPS65912_IRQ_PGOOD_LDO10       29
+
+#define TPS65912_NUM_IRQ               30
+
+/* GPIO 1 and 2 Register Definitions */
+#define GPIO_SLEEP_MASK                        0x80
+#define GPIO_SLEEP_SHIFT               7
+#define GPIO_DEB_MASK                  0x10
+#define GPIO_DEB_SHIFT                 4
+#define GPIO_CFG_MASK                  0x04
+#define GPIO_CFG_SHIFT                 2
+#define GPIO_STS_MASK                  0x02
+#define GPIO_STS_SHIFT                 1
+#define GPIO_SET_MASK                  0x01
+#define GPIO_SET_SHIFT                 0
+
+/* GPIO 3 Register Definitions */
+#define GPIO3_SLEEP_MASK               0x80
+#define GPIO3_SLEEP_SHIFT              7
+#define GPIO3_SEL_MASK                 0x40
+#define GPIO3_SEL_SHIFT                        6
+#define GPIO3_ODEN_MASK                        0x20
+#define GPIO3_ODEN_SHIFT               5
+#define GPIO3_DEB_MASK                 0x10
+#define GPIO3_DEB_SHIFT                        4
+#define GPIO3_PDEN_MASK                        0x08
+#define GPIO3_PDEN_SHIFT               3
+#define GPIO3_CFG_MASK                 0x04
+#define GPIO3_CFG_SHIFT                        2
+#define GPIO3_STS_MASK                 0x02
+#define GPIO3_STS_SHIFT                        1
+#define GPIO3_SET_MASK                 0x01
+#define GPIO3_SET_SHIFT                        0
+
+/* GPIO 4 Register Definitions */
+#define GPIO4_SLEEP_MASK               0x80
+#define GPIO4_SLEEP_SHIFT              7
+#define GPIO4_SEL_MASK                 0x40
+#define GPIO4_SEL_SHIFT                        6
+#define GPIO4_ODEN_MASK                        0x20
+#define GPIO4_ODEN_SHIFT               5
+#define GPIO4_DEB_MASK                 0x10
+#define GPIO4_DEB_SHIFT                        4
+#define GPIO4_PDEN_MASK                        0x08
+#define GPIO4_PDEN_SHIFT               3
+#define GPIO4_CFG_MASK                 0x04
+#define GPIO4_CFG_SHIFT                        2
+#define GPIO4_STS_MASK                 0x02
+#define GPIO4_STS_SHIFT                        1
+#define GPIO4_SET_MASK                 0x01
+#define GPIO4_SET_SHIFT                        0
+
+/* Register THERM  (0x80) register.RegisterDescription */
+#define THERM_THERM_HD_MASK            0x20
+#define THERM_THERM_HD_SHIFT           5
+#define THERM_THERM_TS_MASK            0x10
+#define THERM_THERM_TS_SHIFT           4
+#define THERM_THERM_HDSEL_MASK         0x0C
+#define THERM_THERM_HDSEL_SHIFT                2
+#define THERM_RSVD1_MASK               0x02
+#define THERM_RSVD1_SHIFT              1
+#define THERM_THERM_STATE_MASK         0x01
+#define THERM_THERM_STATE_SHIFT                0
+
+/* Register DCDCCTRL1 register.RegisterDescription */
+#define DCDCCTRL_VCON_ENABLE_MASK      0x80
+#define DCDCCTRL_VCON_ENABLE_SHIFT     7
+#define DCDCCTRL_VCON_RANGE1_MASK      0x40
+#define DCDCCTRL_VCON_RANGE1_SHIFT     6
+#define DCDCCTRL_VCON_RANGE0_MASK      0x20
+#define DCDCCTRL_VCON_RANGE0_SHIFT     5
+#define DCDCCTRL_TSTEP2_MASK           0x10
+#define DCDCCTRL_TSTEP2_SHIFT          4
+#define DCDCCTRL_TSTEP1_MASK           0x08
+#define DCDCCTRL_TSTEP1_SHIFT          3
+#define DCDCCTRL_TSTEP0_MASK           0x04
+#define DCDCCTRL_TSTEP0_SHIFT          2
+#define DCDCCTRL_DCDC1_MODE_MASK       0x02
+#define DCDCCTRL_DCDC1_MODE_SHIFT      1
+
+/* Register DCDCCTRL2 and DCDCCTRL3 register.RegisterDescription */
+#define DCDCCTRL_TSTEP2_MASK           0x10
+#define DCDCCTRL_TSTEP2_SHIFT          4
+#define DCDCCTRL_TSTEP1_MASK           0x08
+#define DCDCCTRL_TSTEP1_SHIFT          3
+#define DCDCCTRL_TSTEP0_MASK           0x04
+#define DCDCCTRL_TSTEP0_SHIFT          2
+#define DCDCCTRL_DCDC_MODE_MASK                0x02
+#define DCDCCTRL_DCDC_MODE_SHIFT       1
+#define DCDCCTRL_RSVD0_MASK            0x01
+#define DCDCCTRL_RSVD0_SHIFT           0
+
+/* Register DCDCCTRL4 register.RegisterDescription */
+#define DCDCCTRL_RAMP_TIME_MASK                0x01
+#define DCDCCTRL_RAMP_TIME_SHIFT       0
+
+/* Register DCDCx_AVS */
+#define DCDC_AVS_ENABLE_MASK           0x80
+#define DCDC_AVS_ENABLE_SHIFT          7
+#define DCDC_AVS_ECO_MASK              0x40
+#define DCDC_AVS_ECO_SHIFT             6
+
+/* Register DCDCx_LIMIT */
+#define DCDC_LIMIT_RANGE_MASK          0xC0
+#define DCDC_LIMIT_RANGE_SHIFT         6
+#define DCDC_LIMIT_MAX_SEL_MASK                0x3F
+#define DCDC_LIMIT_MAX_SEL_SHIFT       0
+
+/**
+ * struct tps65912_board
+ * Board platform dat may be used to initialize regulators.
+ */
+struct tps65912_board {
+       int is_dcdc1_avs;
+       int is_dcdc2_avs;
+       int is_dcdc3_avs;
+       int is_dcdc4_avs;
+       int irq;
+       int irq_base;
+       int gpio_base;
+       struct regulator_init_data *tps65912_pmic_init_data;
+};
+
+/**
+ * struct tps65912 - tps65912 sub-driver chip access routines
+ */
+
+struct tps65912 {
+       struct device *dev;
+       /* for read/write acces */
+       struct mutex io_mutex;
+
+       /* For device IO interfaces: I2C or SPI */
+       void *control_data;
+
+       int (*read)(struct tps65912 *tps65912, u8 reg, int size, void *dest);
+       int (*write)(struct tps65912 *tps65912, u8 reg, int size, void *src);
+
+       /* Client devices */
+       struct tps65912_pmic *pmic;
+
+       /* GPIO Handling */
+       struct gpio_chip gpio;
+
+       /* IRQ Handling */
+       struct mutex irq_lock;
+       int chip_irq;
+       int irq_base;
+       int irq_num;
+       u32 irq_mask;
+};
+
+struct tps65912_platform_data {
+       int irq;
+       int irq_base;
+};
+
+unsigned int tps_chip(void);
+
+int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
+int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
+int tps65912_reg_read(struct tps65912 *tps65912, u8 reg);
+int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val);
+int tps65912_device_init(struct tps65912 *tps65912);
+void tps65912_device_exit(struct tps65912 *tps65912);
+int tps65912_irq_init(struct tps65912 *tps65912, int irq,
+                       struct tps65912_platform_data *pdata);
+
+#endif /*  __LINUX_MFD_TPS65912_H */
index 0d515ee1c24747319b561f0f0bda98dd1e50903e..8dda8ded5cda666c0147a197188c6f5de98030f4 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/completion.h>
 #include <linux/interrupt.h>
+#include <linux/list.h>
 
 /*
  * Register values.
 #define WM831X_ON_PIN_TO_SHIFT                       0  /* ON_PIN_TO - [1:0] */
 #define WM831X_ON_PIN_TO_WIDTH                       2  /* ON_PIN_TO - [1:0] */
 
+/*
+ * R16528 (0x4090) - Clock Control 1
+ */
+#define WM831X_CLKOUT_ENA                       0x8000  /* CLKOUT_ENA */
+#define WM831X_CLKOUT_ENA_MASK                  0x8000  /* CLKOUT_ENA */
+#define WM831X_CLKOUT_ENA_SHIFT                     15  /* CLKOUT_ENA */
+#define WM831X_CLKOUT_ENA_WIDTH                      1  /* CLKOUT_ENA */
+#define WM831X_CLKOUT_OD                        0x2000  /* CLKOUT_OD */
+#define WM831X_CLKOUT_OD_MASK                   0x2000  /* CLKOUT_OD */
+#define WM831X_CLKOUT_OD_SHIFT                      13  /* CLKOUT_OD */
+#define WM831X_CLKOUT_OD_WIDTH                       1  /* CLKOUT_OD */
+#define WM831X_CLKOUT_SLOT_MASK                 0x0700  /* CLKOUT_SLOT - [10:8] */
+#define WM831X_CLKOUT_SLOT_SHIFT                     8  /* CLKOUT_SLOT - [10:8] */
+#define WM831X_CLKOUT_SLOT_WIDTH                     3  /* CLKOUT_SLOT - [10:8] */
+#define WM831X_CLKOUT_SLPSLOT_MASK              0x0070  /* CLKOUT_SLPSLOT - [6:4] */
+#define WM831X_CLKOUT_SLPSLOT_SHIFT                  4  /* CLKOUT_SLPSLOT - [6:4] */
+#define WM831X_CLKOUT_SLPSLOT_WIDTH                  3  /* CLKOUT_SLPSLOT - [6:4] */
+#define WM831X_CLKOUT_SRC                       0x0001  /* CLKOUT_SRC */
+#define WM831X_CLKOUT_SRC_MASK                  0x0001  /* CLKOUT_SRC */
+#define WM831X_CLKOUT_SRC_SHIFT                      0  /* CLKOUT_SRC */
+#define WM831X_CLKOUT_SRC_WIDTH                      1  /* CLKOUT_SRC */
+
+/*
+ * R16529 (0x4091) - Clock Control 2
+ */
+#define WM831X_XTAL_INH                         0x8000  /* XTAL_INH */
+#define WM831X_XTAL_INH_MASK                    0x8000  /* XTAL_INH */
+#define WM831X_XTAL_INH_SHIFT                       15  /* XTAL_INH */
+#define WM831X_XTAL_INH_WIDTH                        1  /* XTAL_INH */
+#define WM831X_XTAL_ENA                         0x2000  /* XTAL_ENA */
+#define WM831X_XTAL_ENA_MASK                    0x2000  /* XTAL_ENA */
+#define WM831X_XTAL_ENA_SHIFT                       13  /* XTAL_ENA */
+#define WM831X_XTAL_ENA_WIDTH                        1  /* XTAL_ENA */
+#define WM831X_XTAL_BKUPENA                     0x1000  /* XTAL_BKUPENA */
+#define WM831X_XTAL_BKUPENA_MASK                0x1000  /* XTAL_BKUPENA */
+#define WM831X_XTAL_BKUPENA_SHIFT                   12  /* XTAL_BKUPENA */
+#define WM831X_XTAL_BKUPENA_WIDTH                    1  /* XTAL_BKUPENA */
+#define WM831X_FLL_AUTO                         0x0080  /* FLL_AUTO */
+#define WM831X_FLL_AUTO_MASK                    0x0080  /* FLL_AUTO */
+#define WM831X_FLL_AUTO_SHIFT                        7  /* FLL_AUTO */
+#define WM831X_FLL_AUTO_WIDTH                        1  /* FLL_AUTO */
+#define WM831X_FLL_AUTO_FREQ_MASK               0x0007  /* FLL_AUTO_FREQ - [2:0] */
+#define WM831X_FLL_AUTO_FREQ_SHIFT                   0  /* FLL_AUTO_FREQ - [2:0] */
+#define WM831X_FLL_AUTO_FREQ_WIDTH                   3  /* FLL_AUTO_FREQ - [2:0] */
+
+/*
+ * R16530 (0x4092) - FLL Control 1
+ */
+#define WM831X_FLL_FRAC                         0x0004  /* FLL_FRAC */
+#define WM831X_FLL_FRAC_MASK                    0x0004  /* FLL_FRAC */
+#define WM831X_FLL_FRAC_SHIFT                        2  /* FLL_FRAC */
+#define WM831X_FLL_FRAC_WIDTH                        1  /* FLL_FRAC */
+#define WM831X_FLL_OSC_ENA                      0x0002  /* FLL_OSC_ENA */
+#define WM831X_FLL_OSC_ENA_MASK                 0x0002  /* FLL_OSC_ENA */
+#define WM831X_FLL_OSC_ENA_SHIFT                     1  /* FLL_OSC_ENA */
+#define WM831X_FLL_OSC_ENA_WIDTH                     1  /* FLL_OSC_ENA */
+#define WM831X_FLL_ENA                          0x0001  /* FLL_ENA */
+#define WM831X_FLL_ENA_MASK                     0x0001  /* FLL_ENA */
+#define WM831X_FLL_ENA_SHIFT                         0  /* FLL_ENA */
+#define WM831X_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+
+/*
+ * R16531 (0x4093) - FLL Control 2
+ */
+#define WM831X_FLL_OUTDIV_MASK                  0x3F00  /* FLL_OUTDIV - [13:8] */
+#define WM831X_FLL_OUTDIV_SHIFT                      8  /* FLL_OUTDIV - [13:8] */
+#define WM831X_FLL_OUTDIV_WIDTH                      6  /* FLL_OUTDIV - [13:8] */
+#define WM831X_FLL_CTRL_RATE_MASK               0x0070  /* FLL_CTRL_RATE - [6:4] */
+#define WM831X_FLL_CTRL_RATE_SHIFT                   4  /* FLL_CTRL_RATE - [6:4] */
+#define WM831X_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [6:4] */
+#define WM831X_FLL_FRATIO_MASK                  0x0007  /* FLL_FRATIO - [2:0] */
+#define WM831X_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [2:0] */
+#define WM831X_FLL_FRATIO_WIDTH                      3  /* FLL_FRATIO - [2:0] */
+
+/*
+ * R16532 (0x4094) - FLL Control 3
+ */
+#define WM831X_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM831X_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM831X_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R16533 (0x4095) - FLL Control 4
+ */
+#define WM831X_FLL_N_MASK                       0x7FE0  /* FLL_N - [14:5] */
+#define WM831X_FLL_N_SHIFT                           5  /* FLL_N - [14:5] */
+#define WM831X_FLL_N_WIDTH                          10  /* FLL_N - [14:5] */
+#define WM831X_FLL_GAIN_MASK                    0x000F  /* FLL_GAIN - [3:0] */
+#define WM831X_FLL_GAIN_SHIFT                        0  /* FLL_GAIN - [3:0] */
+#define WM831X_FLL_GAIN_WIDTH                        4  /* FLL_GAIN - [3:0] */
+
+/*
+ * R16534 (0x4096) - FLL Control 5
+ */
+#define WM831X_FLL_CLK_REF_DIV_MASK             0x0018  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM831X_FLL_CLK_REF_DIV_SHIFT                 3  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM831X_FLL_CLK_REF_DIV_WIDTH                 2  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM831X_FLL_CLK_SRC_MASK                 0x0003  /* FLL_CLK_SRC - [1:0] */
+#define WM831X_FLL_CLK_SRC_SHIFT                     0  /* FLL_CLK_SRC - [1:0] */
+#define WM831X_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [1:0] */
+
 struct regulator_dev;
 
 #define WM831X_NUM_IRQ_REGS 5
+#define WM831X_NUM_GPIO_REGS 16
 
 enum wm831x_parent {
        WM8310 = 0x8310,
@@ -248,6 +351,12 @@ enum wm831x_parent {
        WM8326 = 0x8326,
 };
 
+struct wm831x;
+enum wm831x_auxadc;
+
+typedef int (*wm831x_auxadc_read_fn)(struct wm831x *wm831x,
+                                    enum wm831x_auxadc input);
+
 struct wm831x {
        struct mutex io_lock;
 
@@ -261,7 +370,7 @@ struct wm831x {
 
        int irq;  /* Our chip IRQ */
        struct mutex irq_lock;
-       unsigned int irq_base;
+       int irq_base;
        int irq_masks_cur[WM831X_NUM_IRQ_REGS];   /* Currently active value */
        int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
 
@@ -272,8 +381,13 @@ struct wm831x {
 
        int num_gpio;
 
+       /* Used by the interrupt controller code to post writes */
+       int gpio_update[WM831X_NUM_GPIO_REGS];
+
        struct mutex auxadc_lock;
-       struct completion auxadc_done;
+       struct list_head auxadc_pending;
+       u16 auxadc_active;
+       wm831x_auxadc_read_fn auxadc_read;
 
        /* The WM831x has a security key blocking access to certain
         * registers.  The mutex is taken by the accessors for locking
@@ -300,5 +414,6 @@ void wm831x_device_exit(struct wm831x *wm831x);
 int wm831x_device_suspend(struct wm831x *wm831x);
 int wm831x_irq_init(struct wm831x *wm831x, int irq);
 void wm831x_irq_exit(struct wm831x *wm831x);
+void wm831x_auxadc_init(struct wm831x *wm831x);
 
 #endif
index ff42d700293fc71485d80f092c978fd633919b27..0ba24599fe514e9963d1b665ad83e1ac12f418bf 100644 (file)
@@ -120,6 +120,9 @@ struct wm831x_pdata {
        /** Put the /IRQ line into CMOS mode */
        bool irq_cmos;
 
+       /** Disable the touchscreen */
+       bool disable_touch;
+
        int irq_base;
        int gpio_base;
        int gpio_defaults[WM831X_GPIO_NUM];