Merge branch 'for-next' of git://git.o-hand.com/linux-mfd
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 6 Jan 2009 03:04:09 +0000 (19:04 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 6 Jan 2009 03:04:09 +0000 (19:04 -0800)
* 'for-next' of git://git.o-hand.com/linux-mfd: (30 commits)
  mfd: Fix section mismatch in da903x
  mfd: move drivers/i2c/chips/menelaus.c to drivers/mfd
  mfd: move drivers/i2c/chips/tps65010.c to drivers/mfd
  mfd: dm355evm msp430 driver
  mfd: Add missing break from wm3850-core
  mfd: Add WM8351 support
  mfd: Support configurable numbers of DCDCs and ISINKs on WM8350
  mfd: Handle missing WM8350 platform data
  mfd: Add WM8352 support
  mfd: Use irq_to_desc in twl4030 code
  power_supply: Add Dialog DA9030 battery charger driver
  mfd: Dialog DA9030 battery charger MFD driver
  mfd: Register WM8400 codec device
  mfd: Pass driver_data onto child devices
  mfd: Fix twl4030-core.c build error
  mfd: twl4030 regulator bug fixes
  mfd: twl4030: create some regulator devices
  mfd: twl4030: cleanup symbols and OMAP dependency
  mfd: twl4030: simplified child creation code
  power_supply: Add battery health reporting for WM8350
  ...

31 files changed:
drivers/i2c/chips/Kconfig
drivers/i2c/chips/Makefile
drivers/i2c/chips/menelaus.c [deleted file]
drivers/i2c/chips/tps65010.c [deleted file]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/da903x.c
drivers/mfd/dm355evm_msp.c [new file with mode: 0644]
drivers/mfd/menelaus.c [new file with mode: 0644]
drivers/mfd/mfd-core.c
drivers/mfd/tps65010.c [new file with mode: 0644]
drivers/mfd/twl4030-core.c
drivers/mfd/twl4030-irq.c
drivers/mfd/wm8350-core.c
drivers/mfd/wm8350-i2c.c
drivers/mfd/wm8350-regmap.c
drivers/mfd/wm8400-core.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/da9030_battery.c [new file with mode: 0644]
drivers/power/power_supply_sysfs.c
drivers/power/wm8350_power.c [new file with mode: 0644]
drivers/regulator/wm8350-regulator.c
include/linux/i2c/dm355evm_msp.h [new file with mode: 0644]
include/linux/i2c/twl4030.h
include/linux/mfd/da903x.h
include/linux/mfd/wm8350/comparator.h
include/linux/mfd/wm8350/core.h
include/linux/mfd/wm8350/pmic.h
include/linux/mfd/wm8350/supply.h
include/linux/power_supply.h

index 4c35702830ce5921013e6733e292d23c0b904e4f..864ac561fdbbf42ebb469d0c3ccaa5572d410605 100644 (file)
@@ -126,19 +126,6 @@ config ISP1301_OMAP
          This driver can also be built as a module.  If so, the module
          will be called isp1301_omap.
 
-config TPS65010
-       tristate "TPS6501x Power Management chips"
-       depends on GPIOLIB
-       default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
-       help
-         If you say yes here you get support for the TPS6501x series of
-         Power Management chips.  These include voltage regulators,
-         lithium ion/polymer battery charging, and other features that
-         are often used in portable devices like cell phones and cameras.
-
-         This driver can also be built as a module.  If so, the module
-         will be called tps65010.
-
 config SENSORS_MAX6875
        tristate "Maxim MAX6875 Power supply supervisor"
        depends on EXPERIMENTAL
@@ -164,16 +151,6 @@ config SENSORS_TSL2550
          This driver can also be built as a module.  If so, the module
          will be called tsl2550.
 
-config MENELAUS
-       bool "TWL92330/Menelaus PM chip"
-       depends on I2C=y && ARCH_OMAP24XX
-       help
-         If you say yes here you get support for the Texas Instruments
-         TWL92330/Menelaus Power Management chip. This include voltage
-         regulators, Dual slot memory card tranceivers, real-time clock
-         and other features that are often used in portable devices like
-         cell phones and PDAs.
-
 config MCU_MPC8349EMITX
        tristate "MPC8349E-mITX MCU driver"
        depends on I2C && PPC_83xx
index 23d2a31b0a644bc610429653d7da70520b86e567..8b95f41a5001b58311ed4ea09f6c6764c2cfa67b 100644 (file)
@@ -19,8 +19,6 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
 obj-$(CONFIG_PCF8575)          += pcf8575.o
 obj-$(CONFIG_SENSORS_PCF8591)  += pcf8591.o
 obj-$(CONFIG_ISP1301_OMAP)     += isp1301_omap.o
-obj-$(CONFIG_TPS65010)         += tps65010.o
-obj-$(CONFIG_MENELAUS)         += menelaus.o
 obj-$(CONFIG_SENSORS_TSL2550)  += tsl2550.o
 obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o
 
diff --git a/drivers/i2c/chips/menelaus.c b/drivers/i2c/chips/menelaus.c
deleted file mode 100644 (file)
index 4b364ba..0000000
+++ /dev/null
@@ -1,1285 +0,0 @@
-/*
- * Copyright (C) 2004 Texas Instruments, Inc.
- *
- * Some parts based tps65010.c:
- * Copyright (C) 2004 Texas Instruments and
- * Copyright (C) 2004-2005 David Brownell
- *
- * Some parts based on tlv320aic24.c:
- * Copyright (C) by Kai Svahn <kai.svahn@nokia.com>
- *
- * Changes for interrupt handling and clean-up by
- * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com>
- * Cleanup and generalized support for voltage setting by
- * Juha Yrjola
- * Added support for controlling VCORE and regulator sleep states,
- * Amit Kucheria <amit.kucheria@nokia.com>
- * Copyright (C) 2005, 2006 Nokia Corporation
- *
- * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/mutex.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/rtc.h>
-#include <linux/bcd.h>
-
-#include <asm/mach/irq.h>
-
-#include <mach/gpio.h>
-#include <mach/menelaus.h>
-
-#define DRIVER_NAME                    "menelaus"
-
-#define MENELAUS_I2C_ADDRESS           0x72
-
-#define MENELAUS_REV                   0x01
-#define MENELAUS_VCORE_CTRL1           0x02
-#define MENELAUS_VCORE_CTRL2           0x03
-#define MENELAUS_VCORE_CTRL3           0x04
-#define MENELAUS_VCORE_CTRL4           0x05
-#define MENELAUS_VCORE_CTRL5           0x06
-#define MENELAUS_DCDC_CTRL1            0x07
-#define MENELAUS_DCDC_CTRL2            0x08
-#define MENELAUS_DCDC_CTRL3            0x09
-#define MENELAUS_LDO_CTRL1             0x0A
-#define MENELAUS_LDO_CTRL2             0x0B
-#define MENELAUS_LDO_CTRL3             0x0C
-#define MENELAUS_LDO_CTRL4             0x0D
-#define MENELAUS_LDO_CTRL5             0x0E
-#define MENELAUS_LDO_CTRL6             0x0F
-#define MENELAUS_LDO_CTRL7             0x10
-#define MENELAUS_LDO_CTRL8             0x11
-#define MENELAUS_SLEEP_CTRL1           0x12
-#define MENELAUS_SLEEP_CTRL2           0x13
-#define MENELAUS_DEVICE_OFF            0x14
-#define MENELAUS_OSC_CTRL              0x15
-#define MENELAUS_DETECT_CTRL           0x16
-#define MENELAUS_INT_MASK1             0x17
-#define MENELAUS_INT_MASK2             0x18
-#define MENELAUS_INT_STATUS1           0x19
-#define MENELAUS_INT_STATUS2           0x1A
-#define MENELAUS_INT_ACK1              0x1B
-#define MENELAUS_INT_ACK2              0x1C
-#define MENELAUS_GPIO_CTRL             0x1D
-#define MENELAUS_GPIO_IN               0x1E
-#define MENELAUS_GPIO_OUT              0x1F
-#define MENELAUS_BBSMS                 0x20
-#define MENELAUS_RTC_CTRL              0x21
-#define MENELAUS_RTC_UPDATE            0x22
-#define MENELAUS_RTC_SEC               0x23
-#define MENELAUS_RTC_MIN               0x24
-#define MENELAUS_RTC_HR                        0x25
-#define MENELAUS_RTC_DAY               0x26
-#define MENELAUS_RTC_MON               0x27
-#define MENELAUS_RTC_YR                        0x28
-#define MENELAUS_RTC_WKDAY             0x29
-#define MENELAUS_RTC_AL_SEC            0x2A
-#define MENELAUS_RTC_AL_MIN            0x2B
-#define MENELAUS_RTC_AL_HR             0x2C
-#define MENELAUS_RTC_AL_DAY            0x2D
-#define MENELAUS_RTC_AL_MON            0x2E
-#define MENELAUS_RTC_AL_YR             0x2F
-#define MENELAUS_RTC_COMP_MSB          0x30
-#define MENELAUS_RTC_COMP_LSB          0x31
-#define MENELAUS_S1_PULL_EN            0x32
-#define MENELAUS_S1_PULL_DIR           0x33
-#define MENELAUS_S2_PULL_EN            0x34
-#define MENELAUS_S2_PULL_DIR           0x35
-#define MENELAUS_MCT_CTRL1             0x36
-#define MENELAUS_MCT_CTRL2             0x37
-#define MENELAUS_MCT_CTRL3             0x38
-#define MENELAUS_MCT_PIN_ST            0x39
-#define MENELAUS_DEBOUNCE1             0x3A
-
-#define IH_MENELAUS_IRQS               12
-#define MENELAUS_MMC_S1CD_IRQ          0       /* MMC slot 1 card change */
-#define MENELAUS_MMC_S2CD_IRQ          1       /* MMC slot 2 card change */
-#define MENELAUS_MMC_S1D1_IRQ          2       /* MMC DAT1 low in slot 1 */
-#define MENELAUS_MMC_S2D1_IRQ          3       /* MMC DAT1 low in slot 2 */
-#define MENELAUS_LOWBAT_IRQ            4       /* Low battery */
-#define MENELAUS_HOTDIE_IRQ            5       /* Hot die detect */
-#define MENELAUS_UVLO_IRQ              6       /* UVLO detect */
-#define MENELAUS_TSHUT_IRQ             7       /* Thermal shutdown */
-#define MENELAUS_RTCTMR_IRQ            8       /* RTC timer */
-#define MENELAUS_RTCALM_IRQ            9       /* RTC alarm */
-#define MENELAUS_RTCERR_IRQ            10      /* RTC error */
-#define MENELAUS_PSHBTN_IRQ            11      /* Push button */
-#define MENELAUS_RESERVED12_IRQ                12      /* Reserved */
-#define MENELAUS_RESERVED13_IRQ                13      /* Reserved */
-#define MENELAUS_RESERVED14_IRQ                14      /* Reserved */
-#define MENELAUS_RESERVED15_IRQ                15      /* Reserved */
-
-static void menelaus_work(struct work_struct *_menelaus);
-
-struct menelaus_chip {
-       struct mutex            lock;
-       struct i2c_client       *client;
-       struct work_struct      work;
-#ifdef CONFIG_RTC_DRV_TWL92330
-       struct rtc_device       *rtc;
-       u8                      rtc_control;
-       unsigned                uie:1;
-#endif
-       unsigned                vcore_hw_mode:1;
-       u8                      mask1, mask2;
-       void                    (*handlers[16])(struct menelaus_chip *);
-       void                    (*mmc_callback)(void *data, u8 mask);
-       void                    *mmc_callback_data;
-};
-
-static struct menelaus_chip *the_menelaus;
-
-static int menelaus_write_reg(int reg, u8 value)
-{
-       int val = i2c_smbus_write_byte_data(the_menelaus->client, reg, value);
-
-       if (val < 0) {
-               pr_err(DRIVER_NAME ": write error");
-               return val;
-       }
-
-       return 0;
-}
-
-static int menelaus_read_reg(int reg)
-{
-       int val = i2c_smbus_read_byte_data(the_menelaus->client, reg);
-
-       if (val < 0)
-               pr_err(DRIVER_NAME ": read error");
-
-       return val;
-}
-
-static int menelaus_enable_irq(int irq)
-{
-       if (irq > 7) {
-               irq -= 8;
-               the_menelaus->mask2 &= ~(1 << irq);
-               return menelaus_write_reg(MENELAUS_INT_MASK2,
-                               the_menelaus->mask2);
-       } else {
-               the_menelaus->mask1 &= ~(1 << irq);
-               return menelaus_write_reg(MENELAUS_INT_MASK1,
-                               the_menelaus->mask1);
-       }
-}
-
-static int menelaus_disable_irq(int irq)
-{
-       if (irq > 7) {
-               irq -= 8;
-               the_menelaus->mask2 |= (1 << irq);
-               return menelaus_write_reg(MENELAUS_INT_MASK2,
-                               the_menelaus->mask2);
-       } else {
-               the_menelaus->mask1 |= (1 << irq);
-               return menelaus_write_reg(MENELAUS_INT_MASK1,
-                               the_menelaus->mask1);
-       }
-}
-
-static int menelaus_ack_irq(int irq)
-{
-       if (irq > 7)
-               return menelaus_write_reg(MENELAUS_INT_ACK2, 1 << (irq - 8));
-       else
-               return menelaus_write_reg(MENELAUS_INT_ACK1, 1 << irq);
-}
-
-/* Adds a handler for an interrupt. Does not run in interrupt context */
-static int menelaus_add_irq_work(int irq,
-               void (*handler)(struct menelaus_chip *))
-{
-       int ret = 0;
-
-       mutex_lock(&the_menelaus->lock);
-       the_menelaus->handlers[irq] = handler;
-       ret = menelaus_enable_irq(irq);
-       mutex_unlock(&the_menelaus->lock);
-
-       return ret;
-}
-
-/* Removes handler for an interrupt */
-static int menelaus_remove_irq_work(int irq)
-{
-       int ret = 0;
-
-       mutex_lock(&the_menelaus->lock);
-       ret = menelaus_disable_irq(irq);
-       the_menelaus->handlers[irq] = NULL;
-       mutex_unlock(&the_menelaus->lock);
-
-       return ret;
-}
-
-/*
- * Gets scheduled when a card detect interrupt happens. Note that in some cases
- * this line is wired to card cover switch rather than the card detect switch
- * in each slot. In this case the cards are not seen by menelaus.
- * FIXME: Add handling for D1 too
- */
-static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
-{
-       int reg;
-       unsigned char card_mask = 0;
-
-       reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST);
-       if (reg < 0)
-               return;
-
-       if (!(reg & 0x1))
-               card_mask |= (1 << 0);
-
-       if (!(reg & 0x2))
-               card_mask |= (1 << 1);
-
-       if (menelaus_hw->mmc_callback)
-               menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
-                                         card_mask);
-}
-
-/*
- * Toggles the MMC slots between open-drain and push-pull mode.
- */
-int menelaus_set_mmc_opendrain(int slot, int enable)
-{
-       int ret, val;
-
-       if (slot != 1 && slot != 2)
-               return -EINVAL;
-       mutex_lock(&the_menelaus->lock);
-       ret = menelaus_read_reg(MENELAUS_MCT_CTRL1);
-       if (ret < 0) {
-               mutex_unlock(&the_menelaus->lock);
-               return ret;
-       }
-       val = ret;
-       if (slot == 1) {
-               if (enable)
-                       val |= 1 << 2;
-               else
-                       val &= ~(1 << 2);
-       } else {
-               if (enable)
-                       val |= 1 << 3;
-               else
-                       val &= ~(1 << 3);
-       }
-       ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
-       mutex_unlock(&the_menelaus->lock);
-
-       return ret;
-}
-EXPORT_SYMBOL(menelaus_set_mmc_opendrain);
-
-int menelaus_set_slot_sel(int enable)
-{
-       int ret;
-
-       mutex_lock(&the_menelaus->lock);
-       ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
-       if (ret < 0)
-               goto out;
-       ret |= 0x02;
-       if (enable)
-               ret |= 1 << 5;
-       else
-               ret &= ~(1 << 5);
-       ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
-out:
-       mutex_unlock(&the_menelaus->lock);
-       return ret;
-}
-EXPORT_SYMBOL(menelaus_set_slot_sel);
-
-int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
-{
-       int ret, val;
-
-       if (slot != 1 && slot != 2)
-               return -EINVAL;
-       if (power >= 3)
-               return -EINVAL;
-
-       mutex_lock(&the_menelaus->lock);
-
-       ret = menelaus_read_reg(MENELAUS_MCT_CTRL2);
-       if (ret < 0)
-               goto out;
-       val = ret;
-       if (slot == 1) {
-               if (cd_en)
-                       val |= (1 << 4) | (1 << 6);
-               else
-                       val &= ~((1 << 4) | (1 << 6));
-       } else {
-               if (cd_en)
-                       val |= (1 << 5) | (1 << 7);
-               else
-                       val &= ~((1 << 5) | (1 << 7));
-       }
-       ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
-       if (ret < 0)
-               goto out;
-
-       ret = menelaus_read_reg(MENELAUS_MCT_CTRL3);
-       if (ret < 0)
-               goto out;
-       val = ret;
-       if (slot == 1) {
-               if (enable)
-                       val |= 1 << 0;
-               else
-                       val &= ~(1 << 0);
-       } else {
-               int b;
-
-               if (enable)
-                       ret |= 1 << 1;
-               else
-                       ret &= ~(1 << 1);
-               b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
-               b &= ~0x03;
-               b |= power;
-               ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
-               if (ret < 0)
-                       goto out;
-       }
-       /* Disable autonomous shutdown */
-       val &= ~(0x03 << 2);
-       ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
-out:
-       mutex_unlock(&the_menelaus->lock);
-       return ret;
-}
-EXPORT_SYMBOL(menelaus_set_mmc_slot);
-
-int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask),
-                                  void *data)
-{
-       int ret = 0;
-
-       the_menelaus->mmc_callback_data = data;
-       the_menelaus->mmc_callback = callback;
-       ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ,
-                                   menelaus_mmc_cd_work);
-       if (ret < 0)
-               return ret;
-       ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ,
-                                   menelaus_mmc_cd_work);
-       if (ret < 0)
-               return ret;
-       ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ,
-                                   menelaus_mmc_cd_work);
-       if (ret < 0)
-               return ret;
-       ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ,
-                                   menelaus_mmc_cd_work);
-
-       return ret;
-}
-EXPORT_SYMBOL(menelaus_register_mmc_callback);
-
-void menelaus_unregister_mmc_callback(void)
-{
-       menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ);
-       menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ);
-       menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ);
-       menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ);
-
-       the_menelaus->mmc_callback = NULL;
-       the_menelaus->mmc_callback_data = 0;
-}
-EXPORT_SYMBOL(menelaus_unregister_mmc_callback);
-
-struct menelaus_vtg {
-       const char *name;
-       u8 vtg_reg;
-       u8 vtg_shift;
-       u8 vtg_bits;
-       u8 mode_reg;
-};
-
-struct menelaus_vtg_value {
-       u16 vtg;
-       u16 val;
-};
-
-static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV,
-                               int vtg_val, int mode)
-{
-       int val, ret;
-       struct i2c_client *c = the_menelaus->client;
-
-       mutex_lock(&the_menelaus->lock);
-       if (vtg == 0)
-               goto set_voltage;
-
-       ret = menelaus_read_reg(vtg->vtg_reg);
-       if (ret < 0)
-               goto out;
-       val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift);
-       val |= vtg_val << vtg->vtg_shift;
-
-       dev_dbg(&c->dev, "Setting voltage '%s'"
-                        "to %d mV (reg 0x%02x, val 0x%02x)\n",
-                       vtg->name, mV, vtg->vtg_reg, val);
-
-       ret = menelaus_write_reg(vtg->vtg_reg, val);
-       if (ret < 0)
-               goto out;
-set_voltage:
-       ret = menelaus_write_reg(vtg->mode_reg, mode);
-out:
-       mutex_unlock(&the_menelaus->lock);
-       if (ret == 0) {
-               /* Wait for voltage to stabilize */
-               msleep(1);
-       }
-       return ret;
-}
-
-static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl,
-                                 int n)
-{
-       int i;
-
-       for (i = 0; i < n; i++, tbl++)
-               if (tbl->vtg == vtg)
-                       return tbl->val;
-       return -EINVAL;
-}
-
-/*
- * Vcore can be programmed in two ways:
- * SW-controlled: Required voltage is programmed into VCORE_CTRL1
- * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3
- * and VCORE_CTRL4
- *
- * Call correct 'set' function accordingly
- */
-
-static const struct menelaus_vtg_value vcore_values[] = {
-       { 1000, 0 },
-       { 1025, 1 },
-       { 1050, 2 },
-       { 1075, 3 },
-       { 1100, 4 },
-       { 1125, 5 },
-       { 1150, 6 },
-       { 1175, 7 },
-       { 1200, 8 },
-       { 1225, 9 },
-       { 1250, 10 },
-       { 1275, 11 },
-       { 1300, 12 },
-       { 1325, 13 },
-       { 1350, 14 },
-       { 1375, 15 },
-       { 1400, 16 },
-       { 1425, 17 },
-       { 1450, 18 },
-};
-
-int menelaus_set_vcore_sw(unsigned int mV)
-{
-       int val, ret;
-       struct i2c_client *c = the_menelaus->client;
-
-       val = menelaus_get_vtg_value(mV, vcore_values,
-                                    ARRAY_SIZE(vcore_values));
-       if (val < 0)
-               return -EINVAL;
-
-       dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val);
-
-       /* Set SW mode and the voltage in one go. */
-       mutex_lock(&the_menelaus->lock);
-       ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
-       if (ret == 0)
-               the_menelaus->vcore_hw_mode = 0;
-       mutex_unlock(&the_menelaus->lock);
-       msleep(1);
-
-       return ret;
-}
-
-int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
-{
-       int fval, rval, val, ret;
-       struct i2c_client *c = the_menelaus->client;
-
-       rval = menelaus_get_vtg_value(roof_mV, vcore_values,
-                                     ARRAY_SIZE(vcore_values));
-       if (rval < 0)
-               return -EINVAL;
-       fval = menelaus_get_vtg_value(floor_mV, vcore_values,
-                                     ARRAY_SIZE(vcore_values));
-       if (fval < 0)
-               return -EINVAL;
-
-       dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n",
-              floor_mV, roof_mV);
-
-       mutex_lock(&the_menelaus->lock);
-       ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval);
-       if (ret < 0)
-               goto out;
-       ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval);
-       if (ret < 0)
-               goto out;
-       if (!the_menelaus->vcore_hw_mode) {
-               val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
-               /* HW mode, turn OFF byte comparator */
-               val |= ((1 << 7) | (1 << 5));
-               ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
-               the_menelaus->vcore_hw_mode = 1;
-       }
-       msleep(1);
-out:
-       mutex_unlock(&the_menelaus->lock);
-       return ret;
-}
-
-static const struct menelaus_vtg vmem_vtg = {
-       .name = "VMEM",
-       .vtg_reg = MENELAUS_LDO_CTRL1,
-       .vtg_shift = 0,
-       .vtg_bits = 2,
-       .mode_reg = MENELAUS_LDO_CTRL3,
-};
-
-static const struct menelaus_vtg_value vmem_values[] = {
-       { 1500, 0 },
-       { 1800, 1 },
-       { 1900, 2 },
-       { 2500, 3 },
-};
-
-int menelaus_set_vmem(unsigned int mV)
-{
-       int val;
-
-       if (mV == 0)
-               return menelaus_set_voltage(&vmem_vtg, 0, 0, 0);
-
-       val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values));
-       if (val < 0)
-               return -EINVAL;
-       return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02);
-}
-EXPORT_SYMBOL(menelaus_set_vmem);
-
-static const struct menelaus_vtg vio_vtg = {
-       .name = "VIO",
-       .vtg_reg = MENELAUS_LDO_CTRL1,
-       .vtg_shift = 2,
-       .vtg_bits = 2,
-       .mode_reg = MENELAUS_LDO_CTRL4,
-};
-
-static const struct menelaus_vtg_value vio_values[] = {
-       { 1500, 0 },
-       { 1800, 1 },
-       { 2500, 2 },
-       { 2800, 3 },
-};
-
-int menelaus_set_vio(unsigned int mV)
-{
-       int val;
-
-       if (mV == 0)
-               return menelaus_set_voltage(&vio_vtg, 0, 0, 0);
-
-       val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values));
-       if (val < 0)
-               return -EINVAL;
-       return menelaus_set_voltage(&vio_vtg, mV, val, 0x02);
-}
-EXPORT_SYMBOL(menelaus_set_vio);
-
-static const struct menelaus_vtg_value vdcdc_values[] = {
-       { 1500, 0 },
-       { 1800, 1 },
-       { 2000, 2 },
-       { 2200, 3 },
-       { 2400, 4 },
-       { 2800, 5 },
-       { 3000, 6 },
-       { 3300, 7 },
-};
-
-static const struct menelaus_vtg vdcdc2_vtg = {
-       .name = "VDCDC2",
-       .vtg_reg = MENELAUS_DCDC_CTRL1,
-       .vtg_shift = 0,
-       .vtg_bits = 3,
-       .mode_reg = MENELAUS_DCDC_CTRL2,
-};
-
-static const struct menelaus_vtg vdcdc3_vtg = {
-       .name = "VDCDC3",
-       .vtg_reg = MENELAUS_DCDC_CTRL1,
-       .vtg_shift = 3,
-       .vtg_bits = 3,
-       .mode_reg = MENELAUS_DCDC_CTRL3,
-};
-
-int menelaus_set_vdcdc(int dcdc, unsigned int mV)
-{
-       const struct menelaus_vtg *vtg;
-       int val;
-
-       if (dcdc != 2 && dcdc != 3)
-               return -EINVAL;
-       if (dcdc == 2)
-               vtg = &vdcdc2_vtg;
-       else
-               vtg = &vdcdc3_vtg;
-
-       if (mV == 0)
-               return menelaus_set_voltage(vtg, 0, 0, 0);
-
-       val = menelaus_get_vtg_value(mV, vdcdc_values,
-                                    ARRAY_SIZE(vdcdc_values));
-       if (val < 0)
-               return -EINVAL;
-       return menelaus_set_voltage(vtg, mV, val, 0x03);
-}
-
-static const struct menelaus_vtg_value vmmc_values[] = {
-       { 1850, 0 },
-       { 2800, 1 },
-       { 3000, 2 },
-       { 3100, 3 },
-};
-
-static const struct menelaus_vtg vmmc_vtg = {
-       .name = "VMMC",
-       .vtg_reg = MENELAUS_LDO_CTRL1,
-       .vtg_shift = 6,
-       .vtg_bits = 2,
-       .mode_reg = MENELAUS_LDO_CTRL7,
-};
-
-int menelaus_set_vmmc(unsigned int mV)
-{
-       int val;
-
-       if (mV == 0)
-               return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0);
-
-       val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values));
-       if (val < 0)
-               return -EINVAL;
-       return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02);
-}
-EXPORT_SYMBOL(menelaus_set_vmmc);
-
-
-static const struct menelaus_vtg_value vaux_values[] = {
-       { 1500, 0 },
-       { 1800, 1 },
-       { 2500, 2 },
-       { 2800, 3 },
-};
-
-static const struct menelaus_vtg vaux_vtg = {
-       .name = "VAUX",
-       .vtg_reg = MENELAUS_LDO_CTRL1,
-       .vtg_shift = 4,
-       .vtg_bits = 2,
-       .mode_reg = MENELAUS_LDO_CTRL6,
-};
-
-int menelaus_set_vaux(unsigned int mV)
-{
-       int val;
-
-       if (mV == 0)
-               return menelaus_set_voltage(&vaux_vtg, 0, 0, 0);
-
-       val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values));
-       if (val < 0)
-               return -EINVAL;
-       return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02);
-}
-EXPORT_SYMBOL(menelaus_set_vaux);
-
-int menelaus_get_slot_pin_states(void)
-{
-       return menelaus_read_reg(MENELAUS_MCT_PIN_ST);
-}
-EXPORT_SYMBOL(menelaus_get_slot_pin_states);
-
-int menelaus_set_regulator_sleep(int enable, u32 val)
-{
-       int t, ret;
-       struct i2c_client *c = the_menelaus->client;
-
-       mutex_lock(&the_menelaus->lock);
-       ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val);
-       if (ret < 0)
-               goto out;
-
-       dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val);
-
-       ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
-       if (ret < 0)
-               goto out;
-       t = ((1 << 6) | 0x04);
-       if (enable)
-               ret |= t;
-       else
-               ret &= ~t;
-       ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
-out:
-       mutex_unlock(&the_menelaus->lock);
-       return ret;
-}
-
-/*-----------------------------------------------------------------------*/
-
-/* Handles Menelaus interrupts. Does not run in interrupt context */
-static void menelaus_work(struct work_struct *_menelaus)
-{
-       struct menelaus_chip *menelaus =
-                       container_of(_menelaus, struct menelaus_chip, work);
-       void (*handler)(struct menelaus_chip *menelaus);
-
-       while (1) {
-               unsigned isr;
-
-               isr = (menelaus_read_reg(MENELAUS_INT_STATUS2)
-                               & ~menelaus->mask2) << 8;
-               isr |= menelaus_read_reg(MENELAUS_INT_STATUS1)
-                               & ~menelaus->mask1;
-               if (!isr)
-                       break;
-
-               while (isr) {
-                       int irq = fls(isr) - 1;
-                       isr &= ~(1 << irq);
-
-                       mutex_lock(&menelaus->lock);
-                       menelaus_disable_irq(irq);
-                       menelaus_ack_irq(irq);
-                       handler = menelaus->handlers[irq];
-                       if (handler)
-                               handler(menelaus);
-                       menelaus_enable_irq(irq);
-                       mutex_unlock(&menelaus->lock);
-               }
-       }
-       enable_irq(menelaus->client->irq);
-}
-
-/*
- * We cannot use I2C in interrupt context, so we just schedule work.
- */
-static irqreturn_t menelaus_irq(int irq, void *_menelaus)
-{
-       struct menelaus_chip *menelaus = _menelaus;
-
-       disable_irq_nosync(irq);
-       (void)schedule_work(&menelaus->work);
-
-       return IRQ_HANDLED;
-}
-
-/*-----------------------------------------------------------------------*/
-
-/*
- * The RTC needs to be set once, then it runs on backup battery power.
- * It supports alarms, including system wake alarms (from some modes);
- * and 1/second IRQs if requested.
- */
-#ifdef CONFIG_RTC_DRV_TWL92330
-
-#define RTC_CTRL_RTC_EN                (1 << 0)
-#define RTC_CTRL_AL_EN         (1 << 1)
-#define RTC_CTRL_MODE12                (1 << 2)
-#define RTC_CTRL_EVERY_MASK    (3 << 3)
-#define RTC_CTRL_EVERY_SEC     (0 << 3)
-#define RTC_CTRL_EVERY_MIN     (1 << 3)
-#define RTC_CTRL_EVERY_HR      (2 << 3)
-#define RTC_CTRL_EVERY_DAY     (3 << 3)
-
-#define RTC_UPDATE_EVERY       0x08
-
-#define RTC_HR_PM              (1 << 7)
-
-static void menelaus_to_time(char *regs, struct rtc_time *t)
-{
-       t->tm_sec = bcd2bin(regs[0]);
-       t->tm_min = bcd2bin(regs[1]);
-       if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
-               t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1;
-               if (regs[2] & RTC_HR_PM)
-                       t->tm_hour += 12;
-       } else
-               t->tm_hour = bcd2bin(regs[2] & 0x3f);
-       t->tm_mday = bcd2bin(regs[3]);
-       t->tm_mon = bcd2bin(regs[4]) - 1;
-       t->tm_year = bcd2bin(regs[5]) + 100;
-}
-
-static int time_to_menelaus(struct rtc_time *t, int regnum)
-{
-       int     hour, status;
-
-       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec));
-       if (status < 0)
-               goto fail;
-
-       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min));
-       if (status < 0)
-               goto fail;
-
-       if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
-               hour = t->tm_hour + 1;
-               if (hour > 12)
-                       hour = RTC_HR_PM | bin2bcd(hour - 12);
-               else
-                       hour = bin2bcd(hour);
-       } else
-               hour = bin2bcd(t->tm_hour);
-       status = menelaus_write_reg(regnum++, hour);
-       if (status < 0)
-               goto fail;
-
-       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday));
-       if (status < 0)
-               goto fail;
-
-       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1));
-       if (status < 0)
-               goto fail;
-
-       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100));
-       if (status < 0)
-               goto fail;
-
-       return 0;
-fail:
-       dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n",
-                       --regnum, status);
-       return status;
-}
-
-static int menelaus_read_time(struct device *dev, struct rtc_time *t)
-{
-       struct i2c_msg  msg[2];
-       char            regs[7];
-       int             status;
-
-       /* block read date and time registers */
-       regs[0] = MENELAUS_RTC_SEC;
-
-       msg[0].addr = MENELAUS_I2C_ADDRESS;
-       msg[0].flags = 0;
-       msg[0].len = 1;
-       msg[0].buf = regs;
-
-       msg[1].addr = MENELAUS_I2C_ADDRESS;
-       msg[1].flags = I2C_M_RD;
-       msg[1].len = sizeof(regs);
-       msg[1].buf = regs;
-
-       status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
-       if (status != 2) {
-               dev_err(dev, "%s error %d\n", "read", status);
-               return -EIO;
-       }
-
-       menelaus_to_time(regs, t);
-       t->tm_wday = bcd2bin(regs[6]);
-
-       return 0;
-}
-
-static int menelaus_set_time(struct device *dev, struct rtc_time *t)
-{
-       int             status;
-
-       /* write date and time registers */
-       status = time_to_menelaus(t, MENELAUS_RTC_SEC);
-       if (status < 0)
-               return status;
-       status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday));
-       if (status < 0) {
-               dev_err(&the_menelaus->client->dev, "rtc write reg %02x "
-                               "err %d\n", MENELAUS_RTC_WKDAY, status);
-               return status;
-       }
-
-       /* now commit the write */
-       status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY);
-       if (status < 0)
-               dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n",
-                               status);
-
-       return 0;
-}
-
-static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w)
-{
-       struct i2c_msg  msg[2];
-       char            regs[6];
-       int             status;
-
-       /* block read alarm registers */
-       regs[0] = MENELAUS_RTC_AL_SEC;
-
-       msg[0].addr = MENELAUS_I2C_ADDRESS;
-       msg[0].flags = 0;
-       msg[0].len = 1;
-       msg[0].buf = regs;
-
-       msg[1].addr = MENELAUS_I2C_ADDRESS;
-       msg[1].flags = I2C_M_RD;
-       msg[1].len = sizeof(regs);
-       msg[1].buf = regs;
-
-       status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
-       if (status != 2) {
-               dev_err(dev, "%s error %d\n", "alarm read", status);
-               return -EIO;
-       }
-
-       menelaus_to_time(regs, &w->time);
-
-       w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN);
-
-       /* NOTE we *could* check if actually pending... */
-       w->pending = 0;
-
-       return 0;
-}
-
-static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w)
-{
-       int             status;
-
-       if (the_menelaus->client->irq <= 0 && w->enabled)
-               return -ENODEV;
-
-       /* clear previous alarm enable */
-       if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) {
-               the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
-               status = menelaus_write_reg(MENELAUS_RTC_CTRL,
-                               the_menelaus->rtc_control);
-               if (status < 0)
-                       return status;
-       }
-
-       /* write alarm registers */
-       status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC);
-       if (status < 0)
-               return status;
-
-       /* enable alarm if requested */
-       if (w->enabled) {
-               the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
-               status = menelaus_write_reg(MENELAUS_RTC_CTRL,
-                               the_menelaus->rtc_control);
-       }
-
-       return status;
-}
-
-#ifdef CONFIG_RTC_INTF_DEV
-
-static void menelaus_rtc_update_work(struct menelaus_chip *m)
-{
-       /* report 1/sec update */
-       local_irq_disable();
-       rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF);
-       local_irq_enable();
-}
-
-static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg)
-{
-       int     status;
-
-       if (the_menelaus->client->irq <= 0)
-               return -ENOIOCTLCMD;
-
-       switch (cmd) {
-       /* alarm IRQ */
-       case RTC_AIE_ON:
-               if (the_menelaus->rtc_control & RTC_CTRL_AL_EN)
-                       return 0;
-               the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
-               break;
-       case RTC_AIE_OFF:
-               if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN))
-                       return 0;
-               the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
-               break;
-       /* 1/second "update" IRQ */
-       case RTC_UIE_ON:
-               if (the_menelaus->uie)
-                       return 0;
-               status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
-               status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ,
-                               menelaus_rtc_update_work);
-               if (status == 0)
-                       the_menelaus->uie = 1;
-               return status;
-       case RTC_UIE_OFF:
-               if (!the_menelaus->uie)
-                       return 0;
-               status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
-               if (status == 0)
-                       the_menelaus->uie = 0;
-               return status;
-       default:
-               return -ENOIOCTLCMD;
-       }
-       return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
-}
-
-#else
-#define menelaus_ioctl NULL
-#endif
-
-/* REVISIT no compensation register support ... */
-
-static const struct rtc_class_ops menelaus_rtc_ops = {
-       .ioctl                  = menelaus_ioctl,
-       .read_time              = menelaus_read_time,
-       .set_time               = menelaus_set_time,
-       .read_alarm             = menelaus_read_alarm,
-       .set_alarm              = menelaus_set_alarm,
-};
-
-static void menelaus_rtc_alarm_work(struct menelaus_chip *m)
-{
-       /* report alarm */
-       local_irq_disable();
-       rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF);
-       local_irq_enable();
-
-       /* then disable it; alarms are oneshot */
-       the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
-       menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
-}
-
-static inline void menelaus_rtc_init(struct menelaus_chip *m)
-{
-       int     alarm = (m->client->irq > 0);
-
-       /* assume 32KDETEN pin is pulled high */
-       if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) {
-               dev_dbg(&m->client->dev, "no 32k oscillator\n");
-               return;
-       }
-
-       /* support RTC alarm; it can issue wakeups */
-       if (alarm) {
-               if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ,
-                               menelaus_rtc_alarm_work) < 0) {
-                       dev_err(&m->client->dev, "can't handle RTC alarm\n");
-                       return;
-               }
-               device_init_wakeup(&m->client->dev, 1);
-       }
-
-       /* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */
-       m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL);
-       if (!(m->rtc_control & RTC_CTRL_RTC_EN)
-                       || (m->rtc_control & RTC_CTRL_AL_EN)
-                       || (m->rtc_control & RTC_CTRL_EVERY_MASK)) {
-               if (!(m->rtc_control & RTC_CTRL_RTC_EN)) {
-                       dev_warn(&m->client->dev, "rtc clock needs setting\n");
-                       m->rtc_control |= RTC_CTRL_RTC_EN;
-               }
-               m->rtc_control &= ~RTC_CTRL_EVERY_MASK;
-               m->rtc_control &= ~RTC_CTRL_AL_EN;
-               menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control);
-       }
-
-       m->rtc = rtc_device_register(DRIVER_NAME,
-                       &m->client->dev,
-                       &menelaus_rtc_ops, THIS_MODULE);
-       if (IS_ERR(m->rtc)) {
-               if (alarm) {
-                       menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ);
-                       device_init_wakeup(&m->client->dev, 0);
-               }
-               dev_err(&m->client->dev, "can't register RTC: %d\n",
-                               (int) PTR_ERR(m->rtc));
-               the_menelaus->rtc = NULL;
-       }
-}
-
-#else
-
-static inline void menelaus_rtc_init(struct menelaus_chip *m)
-{
-       /* nothing */
-}
-
-#endif
-
-/*-----------------------------------------------------------------------*/
-
-static struct i2c_driver menelaus_i2c_driver;
-
-static int menelaus_probe(struct i2c_client *client,
-                         const struct i2c_device_id *id)
-{
-       struct menelaus_chip    *menelaus;
-       int                     rev = 0, val;
-       int                     err = 0;
-       struct menelaus_platform_data *menelaus_pdata =
-                                       client->dev.platform_data;
-
-       if (the_menelaus) {
-               dev_dbg(&client->dev, "only one %s for now\n",
-                               DRIVER_NAME);
-               return -ENODEV;
-       }
-
-       menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL);
-       if (!menelaus)
-               return -ENOMEM;
-
-       i2c_set_clientdata(client, menelaus);
-
-       the_menelaus = menelaus;
-       menelaus->client = client;
-
-       /* If a true probe check the device */
-       rev = menelaus_read_reg(MENELAUS_REV);
-       if (rev < 0) {
-               pr_err(DRIVER_NAME ": device not found");
-               err = -ENODEV;
-               goto fail1;
-       }
-
-       /* Ack and disable all Menelaus interrupts */
-       menelaus_write_reg(MENELAUS_INT_ACK1, 0xff);
-       menelaus_write_reg(MENELAUS_INT_ACK2, 0xff);
-       menelaus_write_reg(MENELAUS_INT_MASK1, 0xff);
-       menelaus_write_reg(MENELAUS_INT_MASK2, 0xff);
-       menelaus->mask1 = 0xff;
-       menelaus->mask2 = 0xff;
-
-       /* Set output buffer strengths */
-       menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);
-
-       if (client->irq > 0) {
-               err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED,
-                                 DRIVER_NAME, menelaus);
-               if (err) {
-                       dev_dbg(&client->dev,  "can't get IRQ %d, err %d\n",
-                                       client->irq, err);
-                       goto fail1;
-               }
-       }
-
-       mutex_init(&menelaus->lock);
-       INIT_WORK(&menelaus->work, menelaus_work);
-
-       pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f);
-
-       val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
-       if (val < 0)
-               goto fail2;
-       if (val & (1 << 7))
-               menelaus->vcore_hw_mode = 1;
-       else
-               menelaus->vcore_hw_mode = 0;
-
-       if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) {
-               err = menelaus_pdata->late_init(&client->dev);
-               if (err < 0)
-                       goto fail2;
-       }
-
-       menelaus_rtc_init(menelaus);
-
-       return 0;
-fail2:
-       free_irq(client->irq, menelaus);
-       flush_scheduled_work();
-fail1:
-       kfree(menelaus);
-       return err;
-}
-
-static int __exit menelaus_remove(struct i2c_client *client)
-{
-       struct menelaus_chip    *menelaus = i2c_get_clientdata(client);
-
-       free_irq(client->irq, menelaus);
-       kfree(menelaus);
-       i2c_set_clientdata(client, NULL);
-       the_menelaus = NULL;
-       return 0;
-}
-
-static const struct i2c_device_id menelaus_id[] = {
-       { "menelaus", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, menelaus_id);
-
-static struct i2c_driver menelaus_i2c_driver = {
-       .driver = {
-               .name           = DRIVER_NAME,
-       },
-       .probe          = menelaus_probe,
-       .remove         = __exit_p(menelaus_remove),
-       .id_table       = menelaus_id,
-};
-
-static int __init menelaus_init(void)
-{
-       int res;
-
-       res = i2c_add_driver(&menelaus_i2c_driver);
-       if (res < 0) {
-               pr_err(DRIVER_NAME ": driver registration failed\n");
-               return res;
-       }
-
-       return 0;
-}
-
-static void __exit menelaus_exit(void)
-{
-       i2c_del_driver(&menelaus_i2c_driver);
-
-       /* FIXME: Shutdown menelaus parts that can be shut down */
-}
-
-MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
-MODULE_DESCRIPTION("I2C interface for Menelaus.");
-MODULE_LICENSE("GPL");
-
-module_init(menelaus_init);
-module_exit(menelaus_exit);
diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
deleted file mode 100644 (file)
index acf8b9d..0000000
+++ /dev/null
@@ -1,1072 +0,0 @@
-/*
- * tps65010 - driver for tps6501x power management chips
- *
- * Copyright (C) 2004 Texas Instruments
- * Copyright (C) 2004-2005 David Brownell
- *
- * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/workqueue.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/mutex.h>
-#include <linux/platform_device.h>
-
-#include <linux/i2c/tps65010.h>
-
-#include <asm/gpio.h>
-
-
-/*-------------------------------------------------------------------------*/
-
-#define        DRIVER_VERSION  "2 May 2005"
-#define        DRIVER_NAME     (tps65010_driver.driver.name)
-
-MODULE_DESCRIPTION("TPS6501x Power Management Driver");
-MODULE_LICENSE("GPL");
-
-static struct i2c_driver tps65010_driver;
-
-/*-------------------------------------------------------------------------*/
-
-/* This driver handles a family of multipurpose chips, which incorporate
- * voltage regulators, lithium ion/polymer battery charging, GPIOs, LEDs,
- * and other features often needed in portable devices like cell phones
- * or digital cameras.
- *
- * The tps65011 and tps65013 have different voltage settings compared
- * to tps65010 and tps65012.  The tps65013 has a NO_CHG status/irq.
- * All except tps65010 have "wait" mode, possibly defaulted so that
- * battery-insert != device-on.
- *
- * We could distinguish between some models by checking VDCDC1.UVLO or
- * other registers, unless they've been changed already after powerup
- * as part of board setup by a bootloader.
- */
-enum tps_model {
-       TPS65010,
-       TPS65011,
-       TPS65012,
-       TPS65013,
-};
-
-struct tps65010 {
-       struct i2c_client       *client;
-       struct mutex            lock;
-       struct delayed_work     work;
-       struct dentry           *file;
-       unsigned                charging:1;
-       unsigned                por:1;
-       unsigned                model:8;
-       u16                     vbus;
-       unsigned long           flags;
-#define        FLAG_VBUS_CHANGED       0
-#define        FLAG_IRQ_ENABLE         1
-
-       /* copies of last register state */
-       u8                      chgstatus, regstatus, chgconf;
-       u8                      nmask1, nmask2;
-
-       u8                      outmask;
-       struct gpio_chip        chip;
-       struct platform_device  *leds;
-};
-
-#define        POWER_POLL_DELAY        msecs_to_jiffies(5000)
-
-/*-------------------------------------------------------------------------*/
-
-#if    defined(DEBUG) || defined(CONFIG_DEBUG_FS)
-
-static void dbg_chgstat(char *buf, size_t len, u8 chgstatus)
-{
-       snprintf(buf, len, "%02x%s%s%s%s%s%s%s%s\n",
-               chgstatus,
-               (chgstatus & TPS_CHG_USB) ? " USB" : "",
-               (chgstatus & TPS_CHG_AC) ? " AC" : "",
-               (chgstatus & TPS_CHG_THERM) ? " therm" : "",
-               (chgstatus & TPS_CHG_TERM) ? " done" :
-                       ((chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
-                               ? " (charging)" : ""),
-               (chgstatus & TPS_CHG_TAPER_TMO) ? " taper_tmo" : "",
-               (chgstatus & TPS_CHG_CHG_TMO) ? " charge_tmo" : "",
-               (chgstatus & TPS_CHG_PRECHG_TMO) ? " prechg_tmo" : "",
-               (chgstatus & TPS_CHG_TEMP_ERR) ? " temp_err" : "");
-}
-
-static void dbg_regstat(char *buf, size_t len, u8 regstatus)
-{
-       snprintf(buf, len, "%02x %s%s%s%s%s%s%s%s\n",
-               regstatus,
-               (regstatus & TPS_REG_ONOFF) ? "off" : "(on)",
-               (regstatus & TPS_REG_COVER) ? " uncover" : "",
-               (regstatus & TPS_REG_UVLO) ? " UVLO" : "",
-               (regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "",
-               (regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "",
-               (regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "",
-               (regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "",
-               (regstatus & TPS_REG_PG_CORE) ? " core_bad" : "");
-}
-
-static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig)
-{
-       const char *hibit;
-
-       if (por)
-               hibit = (chgconfig & TPS_CHARGE_POR)
-                               ? "POR=69ms" : "POR=1sec";
-       else
-               hibit = (chgconfig & TPS65013_AUA) ? "AUA" : "";
-
-       snprintf(buf, len, "%02x %s%s%s AC=%d%% USB=%dmA %sCharge\n",
-               chgconfig, hibit,
-               (chgconfig & TPS_CHARGE_RESET) ? " reset" : "",
-               (chgconfig & TPS_CHARGE_FAST) ? " fast" : "",
-               ({int p; switch ((chgconfig >> 3) & 3) {
-               case 3:         p = 100; break;
-               case 2:         p = 75; break;
-               case 1:         p = 50; break;
-               default:        p = 25; break;
-               }; p; }),
-               (chgconfig & TPS_VBUS_CHARGING)
-                       ? ((chgconfig & TPS_VBUS_500MA) ? 500 : 100)
-                       : 0,
-               (chgconfig & TPS_CHARGE_ENABLE) ? "" : "No");
-}
-
-#endif
-
-#ifdef DEBUG
-
-static void show_chgstatus(const char *label, u8 chgstatus)
-{
-       char buf [100];
-
-       dbg_chgstat(buf, sizeof buf, chgstatus);
-       pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
-}
-
-static void show_regstatus(const char *label, u8 regstatus)
-{
-       char buf [100];
-
-       dbg_regstat(buf, sizeof buf, regstatus);
-       pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
-}
-
-static void show_chgconfig(int por, const char *label, u8 chgconfig)
-{
-       char buf [100];
-
-       dbg_chgconf(por, buf, sizeof buf, chgconfig);
-       pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
-}
-
-#else
-
-static inline void show_chgstatus(const char *label, u8 chgstatus) { }
-static inline void show_regstatus(const char *label, u8 chgstatus) { }
-static inline void show_chgconfig(int por, const char *label, u8 chgconfig) { }
-
-#endif
-
-#ifdef CONFIG_DEBUG_FS
-
-static int dbg_show(struct seq_file *s, void *_)
-{
-       struct tps65010 *tps = s->private;
-       u8              value, v2;
-       unsigned        i;
-       char            buf[100];
-       const char      *chip;
-
-       switch (tps->model) {
-       case TPS65010:  chip = "tps65010"; break;
-       case TPS65011:  chip = "tps65011"; break;
-       case TPS65012:  chip = "tps65012"; break;
-       case TPS65013:  chip = "tps65013"; break;
-       default:        chip = NULL; break;
-       }
-       seq_printf(s, "driver  %s\nversion %s\nchip    %s\n\n",
-                       DRIVER_NAME, DRIVER_VERSION, chip);
-
-       mutex_lock(&tps->lock);
-
-       /* FIXME how can we tell whether a battery is present?
-        * likely involves a charge gauging chip (like BQ26501).
-        */
-
-       seq_printf(s, "%scharging\n\n", tps->charging ? "" : "(not) ");
-
-
-       /* registers for monitoring battery charging and status; note
-        * that reading chgstat and regstat may ack IRQs...
-        */
-       value = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
-       dbg_chgconf(tps->por, buf, sizeof buf, value);
-       seq_printf(s, "chgconfig %s", buf);
-
-       value = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
-       dbg_chgstat(buf, sizeof buf, value);
-       seq_printf(s, "chgstat   %s", buf);
-       value = i2c_smbus_read_byte_data(tps->client, TPS_MASK1);
-       dbg_chgstat(buf, sizeof buf, value);
-       seq_printf(s, "mask1     %s", buf);
-       /* ignore ackint1 */
-
-       value = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
-       dbg_regstat(buf, sizeof buf, value);
-       seq_printf(s, "regstat   %s", buf);
-       value = i2c_smbus_read_byte_data(tps->client, TPS_MASK2);
-       dbg_regstat(buf, sizeof buf, value);
-       seq_printf(s, "mask2     %s\n", buf);
-       /* ignore ackint2 */
-
-       (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
-
-
-       /* VMAIN voltage, enable lowpower, etc */
-       value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1);
-       seq_printf(s, "vdcdc1    %02x\n", value);
-
-       /* VCORE voltage, vibrator on/off */
-       value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC2);
-       seq_printf(s, "vdcdc2    %02x\n", value);
-
-       /* both LD0s, and their lowpower behavior */
-       value = i2c_smbus_read_byte_data(tps->client, TPS_VREGS1);
-       seq_printf(s, "vregs1    %02x\n\n", value);
-
-
-       /* LEDs and GPIOs */
-       value = i2c_smbus_read_byte_data(tps->client, TPS_LED1_ON);
-       v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER);
-       seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n",
-               (value & 0x80)
-                       ? ((v2 & 0x80) ? "on" : "off")
-                       : ((v2 & 0x80) ? "blink" : "(nPG)"),
-               value, v2,
-               (value & 0x7f) * 10, (v2 & 0x7f) * 100);
-
-       value = i2c_smbus_read_byte_data(tps->client, TPS_LED2_ON);
-       v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER);
-       seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n",
-               (value & 0x80)
-                       ? ((v2 & 0x80) ? "on" : "off")
-                       : ((v2 & 0x80) ? "blink" : "off"),
-               value, v2,
-               (value & 0x7f) * 10, (v2 & 0x7f) * 100);
-
-       value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
-       v2 = i2c_smbus_read_byte_data(tps->client, TPS_MASK3);
-       seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2);
-
-       for (i = 0; i < 4; i++) {
-               if (value & (1 << (4 + i)))
-                       seq_printf(s, "  gpio%d-out %s\n", i + 1,
-                               (value & (1 << i)) ? "low" : "hi ");
-               else
-                       seq_printf(s, "  gpio%d-in  %s %s %s\n", i + 1,
-                               (value & (1 << i)) ? "hi " : "low",
-                               (v2 & (1 << i)) ? "no-irq" : "irq",
-                               (v2 & (1 << (4 + i))) ? "rising" : "falling");
-       }
-
-       mutex_unlock(&tps->lock);
-       return 0;
-}
-
-static int dbg_tps_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, dbg_show, inode->i_private);
-}
-
-static const struct file_operations debug_fops = {
-       .open           = dbg_tps_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-#define        DEBUG_FOPS      &debug_fops
-
-#else
-#define        DEBUG_FOPS      NULL
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-/* handle IRQS in a task context, so we can use I2C calls */
-static void tps65010_interrupt(struct tps65010 *tps)
-{
-       u8 tmp = 0, mask, poll;
-
-       /* IRQs won't trigger for certain events, but we can get
-        * others by polling (normally, with external power applied).
-        */
-       poll = 0;
-
-       /* regstatus irqs */
-       if (tps->nmask2) {
-               tmp = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
-               mask = tmp ^ tps->regstatus;
-               tps->regstatus = tmp;
-               mask &= tps->nmask2;
-       } else
-               mask = 0;
-       if (mask) {
-               tps->regstatus =  tmp;
-               /* may need to shut something down ... */
-
-               /* "off" usually means deep sleep */
-               if (tmp & TPS_REG_ONOFF) {
-                       pr_info("%s: power off button\n", DRIVER_NAME);
-#if 0
-                       /* REVISIT:  this might need its own workqueue
-                        * plus tweaks including deadlock avoidance ...
-                        * also needs to get error handling and probably
-                        * an #ifdef CONFIG_HIBERNATION
-                        */
-                       hibernate();
-#endif
-                       poll = 1;
-               }
-       }
-
-       /* chgstatus irqs */
-       if (tps->nmask1) {
-               tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
-               mask = tmp ^ tps->chgstatus;
-               tps->chgstatus = tmp;
-               mask &= tps->nmask1;
-       } else
-               mask = 0;
-       if (mask) {
-               unsigned        charging = 0;
-
-               show_chgstatus("chg/irq", tmp);
-               if (tmp & (TPS_CHG_USB|TPS_CHG_AC))
-                       show_chgconfig(tps->por, "conf", tps->chgconf);
-
-               /* Unless it was turned off or disabled, we charge any
-                * battery whenever there's power available for it
-                * and the charger hasn't been disabled.
-                */
-               if (!(tps->chgstatus & ~(TPS_CHG_USB|TPS_CHG_AC))
-                               && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
-                               && (tps->chgconf & TPS_CHARGE_ENABLE)
-                               ) {
-                       if (tps->chgstatus & TPS_CHG_USB) {
-                               /* VBUS options are readonly until reconnect */
-                               if (mask & TPS_CHG_USB)
-                                       set_bit(FLAG_VBUS_CHANGED, &tps->flags);
-                               charging = 1;
-                       } else if (tps->chgstatus & TPS_CHG_AC)
-                               charging = 1;
-               }
-               if (charging != tps->charging) {
-                       tps->charging = charging;
-                       pr_info("%s: battery %scharging\n",
-                               DRIVER_NAME, charging ? "" :
-                               ((tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
-                                       ? "NOT " : "dis"));
-               }
-       }
-
-       /* always poll to detect (a) power removal, without tps65013
-        * NO_CHG IRQ; or (b) restart of charging after stop.
-        */
-       if ((tps->model != TPS65013 || !tps->charging)
-                       && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)))
-               poll = 1;
-       if (poll)
-               (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
-
-       /* also potentially gpio-in rise or fall */
-}
-
-/* handle IRQs and polling using keventd for now */
-static void tps65010_work(struct work_struct *work)
-{
-       struct tps65010         *tps;
-
-       tps = container_of(work, struct tps65010, work.work);
-       mutex_lock(&tps->lock);
-
-       tps65010_interrupt(tps);
-
-       if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) {
-               int     status;
-               u8      chgconfig, tmp;
-
-               chgconfig = i2c_smbus_read_byte_data(tps->client,
-                                       TPS_CHGCONFIG);
-               chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING);
-               if (tps->vbus == 500)
-                       chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING;
-               else if (tps->vbus >= 100)
-                       chgconfig |= TPS_VBUS_CHARGING;
-
-               status = i2c_smbus_write_byte_data(tps->client,
-                               TPS_CHGCONFIG, chgconfig);
-
-               /* vbus update fails unless VBUS is connected! */
-               tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
-               tps->chgconf = tmp;
-               show_chgconfig(tps->por, "update vbus", tmp);
-       }
-
-       if (test_and_clear_bit(FLAG_IRQ_ENABLE, &tps->flags))
-               enable_irq(tps->client->irq);
-
-       mutex_unlock(&tps->lock);
-}
-
-static irqreturn_t tps65010_irq(int irq, void *_tps)
-{
-       struct tps65010         *tps = _tps;
-
-       disable_irq_nosync(irq);
-       set_bit(FLAG_IRQ_ENABLE, &tps->flags);
-       (void) schedule_work(&tps->work.work);
-       return IRQ_HANDLED;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* offsets 0..3 == GPIO1..GPIO4
- * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
- * offset 6 == vibrator motor driver
- */
-static void
-tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
-       if (offset < 4)
-               tps65010_set_gpio_out_value(offset + 1, value);
-       else if (offset < 6)
-               tps65010_set_led(offset - 3, value ? ON : OFF);
-       else
-               tps65010_set_vib(value);
-}
-
-static int
-tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
-{
-       /* GPIOs may be input-only */
-       if (offset < 4) {
-               struct tps65010         *tps;
-
-               tps = container_of(chip, struct tps65010, chip);
-               if (!(tps->outmask & (1 << offset)))
-                       return -EINVAL;
-               tps65010_set_gpio_out_value(offset + 1, value);
-       } else if (offset < 6)
-               tps65010_set_led(offset - 3, value ? ON : OFF);
-       else
-               tps65010_set_vib(value);
-
-       return 0;
-}
-
-static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
-       int                     value;
-       struct tps65010         *tps;
-
-       tps = container_of(chip, struct tps65010, chip);
-
-       if (offset < 4) {
-               value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
-               if (value < 0)
-                       return 0;
-               if (value & (1 << (offset + 4)))        /* output */
-                       return !(value & (1 << offset));
-               else                                    /* input */
-                       return (value & (1 << offset));
-       }
-
-       /* REVISIT we *could* report LED1/nPG and LED2 state ... */
-       return 0;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static struct tps65010 *the_tps;
-
-static int __exit tps65010_remove(struct i2c_client *client)
-{
-       struct tps65010         *tps = i2c_get_clientdata(client);
-       struct tps65010_board   *board = client->dev.platform_data;
-
-       if (board && board->teardown) {
-               int status = board->teardown(client, board->context);
-               if (status < 0)
-                       dev_dbg(&client->dev, "board %s %s err %d\n",
-                               "teardown", client->name, status);
-       }
-       if (client->irq > 0)
-               free_irq(client->irq, tps);
-       cancel_delayed_work(&tps->work);
-       flush_scheduled_work();
-       debugfs_remove(tps->file);
-       kfree(tps);
-       i2c_set_clientdata(client, NULL);
-       the_tps = NULL;
-       return 0;
-}
-
-static int tps65010_probe(struct i2c_client *client,
-                         const struct i2c_device_id *id)
-{
-       struct tps65010         *tps;
-       int                     status;
-       struct tps65010_board   *board = client->dev.platform_data;
-
-       if (the_tps) {
-               dev_dbg(&client->dev, "only one tps6501x chip allowed\n");
-               return -ENODEV;
-       }
-
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-               return -EINVAL;
-
-       tps = kzalloc(sizeof *tps, GFP_KERNEL);
-       if (!tps)
-               return -ENOMEM;
-
-       mutex_init(&tps->lock);
-       INIT_DELAYED_WORK(&tps->work, tps65010_work);
-       tps->client = client;
-       tps->model = id->driver_data;
-
-       /* the IRQ is active low, but many gpio lines can't support that
-        * so this driver uses falling-edge triggers instead.
-        */
-       if (client->irq > 0) {
-               status = request_irq(client->irq, tps65010_irq,
-                       IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
-                       DRIVER_NAME, tps);
-               if (status < 0) {
-                       dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
-                                       client->irq, status);
-                       goto fail1;
-               }
-               /* annoying race here, ideally we'd have an option
-                * to claim the irq now and enable it later.
-                * FIXME genirq IRQF_NOAUTOEN now solves that ...
-                */
-               disable_irq(client->irq);
-               set_bit(FLAG_IRQ_ENABLE, &tps->flags);
-       } else
-               dev_warn(&client->dev, "IRQ not configured!\n");
-
-
-       switch (tps->model) {
-       case TPS65010:
-       case TPS65012:
-               tps->por = 1;
-               break;
-       /* else CHGCONFIG.POR is replaced by AUA, enabling a WAIT mode */
-       }
-       tps->chgconf = i2c_smbus_read_byte_data(client, TPS_CHGCONFIG);
-       show_chgconfig(tps->por, "conf/init", tps->chgconf);
-
-       show_chgstatus("chg/init",
-               i2c_smbus_read_byte_data(client, TPS_CHGSTATUS));
-       show_regstatus("reg/init",
-               i2c_smbus_read_byte_data(client, TPS_REGSTATUS));
-
-       pr_debug("%s: vdcdc1 0x%02x, vdcdc2 %02x, vregs1 %02x\n", DRIVER_NAME,
-               i2c_smbus_read_byte_data(client, TPS_VDCDC1),
-               i2c_smbus_read_byte_data(client, TPS_VDCDC2),
-               i2c_smbus_read_byte_data(client, TPS_VREGS1));
-       pr_debug("%s: defgpio 0x%02x, mask3 0x%02x\n", DRIVER_NAME,
-               i2c_smbus_read_byte_data(client, TPS_DEFGPIO),
-               i2c_smbus_read_byte_data(client, TPS_MASK3));
-
-       i2c_set_clientdata(client, tps);
-       the_tps = tps;
-
-#if    defined(CONFIG_USB_GADGET) && !defined(CONFIG_USB_OTG)
-       /* USB hosts can't draw VBUS.  OTG devices could, later
-        * when OTG infrastructure enables it.  USB peripherals
-        * could be relying on VBUS while booting, though.
-        */
-       tps->vbus = 100;
-#endif
-
-       /* unmask the "interesting" irqs, then poll once to
-        * kickstart monitoring, initialize shadowed status
-        * registers, and maybe disable VBUS draw.
-        */
-       tps->nmask1 = ~0;
-       (void) i2c_smbus_write_byte_data(client, TPS_MASK1, ~tps->nmask1);
-
-       tps->nmask2 = TPS_REG_ONOFF;
-       if (tps->model == TPS65013)
-               tps->nmask2 |= TPS_REG_NO_CHG;
-       (void) i2c_smbus_write_byte_data(client, TPS_MASK2, ~tps->nmask2);
-
-       (void) i2c_smbus_write_byte_data(client, TPS_MASK3, 0x0f
-               | i2c_smbus_read_byte_data(client, TPS_MASK3));
-
-       tps65010_work(&tps->work.work);
-
-       tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
-                               tps, DEBUG_FOPS);
-
-       /* optionally register GPIOs */
-       if (board && board->base > 0) {
-               tps->outmask = board->outmask;
-
-               tps->chip.label = client->name;
-               tps->chip.dev = &client->dev;
-               tps->chip.owner = THIS_MODULE;
-
-               tps->chip.set = tps65010_gpio_set;
-               tps->chip.direction_output = tps65010_output;
-
-               /* NOTE:  only partial support for inputs; nyet IRQs */
-               tps->chip.get = tps65010_gpio_get;
-
-               tps->chip.base = board->base;
-               tps->chip.ngpio = 7;
-               tps->chip.can_sleep = 1;
-
-               status = gpiochip_add(&tps->chip);
-               if (status < 0)
-                       dev_err(&client->dev, "can't add gpiochip, err %d\n",
-                                       status);
-               else if (board->setup) {
-                       status = board->setup(client, board->context);
-                       if (status < 0) {
-                               dev_dbg(&client->dev,
-                                       "board %s %s err %d\n",
-                                       "setup", client->name, status);
-                               status = 0;
-                       }
-               }
-       }
-
-       return 0;
-fail1:
-       kfree(tps);
-       return status;
-}
-
-static const struct i2c_device_id tps65010_id[] = {
-       { "tps65010", TPS65010 },
-       { "tps65011", TPS65011 },
-       { "tps65012", TPS65012 },
-       { "tps65013", TPS65013 },
-       { "tps65014", TPS65011 },       /* tps65011 charging at 6.5V max */
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, tps65010_id);
-
-static struct i2c_driver tps65010_driver = {
-       .driver = {
-               .name   = "tps65010",
-       },
-       .probe  = tps65010_probe,
-       .remove = __exit_p(tps65010_remove),
-       .id_table = tps65010_id,
-};
-
-/*-------------------------------------------------------------------------*/
-
-/* Draw from VBUS:
- *   0 mA -- DON'T DRAW (might supply power instead)
- * 100 mA -- usb unit load (slowest charge rate)
- * 500 mA -- usb high power (fast battery charge)
- */
-int tps65010_set_vbus_draw(unsigned mA)
-{
-       unsigned long   flags;
-
-       if (!the_tps)
-               return -ENODEV;
-
-       /* assumes non-SMP */
-       local_irq_save(flags);
-       if (mA >= 500)
-               mA = 500;
-       else if (mA >= 100)
-               mA = 100;
-       else
-               mA = 0;
-       the_tps->vbus = mA;
-       if ((the_tps->chgstatus & TPS_CHG_USB)
-                       && test_and_set_bit(
-                               FLAG_VBUS_CHANGED, &the_tps->flags)) {
-               /* gadget drivers call this in_irq() */
-               (void) schedule_work(&the_tps->work.work);
-       }
-       local_irq_restore(flags);
-
-       return 0;
-}
-EXPORT_SYMBOL(tps65010_set_vbus_draw);
-
-/*-------------------------------------------------------------------------*/
-/* tps65010_set_gpio_out_value parameter:
- * gpio:  GPIO1, GPIO2, GPIO3 or GPIO4
- * value: LOW or HIGH
- */
-int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
-{
-       int      status;
-       unsigned defgpio;
-
-       if (!the_tps)
-               return -ENODEV;
-       if ((gpio < GPIO1) || (gpio > GPIO4))
-               return -EINVAL;
-
-       mutex_lock(&the_tps->lock);
-
-       defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO);
-
-       /* Configure GPIO for output */
-       defgpio |= 1 << (gpio + 3);
-
-       /* Writing 1 forces a logic 0 on that GPIO and vice versa */
-       switch (value) {
-       case LOW:
-               defgpio |= 1 << (gpio - 1);    /* set GPIO low by writing 1 */
-               break;
-       /* case HIGH: */
-       default:
-               defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */
-               break;
-       }
-
-       status = i2c_smbus_write_byte_data(the_tps->client,
-               TPS_DEFGPIO, defgpio);
-
-       pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME,
-               gpio, value ? "high" : "low",
-               i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO));
-
-       mutex_unlock(&the_tps->lock);
-       return status;
-}
-EXPORT_SYMBOL(tps65010_set_gpio_out_value);
-
-/*-------------------------------------------------------------------------*/
-/* tps65010_set_led parameter:
- * led:  LED1 or LED2
- * mode: ON, OFF or BLINK
- */
-int tps65010_set_led(unsigned led, unsigned mode)
-{
-       int      status;
-       unsigned led_on, led_per, offs;
-
-       if (!the_tps)
-               return -ENODEV;
-
-       if (led == LED1)
-               offs = 0;
-       else {
-               offs = 2;
-               led = LED2;
-       }
-
-       mutex_lock(&the_tps->lock);
-
-       pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
-               i2c_smbus_read_byte_data(the_tps->client,
-                               TPS_LED1_ON + offs));
-
-       pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
-               i2c_smbus_read_byte_data(the_tps->client,
-                               TPS_LED1_PER + offs));
-
-       switch (mode) {
-       case OFF:
-               led_on  = 1 << 7;
-               led_per = 0 << 7;
-               break;
-       case ON:
-               led_on  = 1 << 7;
-               led_per = 1 << 7;
-               break;
-       case BLINK:
-               led_on  = 0x30 | (0 << 7);
-               led_per = 0x08 | (1 << 7);
-               break;
-       default:
-               printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n",
-                      DRIVER_NAME);
-               mutex_unlock(&the_tps->lock);
-               return -EINVAL;
-       }
-
-       status = i2c_smbus_write_byte_data(the_tps->client,
-                       TPS_LED1_ON + offs, led_on);
-
-       if (status != 0) {
-               printk(KERN_ERR "%s: Failed to write led%i_on register\n",
-                      DRIVER_NAME, led);
-               mutex_unlock(&the_tps->lock);
-               return status;
-       }
-
-       pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
-               i2c_smbus_read_byte_data(the_tps->client, TPS_LED1_ON + offs));
-
-       status = i2c_smbus_write_byte_data(the_tps->client,
-                       TPS_LED1_PER + offs, led_per);
-
-       if (status != 0) {
-               printk(KERN_ERR "%s: Failed to write led%i_per register\n",
-                      DRIVER_NAME, led);
-               mutex_unlock(&the_tps->lock);
-               return status;
-       }
-
-       pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
-               i2c_smbus_read_byte_data(the_tps->client,
-                               TPS_LED1_PER + offs));
-
-       mutex_unlock(&the_tps->lock);
-
-       return status;
-}
-EXPORT_SYMBOL(tps65010_set_led);
-
-/*-------------------------------------------------------------------------*/
-/* tps65010_set_vib parameter:
- * value: ON or OFF
- */
-int tps65010_set_vib(unsigned value)
-{
-       int      status;
-       unsigned vdcdc2;
-
-       if (!the_tps)
-               return -ENODEV;
-
-       mutex_lock(&the_tps->lock);
-
-       vdcdc2 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC2);
-       vdcdc2 &= ~(1 << 1);
-       if (value)
-               vdcdc2 |= (1 << 1);
-       status = i2c_smbus_write_byte_data(the_tps->client,
-               TPS_VDCDC2, vdcdc2);
-
-       pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off");
-
-       mutex_unlock(&the_tps->lock);
-       return status;
-}
-EXPORT_SYMBOL(tps65010_set_vib);
-
-/*-------------------------------------------------------------------------*/
-/* tps65010_set_low_pwr parameter:
- * mode: ON or OFF
- */
-int tps65010_set_low_pwr(unsigned mode)
-{
-       int      status;
-       unsigned vdcdc1;
-
-       if (!the_tps)
-               return -ENODEV;
-
-       mutex_lock(&the_tps->lock);
-
-       pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME,
-               mode ? "enable" : "disable",
-               i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
-
-       vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
-
-       switch (mode) {
-       case OFF:
-               vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
-               break;
-       /* case ON: */
-       default:
-               vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
-               break;
-       }
-
-       status = i2c_smbus_write_byte_data(the_tps->client,
-                       TPS_VDCDC1, vdcdc1);
-
-       if (status != 0)
-               printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
-                       DRIVER_NAME);
-       else
-               pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
-                       i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
-
-       mutex_unlock(&the_tps->lock);
-
-       return status;
-}
-EXPORT_SYMBOL(tps65010_set_low_pwr);
-
-/*-------------------------------------------------------------------------*/
-/* tps65010_config_vregs1 parameter:
- * value to be written to VREGS1 register
- * Note: The complete register is written, set all bits you need
- */
-int tps65010_config_vregs1(unsigned value)
-{
-       int      status;
-
-       if (!the_tps)
-               return -ENODEV;
-
-       mutex_lock(&the_tps->lock);
-
-       pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
-                       i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
-
-       status = i2c_smbus_write_byte_data(the_tps->client,
-                       TPS_VREGS1, value);
-
-       if (status != 0)
-               printk(KERN_ERR "%s: Failed to write vregs1 register\n",
-                       DRIVER_NAME);
-       else
-               pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
-                       i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
-
-       mutex_unlock(&the_tps->lock);
-
-       return status;
-}
-EXPORT_SYMBOL(tps65010_config_vregs1);
-
-/*-------------------------------------------------------------------------*/
-/* tps65013_set_low_pwr parameter:
- * mode: ON or OFF
- */
-
-/* FIXME: Assumes AC or USB power is present. Setting AUA bit is not
-       required if power supply is through a battery */
-
-int tps65013_set_low_pwr(unsigned mode)
-{
-       int      status;
-       unsigned vdcdc1, chgconfig;
-
-       if (!the_tps || the_tps->por)
-               return -ENODEV;
-
-       mutex_lock(&the_tps->lock);
-
-       pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n",
-               DRIVER_NAME,
-               mode ? "enable" : "disable",
-               i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG),
-               i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
-
-       chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
-       vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
-
-       switch (mode) {
-       case OFF:
-               chgconfig &= ~TPS65013_AUA; /* disable AUA bit */
-               vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
-               break;
-       /* case ON: */
-       default:
-               chgconfig |= TPS65013_AUA;  /* enable AUA bit */
-               vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
-               break;
-       }
-
-       status = i2c_smbus_write_byte_data(the_tps->client,
-                       TPS_CHGCONFIG, chgconfig);
-       if (status != 0) {
-               printk(KERN_ERR "%s: Failed to write chconfig register\n",
-        DRIVER_NAME);
-               mutex_unlock(&the_tps->lock);
-               return status;
-       }
-
-       chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
-       the_tps->chgconf = chgconfig;
-       show_chgconfig(0, "chgconf", chgconfig);
-
-       status = i2c_smbus_write_byte_data(the_tps->client,
-                       TPS_VDCDC1, vdcdc1);
-
-       if (status != 0)
-               printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
-        DRIVER_NAME);
-       else
-               pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
-                       i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
-
-       mutex_unlock(&the_tps->lock);
-
-       return status;
-}
-EXPORT_SYMBOL(tps65013_set_low_pwr);
-
-/*-------------------------------------------------------------------------*/
-
-static int __init tps_init(void)
-{
-       u32     tries = 3;
-       int     status = -ENODEV;
-
-       printk(KERN_INFO "%s: version %s\n", DRIVER_NAME, DRIVER_VERSION);
-
-       /* some boards have startup glitches */
-       while (tries--) {
-               status = i2c_add_driver(&tps65010_driver);
-               if (the_tps)
-                       break;
-               i2c_del_driver(&tps65010_driver);
-               if (!tries) {
-                       printk(KERN_ERR "%s: no chip?\n", DRIVER_NAME);
-                       return -ENODEV;
-               }
-               pr_debug("%s: re-probe ...\n", DRIVER_NAME);
-               msleep(10);
-       }
-
-       return status;
-}
-/* NOTE:  this MUST be initialized before the other parts of the system
- * that rely on it ... but after the i2c bus on which this relies.
- * That is, much earlier than on PC-type systems, which don't often use
- * I2C as a core system bus.
- */
-subsys_initcall(tps_init);
-
-static void __exit tps_exit(void)
-{
-       i2c_del_driver(&tps65010_driver);
-}
-module_exit(tps_exit);
-
index 257277394f8c5527d164d6273c8f7b25a157544f..416f9e7286bae2a53f78dfffbc56e0359abe42fc 100644 (file)
@@ -34,6 +34,14 @@ config MFD_ASIC3
          This driver supports the ASIC3 multifunction chip found on many
          PDAs (mainly iPAQ and HTC based ones)
 
+config MFD_DM355EVM_MSP
+       bool "DaVinci DM355 EVM microcontroller"
+       depends on I2C && MACH_DAVINCI_DM355_EVM
+       help
+         This driver supports the MSP430 microcontroller used on these
+         boards.  MSP430 firmware manages resets and power sequencing,
+         inputs from buttons and the IR remote, LEDs, an RTC, and more.
+
 config HTC_EGPIO
        bool "HTC EGPIO support"
        depends on GENERIC_HARDIRQS && GPIOLIB && ARM
@@ -61,9 +69,32 @@ config UCB1400_CORE
          To compile this driver as a module, choose M here: the
          module will be called ucb1400_core.
 
+config TPS65010
+       tristate "TPS6501x Power Management chips"
+       depends on I2C && GPIOLIB
+       default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
+       help
+         If you say yes here you get support for the TPS6501x series of
+         Power Management chips.  These include voltage regulators,
+         lithium ion/polymer battery charging, and other features that
+         are often used in portable devices like cell phones and cameras.
+
+         This driver can also be built as a module.  If so, the module
+         will be called tps65010.
+
+config MENELAUS
+       bool "Texas Instruments TWL92330/Menelaus PM chip"
+       depends on I2C=y && ARCH_OMAP24XX
+       help
+         If you say yes here you get support for the Texas Instruments
+         TWL92330/Menelaus Power Management chip. This include voltage
+         regulators, Dual slot memory card tranceivers, real-time clock
+         and other features that are often used in portable devices like
+         cell phones and PDAs.
+
 config TWL4030_CORE
        bool "Texas Instruments TWL4030/TPS659x0 Support"
-       depends on I2C=y && GENERIC_HARDIRQS && (ARCH_OMAP2 || ARCH_OMAP3)
+       depends on I2C=y && GENERIC_HARDIRQS
        help
          Say yes here if you have TWL4030 family chip on your board.
          This core driver provides register access and IRQ handling
@@ -116,6 +147,7 @@ config PMIC_DA903X
 
 config MFD_WM8400
        tristate "Support Wolfson Microelectronics WM8400"
+       select MFD_CORE
        depends on I2C
        help
          Support for the Wolfson Microelecronics WM8400 PMIC and audio
@@ -142,6 +174,38 @@ config MFD_WM8350_CONFIG_MODE_3
        bool
        depends on MFD_WM8350
 
+config MFD_WM8351_CONFIG_MODE_0
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8351_CONFIG_MODE_1
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8351_CONFIG_MODE_2
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8351_CONFIG_MODE_3
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_0
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_1
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_2
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_3
+       bool
+       depends on MFD_WM8350
+
 config MFD_WM8350_I2C
        tristate "Support Wolfson Microelectronics WM8350 with I2C"
        select MFD_WM8350
index 9a5ad8af9116327e07369ba81c80a9e8e00ec882..0c9418b36c2692a8c700f9f7f69222a4de774419 100644 (file)
@@ -8,6 +8,8 @@ obj-$(CONFIG_MFD_ASIC3)         += asic3.o
 obj-$(CONFIG_HTC_EGPIO)                += htc-egpio.o
 obj-$(CONFIG_HTC_PASIC3)       += htc-pasic3.o
 
+obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
+
 obj-$(CONFIG_MFD_T7L66XB)      += t7l66xb.o
 obj-$(CONFIG_MFD_TC6387XB)     += tc6387xb.o
 obj-$(CONFIG_MFD_TC6393XB)     += tc6393xb.o
@@ -17,6 +19,9 @@ wm8350-objs                   := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
 obj-$(CONFIG_MFD_WM8350)       += wm8350.o
 obj-$(CONFIG_MFD_WM8350_I2C)   += wm8350-i2c.o
 
+obj-$(CONFIG_TPS65010)         += tps65010.o
+obj-$(CONFIG_MENELAUS)         += menelaus.o
+
 obj-$(CONFIG_TWL4030_CORE)     += twl4030-core.o twl4030-irq.o
 
 obj-$(CONFIG_MFD_CORE)         += mfd-core.o
@@ -31,4 +36,4 @@ obj-$(CONFIG_MCP_UCB1200)     += ucb1x00-assabet.o
 endif
 obj-$(CONFIG_UCB1400_CORE)     += ucb1400_core.o
 
-obj-$(CONFIG_PMIC_DA903X)      += da903x.o
\ No newline at end of file
+obj-$(CONFIG_PMIC_DA903X)      += da903x.o
index 0b5bd85dfcec5e4d4bdabfeae15a4a19f19f28ed..99f8dcfe3d982856a99deb42b405bf921619b7d5 100644 (file)
@@ -151,12 +151,24 @@ int da903x_write(struct device *dev, int reg, uint8_t val)
 }
 EXPORT_SYMBOL_GPL(da903x_write);
 
+int da903x_writes(struct device *dev, int reg, int len, uint8_t *val)
+{
+       return __da903x_writes(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(da903x_writes);
+
 int da903x_read(struct device *dev, int reg, uint8_t *val)
 {
        return __da903x_read(to_i2c_client(dev), reg, val);
 }
 EXPORT_SYMBOL_GPL(da903x_read);
 
+int da903x_reads(struct device *dev, int reg, int len, uint8_t *val)
+{
+       return __da903x_reads(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(da903x_reads);
+
 int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
 {
        struct da903x_chip *chip = dev_get_drvdata(dev);
@@ -435,13 +447,13 @@ static const struct i2c_device_id da903x_id_table[] = {
 };
 MODULE_DEVICE_TABLE(i2c, da903x_id_table);
 
-static int __devexit __remove_subdev(struct device *dev, void *unused)
+static int __remove_subdev(struct device *dev, void *unused)
 {
        platform_device_unregister(to_platform_device(dev));
        return 0;
 }
 
-static int __devexit da903x_remove_subdevs(struct da903x_chip *chip)
+static int da903x_remove_subdevs(struct da903x_chip *chip)
 {
        return device_for_each_child(chip->dev, NULL, __remove_subdev);
 }
diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c
new file mode 100644 (file)
index 0000000..4214b3f
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * dm355evm_msp.c - driver for MSP430 firmware on DM355EVM board
+ *
+ * Copyright (C) 2008 David Brownell
+ *
+ * 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/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+#include <linux/i2c.h>
+#include <linux/i2c/dm355evm_msp.h>
+
+
+/*
+ * The DM355 is a DaVinci chip with video support but no C64+ DSP.  Its
+ * EVM board has an MSP430 programmed with firmware for various board
+ * support functions.  This driver exposes some of them directly, and
+ * supports other drivers (e.g. RTC, input) for more complex access.
+ *
+ * Because this firmware is entirely board-specific, this file embeds
+ * knowledge that would be passed as platform_data in a generic driver.
+ *
+ * This driver was tested with firmware revision A4.
+ */
+
+#if defined(CONFIG_KEYBOARD_DM355EVM) \
+               || defined(CONFIG_KEYBOARD_DM355EVM_MODULE)
+#define msp_has_keyboard()     true
+#else
+#define msp_has_keyboard()     false
+#endif
+
+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
+#define msp_has_leds()         true
+#else
+#define msp_has_leds()         false
+#endif
+
+#if defined(CONFIG_RTC_DRV_DM355EVM) || defined(CONFIG_RTC_DRV_DM355EVM_MODULE)
+#define msp_has_rtc()          true
+#else
+#define msp_has_rtc()          false
+#endif
+
+#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE)
+#define msp_has_tvp()          true
+#else
+#define msp_has_tvp()          false
+#endif
+
+
+/*----------------------------------------------------------------------*/
+
+/* REVISIT for paranoia's sake, retry reads/writes on error */
+
+static struct i2c_client *msp430;
+
+/**
+ * dm355evm_msp_write - Writes a register in dm355evm_msp
+ * @value: the value to be written
+ * @reg: register address
+ *
+ * Returns result of operation - 0 is success, else negative errno
+ */
+int dm355evm_msp_write(u8 value, u8 reg)
+{
+       return i2c_smbus_write_byte_data(msp430, reg, value);
+}
+EXPORT_SYMBOL(dm355evm_msp_write);
+
+/**
+ * dm355evm_msp_read - Reads a register from dm355evm_msp
+ * @reg: register address
+ *
+ * Returns result of operation - value, or negative errno
+ */
+int dm355evm_msp_read(u8 reg)
+{
+       return i2c_smbus_read_byte_data(msp430, reg);
+}
+EXPORT_SYMBOL(dm355evm_msp_read);
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Many of the msp430 pins are just used as fixed-direction GPIOs.
+ * We could export a few more of them this way, if we wanted.
+ */
+#define MSP_GPIO(bit,reg)      ((DM355EVM_MSP_ ## reg) << 3 | (bit))
+
+static const u8 msp_gpios[] = {
+       /* eight leds */
+       MSP_GPIO(0, LED), MSP_GPIO(1, LED),
+       MSP_GPIO(2, LED), MSP_GPIO(3, LED),
+       MSP_GPIO(4, LED), MSP_GPIO(5, LED),
+       MSP_GPIO(6, LED), MSP_GPIO(7, LED),
+       /* SW6 and the NTSC/nPAL jumper */
+       MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1),
+       MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1),
+       MSP_GPIO(4, SWITCH1),
+};
+
+#define MSP_GPIO_REG(offset)   (msp_gpios[(offset)] >> 3)
+#define MSP_GPIO_MASK(offset)  BIT(msp_gpios[(offset)] & 0x07)
+
+static int msp_gpio_in(struct gpio_chip *chip, unsigned offset)
+{
+       switch (MSP_GPIO_REG(offset)) {
+       case DM355EVM_MSP_SWITCH1:
+       case DM355EVM_MSP_SWITCH2:
+       case DM355EVM_MSP_SDMMC:
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static u8 msp_led_cache;
+
+static int msp_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       int reg, status;
+
+       reg = MSP_GPIO_REG(offset);
+       status = dm355evm_msp_read(reg);
+       if (status < 0)
+               return status;
+       if (reg == DM355EVM_MSP_LED)
+               msp_led_cache = status;
+       return status & MSP_GPIO_MASK(offset);
+}
+
+static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+       int mask, bits;
+
+       /* NOTE:  there are some other signals that could be
+        * packaged as output GPIOs, but they aren't as useful
+        * as the LEDs ... so for now we don't.
+        */
+       if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED)
+               return -EINVAL;
+
+       mask = MSP_GPIO_MASK(offset);
+       bits = msp_led_cache;
+
+       bits &= ~mask;
+       if (value)
+               bits |= mask;
+       msp_led_cache = bits;
+
+       return dm355evm_msp_write(bits, DM355EVM_MSP_LED);
+}
+
+static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       msp_gpio_out(chip, offset, value);
+}
+
+static struct gpio_chip dm355evm_msp_gpio = {
+       .label                  = "dm355evm_msp",
+       .owner                  = THIS_MODULE,
+       .direction_input        = msp_gpio_in,
+       .get                    = msp_gpio_get,
+       .direction_output       = msp_gpio_out,
+       .set                    = msp_gpio_set,
+       .base                   = -EINVAL,              /* dynamic assignment */
+       .ngpio                  = ARRAY_SIZE(msp_gpios),
+       .can_sleep              = true,
+};
+
+/*----------------------------------------------------------------------*/
+
+static struct device *add_child(struct i2c_client *client, const char *name,
+               void *pdata, unsigned pdata_len,
+               bool can_wakeup, int irq)
+{
+       struct platform_device  *pdev;
+       int                     status;
+
+       pdev = platform_device_alloc(name, -1);
+       if (!pdev) {
+               dev_dbg(&client->dev, "can't alloc dev\n");
+               status = -ENOMEM;
+               goto err;
+       }
+
+       device_init_wakeup(&pdev->dev, can_wakeup);
+       pdev->dev.parent = &client->dev;
+
+       if (pdata) {
+               status = platform_device_add_data(pdev, pdata, pdata_len);
+               if (status < 0) {
+                       dev_dbg(&pdev->dev, "can't add platform_data\n");
+                       goto err;
+               }
+       }
+
+       if (irq) {
+               struct resource r = {
+                       .start = irq,
+                       .flags = IORESOURCE_IRQ,
+               };
+
+               status = platform_device_add_resources(pdev, &r, 1);
+               if (status < 0) {
+                       dev_dbg(&pdev->dev, "can't add irq\n");
+                       goto err;
+               }
+       }
+
+       status = platform_device_add(pdev);
+
+err:
+       if (status < 0) {
+               platform_device_put(pdev);
+               dev_err(&client->dev, "can't add %s dev\n", name);
+               return ERR_PTR(status);
+       }
+       return &pdev->dev;
+}
+
+static int add_children(struct i2c_client *client)
+{
+       static const struct {
+               int offset;
+               char *label;
+       } config_inputs[] = {
+               /* 8 == right after the LEDs */
+               { 8 + 0, "sw6_1", },
+               { 8 + 1, "sw6_2", },
+               { 8 + 2, "sw6_3", },
+               { 8 + 3, "sw6_4", },
+               { 8 + 4, "NTSC/nPAL", },
+       };
+
+       struct device   *child;
+       int             status;
+       int             i;
+
+       /* GPIO-ish stuff */
+       dm355evm_msp_gpio.dev = &client->dev;
+       status = gpiochip_add(&dm355evm_msp_gpio);
+       if (status < 0)
+               return status;
+
+       /* LED output */
+       if (msp_has_leds()) {
+#define GPIO_LED(l)    .name = l, .active_low = true
+               static struct gpio_led evm_leds[] = {
+                       { GPIO_LED("dm355evm::ds14"),
+                               .default_trigger = "heartbeat", },
+                       { GPIO_LED("dm355evm::ds15"),
+                               .default_trigger = "mmc0", },
+                       { GPIO_LED("dm355evm::ds16"),
+                               /* could also be a CE-ATA drive */
+                               .default_trigger = "mmc1", },
+                       { GPIO_LED("dm355evm::ds17"),
+                               .default_trigger = "nand-disk", },
+                       { GPIO_LED("dm355evm::ds18"), },
+                       { GPIO_LED("dm355evm::ds19"), },
+                       { GPIO_LED("dm355evm::ds20"), },
+                       { GPIO_LED("dm355evm::ds21"), },
+               };
+#undef GPIO_LED
+
+               struct gpio_led_platform_data evm_led_data = {
+                       .num_leds       = ARRAY_SIZE(evm_leds),
+                       .leds           = evm_leds,
+               };
+
+               for (i = 0; i < ARRAY_SIZE(evm_leds); i++)
+                       evm_leds[i].gpio = i + dm355evm_msp_gpio.base;
+
+               /* NOTE:  these are the only fully programmable LEDs
+                * on the board, since GPIO-61/ds22 (and many signals
+                * going to DC7) must be used for AEMIF address lines
+                * unless the top 1 GB of NAND is unused...
+                */
+               child = add_child(client, "leds-gpio",
+                               &evm_led_data, sizeof(evm_led_data),
+                               false, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
+       /* configuration inputs */
+       for (i = 0; i < ARRAY_SIZE(config_inputs); i++) {
+               int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset;
+
+               gpio_request(gpio, config_inputs[i].label);
+               gpio_direction_input(gpio);
+
+               /* make it easy for userspace to see these */
+               gpio_export(gpio, false);
+       }
+
+       /* RTC is a 32 bit counter, no alarm */
+       if (msp_has_rtc()) {
+               child = add_child(client, "rtc-dm355evm",
+                               NULL, 0, false, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
+       /* input from buttons and IR remote (uses the IRQ) */
+       if (msp_has_keyboard()) {
+               child = add_child(client, "dm355evm_keys",
+                               NULL, 0, true, client->irq);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
+       return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+static void dm355evm_command(unsigned command)
+{
+       int status;
+
+       status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND);
+       if (status < 0)
+               dev_err(&msp430->dev, "command %d failure %d\n",
+                               command, status);
+}
+
+static void dm355evm_power_off(void)
+{
+       dm355evm_command(MSP_COMMAND_POWEROFF);
+}
+
+static int dm355evm_msp_remove(struct i2c_client *client)
+{
+       pm_power_off = NULL;
+       msp430 = NULL;
+       return 0;
+}
+
+static int
+dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       int             status;
+       const char      *video = msp_has_tvp() ? "TVP5146" : "imager";
+
+       if (msp430)
+               return -EBUSY;
+       msp430 = client;
+
+       /* display revision status; doubles as sanity check */
+       status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
+       if (status < 0)
+               goto fail;
+       dev_info(&client->dev, "firmware v.%02X, %s as video-in\n",
+                       status, video);
+
+       /* mux video input:  either tvp5146 or some external imager */
+       status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER,
+                       DM355EVM_MSP_VIDEO_IN);
+       if (status < 0)
+               dev_warn(&client->dev, "error %d muxing %s as video-in\n",
+                       status, video);
+
+       /* init LED cache, and turn off the LEDs */
+       msp_led_cache = 0xff;
+       dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED);
+
+       /* export capabilities we support */
+       status = add_children(client);
+       if (status < 0)
+               goto fail;
+
+       /* PM hookup */
+       pm_power_off = dm355evm_power_off;
+
+       return 0;
+
+fail:
+       /* FIXME remove children ... */
+       dm355evm_msp_remove(client);
+       return status;
+}
+
+static const struct i2c_device_id dm355evm_msp_ids[] = {
+       { "dm355evm_msp", 0 },
+       { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids);
+
+static struct i2c_driver dm355evm_msp_driver = {
+       .driver.name    = "dm355evm_msp",
+       .id_table       = dm355evm_msp_ids,
+       .probe          = dm355evm_msp_probe,
+       .remove         = dm355evm_msp_remove,
+};
+
+static int __init dm355evm_msp_init(void)
+{
+       return i2c_add_driver(&dm355evm_msp_driver);
+}
+subsys_initcall(dm355evm_msp_init);
+
+static void __exit dm355evm_msp_exit(void)
+{
+       i2c_del_driver(&dm355evm_msp_driver);
+}
+module_exit(dm355evm_msp_exit);
+
+MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
new file mode 100644 (file)
index 0000000..4b364ba
--- /dev/null
@@ -0,0 +1,1285 @@
+/*
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Some parts based tps65010.c:
+ * Copyright (C) 2004 Texas Instruments and
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * Some parts based on tlv320aic24.c:
+ * Copyright (C) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Changes for interrupt handling and clean-up by
+ * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com>
+ * Cleanup and generalized support for voltage setting by
+ * Juha Yrjola
+ * Added support for controlling VCORE and regulator sleep states,
+ * Amit Kucheria <amit.kucheria@nokia.com>
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/mach/irq.h>
+
+#include <mach/gpio.h>
+#include <mach/menelaus.h>
+
+#define DRIVER_NAME                    "menelaus"
+
+#define MENELAUS_I2C_ADDRESS           0x72
+
+#define MENELAUS_REV                   0x01
+#define MENELAUS_VCORE_CTRL1           0x02
+#define MENELAUS_VCORE_CTRL2           0x03
+#define MENELAUS_VCORE_CTRL3           0x04
+#define MENELAUS_VCORE_CTRL4           0x05
+#define MENELAUS_VCORE_CTRL5           0x06
+#define MENELAUS_DCDC_CTRL1            0x07
+#define MENELAUS_DCDC_CTRL2            0x08
+#define MENELAUS_DCDC_CTRL3            0x09
+#define MENELAUS_LDO_CTRL1             0x0A
+#define MENELAUS_LDO_CTRL2             0x0B
+#define MENELAUS_LDO_CTRL3             0x0C
+#define MENELAUS_LDO_CTRL4             0x0D
+#define MENELAUS_LDO_CTRL5             0x0E
+#define MENELAUS_LDO_CTRL6             0x0F
+#define MENELAUS_LDO_CTRL7             0x10
+#define MENELAUS_LDO_CTRL8             0x11
+#define MENELAUS_SLEEP_CTRL1           0x12
+#define MENELAUS_SLEEP_CTRL2           0x13
+#define MENELAUS_DEVICE_OFF            0x14
+#define MENELAUS_OSC_CTRL              0x15
+#define MENELAUS_DETECT_CTRL           0x16
+#define MENELAUS_INT_MASK1             0x17
+#define MENELAUS_INT_MASK2             0x18
+#define MENELAUS_INT_STATUS1           0x19
+#define MENELAUS_INT_STATUS2           0x1A
+#define MENELAUS_INT_ACK1              0x1B
+#define MENELAUS_INT_ACK2              0x1C
+#define MENELAUS_GPIO_CTRL             0x1D
+#define MENELAUS_GPIO_IN               0x1E
+#define MENELAUS_GPIO_OUT              0x1F
+#define MENELAUS_BBSMS                 0x20
+#define MENELAUS_RTC_CTRL              0x21
+#define MENELAUS_RTC_UPDATE            0x22
+#define MENELAUS_RTC_SEC               0x23
+#define MENELAUS_RTC_MIN               0x24
+#define MENELAUS_RTC_HR                        0x25
+#define MENELAUS_RTC_DAY               0x26
+#define MENELAUS_RTC_MON               0x27
+#define MENELAUS_RTC_YR                        0x28
+#define MENELAUS_RTC_WKDAY             0x29
+#define MENELAUS_RTC_AL_SEC            0x2A
+#define MENELAUS_RTC_AL_MIN            0x2B
+#define MENELAUS_RTC_AL_HR             0x2C
+#define MENELAUS_RTC_AL_DAY            0x2D
+#define MENELAUS_RTC_AL_MON            0x2E
+#define MENELAUS_RTC_AL_YR             0x2F
+#define MENELAUS_RTC_COMP_MSB          0x30
+#define MENELAUS_RTC_COMP_LSB          0x31
+#define MENELAUS_S1_PULL_EN            0x32
+#define MENELAUS_S1_PULL_DIR           0x33
+#define MENELAUS_S2_PULL_EN            0x34
+#define MENELAUS_S2_PULL_DIR           0x35
+#define MENELAUS_MCT_CTRL1             0x36
+#define MENELAUS_MCT_CTRL2             0x37
+#define MENELAUS_MCT_CTRL3             0x38
+#define MENELAUS_MCT_PIN_ST            0x39
+#define MENELAUS_DEBOUNCE1             0x3A
+
+#define IH_MENELAUS_IRQS               12
+#define MENELAUS_MMC_S1CD_IRQ          0       /* MMC slot 1 card change */
+#define MENELAUS_MMC_S2CD_IRQ          1       /* MMC slot 2 card change */
+#define MENELAUS_MMC_S1D1_IRQ          2       /* MMC DAT1 low in slot 1 */
+#define MENELAUS_MMC_S2D1_IRQ          3       /* MMC DAT1 low in slot 2 */
+#define MENELAUS_LOWBAT_IRQ            4       /* Low battery */
+#define MENELAUS_HOTDIE_IRQ            5       /* Hot die detect */
+#define MENELAUS_UVLO_IRQ              6       /* UVLO detect */
+#define MENELAUS_TSHUT_IRQ             7       /* Thermal shutdown */
+#define MENELAUS_RTCTMR_IRQ            8       /* RTC timer */
+#define MENELAUS_RTCALM_IRQ            9       /* RTC alarm */
+#define MENELAUS_RTCERR_IRQ            10      /* RTC error */
+#define MENELAUS_PSHBTN_IRQ            11      /* Push button */
+#define MENELAUS_RESERVED12_IRQ                12      /* Reserved */
+#define MENELAUS_RESERVED13_IRQ                13      /* Reserved */
+#define MENELAUS_RESERVED14_IRQ                14      /* Reserved */
+#define MENELAUS_RESERVED15_IRQ                15      /* Reserved */
+
+static void menelaus_work(struct work_struct *_menelaus);
+
+struct menelaus_chip {
+       struct mutex            lock;
+       struct i2c_client       *client;
+       struct work_struct      work;
+#ifdef CONFIG_RTC_DRV_TWL92330
+       struct rtc_device       *rtc;
+       u8                      rtc_control;
+       unsigned                uie:1;
+#endif
+       unsigned                vcore_hw_mode:1;
+       u8                      mask1, mask2;
+       void                    (*handlers[16])(struct menelaus_chip *);
+       void                    (*mmc_callback)(void *data, u8 mask);
+       void                    *mmc_callback_data;
+};
+
+static struct menelaus_chip *the_menelaus;
+
+static int menelaus_write_reg(int reg, u8 value)
+{
+       int val = i2c_smbus_write_byte_data(the_menelaus->client, reg, value);
+
+       if (val < 0) {
+               pr_err(DRIVER_NAME ": write error");
+               return val;
+       }
+
+       return 0;
+}
+
+static int menelaus_read_reg(int reg)
+{
+       int val = i2c_smbus_read_byte_data(the_menelaus->client, reg);
+
+       if (val < 0)
+               pr_err(DRIVER_NAME ": read error");
+
+       return val;
+}
+
+static int menelaus_enable_irq(int irq)
+{
+       if (irq > 7) {
+               irq -= 8;
+               the_menelaus->mask2 &= ~(1 << irq);
+               return menelaus_write_reg(MENELAUS_INT_MASK2,
+                               the_menelaus->mask2);
+       } else {
+               the_menelaus->mask1 &= ~(1 << irq);
+               return menelaus_write_reg(MENELAUS_INT_MASK1,
+                               the_menelaus->mask1);
+       }
+}
+
+static int menelaus_disable_irq(int irq)
+{
+       if (irq > 7) {
+               irq -= 8;
+               the_menelaus->mask2 |= (1 << irq);
+               return menelaus_write_reg(MENELAUS_INT_MASK2,
+                               the_menelaus->mask2);
+       } else {
+               the_menelaus->mask1 |= (1 << irq);
+               return menelaus_write_reg(MENELAUS_INT_MASK1,
+                               the_menelaus->mask1);
+       }
+}
+
+static int menelaus_ack_irq(int irq)
+{
+       if (irq > 7)
+               return menelaus_write_reg(MENELAUS_INT_ACK2, 1 << (irq - 8));
+       else
+               return menelaus_write_reg(MENELAUS_INT_ACK1, 1 << irq);
+}
+
+/* Adds a handler for an interrupt. Does not run in interrupt context */
+static int menelaus_add_irq_work(int irq,
+               void (*handler)(struct menelaus_chip *))
+{
+       int ret = 0;
+
+       mutex_lock(&the_menelaus->lock);
+       the_menelaus->handlers[irq] = handler;
+       ret = menelaus_enable_irq(irq);
+       mutex_unlock(&the_menelaus->lock);
+
+       return ret;
+}
+
+/* Removes handler for an interrupt */
+static int menelaus_remove_irq_work(int irq)
+{
+       int ret = 0;
+
+       mutex_lock(&the_menelaus->lock);
+       ret = menelaus_disable_irq(irq);
+       the_menelaus->handlers[irq] = NULL;
+       mutex_unlock(&the_menelaus->lock);
+
+       return ret;
+}
+
+/*
+ * Gets scheduled when a card detect interrupt happens. Note that in some cases
+ * this line is wired to card cover switch rather than the card detect switch
+ * in each slot. In this case the cards are not seen by menelaus.
+ * FIXME: Add handling for D1 too
+ */
+static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
+{
+       int reg;
+       unsigned char card_mask = 0;
+
+       reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST);
+       if (reg < 0)
+               return;
+
+       if (!(reg & 0x1))
+               card_mask |= (1 << 0);
+
+       if (!(reg & 0x2))
+               card_mask |= (1 << 1);
+
+       if (menelaus_hw->mmc_callback)
+               menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
+                                         card_mask);
+}
+
+/*
+ * Toggles the MMC slots between open-drain and push-pull mode.
+ */
+int menelaus_set_mmc_opendrain(int slot, int enable)
+{
+       int ret, val;
+
+       if (slot != 1 && slot != 2)
+               return -EINVAL;
+       mutex_lock(&the_menelaus->lock);
+       ret = menelaus_read_reg(MENELAUS_MCT_CTRL1);
+       if (ret < 0) {
+               mutex_unlock(&the_menelaus->lock);
+               return ret;
+       }
+       val = ret;
+       if (slot == 1) {
+               if (enable)
+                       val |= 1 << 2;
+               else
+                       val &= ~(1 << 2);
+       } else {
+               if (enable)
+                       val |= 1 << 3;
+               else
+                       val &= ~(1 << 3);
+       }
+       ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
+       mutex_unlock(&the_menelaus->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(menelaus_set_mmc_opendrain);
+
+int menelaus_set_slot_sel(int enable)
+{
+       int ret;
+
+       mutex_lock(&the_menelaus->lock);
+       ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
+       if (ret < 0)
+               goto out;
+       ret |= 0x02;
+       if (enable)
+               ret |= 1 << 5;
+       else
+               ret &= ~(1 << 5);
+       ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
+out:
+       mutex_unlock(&the_menelaus->lock);
+       return ret;
+}
+EXPORT_SYMBOL(menelaus_set_slot_sel);
+
+int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
+{
+       int ret, val;
+
+       if (slot != 1 && slot != 2)
+               return -EINVAL;
+       if (power >= 3)
+               return -EINVAL;
+
+       mutex_lock(&the_menelaus->lock);
+
+       ret = menelaus_read_reg(MENELAUS_MCT_CTRL2);
+       if (ret < 0)
+               goto out;
+       val = ret;
+       if (slot == 1) {
+               if (cd_en)
+                       val |= (1 << 4) | (1 << 6);
+               else
+                       val &= ~((1 << 4) | (1 << 6));
+       } else {
+               if (cd_en)
+                       val |= (1 << 5) | (1 << 7);
+               else
+                       val &= ~((1 << 5) | (1 << 7));
+       }
+       ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
+       if (ret < 0)
+               goto out;
+
+       ret = menelaus_read_reg(MENELAUS_MCT_CTRL3);
+       if (ret < 0)
+               goto out;
+       val = ret;
+       if (slot == 1) {
+               if (enable)
+                       val |= 1 << 0;
+               else
+                       val &= ~(1 << 0);
+       } else {
+               int b;
+
+               if (enable)
+                       ret |= 1 << 1;
+               else
+                       ret &= ~(1 << 1);
+               b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
+               b &= ~0x03;
+               b |= power;
+               ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
+               if (ret < 0)
+                       goto out;
+       }
+       /* Disable autonomous shutdown */
+       val &= ~(0x03 << 2);
+       ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
+out:
+       mutex_unlock(&the_menelaus->lock);
+       return ret;
+}
+EXPORT_SYMBOL(menelaus_set_mmc_slot);
+
+int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask),
+                                  void *data)
+{
+       int ret = 0;
+
+       the_menelaus->mmc_callback_data = data;
+       the_menelaus->mmc_callback = callback;
+       ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ,
+                                   menelaus_mmc_cd_work);
+       if (ret < 0)
+               return ret;
+       ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ,
+                                   menelaus_mmc_cd_work);
+       if (ret < 0)
+               return ret;
+       ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ,
+                                   menelaus_mmc_cd_work);
+       if (ret < 0)
+               return ret;
+       ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ,
+                                   menelaus_mmc_cd_work);
+
+       return ret;
+}
+EXPORT_SYMBOL(menelaus_register_mmc_callback);
+
+void menelaus_unregister_mmc_callback(void)
+{
+       menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ);
+       menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ);
+       menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ);
+       menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ);
+
+       the_menelaus->mmc_callback = NULL;
+       the_menelaus->mmc_callback_data = 0;
+}
+EXPORT_SYMBOL(menelaus_unregister_mmc_callback);
+
+struct menelaus_vtg {
+       const char *name;
+       u8 vtg_reg;
+       u8 vtg_shift;
+       u8 vtg_bits;
+       u8 mode_reg;
+};
+
+struct menelaus_vtg_value {
+       u16 vtg;
+       u16 val;
+};
+
+static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV,
+                               int vtg_val, int mode)
+{
+       int val, ret;
+       struct i2c_client *c = the_menelaus->client;
+
+       mutex_lock(&the_menelaus->lock);
+       if (vtg == 0)
+               goto set_voltage;
+
+       ret = menelaus_read_reg(vtg->vtg_reg);
+       if (ret < 0)
+               goto out;
+       val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift);
+       val |= vtg_val << vtg->vtg_shift;
+
+       dev_dbg(&c->dev, "Setting voltage '%s'"
+                        "to %d mV (reg 0x%02x, val 0x%02x)\n",
+                       vtg->name, mV, vtg->vtg_reg, val);
+
+       ret = menelaus_write_reg(vtg->vtg_reg, val);
+       if (ret < 0)
+               goto out;
+set_voltage:
+       ret = menelaus_write_reg(vtg->mode_reg, mode);
+out:
+       mutex_unlock(&the_menelaus->lock);
+       if (ret == 0) {
+               /* Wait for voltage to stabilize */
+               msleep(1);
+       }
+       return ret;
+}
+
+static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl,
+                                 int n)
+{
+       int i;
+
+       for (i = 0; i < n; i++, tbl++)
+               if (tbl->vtg == vtg)
+                       return tbl->val;
+       return -EINVAL;
+}
+
+/*
+ * Vcore can be programmed in two ways:
+ * SW-controlled: Required voltage is programmed into VCORE_CTRL1
+ * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3
+ * and VCORE_CTRL4
+ *
+ * Call correct 'set' function accordingly
+ */
+
+static const struct menelaus_vtg_value vcore_values[] = {
+       { 1000, 0 },
+       { 1025, 1 },
+       { 1050, 2 },
+       { 1075, 3 },
+       { 1100, 4 },
+       { 1125, 5 },
+       { 1150, 6 },
+       { 1175, 7 },
+       { 1200, 8 },
+       { 1225, 9 },
+       { 1250, 10 },
+       { 1275, 11 },
+       { 1300, 12 },
+       { 1325, 13 },
+       { 1350, 14 },
+       { 1375, 15 },
+       { 1400, 16 },
+       { 1425, 17 },
+       { 1450, 18 },
+};
+
+int menelaus_set_vcore_sw(unsigned int mV)
+{
+       int val, ret;
+       struct i2c_client *c = the_menelaus->client;
+
+       val = menelaus_get_vtg_value(mV, vcore_values,
+                                    ARRAY_SIZE(vcore_values));
+       if (val < 0)
+               return -EINVAL;
+
+       dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val);
+
+       /* Set SW mode and the voltage in one go. */
+       mutex_lock(&the_menelaus->lock);
+       ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
+       if (ret == 0)
+               the_menelaus->vcore_hw_mode = 0;
+       mutex_unlock(&the_menelaus->lock);
+       msleep(1);
+
+       return ret;
+}
+
+int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
+{
+       int fval, rval, val, ret;
+       struct i2c_client *c = the_menelaus->client;
+
+       rval = menelaus_get_vtg_value(roof_mV, vcore_values,
+                                     ARRAY_SIZE(vcore_values));
+       if (rval < 0)
+               return -EINVAL;
+       fval = menelaus_get_vtg_value(floor_mV, vcore_values,
+                                     ARRAY_SIZE(vcore_values));
+       if (fval < 0)
+               return -EINVAL;
+
+       dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n",
+              floor_mV, roof_mV);
+
+       mutex_lock(&the_menelaus->lock);
+       ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval);
+       if (ret < 0)
+               goto out;
+       ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval);
+       if (ret < 0)
+               goto out;
+       if (!the_menelaus->vcore_hw_mode) {
+               val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
+               /* HW mode, turn OFF byte comparator */
+               val |= ((1 << 7) | (1 << 5));
+               ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
+               the_menelaus->vcore_hw_mode = 1;
+       }
+       msleep(1);
+out:
+       mutex_unlock(&the_menelaus->lock);
+       return ret;
+}
+
+static const struct menelaus_vtg vmem_vtg = {
+       .name = "VMEM",
+       .vtg_reg = MENELAUS_LDO_CTRL1,
+       .vtg_shift = 0,
+       .vtg_bits = 2,
+       .mode_reg = MENELAUS_LDO_CTRL3,
+};
+
+static const struct menelaus_vtg_value vmem_values[] = {
+       { 1500, 0 },
+       { 1800, 1 },
+       { 1900, 2 },
+       { 2500, 3 },
+};
+
+int menelaus_set_vmem(unsigned int mV)
+{
+       int val;
+
+       if (mV == 0)
+               return menelaus_set_voltage(&vmem_vtg, 0, 0, 0);
+
+       val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values));
+       if (val < 0)
+               return -EINVAL;
+       return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vmem);
+
+static const struct menelaus_vtg vio_vtg = {
+       .name = "VIO",
+       .vtg_reg = MENELAUS_LDO_CTRL1,
+       .vtg_shift = 2,
+       .vtg_bits = 2,
+       .mode_reg = MENELAUS_LDO_CTRL4,
+};
+
+static const struct menelaus_vtg_value vio_values[] = {
+       { 1500, 0 },
+       { 1800, 1 },
+       { 2500, 2 },
+       { 2800, 3 },
+};
+
+int menelaus_set_vio(unsigned int mV)
+{
+       int val;
+
+       if (mV == 0)
+               return menelaus_set_voltage(&vio_vtg, 0, 0, 0);
+
+       val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values));
+       if (val < 0)
+               return -EINVAL;
+       return menelaus_set_voltage(&vio_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vio);
+
+static const struct menelaus_vtg_value vdcdc_values[] = {
+       { 1500, 0 },
+       { 1800, 1 },
+       { 2000, 2 },
+       { 2200, 3 },
+       { 2400, 4 },
+       { 2800, 5 },
+       { 3000, 6 },
+       { 3300, 7 },
+};
+
+static const struct menelaus_vtg vdcdc2_vtg = {
+       .name = "VDCDC2",
+       .vtg_reg = MENELAUS_DCDC_CTRL1,
+       .vtg_shift = 0,
+       .vtg_bits = 3,
+       .mode_reg = MENELAUS_DCDC_CTRL2,
+};
+
+static const struct menelaus_vtg vdcdc3_vtg = {
+       .name = "VDCDC3",
+       .vtg_reg = MENELAUS_DCDC_CTRL1,
+       .vtg_shift = 3,
+       .vtg_bits = 3,
+       .mode_reg = MENELAUS_DCDC_CTRL3,
+};
+
+int menelaus_set_vdcdc(int dcdc, unsigned int mV)
+{
+       const struct menelaus_vtg *vtg;
+       int val;
+
+       if (dcdc != 2 && dcdc != 3)
+               return -EINVAL;
+       if (dcdc == 2)
+               vtg = &vdcdc2_vtg;
+       else
+               vtg = &vdcdc3_vtg;
+
+       if (mV == 0)
+               return menelaus_set_voltage(vtg, 0, 0, 0);
+
+       val = menelaus_get_vtg_value(mV, vdcdc_values,
+                                    ARRAY_SIZE(vdcdc_values));
+       if (val < 0)
+               return -EINVAL;
+       return menelaus_set_voltage(vtg, mV, val, 0x03);
+}
+
+static const struct menelaus_vtg_value vmmc_values[] = {
+       { 1850, 0 },
+       { 2800, 1 },
+       { 3000, 2 },
+       { 3100, 3 },
+};
+
+static const struct menelaus_vtg vmmc_vtg = {
+       .name = "VMMC",
+       .vtg_reg = MENELAUS_LDO_CTRL1,
+       .vtg_shift = 6,
+       .vtg_bits = 2,
+       .mode_reg = MENELAUS_LDO_CTRL7,
+};
+
+int menelaus_set_vmmc(unsigned int mV)
+{
+       int val;
+
+       if (mV == 0)
+               return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0);
+
+       val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values));
+       if (val < 0)
+               return -EINVAL;
+       return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vmmc);
+
+
+static const struct menelaus_vtg_value vaux_values[] = {
+       { 1500, 0 },
+       { 1800, 1 },
+       { 2500, 2 },
+       { 2800, 3 },
+};
+
+static const struct menelaus_vtg vaux_vtg = {
+       .name = "VAUX",
+       .vtg_reg = MENELAUS_LDO_CTRL1,
+       .vtg_shift = 4,
+       .vtg_bits = 2,
+       .mode_reg = MENELAUS_LDO_CTRL6,
+};
+
+int menelaus_set_vaux(unsigned int mV)
+{
+       int val;
+
+       if (mV == 0)
+               return menelaus_set_voltage(&vaux_vtg, 0, 0, 0);
+
+       val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values));
+       if (val < 0)
+               return -EINVAL;
+       return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vaux);
+
+int menelaus_get_slot_pin_states(void)
+{
+       return menelaus_read_reg(MENELAUS_MCT_PIN_ST);
+}
+EXPORT_SYMBOL(menelaus_get_slot_pin_states);
+
+int menelaus_set_regulator_sleep(int enable, u32 val)
+{
+       int t, ret;
+       struct i2c_client *c = the_menelaus->client;
+
+       mutex_lock(&the_menelaus->lock);
+       ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val);
+       if (ret < 0)
+               goto out;
+
+       dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val);
+
+       ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
+       if (ret < 0)
+               goto out;
+       t = ((1 << 6) | 0x04);
+       if (enable)
+               ret |= t;
+       else
+               ret &= ~t;
+       ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
+out:
+       mutex_unlock(&the_menelaus->lock);
+       return ret;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* Handles Menelaus interrupts. Does not run in interrupt context */
+static void menelaus_work(struct work_struct *_menelaus)
+{
+       struct menelaus_chip *menelaus =
+                       container_of(_menelaus, struct menelaus_chip, work);
+       void (*handler)(struct menelaus_chip *menelaus);
+
+       while (1) {
+               unsigned isr;
+
+               isr = (menelaus_read_reg(MENELAUS_INT_STATUS2)
+                               & ~menelaus->mask2) << 8;
+               isr |= menelaus_read_reg(MENELAUS_INT_STATUS1)
+                               & ~menelaus->mask1;
+               if (!isr)
+                       break;
+
+               while (isr) {
+                       int irq = fls(isr) - 1;
+                       isr &= ~(1 << irq);
+
+                       mutex_lock(&menelaus->lock);
+                       menelaus_disable_irq(irq);
+                       menelaus_ack_irq(irq);
+                       handler = menelaus->handlers[irq];
+                       if (handler)
+                               handler(menelaus);
+                       menelaus_enable_irq(irq);
+                       mutex_unlock(&menelaus->lock);
+               }
+       }
+       enable_irq(menelaus->client->irq);
+}
+
+/*
+ * We cannot use I2C in interrupt context, so we just schedule work.
+ */
+static irqreturn_t menelaus_irq(int irq, void *_menelaus)
+{
+       struct menelaus_chip *menelaus = _menelaus;
+
+       disable_irq_nosync(irq);
+       (void)schedule_work(&menelaus->work);
+
+       return IRQ_HANDLED;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/*
+ * The RTC needs to be set once, then it runs on backup battery power.
+ * It supports alarms, including system wake alarms (from some modes);
+ * and 1/second IRQs if requested.
+ */
+#ifdef CONFIG_RTC_DRV_TWL92330
+
+#define RTC_CTRL_RTC_EN                (1 << 0)
+#define RTC_CTRL_AL_EN         (1 << 1)
+#define RTC_CTRL_MODE12                (1 << 2)
+#define RTC_CTRL_EVERY_MASK    (3 << 3)
+#define RTC_CTRL_EVERY_SEC     (0 << 3)
+#define RTC_CTRL_EVERY_MIN     (1 << 3)
+#define RTC_CTRL_EVERY_HR      (2 << 3)
+#define RTC_CTRL_EVERY_DAY     (3 << 3)
+
+#define RTC_UPDATE_EVERY       0x08
+
+#define RTC_HR_PM              (1 << 7)
+
+static void menelaus_to_time(char *regs, struct rtc_time *t)
+{
+       t->tm_sec = bcd2bin(regs[0]);
+       t->tm_min = bcd2bin(regs[1]);
+       if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
+               t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1;
+               if (regs[2] & RTC_HR_PM)
+                       t->tm_hour += 12;
+       } else
+               t->tm_hour = bcd2bin(regs[2] & 0x3f);
+       t->tm_mday = bcd2bin(regs[3]);
+       t->tm_mon = bcd2bin(regs[4]) - 1;
+       t->tm_year = bcd2bin(regs[5]) + 100;
+}
+
+static int time_to_menelaus(struct rtc_time *t, int regnum)
+{
+       int     hour, status;
+
+       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec));
+       if (status < 0)
+               goto fail;
+
+       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min));
+       if (status < 0)
+               goto fail;
+
+       if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
+               hour = t->tm_hour + 1;
+               if (hour > 12)
+                       hour = RTC_HR_PM | bin2bcd(hour - 12);
+               else
+                       hour = bin2bcd(hour);
+       } else
+               hour = bin2bcd(t->tm_hour);
+       status = menelaus_write_reg(regnum++, hour);
+       if (status < 0)
+               goto fail;
+
+       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday));
+       if (status < 0)
+               goto fail;
+
+       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1));
+       if (status < 0)
+               goto fail;
+
+       status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100));
+       if (status < 0)
+               goto fail;
+
+       return 0;
+fail:
+       dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n",
+                       --regnum, status);
+       return status;
+}
+
+static int menelaus_read_time(struct device *dev, struct rtc_time *t)
+{
+       struct i2c_msg  msg[2];
+       char            regs[7];
+       int             status;
+
+       /* block read date and time registers */
+       regs[0] = MENELAUS_RTC_SEC;
+
+       msg[0].addr = MENELAUS_I2C_ADDRESS;
+       msg[0].flags = 0;
+       msg[0].len = 1;
+       msg[0].buf = regs;
+
+       msg[1].addr = MENELAUS_I2C_ADDRESS;
+       msg[1].flags = I2C_M_RD;
+       msg[1].len = sizeof(regs);
+       msg[1].buf = regs;
+
+       status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
+       if (status != 2) {
+               dev_err(dev, "%s error %d\n", "read", status);
+               return -EIO;
+       }
+
+       menelaus_to_time(regs, t);
+       t->tm_wday = bcd2bin(regs[6]);
+
+       return 0;
+}
+
+static int menelaus_set_time(struct device *dev, struct rtc_time *t)
+{
+       int             status;
+
+       /* write date and time registers */
+       status = time_to_menelaus(t, MENELAUS_RTC_SEC);
+       if (status < 0)
+               return status;
+       status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday));
+       if (status < 0) {
+               dev_err(&the_menelaus->client->dev, "rtc write reg %02x "
+                               "err %d\n", MENELAUS_RTC_WKDAY, status);
+               return status;
+       }
+
+       /* now commit the write */
+       status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY);
+       if (status < 0)
+               dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n",
+                               status);
+
+       return 0;
+}
+
+static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w)
+{
+       struct i2c_msg  msg[2];
+       char            regs[6];
+       int             status;
+
+       /* block read alarm registers */
+       regs[0] = MENELAUS_RTC_AL_SEC;
+
+       msg[0].addr = MENELAUS_I2C_ADDRESS;
+       msg[0].flags = 0;
+       msg[0].len = 1;
+       msg[0].buf = regs;
+
+       msg[1].addr = MENELAUS_I2C_ADDRESS;
+       msg[1].flags = I2C_M_RD;
+       msg[1].len = sizeof(regs);
+       msg[1].buf = regs;
+
+       status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
+       if (status != 2) {
+               dev_err(dev, "%s error %d\n", "alarm read", status);
+               return -EIO;
+       }
+
+       menelaus_to_time(regs, &w->time);
+
+       w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN);
+
+       /* NOTE we *could* check if actually pending... */
+       w->pending = 0;
+
+       return 0;
+}
+
+static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w)
+{
+       int             status;
+
+       if (the_menelaus->client->irq <= 0 && w->enabled)
+               return -ENODEV;
+
+       /* clear previous alarm enable */
+       if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) {
+               the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+               status = menelaus_write_reg(MENELAUS_RTC_CTRL,
+                               the_menelaus->rtc_control);
+               if (status < 0)
+                       return status;
+       }
+
+       /* write alarm registers */
+       status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC);
+       if (status < 0)
+               return status;
+
+       /* enable alarm if requested */
+       if (w->enabled) {
+               the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
+               status = menelaus_write_reg(MENELAUS_RTC_CTRL,
+                               the_menelaus->rtc_control);
+       }
+
+       return status;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static void menelaus_rtc_update_work(struct menelaus_chip *m)
+{
+       /* report 1/sec update */
+       local_irq_disable();
+       rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF);
+       local_irq_enable();
+}
+
+static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg)
+{
+       int     status;
+
+       if (the_menelaus->client->irq <= 0)
+               return -ENOIOCTLCMD;
+
+       switch (cmd) {
+       /* alarm IRQ */
+       case RTC_AIE_ON:
+               if (the_menelaus->rtc_control & RTC_CTRL_AL_EN)
+                       return 0;
+               the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
+               break;
+       case RTC_AIE_OFF:
+               if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN))
+                       return 0;
+               the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+               break;
+       /* 1/second "update" IRQ */
+       case RTC_UIE_ON:
+               if (the_menelaus->uie)
+                       return 0;
+               status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
+               status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ,
+                               menelaus_rtc_update_work);
+               if (status == 0)
+                       the_menelaus->uie = 1;
+               return status;
+       case RTC_UIE_OFF:
+               if (!the_menelaus->uie)
+                       return 0;
+               status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
+               if (status == 0)
+                       the_menelaus->uie = 0;
+               return status;
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
+}
+
+#else
+#define menelaus_ioctl NULL
+#endif
+
+/* REVISIT no compensation register support ... */
+
+static const struct rtc_class_ops menelaus_rtc_ops = {
+       .ioctl                  = menelaus_ioctl,
+       .read_time              = menelaus_read_time,
+       .set_time               = menelaus_set_time,
+       .read_alarm             = menelaus_read_alarm,
+       .set_alarm              = menelaus_set_alarm,
+};
+
+static void menelaus_rtc_alarm_work(struct menelaus_chip *m)
+{
+       /* report alarm */
+       local_irq_disable();
+       rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF);
+       local_irq_enable();
+
+       /* then disable it; alarms are oneshot */
+       the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+       menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
+}
+
+static inline void menelaus_rtc_init(struct menelaus_chip *m)
+{
+       int     alarm = (m->client->irq > 0);
+
+       /* assume 32KDETEN pin is pulled high */
+       if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) {
+               dev_dbg(&m->client->dev, "no 32k oscillator\n");
+               return;
+       }
+
+       /* support RTC alarm; it can issue wakeups */
+       if (alarm) {
+               if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ,
+                               menelaus_rtc_alarm_work) < 0) {
+                       dev_err(&m->client->dev, "can't handle RTC alarm\n");
+                       return;
+               }
+               device_init_wakeup(&m->client->dev, 1);
+       }
+
+       /* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */
+       m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL);
+       if (!(m->rtc_control & RTC_CTRL_RTC_EN)
+                       || (m->rtc_control & RTC_CTRL_AL_EN)
+                       || (m->rtc_control & RTC_CTRL_EVERY_MASK)) {
+               if (!(m->rtc_control & RTC_CTRL_RTC_EN)) {
+                       dev_warn(&m->client->dev, "rtc clock needs setting\n");
+                       m->rtc_control |= RTC_CTRL_RTC_EN;
+               }
+               m->rtc_control &= ~RTC_CTRL_EVERY_MASK;
+               m->rtc_control &= ~RTC_CTRL_AL_EN;
+               menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control);
+       }
+
+       m->rtc = rtc_device_register(DRIVER_NAME,
+                       &m->client->dev,
+                       &menelaus_rtc_ops, THIS_MODULE);
+       if (IS_ERR(m->rtc)) {
+               if (alarm) {
+                       menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ);
+                       device_init_wakeup(&m->client->dev, 0);
+               }
+               dev_err(&m->client->dev, "can't register RTC: %d\n",
+                               (int) PTR_ERR(m->rtc));
+               the_menelaus->rtc = NULL;
+       }
+}
+
+#else
+
+static inline void menelaus_rtc_init(struct menelaus_chip *m)
+{
+       /* nothing */
+}
+
+#endif
+
+/*-----------------------------------------------------------------------*/
+
+static struct i2c_driver menelaus_i2c_driver;
+
+static int menelaus_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct menelaus_chip    *menelaus;
+       int                     rev = 0, val;
+       int                     err = 0;
+       struct menelaus_platform_data *menelaus_pdata =
+                                       client->dev.platform_data;
+
+       if (the_menelaus) {
+               dev_dbg(&client->dev, "only one %s for now\n",
+                               DRIVER_NAME);
+               return -ENODEV;
+       }
+
+       menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL);
+       if (!menelaus)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, menelaus);
+
+       the_menelaus = menelaus;
+       menelaus->client = client;
+
+       /* If a true probe check the device */
+       rev = menelaus_read_reg(MENELAUS_REV);
+       if (rev < 0) {
+               pr_err(DRIVER_NAME ": device not found");
+               err = -ENODEV;
+               goto fail1;
+       }
+
+       /* Ack and disable all Menelaus interrupts */
+       menelaus_write_reg(MENELAUS_INT_ACK1, 0xff);
+       menelaus_write_reg(MENELAUS_INT_ACK2, 0xff);
+       menelaus_write_reg(MENELAUS_INT_MASK1, 0xff);
+       menelaus_write_reg(MENELAUS_INT_MASK2, 0xff);
+       menelaus->mask1 = 0xff;
+       menelaus->mask2 = 0xff;
+
+       /* Set output buffer strengths */
+       menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);
+
+       if (client->irq > 0) {
+               err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED,
+                                 DRIVER_NAME, menelaus);
+               if (err) {
+                       dev_dbg(&client->dev,  "can't get IRQ %d, err %d\n",
+                                       client->irq, err);
+                       goto fail1;
+               }
+       }
+
+       mutex_init(&menelaus->lock);
+       INIT_WORK(&menelaus->work, menelaus_work);
+
+       pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f);
+
+       val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
+       if (val < 0)
+               goto fail2;
+       if (val & (1 << 7))
+               menelaus->vcore_hw_mode = 1;
+       else
+               menelaus->vcore_hw_mode = 0;
+
+       if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) {
+               err = menelaus_pdata->late_init(&client->dev);
+               if (err < 0)
+                       goto fail2;
+       }
+
+       menelaus_rtc_init(menelaus);
+
+       return 0;
+fail2:
+       free_irq(client->irq, menelaus);
+       flush_scheduled_work();
+fail1:
+       kfree(menelaus);
+       return err;
+}
+
+static int __exit menelaus_remove(struct i2c_client *client)
+{
+       struct menelaus_chip    *menelaus = i2c_get_clientdata(client);
+
+       free_irq(client->irq, menelaus);
+       kfree(menelaus);
+       i2c_set_clientdata(client, NULL);
+       the_menelaus = NULL;
+       return 0;
+}
+
+static const struct i2c_device_id menelaus_id[] = {
+       { "menelaus", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, menelaus_id);
+
+static struct i2c_driver menelaus_i2c_driver = {
+       .driver = {
+               .name           = DRIVER_NAME,
+       },
+       .probe          = menelaus_probe,
+       .remove         = __exit_p(menelaus_remove),
+       .id_table       = menelaus_id,
+};
+
+static int __init menelaus_init(void)
+{
+       int res;
+
+       res = i2c_add_driver(&menelaus_i2c_driver);
+       if (res < 0) {
+               pr_err(DRIVER_NAME ": driver registration failed\n");
+               return res;
+       }
+
+       return 0;
+}
+
+static void __exit menelaus_exit(void)
+{
+       i2c_del_driver(&menelaus_i2c_driver);
+
+       /* FIXME: Shutdown menelaus parts that can be shut down */
+}
+
+MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
+MODULE_DESCRIPTION("I2C interface for Menelaus.");
+MODULE_LICENSE("GPL");
+
+module_init(menelaus_init);
+module_exit(menelaus_exit);
index 6c0d1bec4b76c1ab58ae0cbe86cc2539cc3b6375..54ddf3772e0c104c29e9d98da29e22eb7c300220 100644 (file)
@@ -34,6 +34,7 @@ static int mfd_add_device(struct device *parent, int id,
                goto fail_device;
 
        pdev->dev.parent = parent;
+       platform_set_drvdata(pdev, cell->driver_data);
 
        ret = platform_device_add_data(pdev,
                        cell->platform_data, cell->data_size);
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
new file mode 100644 (file)
index 0000000..acf8b9d
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ * tps65010 - driver for tps6501x power management chips
+ *
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/i2c/tps65010.h>
+
+#include <asm/gpio.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+#define        DRIVER_VERSION  "2 May 2005"
+#define        DRIVER_NAME     (tps65010_driver.driver.name)
+
+MODULE_DESCRIPTION("TPS6501x Power Management Driver");
+MODULE_LICENSE("GPL");
+
+static struct i2c_driver tps65010_driver;
+
+/*-------------------------------------------------------------------------*/
+
+/* This driver handles a family of multipurpose chips, which incorporate
+ * voltage regulators, lithium ion/polymer battery charging, GPIOs, LEDs,
+ * and other features often needed in portable devices like cell phones
+ * or digital cameras.
+ *
+ * The tps65011 and tps65013 have different voltage settings compared
+ * to tps65010 and tps65012.  The tps65013 has a NO_CHG status/irq.
+ * All except tps65010 have "wait" mode, possibly defaulted so that
+ * battery-insert != device-on.
+ *
+ * We could distinguish between some models by checking VDCDC1.UVLO or
+ * other registers, unless they've been changed already after powerup
+ * as part of board setup by a bootloader.
+ */
+enum tps_model {
+       TPS65010,
+       TPS65011,
+       TPS65012,
+       TPS65013,
+};
+
+struct tps65010 {
+       struct i2c_client       *client;
+       struct mutex            lock;
+       struct delayed_work     work;
+       struct dentry           *file;
+       unsigned                charging:1;
+       unsigned                por:1;
+       unsigned                model:8;
+       u16                     vbus;
+       unsigned long           flags;
+#define        FLAG_VBUS_CHANGED       0
+#define        FLAG_IRQ_ENABLE         1
+
+       /* copies of last register state */
+       u8                      chgstatus, regstatus, chgconf;
+       u8                      nmask1, nmask2;
+
+       u8                      outmask;
+       struct gpio_chip        chip;
+       struct platform_device  *leds;
+};
+
+#define        POWER_POLL_DELAY        msecs_to_jiffies(5000)
+
+/*-------------------------------------------------------------------------*/
+
+#if    defined(DEBUG) || defined(CONFIG_DEBUG_FS)
+
+static void dbg_chgstat(char *buf, size_t len, u8 chgstatus)
+{
+       snprintf(buf, len, "%02x%s%s%s%s%s%s%s%s\n",
+               chgstatus,
+               (chgstatus & TPS_CHG_USB) ? " USB" : "",
+               (chgstatus & TPS_CHG_AC) ? " AC" : "",
+               (chgstatus & TPS_CHG_THERM) ? " therm" : "",
+               (chgstatus & TPS_CHG_TERM) ? " done" :
+                       ((chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+                               ? " (charging)" : ""),
+               (chgstatus & TPS_CHG_TAPER_TMO) ? " taper_tmo" : "",
+               (chgstatus & TPS_CHG_CHG_TMO) ? " charge_tmo" : "",
+               (chgstatus & TPS_CHG_PRECHG_TMO) ? " prechg_tmo" : "",
+               (chgstatus & TPS_CHG_TEMP_ERR) ? " temp_err" : "");
+}
+
+static void dbg_regstat(char *buf, size_t len, u8 regstatus)
+{
+       snprintf(buf, len, "%02x %s%s%s%s%s%s%s%s\n",
+               regstatus,
+               (regstatus & TPS_REG_ONOFF) ? "off" : "(on)",
+               (regstatus & TPS_REG_COVER) ? " uncover" : "",
+               (regstatus & TPS_REG_UVLO) ? " UVLO" : "",
+               (regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "",
+               (regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "",
+               (regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "",
+               (regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "",
+               (regstatus & TPS_REG_PG_CORE) ? " core_bad" : "");
+}
+
+static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig)
+{
+       const char *hibit;
+
+       if (por)
+               hibit = (chgconfig & TPS_CHARGE_POR)
+                               ? "POR=69ms" : "POR=1sec";
+       else
+               hibit = (chgconfig & TPS65013_AUA) ? "AUA" : "";
+
+       snprintf(buf, len, "%02x %s%s%s AC=%d%% USB=%dmA %sCharge\n",
+               chgconfig, hibit,
+               (chgconfig & TPS_CHARGE_RESET) ? " reset" : "",
+               (chgconfig & TPS_CHARGE_FAST) ? " fast" : "",
+               ({int p; switch ((chgconfig >> 3) & 3) {
+               case 3:         p = 100; break;
+               case 2:         p = 75; break;
+               case 1:         p = 50; break;
+               default:        p = 25; break;
+               }; p; }),
+               (chgconfig & TPS_VBUS_CHARGING)
+                       ? ((chgconfig & TPS_VBUS_500MA) ? 500 : 100)
+                       : 0,
+               (chgconfig & TPS_CHARGE_ENABLE) ? "" : "No");
+}
+
+#endif
+
+#ifdef DEBUG
+
+static void show_chgstatus(const char *label, u8 chgstatus)
+{
+       char buf [100];
+
+       dbg_chgstat(buf, sizeof buf, chgstatus);
+       pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+static void show_regstatus(const char *label, u8 regstatus)
+{
+       char buf [100];
+
+       dbg_regstat(buf, sizeof buf, regstatus);
+       pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+static void show_chgconfig(int por, const char *label, u8 chgconfig)
+{
+       char buf [100];
+
+       dbg_chgconf(por, buf, sizeof buf, chgconfig);
+       pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+#else
+
+static inline void show_chgstatus(const char *label, u8 chgstatus) { }
+static inline void show_regstatus(const char *label, u8 chgstatus) { }
+static inline void show_chgconfig(int por, const char *label, u8 chgconfig) { }
+
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+static int dbg_show(struct seq_file *s, void *_)
+{
+       struct tps65010 *tps = s->private;
+       u8              value, v2;
+       unsigned        i;
+       char            buf[100];
+       const char      *chip;
+
+       switch (tps->model) {
+       case TPS65010:  chip = "tps65010"; break;
+       case TPS65011:  chip = "tps65011"; break;
+       case TPS65012:  chip = "tps65012"; break;
+       case TPS65013:  chip = "tps65013"; break;
+       default:        chip = NULL; break;
+       }
+       seq_printf(s, "driver  %s\nversion %s\nchip    %s\n\n",
+                       DRIVER_NAME, DRIVER_VERSION, chip);
+
+       mutex_lock(&tps->lock);
+
+       /* FIXME how can we tell whether a battery is present?
+        * likely involves a charge gauging chip (like BQ26501).
+        */
+
+       seq_printf(s, "%scharging\n\n", tps->charging ? "" : "(not) ");
+
+
+       /* registers for monitoring battery charging and status; note
+        * that reading chgstat and regstat may ack IRQs...
+        */
+       value = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
+       dbg_chgconf(tps->por, buf, sizeof buf, value);
+       seq_printf(s, "chgconfig %s", buf);
+
+       value = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
+       dbg_chgstat(buf, sizeof buf, value);
+       seq_printf(s, "chgstat   %s", buf);
+       value = i2c_smbus_read_byte_data(tps->client, TPS_MASK1);
+       dbg_chgstat(buf, sizeof buf, value);
+       seq_printf(s, "mask1     %s", buf);
+       /* ignore ackint1 */
+
+       value = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
+       dbg_regstat(buf, sizeof buf, value);
+       seq_printf(s, "regstat   %s", buf);
+       value = i2c_smbus_read_byte_data(tps->client, TPS_MASK2);
+       dbg_regstat(buf, sizeof buf, value);
+       seq_printf(s, "mask2     %s\n", buf);
+       /* ignore ackint2 */
+
+       (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
+
+
+       /* VMAIN voltage, enable lowpower, etc */
+       value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1);
+       seq_printf(s, "vdcdc1    %02x\n", value);
+
+       /* VCORE voltage, vibrator on/off */
+       value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC2);
+       seq_printf(s, "vdcdc2    %02x\n", value);
+
+       /* both LD0s, and their lowpower behavior */
+       value = i2c_smbus_read_byte_data(tps->client, TPS_VREGS1);
+       seq_printf(s, "vregs1    %02x\n\n", value);
+
+
+       /* LEDs and GPIOs */
+       value = i2c_smbus_read_byte_data(tps->client, TPS_LED1_ON);
+       v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER);
+       seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n",
+               (value & 0x80)
+                       ? ((v2 & 0x80) ? "on" : "off")
+                       : ((v2 & 0x80) ? "blink" : "(nPG)"),
+               value, v2,
+               (value & 0x7f) * 10, (v2 & 0x7f) * 100);
+
+       value = i2c_smbus_read_byte_data(tps->client, TPS_LED2_ON);
+       v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER);
+       seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n",
+               (value & 0x80)
+                       ? ((v2 & 0x80) ? "on" : "off")
+                       : ((v2 & 0x80) ? "blink" : "off"),
+               value, v2,
+               (value & 0x7f) * 10, (v2 & 0x7f) * 100);
+
+       value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
+       v2 = i2c_smbus_read_byte_data(tps->client, TPS_MASK3);
+       seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2);
+
+       for (i = 0; i < 4; i++) {
+               if (value & (1 << (4 + i)))
+                       seq_printf(s, "  gpio%d-out %s\n", i + 1,
+                               (value & (1 << i)) ? "low" : "hi ");
+               else
+                       seq_printf(s, "  gpio%d-in  %s %s %s\n", i + 1,
+                               (value & (1 << i)) ? "hi " : "low",
+                               (v2 & (1 << i)) ? "no-irq" : "irq",
+                               (v2 & (1 << (4 + i))) ? "rising" : "falling");
+       }
+
+       mutex_unlock(&tps->lock);
+       return 0;
+}
+
+static int dbg_tps_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dbg_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+       .open           = dbg_tps_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+#define        DEBUG_FOPS      &debug_fops
+
+#else
+#define        DEBUG_FOPS      NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* handle IRQS in a task context, so we can use I2C calls */
+static void tps65010_interrupt(struct tps65010 *tps)
+{
+       u8 tmp = 0, mask, poll;
+
+       /* IRQs won't trigger for certain events, but we can get
+        * others by polling (normally, with external power applied).
+        */
+       poll = 0;
+
+       /* regstatus irqs */
+       if (tps->nmask2) {
+               tmp = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
+               mask = tmp ^ tps->regstatus;
+               tps->regstatus = tmp;
+               mask &= tps->nmask2;
+       } else
+               mask = 0;
+       if (mask) {
+               tps->regstatus =  tmp;
+               /* may need to shut something down ... */
+
+               /* "off" usually means deep sleep */
+               if (tmp & TPS_REG_ONOFF) {
+                       pr_info("%s: power off button\n", DRIVER_NAME);
+#if 0
+                       /* REVISIT:  this might need its own workqueue
+                        * plus tweaks including deadlock avoidance ...
+                        * also needs to get error handling and probably
+                        * an #ifdef CONFIG_HIBERNATION
+                        */
+                       hibernate();
+#endif
+                       poll = 1;
+               }
+       }
+
+       /* chgstatus irqs */
+       if (tps->nmask1) {
+               tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
+               mask = tmp ^ tps->chgstatus;
+               tps->chgstatus = tmp;
+               mask &= tps->nmask1;
+       } else
+               mask = 0;
+       if (mask) {
+               unsigned        charging = 0;
+
+               show_chgstatus("chg/irq", tmp);
+               if (tmp & (TPS_CHG_USB|TPS_CHG_AC))
+                       show_chgconfig(tps->por, "conf", tps->chgconf);
+
+               /* Unless it was turned off or disabled, we charge any
+                * battery whenever there's power available for it
+                * and the charger hasn't been disabled.
+                */
+               if (!(tps->chgstatus & ~(TPS_CHG_USB|TPS_CHG_AC))
+                               && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+                               && (tps->chgconf & TPS_CHARGE_ENABLE)
+                               ) {
+                       if (tps->chgstatus & TPS_CHG_USB) {
+                               /* VBUS options are readonly until reconnect */
+                               if (mask & TPS_CHG_USB)
+                                       set_bit(FLAG_VBUS_CHANGED, &tps->flags);
+                               charging = 1;
+                       } else if (tps->chgstatus & TPS_CHG_AC)
+                               charging = 1;
+               }
+               if (charging != tps->charging) {
+                       tps->charging = charging;
+                       pr_info("%s: battery %scharging\n",
+                               DRIVER_NAME, charging ? "" :
+                               ((tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+                                       ? "NOT " : "dis"));
+               }
+       }
+
+       /* always poll to detect (a) power removal, without tps65013
+        * NO_CHG IRQ; or (b) restart of charging after stop.
+        */
+       if ((tps->model != TPS65013 || !tps->charging)
+                       && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)))
+               poll = 1;
+       if (poll)
+               (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
+
+       /* also potentially gpio-in rise or fall */
+}
+
+/* handle IRQs and polling using keventd for now */
+static void tps65010_work(struct work_struct *work)
+{
+       struct tps65010         *tps;
+
+       tps = container_of(work, struct tps65010, work.work);
+       mutex_lock(&tps->lock);
+
+       tps65010_interrupt(tps);
+
+       if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) {
+               int     status;
+               u8      chgconfig, tmp;
+
+               chgconfig = i2c_smbus_read_byte_data(tps->client,
+                                       TPS_CHGCONFIG);
+               chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING);
+               if (tps->vbus == 500)
+                       chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING;
+               else if (tps->vbus >= 100)
+                       chgconfig |= TPS_VBUS_CHARGING;
+
+               status = i2c_smbus_write_byte_data(tps->client,
+                               TPS_CHGCONFIG, chgconfig);
+
+               /* vbus update fails unless VBUS is connected! */
+               tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
+               tps->chgconf = tmp;
+               show_chgconfig(tps->por, "update vbus", tmp);
+       }
+
+       if (test_and_clear_bit(FLAG_IRQ_ENABLE, &tps->flags))
+               enable_irq(tps->client->irq);
+
+       mutex_unlock(&tps->lock);
+}
+
+static irqreturn_t tps65010_irq(int irq, void *_tps)
+{
+       struct tps65010         *tps = _tps;
+
+       disable_irq_nosync(irq);
+       set_bit(FLAG_IRQ_ENABLE, &tps->flags);
+       (void) schedule_work(&tps->work.work);
+       return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* offsets 0..3 == GPIO1..GPIO4
+ * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
+ * offset 6 == vibrator motor driver
+ */
+static void
+tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       if (offset < 4)
+               tps65010_set_gpio_out_value(offset + 1, value);
+       else if (offset < 6)
+               tps65010_set_led(offset - 3, value ? ON : OFF);
+       else
+               tps65010_set_vib(value);
+}
+
+static int
+tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+       /* GPIOs may be input-only */
+       if (offset < 4) {
+               struct tps65010         *tps;
+
+               tps = container_of(chip, struct tps65010, chip);
+               if (!(tps->outmask & (1 << offset)))
+                       return -EINVAL;
+               tps65010_set_gpio_out_value(offset + 1, value);
+       } else if (offset < 6)
+               tps65010_set_led(offset - 3, value ? ON : OFF);
+       else
+               tps65010_set_vib(value);
+
+       return 0;
+}
+
+static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       int                     value;
+       struct tps65010         *tps;
+
+       tps = container_of(chip, struct tps65010, chip);
+
+       if (offset < 4) {
+               value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
+               if (value < 0)
+                       return 0;
+               if (value & (1 << (offset + 4)))        /* output */
+                       return !(value & (1 << offset));
+               else                                    /* input */
+                       return (value & (1 << offset));
+       }
+
+       /* REVISIT we *could* report LED1/nPG and LED2 state ... */
+       return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct tps65010 *the_tps;
+
+static int __exit tps65010_remove(struct i2c_client *client)
+{
+       struct tps65010         *tps = i2c_get_clientdata(client);
+       struct tps65010_board   *board = client->dev.platform_data;
+
+       if (board && board->teardown) {
+               int status = board->teardown(client, board->context);
+               if (status < 0)
+                       dev_dbg(&client->dev, "board %s %s err %d\n",
+                               "teardown", client->name, status);
+       }
+       if (client->irq > 0)
+               free_irq(client->irq, tps);
+       cancel_delayed_work(&tps->work);
+       flush_scheduled_work();
+       debugfs_remove(tps->file);
+       kfree(tps);
+       i2c_set_clientdata(client, NULL);
+       the_tps = NULL;
+       return 0;
+}
+
+static int tps65010_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct tps65010         *tps;
+       int                     status;
+       struct tps65010_board   *board = client->dev.platform_data;
+
+       if (the_tps) {
+               dev_dbg(&client->dev, "only one tps6501x chip allowed\n");
+               return -ENODEV;
+       }
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -EINVAL;
+
+       tps = kzalloc(sizeof *tps, GFP_KERNEL);
+       if (!tps)
+               return -ENOMEM;
+
+       mutex_init(&tps->lock);
+       INIT_DELAYED_WORK(&tps->work, tps65010_work);
+       tps->client = client;
+       tps->model = id->driver_data;
+
+       /* the IRQ is active low, but many gpio lines can't support that
+        * so this driver uses falling-edge triggers instead.
+        */
+       if (client->irq > 0) {
+               status = request_irq(client->irq, tps65010_irq,
+                       IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
+                       DRIVER_NAME, tps);
+               if (status < 0) {
+                       dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
+                                       client->irq, status);
+                       goto fail1;
+               }
+               /* annoying race here, ideally we'd have an option
+                * to claim the irq now and enable it later.
+                * FIXME genirq IRQF_NOAUTOEN now solves that ...
+                */
+               disable_irq(client->irq);
+               set_bit(FLAG_IRQ_ENABLE, &tps->flags);
+       } else
+               dev_warn(&client->dev, "IRQ not configured!\n");
+
+
+       switch (tps->model) {
+       case TPS65010:
+       case TPS65012:
+               tps->por = 1;
+               break;
+       /* else CHGCONFIG.POR is replaced by AUA, enabling a WAIT mode */
+       }
+       tps->chgconf = i2c_smbus_read_byte_data(client, TPS_CHGCONFIG);
+       show_chgconfig(tps->por, "conf/init", tps->chgconf);
+
+       show_chgstatus("chg/init",
+               i2c_smbus_read_byte_data(client, TPS_CHGSTATUS));
+       show_regstatus("reg/init",
+               i2c_smbus_read_byte_data(client, TPS_REGSTATUS));
+
+       pr_debug("%s: vdcdc1 0x%02x, vdcdc2 %02x, vregs1 %02x\n", DRIVER_NAME,
+               i2c_smbus_read_byte_data(client, TPS_VDCDC1),
+               i2c_smbus_read_byte_data(client, TPS_VDCDC2),
+               i2c_smbus_read_byte_data(client, TPS_VREGS1));
+       pr_debug("%s: defgpio 0x%02x, mask3 0x%02x\n", DRIVER_NAME,
+               i2c_smbus_read_byte_data(client, TPS_DEFGPIO),
+               i2c_smbus_read_byte_data(client, TPS_MASK3));
+
+       i2c_set_clientdata(client, tps);
+       the_tps = tps;
+
+#if    defined(CONFIG_USB_GADGET) && !defined(CONFIG_USB_OTG)
+       /* USB hosts can't draw VBUS.  OTG devices could, later
+        * when OTG infrastructure enables it.  USB peripherals
+        * could be relying on VBUS while booting, though.
+        */
+       tps->vbus = 100;
+#endif
+
+       /* unmask the "interesting" irqs, then poll once to
+        * kickstart monitoring, initialize shadowed status
+        * registers, and maybe disable VBUS draw.
+        */
+       tps->nmask1 = ~0;
+       (void) i2c_smbus_write_byte_data(client, TPS_MASK1, ~tps->nmask1);
+
+       tps->nmask2 = TPS_REG_ONOFF;
+       if (tps->model == TPS65013)
+               tps->nmask2 |= TPS_REG_NO_CHG;
+       (void) i2c_smbus_write_byte_data(client, TPS_MASK2, ~tps->nmask2);
+
+       (void) i2c_smbus_write_byte_data(client, TPS_MASK3, 0x0f
+               | i2c_smbus_read_byte_data(client, TPS_MASK3));
+
+       tps65010_work(&tps->work.work);
+
+       tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
+                               tps, DEBUG_FOPS);
+
+       /* optionally register GPIOs */
+       if (board && board->base > 0) {
+               tps->outmask = board->outmask;
+
+               tps->chip.label = client->name;
+               tps->chip.dev = &client->dev;
+               tps->chip.owner = THIS_MODULE;
+
+               tps->chip.set = tps65010_gpio_set;
+               tps->chip.direction_output = tps65010_output;
+
+               /* NOTE:  only partial support for inputs; nyet IRQs */
+               tps->chip.get = tps65010_gpio_get;
+
+               tps->chip.base = board->base;
+               tps->chip.ngpio = 7;
+               tps->chip.can_sleep = 1;
+
+               status = gpiochip_add(&tps->chip);
+               if (status < 0)
+                       dev_err(&client->dev, "can't add gpiochip, err %d\n",
+                                       status);
+               else if (board->setup) {
+                       status = board->setup(client, board->context);
+                       if (status < 0) {
+                               dev_dbg(&client->dev,
+                                       "board %s %s err %d\n",
+                                       "setup", client->name, status);
+                               status = 0;
+                       }
+               }
+       }
+
+       return 0;
+fail1:
+       kfree(tps);
+       return status;
+}
+
+static const struct i2c_device_id tps65010_id[] = {
+       { "tps65010", TPS65010 },
+       { "tps65011", TPS65011 },
+       { "tps65012", TPS65012 },
+       { "tps65013", TPS65013 },
+       { "tps65014", TPS65011 },       /* tps65011 charging at 6.5V max */
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65010_id);
+
+static struct i2c_driver tps65010_driver = {
+       .driver = {
+               .name   = "tps65010",
+       },
+       .probe  = tps65010_probe,
+       .remove = __exit_p(tps65010_remove),
+       .id_table = tps65010_id,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Draw from VBUS:
+ *   0 mA -- DON'T DRAW (might supply power instead)
+ * 100 mA -- usb unit load (slowest charge rate)
+ * 500 mA -- usb high power (fast battery charge)
+ */
+int tps65010_set_vbus_draw(unsigned mA)
+{
+       unsigned long   flags;
+
+       if (!the_tps)
+               return -ENODEV;
+
+       /* assumes non-SMP */
+       local_irq_save(flags);
+       if (mA >= 500)
+               mA = 500;
+       else if (mA >= 100)
+               mA = 100;
+       else
+               mA = 0;
+       the_tps->vbus = mA;
+       if ((the_tps->chgstatus & TPS_CHG_USB)
+                       && test_and_set_bit(
+                               FLAG_VBUS_CHANGED, &the_tps->flags)) {
+               /* gadget drivers call this in_irq() */
+               (void) schedule_work(&the_tps->work.work);
+       }
+       local_irq_restore(flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(tps65010_set_vbus_draw);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_gpio_out_value parameter:
+ * gpio:  GPIO1, GPIO2, GPIO3 or GPIO4
+ * value: LOW or HIGH
+ */
+int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
+{
+       int      status;
+       unsigned defgpio;
+
+       if (!the_tps)
+               return -ENODEV;
+       if ((gpio < GPIO1) || (gpio > GPIO4))
+               return -EINVAL;
+
+       mutex_lock(&the_tps->lock);
+
+       defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO);
+
+       /* Configure GPIO for output */
+       defgpio |= 1 << (gpio + 3);
+
+       /* Writing 1 forces a logic 0 on that GPIO and vice versa */
+       switch (value) {
+       case LOW:
+               defgpio |= 1 << (gpio - 1);    /* set GPIO low by writing 1 */
+               break;
+       /* case HIGH: */
+       default:
+               defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */
+               break;
+       }
+
+       status = i2c_smbus_write_byte_data(the_tps->client,
+               TPS_DEFGPIO, defgpio);
+
+       pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME,
+               gpio, value ? "high" : "low",
+               i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO));
+
+       mutex_unlock(&the_tps->lock);
+       return status;
+}
+EXPORT_SYMBOL(tps65010_set_gpio_out_value);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_led parameter:
+ * led:  LED1 or LED2
+ * mode: ON, OFF or BLINK
+ */
+int tps65010_set_led(unsigned led, unsigned mode)
+{
+       int      status;
+       unsigned led_on, led_per, offs;
+
+       if (!the_tps)
+               return -ENODEV;
+
+       if (led == LED1)
+               offs = 0;
+       else {
+               offs = 2;
+               led = LED2;
+       }
+
+       mutex_lock(&the_tps->lock);
+
+       pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
+               i2c_smbus_read_byte_data(the_tps->client,
+                               TPS_LED1_ON + offs));
+
+       pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
+               i2c_smbus_read_byte_data(the_tps->client,
+                               TPS_LED1_PER + offs));
+
+       switch (mode) {
+       case OFF:
+               led_on  = 1 << 7;
+               led_per = 0 << 7;
+               break;
+       case ON:
+               led_on  = 1 << 7;
+               led_per = 1 << 7;
+               break;
+       case BLINK:
+               led_on  = 0x30 | (0 << 7);
+               led_per = 0x08 | (1 << 7);
+               break;
+       default:
+               printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n",
+                      DRIVER_NAME);
+               mutex_unlock(&the_tps->lock);
+               return -EINVAL;
+       }
+
+       status = i2c_smbus_write_byte_data(the_tps->client,
+                       TPS_LED1_ON + offs, led_on);
+
+       if (status != 0) {
+               printk(KERN_ERR "%s: Failed to write led%i_on register\n",
+                      DRIVER_NAME, led);
+               mutex_unlock(&the_tps->lock);
+               return status;
+       }
+
+       pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
+               i2c_smbus_read_byte_data(the_tps->client, TPS_LED1_ON + offs));
+
+       status = i2c_smbus_write_byte_data(the_tps->client,
+                       TPS_LED1_PER + offs, led_per);
+
+       if (status != 0) {
+               printk(KERN_ERR "%s: Failed to write led%i_per register\n",
+                      DRIVER_NAME, led);
+               mutex_unlock(&the_tps->lock);
+               return status;
+       }
+
+       pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
+               i2c_smbus_read_byte_data(the_tps->client,
+                               TPS_LED1_PER + offs));
+
+       mutex_unlock(&the_tps->lock);
+
+       return status;
+}
+EXPORT_SYMBOL(tps65010_set_led);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_vib parameter:
+ * value: ON or OFF
+ */
+int tps65010_set_vib(unsigned value)
+{
+       int      status;
+       unsigned vdcdc2;
+
+       if (!the_tps)
+               return -ENODEV;
+
+       mutex_lock(&the_tps->lock);
+
+       vdcdc2 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC2);
+       vdcdc2 &= ~(1 << 1);
+       if (value)
+               vdcdc2 |= (1 << 1);
+       status = i2c_smbus_write_byte_data(the_tps->client,
+               TPS_VDCDC2, vdcdc2);
+
+       pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off");
+
+       mutex_unlock(&the_tps->lock);
+       return status;
+}
+EXPORT_SYMBOL(tps65010_set_vib);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_low_pwr parameter:
+ * mode: ON or OFF
+ */
+int tps65010_set_low_pwr(unsigned mode)
+{
+       int      status;
+       unsigned vdcdc1;
+
+       if (!the_tps)
+               return -ENODEV;
+
+       mutex_lock(&the_tps->lock);
+
+       pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME,
+               mode ? "enable" : "disable",
+               i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+       vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
+
+       switch (mode) {
+       case OFF:
+               vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
+               break;
+       /* case ON: */
+       default:
+               vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
+               break;
+       }
+
+       status = i2c_smbus_write_byte_data(the_tps->client,
+                       TPS_VDCDC1, vdcdc1);
+
+       if (status != 0)
+               printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
+                       DRIVER_NAME);
+       else
+               pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
+                       i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+       mutex_unlock(&the_tps->lock);
+
+       return status;
+}
+EXPORT_SYMBOL(tps65010_set_low_pwr);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_config_vregs1 parameter:
+ * value to be written to VREGS1 register
+ * Note: The complete register is written, set all bits you need
+ */
+int tps65010_config_vregs1(unsigned value)
+{
+       int      status;
+
+       if (!the_tps)
+               return -ENODEV;
+
+       mutex_lock(&the_tps->lock);
+
+       pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+                       i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
+
+       status = i2c_smbus_write_byte_data(the_tps->client,
+                       TPS_VREGS1, value);
+
+       if (status != 0)
+               printk(KERN_ERR "%s: Failed to write vregs1 register\n",
+                       DRIVER_NAME);
+       else
+               pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+                       i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
+
+       mutex_unlock(&the_tps->lock);
+
+       return status;
+}
+EXPORT_SYMBOL(tps65010_config_vregs1);
+
+/*-------------------------------------------------------------------------*/
+/* tps65013_set_low_pwr parameter:
+ * mode: ON or OFF
+ */
+
+/* FIXME: Assumes AC or USB power is present. Setting AUA bit is not
+       required if power supply is through a battery */
+
+int tps65013_set_low_pwr(unsigned mode)
+{
+       int      status;
+       unsigned vdcdc1, chgconfig;
+
+       if (!the_tps || the_tps->por)
+               return -ENODEV;
+
+       mutex_lock(&the_tps->lock);
+
+       pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n",
+               DRIVER_NAME,
+               mode ? "enable" : "disable",
+               i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG),
+               i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+       chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
+       vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
+
+       switch (mode) {
+       case OFF:
+               chgconfig &= ~TPS65013_AUA; /* disable AUA bit */
+               vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
+               break;
+       /* case ON: */
+       default:
+               chgconfig |= TPS65013_AUA;  /* enable AUA bit */
+               vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
+               break;
+       }
+
+       status = i2c_smbus_write_byte_data(the_tps->client,
+                       TPS_CHGCONFIG, chgconfig);
+       if (status != 0) {
+               printk(KERN_ERR "%s: Failed to write chconfig register\n",
+        DRIVER_NAME);
+               mutex_unlock(&the_tps->lock);
+               return status;
+       }
+
+       chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
+       the_tps->chgconf = chgconfig;
+       show_chgconfig(0, "chgconf", chgconfig);
+
+       status = i2c_smbus_write_byte_data(the_tps->client,
+                       TPS_VDCDC1, vdcdc1);
+
+       if (status != 0)
+               printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
+        DRIVER_NAME);
+       else
+               pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
+                       i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+       mutex_unlock(&the_tps->lock);
+
+       return status;
+}
+EXPORT_SYMBOL(tps65013_set_low_pwr);
+
+/*-------------------------------------------------------------------------*/
+
+static int __init tps_init(void)
+{
+       u32     tries = 3;
+       int     status = -ENODEV;
+
+       printk(KERN_INFO "%s: version %s\n", DRIVER_NAME, DRIVER_VERSION);
+
+       /* some boards have startup glitches */
+       while (tries--) {
+               status = i2c_add_driver(&tps65010_driver);
+               if (the_tps)
+                       break;
+               i2c_del_driver(&tps65010_driver);
+               if (!tries) {
+                       printk(KERN_ERR "%s: no chip?\n", DRIVER_NAME);
+                       return -ENODEV;
+               }
+               pr_debug("%s: re-probe ...\n", DRIVER_NAME);
+               msleep(10);
+       }
+
+       return status;
+}
+/* NOTE:  this MUST be initialized before the other parts of the system
+ * that rely on it ... but after the i2c bus on which this relies.
+ * That is, much earlier than on PC-type systems, which don't often use
+ * I2C as a core system bus.
+ */
+subsys_initcall(tps_init);
+
+static void __exit tps_exit(void)
+{
+       i2c_del_driver(&tps65010_driver);
+}
+module_exit(tps_exit);
+
index dd843c4fbcc7cc76330199fc917185dbf16680f1..b59c385cbc12f98bc319845d719fc83e518c8dc0 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 
+#include <linux/regulator/machine.h>
+
 #include <linux/i2c.h>
 #include <linux/i2c/twl4030.h>
 
 #define twl_has_gpio() false
 #endif
 
+#if defined(CONFIG_REGULATOR_TWL4030) \
+       || defined(CONFIG_REGULATOR_TWL4030_MODULE)
+#define twl_has_regulator()    true
+#else
+#define twl_has_regulator()    false
+#endif
+
 #if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE)
 #define twl_has_madc() true
 #else
 #define HIGH_PERF_SQ                   (1 << 3)
 
 
+/* chip-specific feature flags, for i2c_device_id.driver_data */
+#define TWL4030_VAUX2          BIT(0)  /* pre-5030 voltage ranges */
+#define TPS_SUBSET             BIT(1)  /* tps659[23]0 have fewer LDOs */
+
 /*----------------------------------------------------------------------*/
 
 /* is driver active, bound to a chip? */
@@ -225,7 +238,7 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
  *
  * Returns the result of operation - 0 is success
  */
-int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes)
+int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
 {
        int ret;
        int sid;
@@ -274,7 +287,7 @@ EXPORT_SYMBOL(twl4030_i2c_write);
  *
  * Returns result of operation - num_bytes is success else failure.
  */
-int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes)
+int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
 {
        int ret;
        u8 val;
@@ -352,258 +365,258 @@ EXPORT_SYMBOL(twl4030_i2c_read_u8);
 
 /*----------------------------------------------------------------------*/
 
-/*
- * NOTE:  We know the first 8 IRQs after pdata->base_irq are
- * for the PIH, and the next are for the PWR_INT SIH, since
- * that's how twl_init_irq() sets things up.
- */
-
-static int add_children(struct twl4030_platform_data *pdata)
+static struct device *
+add_numbered_child(unsigned chip, const char *name, int num,
+               void *pdata, unsigned pdata_len,
+               bool can_wakeup, int irq0, int irq1)
 {
-       struct platform_device  *pdev = NULL;
-       struct twl4030_client   *twl = NULL;
-       int                     status = 0;
+       struct platform_device  *pdev;
+       struct twl4030_client   *twl = &twl4030_modules[chip];
+       int                     status;
+
+       pdev = platform_device_alloc(name, num);
+       if (!pdev) {
+               dev_dbg(&twl->client->dev, "can't alloc dev\n");
+               status = -ENOMEM;
+               goto err;
+       }
 
-       if (twl_has_bci() && pdata->bci) {
-               twl = &twl4030_modules[3];
+       device_init_wakeup(&pdev->dev, can_wakeup);
+       pdev->dev.parent = &twl->client->dev;
 
-               pdev = platform_device_alloc("twl4030_bci", -1);
-               if (!pdev) {
-                       pr_debug("%s: can't alloc bci dev\n", DRIVER_NAME);
-                       status = -ENOMEM;
+       if (pdata) {
+               status = platform_device_add_data(pdev, pdata, pdata_len);
+               if (status < 0) {
+                       dev_dbg(&pdev->dev, "can't add platform_data\n");
                        goto err;
                }
+       }
 
-               if (status == 0) {
-                       pdev->dev.parent = &twl->client->dev;
-                       status = platform_device_add_data(pdev, pdata->bci,
-                                       sizeof(*pdata->bci));
-                       if (status < 0) {
-                               dev_dbg(&twl->client->dev,
-                                       "can't add bci data, %d\n",
-                                       status);
-                               goto err;
-                       }
-               }
-
-               if (status == 0) {
-                       struct resource r = {
-                               .start = pdata->irq_base + 8 + 1,
-                               .flags = IORESOURCE_IRQ,
-                       };
-
-                       status = platform_device_add_resources(pdev, &r, 1);
-               }
-
-               if (status == 0)
-                       status = platform_device_add(pdev);
+       if (irq0) {
+               struct resource r[2] = {
+                       { .start = irq0, .flags = IORESOURCE_IRQ, },
+                       { .start = irq1, .flags = IORESOURCE_IRQ, },
+               };
 
+               status = platform_device_add_resources(pdev, r, irq1 ? 2 : 1);
                if (status < 0) {
-                       platform_device_put(pdev);
-                       dev_dbg(&twl->client->dev,
-                                       "can't create bci dev, %d\n",
-                                       status);
+                       dev_dbg(&pdev->dev, "can't add irqs\n");
                        goto err;
                }
        }
 
-       if (twl_has_gpio() && pdata->gpio) {
-               twl = &twl4030_modules[1];
+       status = platform_device_add(pdev);
 
-               pdev = platform_device_alloc("twl4030_gpio", -1);
-               if (!pdev) {
-                       pr_debug("%s: can't alloc gpio dev\n", DRIVER_NAME);
-                       status = -ENOMEM;
-                       goto err;
-               }
+err:
+       if (status < 0) {
+               platform_device_put(pdev);
+               dev_err(&twl->client->dev, "can't add %s dev\n", name);
+               return ERR_PTR(status);
+       }
+       return &pdev->dev;
+}
 
-               /* more driver model init */
-               if (status == 0) {
-                       pdev->dev.parent = &twl->client->dev;
-                       /* device_init_wakeup(&pdev->dev, 1); */
-
-                       status = platform_device_add_data(pdev, pdata->gpio,
-                                       sizeof(*pdata->gpio));
-                       if (status < 0) {
-                               dev_dbg(&twl->client->dev,
-                                       "can't add gpio data, %d\n",
-                                       status);
-                               goto err;
-                       }
-               }
+static inline struct device *add_child(unsigned chip, const char *name,
+               void *pdata, unsigned pdata_len,
+               bool can_wakeup, int irq0, int irq1)
+{
+       return add_numbered_child(chip, name, -1, pdata, pdata_len,
+               can_wakeup, irq0, irq1);
+}
 
-               /* GPIO module IRQ */
-               if (status == 0) {
-                       struct resource r = {
-                               .start = pdata->irq_base + 0,
-                               .flags = IORESOURCE_IRQ,
-                       };
+static struct device *
+add_regulator_linked(int num, struct regulator_init_data *pdata,
+               struct regulator_consumer_supply *consumers,
+               unsigned num_consumers)
+{
+       /* regulator framework demands init_data ... */
+       if (!pdata)
+               return NULL;
 
-                       status = platform_device_add_resources(pdev, &r, 1);
-               }
+       if (consumers) {
+               pdata->consumer_supplies = consumers;
+               pdata->num_consumer_supplies = num_consumers;
+       }
 
-               if (status == 0)
-                       status = platform_device_add(pdev);
+       /* NOTE:  we currently ignore regulator IRQs, e.g. for short circuits */
+       return add_numbered_child(3, "twl4030_reg", num,
+               pdata, sizeof(*pdata), false, 0, 0);
+}
 
-               if (status < 0) {
-                       platform_device_put(pdev);
-                       dev_dbg(&twl->client->dev,
-                                       "can't create gpio dev, %d\n",
-                                       status);
-                       goto err;
-               }
+static struct device *
+add_regulator(int num, struct regulator_init_data *pdata)
+{
+       return add_regulator_linked(num, pdata, NULL, 0);
+}
+
+/*
+ * NOTE:  We know the first 8 IRQs after pdata->base_irq are
+ * for the PIH, and the next are for the PWR_INT SIH, since
+ * that's how twl_init_irq() sets things up.
+ */
+
+static int
+add_children(struct twl4030_platform_data *pdata, unsigned long features)
+{
+       struct device   *child;
+       struct device   *usb_transceiver = NULL;
+
+       if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) {
+               child = add_child(3, "twl4030_bci",
+                               pdata->bci, sizeof(*pdata->bci),
+                               false,
+                               /* irq0 = CHG_PRES, irq1 = BCI */
+                               pdata->irq_base + 8 + 1, pdata->irq_base + 2);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
+       if (twl_has_gpio() && pdata->gpio) {
+               child = add_child(1, "twl4030_gpio",
+                               pdata->gpio, sizeof(*pdata->gpio),
+                               false, pdata->irq_base + 0, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
        }
 
        if (twl_has_keypad() && pdata->keypad) {
-               pdev = platform_device_alloc("twl4030_keypad", -1);
-               if (pdev) {
-                       twl = &twl4030_modules[2];
-                       pdev->dev.parent = &twl->client->dev;
-                       device_init_wakeup(&pdev->dev, 1);
-                       status = platform_device_add_data(pdev, pdata->keypad,
-                                       sizeof(*pdata->keypad));
-                       if (status < 0) {
-                               dev_dbg(&twl->client->dev,
-                                       "can't add keypad data, %d\n",
-                                       status);
-                               platform_device_put(pdev);
-                               goto err;
-                       }
-                       status = platform_device_add(pdev);
-                       if (status < 0) {
-                               platform_device_put(pdev);
-                               dev_dbg(&twl->client->dev,
-                                               "can't create keypad dev, %d\n",
-                                               status);
-                               goto err;
-                       }
-               } else {
-                       pr_debug("%s: can't alloc keypad dev\n", DRIVER_NAME);
-                       status = -ENOMEM;
-                       goto err;
-               }
+               child = add_child(2, "twl4030_keypad",
+                               pdata->keypad, sizeof(*pdata->keypad),
+                               true, pdata->irq_base + 1, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
        }
 
        if (twl_has_madc() && pdata->madc) {
-               pdev = platform_device_alloc("twl4030_madc", -1);
-               if (pdev) {
-                       twl = &twl4030_modules[2];
-                       pdev->dev.parent = &twl->client->dev;
-                       device_init_wakeup(&pdev->dev, 1);
-                       status = platform_device_add_data(pdev, pdata->madc,
-                                       sizeof(*pdata->madc));
-                       if (status < 0) {
-                               platform_device_put(pdev);
-                               dev_dbg(&twl->client->dev,
-                                       "can't add madc data, %d\n",
-                                       status);
-                               goto err;
-                       }
-                       status = platform_device_add(pdev);
-                       if (status < 0) {
-                               platform_device_put(pdev);
-                               dev_dbg(&twl->client->dev,
-                                               "can't create madc dev, %d\n",
-                                               status);
-                               goto err;
-                       }
-               } else {
-                       pr_debug("%s: can't alloc madc dev\n", DRIVER_NAME);
-                       status = -ENOMEM;
-                       goto err;
-               }
+               child = add_child(2, "twl4030_madc",
+                               pdata->madc, sizeof(*pdata->madc),
+                               true, pdata->irq_base + 3, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
        }
 
        if (twl_has_rtc()) {
-               twl = &twl4030_modules[3];
-
-               pdev = platform_device_alloc("twl4030_rtc", -1);
-               if (!pdev) {
-                       pr_debug("%s: can't alloc rtc dev\n", DRIVER_NAME);
-                       status = -ENOMEM;
-               } else {
-                       pdev->dev.parent = &twl->client->dev;
-                       device_init_wakeup(&pdev->dev, 1);
-               }
-
                /*
-                * REVISIT platform_data here currently might use of
+                * REVISIT platform_data here currently might expose the
                 * "msecure" line ... but for now we just expect board
-                * setup to tell the chip "we are secure" at all times.
+                * setup to tell the chip "it's always ok to SET_TIME".
                 * Eventually, Linux might become more aware of such
                 * HW security concerns, and "least privilege".
                 */
-
-               /* RTC module IRQ */
-               if (status == 0) {
-                       struct resource r = {
-                               .start = pdata->irq_base + 8 + 3,
-                               .flags = IORESOURCE_IRQ,
-                       };
-
-                       status = platform_device_add_resources(pdev, &r, 1);
-               }
-
-               if (status == 0)
-                       status = platform_device_add(pdev);
-
-               if (status < 0) {
-                       platform_device_put(pdev);
-                       dev_dbg(&twl->client->dev,
-                                       "can't create rtc dev, %d\n",
-                                       status);
-                       goto err;
-               }
+               child = add_child(3, "twl4030_rtc",
+                               NULL, 0,
+                               true, pdata->irq_base + 8 + 3, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
        }
 
        if (twl_has_usb() && pdata->usb) {
-               twl = &twl4030_modules[0];
-
-               pdev = platform_device_alloc("twl4030_usb", -1);
-               if (!pdev) {
-                       pr_debug("%s: can't alloc usb dev\n", DRIVER_NAME);
-                       status = -ENOMEM;
-                       goto err;
-               }
-
-               if (status == 0) {
-                       pdev->dev.parent = &twl->client->dev;
-                       device_init_wakeup(&pdev->dev, 1);
-                       status = platform_device_add_data(pdev, pdata->usb,
-                                       sizeof(*pdata->usb));
-                       if (status < 0) {
-                               platform_device_put(pdev);
-                               dev_dbg(&twl->client->dev,
-                                       "can't add usb data, %d\n",
-                                       status);
-                               goto err;
-                       }
-               }
-
-               if (status == 0) {
-                       struct resource r = {
-                               .start = pdata->irq_base + 8 + 2,
-                               .flags = IORESOURCE_IRQ,
-                       };
+               child = add_child(0, "twl4030_usb",
+                               pdata->usb, sizeof(*pdata->usb),
+                               true,
+                               /* irq0 = USB_PRES, irq1 = USB */
+                               pdata->irq_base + 8 + 2, pdata->irq_base + 4);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               /* we need to connect regulators to this transceiver */
+               usb_transceiver = child;
+       }
 
-                       status = platform_device_add_resources(pdev, &r, 1);
-               }
+       if (twl_has_regulator()) {
+               /*
+               child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+               */
+
+               child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL4030_REG_VDAC, pdata->vdac);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator((features & TWL4030_VAUX2)
+                                       ? TWL4030_REG_VAUX2_4030
+                                       : TWL4030_REG_VAUX2,
+                               pdata->vaux2);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
 
-               if (status == 0)
-                       status = platform_device_add(pdev);
+       if (twl_has_regulator() && usb_transceiver) {
+               static struct regulator_consumer_supply usb1v5 = {
+                       .supply =       "usb1v5",
+               };
+               static struct regulator_consumer_supply usb1v8 = {
+                       .supply =       "usb1v8",
+               };
+               static struct regulator_consumer_supply usb3v1 = {
+                       .supply =       "usb3v1",
+               };
+
+               /* this is a template that gets copied */
+               struct regulator_init_data usb_fixed = {
+                       .constraints.valid_modes_mask =
+                                 REGULATOR_MODE_NORMAL
+                               | REGULATOR_MODE_STANDBY,
+                       .constraints.valid_ops_mask =
+                                 REGULATOR_CHANGE_MODE
+                               | REGULATOR_CHANGE_STATUS,
+               };
+
+               usb1v5.dev = usb_transceiver;
+               usb1v8.dev = usb_transceiver;
+               usb3v1.dev = usb_transceiver;
+
+               child = add_regulator_linked(TWL4030_REG_VUSB1V5, &usb_fixed,
+                               &usb1v5, 1);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator_linked(TWL4030_REG_VUSB1V8, &usb_fixed,
+                               &usb1v8, 1);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator_linked(TWL4030_REG_VUSB3V1, &usb_fixed,
+                               &usb3v1, 1);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
 
-               if (status < 0) {
-                       platform_device_put(pdev);
-                       dev_dbg(&twl->client->dev,
-                                       "can't create usb dev, %d\n",
-                                       status);
-               }
+       /* maybe add LDOs that are omitted on cost-reduced parts */
+       if (twl_has_regulator() && !(features & TPS_SUBSET)) {
+               /*
+               child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+               */
+
+               child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL4030_REG_VSIM, pdata->vsim);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
        }
 
-err:
-       if (status)
-               pr_err("failed to add twl4030's children (status %d)\n", status);
-       return status;
+       return 0;
 }
 
 /*----------------------------------------------------------------------*/
@@ -645,12 +658,7 @@ static void __init clocks_init(void)
                osc = clk_get(NULL, "osc_ck");
        else
                osc = clk_get(NULL, "osc_sys_ck");
-#else
-       /* REVISIT for non-OMAP systems, pass the clock rate from
-        * board init code, using platform_data.
-        */
-       osc = ERR_PTR(-EIO);
-#endif
+
        if (IS_ERR(osc)) {
                printk(KERN_WARNING "Skipping twl4030 internal clock init and "
                                "using bootloader value (unknown osc rate)\n");
@@ -660,6 +668,18 @@ static void __init clocks_init(void)
        rate = clk_get_rate(osc);
        clk_put(osc);
 
+#else
+       /* REVISIT for non-OMAP systems, pass the clock rate from
+        * board init code, using platform_data.
+        */
+       osc = ERR_PTR(-EIO);
+
+       printk(KERN_WARNING "Skipping twl4030 internal clock init and "
+              "using bootloader value (unknown osc rate)\n");
+
+       return;
+#endif
+
        switch (rate) {
        case 19200000:
                ctrl = HFCLK_FREQ_19p2_MHZ;
@@ -764,7 +784,7 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
                        goto fail;
        }
 
-       status = add_children(pdata);
+       status = add_children(pdata, id->driver_data);
 fail:
        if (status < 0)
                twl4030_remove(client);
@@ -772,11 +792,11 @@ fail:
 }
 
 static const struct i2c_device_id twl4030_ids[] = {
-       { "twl4030", 0 },       /* "Triton 2" */
-       { "tps65950", 0 },      /* catalog version of twl4030 */
-       { "tps65930", 0 },      /* fewer LDOs and DACs; no charger */
-       { "tps65920", 0 },      /* fewer LDOs; no codec or charger */
-       { "twl5030", 0 },       /* T2 updated */
+       { "twl4030", TWL4030_VAUX2 },   /* "Triton 2" */
+       { "twl5030", 0 },               /* T2 updated */
+       { "tps65950", 0 },              /* catalog version of twl5030 */
+       { "tps65930", TPS_SUBSET },     /* fewer LDOs and DACs; no charger */
+       { "tps65920", TPS_SUBSET },     /* fewer LDOs; no codec or charger */
        { /* end of list */ },
 };
 MODULE_DEVICE_TABLE(i2c, twl4030_ids);
index fae868a8d499257caaedc3be66391cf25310af82..b10876036983e241974bc36fa8638850fc9245b7 100644 (file)
@@ -180,10 +180,15 @@ static struct completion irq_event;
 static int twl4030_irq_thread(void *data)
 {
        long irq = (long)data;
-       irq_desc_t *desc = irq_desc + irq;
+       struct irq_desc *desc = irq_to_desc(irq);
        static unsigned i2c_errors;
        const static unsigned max_i2c_errors = 100;
 
+       if (!desc) {
+               pr_err("twl4030: Invalid IRQ: %ld\n", irq);
+               return -EINVAL;
+       }
+
        current->flags |= PF_NOFREEZE;
 
        while (!kthread_should_stop()) {
@@ -215,7 +220,13 @@ static int twl4030_irq_thread(void *data)
                                pih_isr;
                                pih_isr >>= 1, module_irq++) {
                        if (pih_isr & 0x1) {
-                               irq_desc_t *d = irq_desc + module_irq;
+                               struct irq_desc *d = irq_to_desc(module_irq);
+
+                               if (!d) {
+                                       pr_err("twl4030: Invalid SIH IRQ: %d\n",
+                                              module_irq);
+                                       return -EINVAL;
+                               }
 
                                /* These can't be masked ... always warn
                                 * if we get any surprises.
@@ -452,10 +463,16 @@ static void twl4030_sih_do_edge(struct work_struct *work)
        /* Modify only the bits we know must change */
        while (edge_change) {
                int             i = fls(edge_change) - 1;
-               struct irq_desc *d = irq_desc + i + agent->irq_base;
+               struct irq_desc *d = irq_to_desc(i + agent->irq_base);
                int             byte = 1 + (i >> 2);
                int             off = (i & 0x3) * 2;
 
+               if (!d) {
+                       pr_err("twl4030: Invalid IRQ: %d\n",
+                              i + agent->irq_base);
+                       return;
+               }
+
                bytes[byte] &= ~(0x03 << off);
 
                spin_lock_irq(&d->lock);
@@ -512,9 +529,14 @@ static void twl4030_sih_unmask(unsigned irq)
 static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 {
        struct sih_agent *sih = get_irq_chip_data(irq);
-       struct irq_desc *desc = irq_desc + irq;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
+       if (!desc) {
+               pr_err("twl4030: Invalid IRQ: %d\n", irq);
+               return -EINVAL;
+       }
+
        if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
                return -EINVAL;
 
index 0d47fb9e4b3bcec5248e510504d8c6b5f2b059ec..3a273ccef3f2660b127ab426755650950377827c 100644 (file)
@@ -63,7 +63,6 @@
  */
 static DEFINE_MUTEX(io_mutex);
 static DEFINE_MUTEX(reg_lock_mutex);
-static DEFINE_MUTEX(auxadc_mutex);
 
 /* Perform a physical read from the device.
  */
@@ -299,6 +298,13 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
 }
 EXPORT_SYMBOL_GPL(wm8350_block_write);
 
+/**
+ * wm8350_reg_lock()
+ *
+ * The WM8350 has a hardware lock which can be used to prevent writes to
+ * some registers (generally those which can cause particularly serious
+ * problems if misused).  This function enables that lock.
+ */
 int wm8350_reg_lock(struct wm8350 *wm8350)
 {
        u16 key = WM8350_LOCK_KEY;
@@ -314,6 +320,15 @@ int wm8350_reg_lock(struct wm8350 *wm8350)
 }
 EXPORT_SYMBOL_GPL(wm8350_reg_lock);
 
+/**
+ * wm8350_reg_unlock()
+ *
+ * The WM8350 has a hardware lock which can be used to prevent writes to
+ * some registers (generally those which can cause particularly serious
+ * problems if misused).  This function disables that lock so updates
+ * can be performed.  For maximum safety this should be done only when
+ * required.
+ */
 int wm8350_reg_unlock(struct wm8350 *wm8350)
 {
        u16 key = WM8350_UNLOCK_KEY;
@@ -1066,38 +1081,158 @@ int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
 }
 EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
 
+int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
+{
+       u16 reg, result = 0;
+       int tries = 5;
+
+       if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP)
+               return -EINVAL;
+       if (channel >= WM8350_AUXADC_USB && channel <= WM8350_AUXADC_TEMP
+           && (scale != 0 || vref != 0))
+               return -EINVAL;
+
+       mutex_lock(&wm8350->auxadc_mutex);
+
+       /* Turn on the ADC */
+       reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
+       wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg | WM8350_AUXADC_ENA);
+
+       if (scale || vref) {
+               reg = scale << 13;
+               reg |= vref << 12;
+               wm8350_reg_write(wm8350, WM8350_AUX1_READBACK + channel, reg);
+       }
+
+       reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
+       reg |= 1 << channel | WM8350_AUXADC_POLL;
+       wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg);
+
+       do {
+               schedule_timeout_interruptible(1);
+               reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
+       } while (tries-- && (reg & WM8350_AUXADC_POLL));
+
+       if (!tries)
+               dev_err(wm8350->dev, "adc chn %d read timeout\n", channel);
+       else
+               result = wm8350_reg_read(wm8350,
+                                        WM8350_AUX1_READBACK + channel);
+
+       /* Turn off the ADC */
+       reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
+       wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5,
+                        reg & ~WM8350_AUXADC_ENA);
+
+       mutex_unlock(&wm8350->auxadc_mutex);
+
+       return result & WM8350_AUXADC_DATA1_MASK;
+}
+EXPORT_SYMBOL_GPL(wm8350_read_auxadc);
+
 /*
  * Cache is always host endian.
  */
-static int wm8350_create_cache(struct wm8350 *wm8350, int mode)
+static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
 {
        int i, ret = 0;
        u16 value;
        const u16 *reg_map;
 
-       switch (mode) {
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+       switch (type) {
        case 0:
-               reg_map = wm8350_mode0_defaults;
-               break;
+               switch (mode) {
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+               case 0:
+                       reg_map = wm8350_mode0_defaults;
+                       break;
 #endif
 #ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
-       case 1:
-               reg_map = wm8350_mode1_defaults;
-               break;
+               case 1:
+                       reg_map = wm8350_mode1_defaults;
+                       break;
 #endif
 #ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
-       case 2:
-               reg_map = wm8350_mode2_defaults;
-               break;
+               case 2:
+                       reg_map = wm8350_mode2_defaults;
+                       break;
 #endif
 #ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
-       case 3:
-               reg_map = wm8350_mode3_defaults;
+               case 3:
+                       reg_map = wm8350_mode3_defaults;
+                       break;
+#endif
+               default:
+                       dev_err(wm8350->dev,
+                               "WM8350 configuration mode %d not supported\n",
+                               mode);
+                       return -EINVAL;
+               }
+               break;
+
+       case 1:
+               switch (mode) {
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
+               case 0:
+                       reg_map = wm8351_mode0_defaults;
+                       break;
+#endif
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
+               case 1:
+                       reg_map = wm8351_mode1_defaults;
+                       break;
+#endif
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
+               case 2:
+                       reg_map = wm8351_mode2_defaults;
+                       break;
+#endif
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
+               case 3:
+                       reg_map = wm8351_mode3_defaults;
+                       break;
+#endif
+               default:
+                       dev_err(wm8350->dev,
+                               "WM8351 configuration mode %d not supported\n",
+                               mode);
+                       return -EINVAL;
+               }
                break;
+
+       case 2:
+               switch (mode) {
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
+               case 0:
+                       reg_map = wm8352_mode0_defaults;
+                       break;
+#endif
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
+               case 1:
+                       reg_map = wm8352_mode1_defaults;
+                       break;
 #endif
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
+               case 2:
+                       reg_map = wm8352_mode2_defaults;
+                       break;
+#endif
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
+               case 3:
+                       reg_map = wm8352_mode3_defaults;
+                       break;
+#endif
+               default:
+                       dev_err(wm8350->dev,
+                               "WM8352 configuration mode %d not supported\n",
+                               mode);
+                       return -EINVAL;
+               }
+               break;
+
        default:
-               dev_err(wm8350->dev, "Configuration mode %d not supported\n",
+               dev_err(wm8350->dev,
+                       "WM835x configuration mode %d not supported\n",
                        mode);
                return -EINVAL;
        }
@@ -1163,53 +1298,113 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
                       struct wm8350_platform_data *pdata)
 {
        int ret = -EINVAL;
-       u16 id1, id2, mask, mode;
+       u16 id1, id2, mask_rev;
+       u16 cust_id, mode, chip_rev;
 
        /* get WM8350 revision and config mode */
        wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
        wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2);
+       wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), &mask_rev);
 
        id1 = be16_to_cpu(id1);
        id2 = be16_to_cpu(id2);
+       mask_rev = be16_to_cpu(mask_rev);
 
-       if (id1 == 0x6143) {
-               switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) {
+       if (id1 != 0x6143) {
+               dev_err(wm8350->dev,
+                       "Device with ID %x is not a WM8350\n", id1);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       mode = id2 & WM8350_CONF_STS_MASK >> 10;
+       cust_id = id2 & WM8350_CUST_ID_MASK;
+       chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12;
+       dev_info(wm8350->dev,
+                "CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d\n",
+                mode, cust_id, mask_rev, chip_rev);
+
+       if (cust_id != 0) {
+               dev_err(wm8350->dev, "Unsupported CUST_ID\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       switch (mask_rev) {
+       case 0:
+               wm8350->pmic.max_dcdc = WM8350_DCDC_6;
+               wm8350->pmic.max_isink = WM8350_ISINK_B;
+
+               switch (chip_rev) {
                case WM8350_REV_E:
-                       dev_info(wm8350->dev, "Found Rev E device\n");
-                       wm8350->rev = WM8350_REV_E;
+                       dev_info(wm8350->dev, "WM8350 Rev E\n");
                        break;
                case WM8350_REV_F:
-                       dev_info(wm8350->dev, "Found Rev F device\n");
-                       wm8350->rev = WM8350_REV_F;
+                       dev_info(wm8350->dev, "WM8350 Rev F\n");
                        break;
                case WM8350_REV_G:
-                       dev_info(wm8350->dev, "Found Rev G device\n");
-                       wm8350->rev = WM8350_REV_G;
+                       dev_info(wm8350->dev, "WM8350 Rev G\n");
+                       wm8350->power.rev_g_coeff = 1;
+                       break;
+               case WM8350_REV_H:
+                       dev_info(wm8350->dev, "WM8350 Rev H\n");
+                       wm8350->power.rev_g_coeff = 1;
                        break;
                default:
                        /* For safety we refuse to run on unknown hardware */
-                       dev_info(wm8350->dev, "Found unknown rev\n");
+                       dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV\n");
                        ret = -ENODEV;
                        goto err;
                }
-       } else {
-               dev_info(wm8350->dev, "Device with ID %x is not a WM8350\n",
-                        id1);
+               break;
+
+       case 1:
+               wm8350->pmic.max_dcdc = WM8350_DCDC_4;
+               wm8350->pmic.max_isink = WM8350_ISINK_A;
+
+               switch (chip_rev) {
+               case 0:
+                       dev_info(wm8350->dev, "WM8351 Rev A\n");
+                       wm8350->power.rev_g_coeff = 1;
+                       break;
+
+               default:
+                       dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV\n");
+                       ret = -ENODEV;
+                       goto err;
+               }
+               break;
+
+       case 2:
+               wm8350->pmic.max_dcdc = WM8350_DCDC_6;
+               wm8350->pmic.max_isink = WM8350_ISINK_B;
+
+               switch (chip_rev) {
+               case 0:
+                       dev_info(wm8350->dev, "WM8352 Rev A\n");
+                       wm8350->power.rev_g_coeff = 1;
+                       break;
+
+               default:
+                       dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV\n");
+                       ret = -ENODEV;
+                       goto err;
+               }
+               break;
+
+       default:
+               dev_err(wm8350->dev, "Unknown MASK_REV\n");
                ret = -ENODEV;
                goto err;
        }
 
-       mode = id2 & WM8350_CONF_STS_MASK >> 10;
-       mask = id2 & WM8350_CUST_ID_MASK;
-       dev_info(wm8350->dev, "Config mode %d, ROM mask %d\n", mode, mask);
-
-       ret = wm8350_create_cache(wm8350, mode);
+       ret = wm8350_create_cache(wm8350, mask_rev, mode);
        if (ret < 0) {
-               printk(KERN_ERR "wm8350: failed to create register cache\n");
+               dev_err(wm8350->dev, "Failed to create register cache\n");
                return ret;
        }
 
-       if (pdata->init) {
+       if (pdata && pdata->init) {
                ret = pdata->init(wm8350);
                if (ret != 0) {
                        dev_err(wm8350->dev, "Platform init() failed: %d\n",
@@ -1218,6 +1413,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
                }
        }
 
+       mutex_init(&wm8350->auxadc_mutex);
        mutex_init(&wm8350->irq_mutex);
        INIT_WORK(&wm8350->irq_work, wm8350_irq_worker);
        if (irq) {
index 3e0ce0e50ea2263edcec7fcb3300ac20d693c866..8d8c932175729621f77b43b360b5fea9871b3b4c 100644 (file)
@@ -1,8 +1,6 @@
 /*
  * wm8350-i2c.c  --  Generic I2C driver for Wolfson WM8350 PMIC
  *
- * This driver defines and configures the WM8350 for the Freescale i.MX32ADS.
- *
  * Copyright 2007, 2008 Wolfson Microelectronics PLC.
  *
  * Author: Liam Girdwood
@@ -99,6 +97,8 @@ static int wm8350_i2c_remove(struct i2c_client *i2c)
 
 static const struct i2c_device_id wm8350_i2c_id[] = {
        { "wm8350", 0 },
+       { "wm8351", 0 },
+       { "wm8352", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id);
index 974678db22cd812e27bd49817dc97ab28f60acf6..68887b817d17e071a834fbbd71c4a86c44fc2796 100644 (file)
@@ -1074,6 +1074,2102 @@ const u16 wm8350_mode3_defaults[] = {
 };
 #endif
 
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode0_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0001,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0004,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x0000,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0FFC,     /* R134 - GPIO Configuration (i/o) */
+       0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0013,     /* R140 - GPIO Function Select 1 */
+       0x0000,     /* R141 - GPIO Function Select 2 */
+       0x0000,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 */
+       0x0000,     /* R175 */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0000,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0000,     /* R186 - DCDC3 Control */
+       0x0000,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0000,     /* R189 - DCDC4 Control */
+       0x0000,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 */
+       0x0000,     /* R193 */
+       0x0000,     /* R194 */
+       0x0000,     /* R195 */
+       0x0000,     /* R196 */
+       0x0006,     /* R197 */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001C,     /* R200 - LDO1 Control */
+       0x0000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x001B,     /* R203 - LDO2 Control */
+       0x0000,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001B,     /* R206 - LDO3 Control */
+       0x0000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001B,     /* R209 - LDO4 Control */
+       0x0000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 - FLL Test 1 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x0000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x1000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode1_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0001,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0204,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x0000,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0CFB,     /* R134 - GPIO Configuration (i/o) */
+       0x0C1F,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0300,     /* R140 - GPIO Function Select 1 */
+       0x1110,     /* R141 - GPIO Function Select 2 */
+       0x0013,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 */
+       0x0000,     /* R175 */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0C00,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0026,     /* R186 - DCDC3 Control */
+       0x0400,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0062,     /* R189 - DCDC4 Control */
+       0x0800,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 */
+       0x0000,     /* R193 */
+       0x0000,     /* R194 */
+       0x000A,     /* R195 */
+       0x1000,     /* R196 */
+       0x0006,     /* R197 */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x0006,     /* R200 - LDO1 Control */
+       0x0000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x0010,     /* R203 - LDO2 Control */
+       0x0C00,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001F,     /* R206 - LDO3 Control */
+       0x0800,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x000A,     /* R209 - LDO4 Control */
+       0x0800,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 - FLL Test 1 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x1000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x1000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode2_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0001,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0214,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x0110,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x09FA,     /* R134 - GPIO Configuration (i/o) */
+       0x0DF6,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x1310,     /* R140 - GPIO Function Select 1 */
+       0x0003,     /* R141 - GPIO Function Select 2 */
+       0x2000,     /* R142 - GPIO Function Select 3 */
+       0x0000,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 */
+       0x0000,     /* R175 */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x001A,     /* R180 - DCDC1 Control */
+       0x0800,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0056,     /* R186 - DCDC3 Control */
+       0x0400,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0026,     /* R189 - DCDC4 Control */
+       0x0C00,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 */
+       0x0000,     /* R193 */
+       0x0000,     /* R194 */
+       0x0026,     /* R195 */
+       0x0C00,     /* R196 */
+       0x0006,     /* R197 */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001C,     /* R200 - LDO1 Control */
+       0x0400,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x0010,     /* R203 - LDO2 Control */
+       0x0C00,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x0015,     /* R206 - LDO3 Control */
+       0x0000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001A,     /* R209 - LDO4 Control */
+       0x0000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 - FLL Test 1 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x0000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x1000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode3_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0001,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0204,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0010,     /* R129 - GPIO Pin pull up Control */
+       0x0000,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0BFB,     /* R134 - GPIO Configuration (i/o) */
+       0x0FFD,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0310,     /* R140 - GPIO Function Select 1 */
+       0x0001,     /* R141 - GPIO Function Select 2 */
+       0x2300,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 */
+       0x0000,     /* R175 */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0400,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0026,     /* R186 - DCDC3 Control */
+       0x0800,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0062,     /* R189 - DCDC4 Control */
+       0x1400,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 */
+       0x0000,     /* R193 */
+       0x0000,     /* R194 */
+       0x0026,     /* R195 */
+       0x0400,     /* R196 */
+       0x0006,     /* R197 */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x0006,     /* R200 - LDO1 Control */
+       0x0C00,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x0016,     /* R203 - LDO2 Control */
+       0x0000,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x0019,     /* R206 - LDO3 Control */
+       0x0000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001A,     /* R209 - LDO4 Control */
+       0x1000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 - FLL Test 1 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x0000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x1000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode0_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0002,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0004,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x0000,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0FFC,     /* R134 - GPIO Configuration (i/o) */
+       0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0013,     /* R140 - GPIO Function Select 1 */
+       0x0000,     /* R141 - GPIO Function Select 2 */
+       0x0000,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0000,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0000,     /* R186 - DCDC3 Control */
+       0x0000,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0000,     /* R189 - DCDC4 Control */
+       0x0000,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0000,     /* R195 - DCDC6 Control */
+       0x0000,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001C,     /* R200 - LDO1 Control */
+       0x0000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x001B,     /* R203 - LDO2 Control */
+       0x0000,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001B,     /* R206 - LDO3 Control */
+       0x0000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001B,     /* R209 - LDO4 Control */
+       0x0000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x0000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x5000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+       0x5100,     /* R252 */
+       0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode1_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0002,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0204,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x0000,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0BFB,     /* R134 - GPIO Configuration (i/o) */
+       0x0FFF,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0300,     /* R140 - GPIO Function Select 1 */
+       0x0000,     /* R141 - GPIO Function Select 2 */
+       0x2300,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x0062,     /* R180 - DCDC1 Control */
+       0x0400,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0006,     /* R186 - DCDC3 Control */
+       0x0800,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0006,     /* R189 - DCDC4 Control */
+       0x0C00,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0026,     /* R195 - DCDC6 Control */
+       0x1000,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x0002,     /* R200 - LDO1 Control */
+       0x0000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x001A,     /* R203 - LDO2 Control */
+       0x0000,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001F,     /* R206 - LDO3 Control */
+       0x0000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001F,     /* R209 - LDO4 Control */
+       0x0000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x0000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x5000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+       0x5100,     /* R252 */
+       0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode2_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0002,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0204,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x0110,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x09DA,     /* R134 - GPIO Configuration (i/o) */
+       0x0DD6,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x1310,     /* R140 - GPIO Function Select 1 */
+       0x0033,     /* R141 - GPIO Function Select 2 */
+       0x2000,     /* R142 - GPIO Function Select 3 */
+       0x0000,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0800,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0056,     /* R186 - DCDC3 Control */
+       0x1800,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x000E,     /* R189 - DCDC4 Control */
+       0x1000,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0026,     /* R195 - DCDC6 Control */
+       0x0C00,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001C,     /* R200 - LDO1 Control */
+       0x0000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x0006,     /* R203 - LDO2 Control */
+       0x0400,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001C,     /* R206 - LDO3 Control */
+       0x1400,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001A,     /* R209 - LDO4 Control */
+       0x0000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x0000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x5000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+       0x5100,     /* R252 */
+       0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode3_defaults[] = {
+       0x6143,     /* R0   - Reset/ID */
+       0x0000,     /* R1   - ID */
+       0x0002,     /* R2   - Revision */
+       0x1C02,     /* R3   - System Control 1 */
+       0x0204,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27 */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35 */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3A00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - OUT1L Volume */
+       0x00E4,     /* R105 - OUT1R Volume */
+       0x00E4,     /* R106 - OUT2L Volume */
+       0x02E4,     /* R107 - OUT2R Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0010,     /* R129 - GPIO Pin pull up Control */
+       0x0000,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0BFB,     /* R134 - GPIO Configuration (i/o) */
+       0x0FFD,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0310,     /* R140 - GPIO Function Select 1 */
+       0x0001,     /* R141 - GPIO Function Select 2 */
+       0x2300,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x032D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x0006,     /* R180 - DCDC1 Control */
+       0x0400,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0050,     /* R186 - DCDC3 Control */
+       0x0C00,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x000E,     /* R189 - DCDC4 Control */
+       0x0400,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0029,     /* R195 - DCDC6 Control */
+       0x0800,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001D,     /* R200 - LDO1 Control */
+       0x1000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x0017,     /* R203 - LDO2 Control */
+       0x1000,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x0006,     /* R206 - LDO3 Control */
+       0x1000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x0010,     /* R209 - LDO4 Control */
+       0x1000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 - Security1 */
+       0x4000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 - Signal overrides */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 - Charger Overides/status */
+       0x0000,     /* R227 - misc overrides */
+       0x0000,     /* R228 - Supply overrides/status 1 */
+       0x0000,     /* R229 - Supply overrides/status 2 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 - comparotor overrides */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 - State Machine status */
+       0x1200,     /* R234 */
+       0x0000,     /* R235 */
+       0x8000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0003,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0004,     /* R243 */
+       0x0300,     /* R244 */
+       0x0000,     /* R245 */
+       0x0200,     /* R246 */
+       0x0000,     /* R247 */
+       0x1000,     /* R248 - DCDC1 Test Controls */
+       0x5000,     /* R249 */
+       0x1000,     /* R250 - DCDC3 Test Controls */
+       0x1000,     /* R251 - DCDC4 Test Controls */
+       0x5100,     /* R252 */
+       0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
 /* The register defaults for the config mode used must be compiled in but
  * due to the impact on kernel size it is possible to disable
  */
@@ -1307,14 +3403,14 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = {
        { 0xFF3F, 0xE03F, 0x0000 }, /* R216 - Main Bandgap Control */
        { 0xEF2F, 0xE02F, 0x0000 }, /* R217 - OSC Control */
        { 0xF3FF, 0xB3FF, 0xc000 }, /* R218 - RTC Tick Control */
-       { 0xFFFF, 0xFFFF, 0xFFFF }, /* R219 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R219 - Security */
        { 0x09FF, 0x01FF, 0x0000 }, /* R220 - RAM BIST 1 */
        { 0x0000, 0x0000, 0x0000 }, /* R221 */
        { 0xFFFF, 0xFFFF, 0xFFFF }, /* R222 */
        { 0xFFFF, 0xFFFF, 0xFFFF }, /* R223 */
        { 0x0000, 0x0000, 0x0000 }, /* R224 */
        { 0x8F3F, 0x0000, 0xFFFF }, /* R225 - DCDC/LDO status */
-       { 0x0000, 0x0000, 0x0000 }, /* R226 */
+       { 0x0000, 0x0000, 0xFFFF }, /* R226 - Charger status */
        { 0x0000, 0x0000, 0xFFFF }, /* R227 */
        { 0x0000, 0x0000, 0x0000 }, /* R228 */
        { 0x0000, 0x0000, 0x0000 }, /* R229 */
index 6a0cedb5bb8a1356fa86f28d73c82a5117402a1a..cf30d06a0104a6ab4f6295a7abf2a8ad85700a9a 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/bug.h>
 #include <linux/i2c.h>
 #include <linux/kernel.h>
+#include <linux/mfd/core.h>
 #include <linux/mfd/wm8400-private.h>
 #include <linux/mfd/wm8400-audio.h>
 
@@ -239,6 +240,16 @@ void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
 }
 EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
 
+static int wm8400_register_codec(struct wm8400 *wm8400)
+{
+       struct mfd_cell cell = {
+               .name = "wm8400-codec",
+               .driver_data = wm8400,
+       };
+
+       return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0);
+}
+
 /*
  * wm8400_init - Generic initialisation
  *
@@ -296,24 +307,32 @@ static int wm8400_init(struct wm8400 *wm8400,
        reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT;
        dev_info(wm8400->dev, "WM8400 revision %x\n", reg);
 
+       ret = wm8400_register_codec(wm8400);
+       if (ret != 0) {
+               dev_err(wm8400->dev, "Failed to register codec\n");
+               goto err_children;
+       }
+
        if (pdata && pdata->platform_init) {
                ret = pdata->platform_init(wm8400->dev);
-               if (ret != 0)
+               if (ret != 0) {
                        dev_err(wm8400->dev, "Platform init failed: %d\n",
                                ret);
+                       goto err_children;
+               }
        } else
                dev_warn(wm8400->dev, "No platform initialisation supplied\n");
 
+       return 0;
+
+err_children:
+       mfd_remove_devices(wm8400->dev);
        return ret;
 }
 
 static void wm8400_release(struct wm8400 *wm8400)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8400->regulators); i++)
-               if (wm8400->regulators[i].name)
-                       platform_device_unregister(&wm8400->regulators[i]);
+       mfd_remove_devices(wm8400->dev);
 }
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
index 8e0c2b47803ce9f8b0670953eadefdc994ade68d..668472405a57a9f951ef8e510082ff94db039504 100644 (file)
@@ -29,6 +29,13 @@ config APM_POWER
          Say Y here to enable support APM status emulation using
          battery class devices.
 
+config WM8350_POWER
+        tristate "WM8350 PMU support"
+        depends on MFD_WM8350
+        help
+          Say Y here to enable support for the power management unit
+         provided by the Wolfson Microelectronics WM8350 PMIC.
+
 config BATTERY_DS2760
        tristate "DS2760 battery driver (HP iPAQ & others)"
        select W1
@@ -68,4 +75,11 @@ config BATTERY_BQ27x00
        help
          Say Y here to enable support for batteries with BQ27200(I2C) chip.
 
+config BATTERY_DA9030
+       tristate "DA9030 battery driver"
+       depends on PMIC_DA903X
+       help
+         Say Y here to enable support for batteries charger integrated into
+         DA9030 PMIC.
+
 endif # POWER_SUPPLY
index e8f1ecec5d8fd2f5467ec1111061a96f0a1420d7..eebb15505a40a80c9b4458fc3018162b0e837fd7 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY)    += power_supply.o
 
 obj-$(CONFIG_PDA_POWER)                += pda_power.o
 obj-$(CONFIG_APM_POWER)                += apm_power.o
+obj-$(CONFIG_WM8350_POWER)     += wm8350_power.o
 
 obj-$(CONFIG_BATTERY_DS2760)   += ds2760_battery.o
 obj-$(CONFIG_BATTERY_PMU)      += pmu_battery.o
@@ -23,3 +24,4 @@ obj-$(CONFIG_BATTERY_OLPC)    += olpc_battery.o
 obj-$(CONFIG_BATTERY_TOSA)     += tosa_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)  += bq27x00_battery.o
+obj-$(CONFIG_BATTERY_DA9030)   += da9030_battery.o
diff --git a/drivers/power/da9030_battery.c b/drivers/power/da9030_battery.c
new file mode 100644 (file)
index 0000000..1662bb0
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * Battery charger driver for Dialog Semiconductor DA9030
+ *
+ * Copyright (C) 2008 Compulab, Ltd.
+ *     Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/da903x.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#define DA9030_STATUS_CHDET    (1 << 3)
+
+#define DA9030_FAULT_LOG               0x0a
+#define DA9030_FAULT_LOG_OVER_TEMP     (1 << 7)
+#define DA9030_FAULT_LOG_VBAT_OVER     (1 << 4)
+
+#define DA9030_CHARGE_CONTROL          0x28
+#define DA9030_CHRG_CHARGER_ENABLE     (1 << 7)
+
+#define DA9030_ADC_MAN_CONTROL         0x30
+#define DA9030_ADC_TBATREF_ENABLE      (1 << 5)
+#define DA9030_ADC_LDO_INT_ENABLE      (1 << 4)
+
+#define DA9030_ADC_AUTO_CONTROL                0x31
+#define DA9030_ADC_TBAT_ENABLE         (1 << 5)
+#define DA9030_ADC_VBAT_IN_TXON                (1 << 4)
+#define DA9030_ADC_VCH_ENABLE          (1 << 3)
+#define DA9030_ADC_ICH_ENABLE          (1 << 2)
+#define DA9030_ADC_VBAT_ENABLE         (1 << 1)
+#define DA9030_ADC_AUTO_SLEEP_ENABLE   (1 << 0)
+
+#define DA9030_VBATMON         0x32
+#define DA9030_VBATMONTXON     0x33
+#define DA9030_TBATHIGHP       0x34
+#define DA9030_TBATHIGHN       0x35
+#define DA9030_TBATLOW         0x36
+
+#define DA9030_VBAT_RES                0x41
+#define DA9030_VBATMIN_RES     0x42
+#define DA9030_VBATMINTXON_RES 0x43
+#define DA9030_ICHMAX_RES      0x44
+#define DA9030_ICHMIN_RES      0x45
+#define DA9030_ICHAVERAGE_RES  0x46
+#define DA9030_VCHMAX_RES      0x47
+#define DA9030_VCHMIN_RES      0x48
+#define DA9030_TBAT_RES                0x49
+
+struct da9030_adc_res {
+       uint8_t vbat_res;
+       uint8_t vbatmin_res;
+       uint8_t vbatmintxon;
+       uint8_t ichmax_res;
+       uint8_t ichmin_res;
+       uint8_t ichaverage_res;
+       uint8_t vchmax_res;
+       uint8_t vchmin_res;
+       uint8_t tbat_res;
+       uint8_t adc_in4_res;
+       uint8_t adc_in5_res;
+};
+
+struct da9030_battery_thresholds {
+       int tbat_low;
+       int tbat_high;
+       int tbat_restart;
+
+       int vbat_low;
+       int vbat_crit;
+       int vbat_charge_start;
+       int vbat_charge_stop;
+       int vbat_charge_restart;
+
+       int vcharge_min;
+       int vcharge_max;
+};
+
+struct da9030_charger {
+       struct power_supply psy;
+
+       struct device *master;
+
+       struct da9030_adc_res adc;
+       struct delayed_work work;
+       unsigned int interval;
+
+       struct power_supply_info *battery_info;
+
+       struct da9030_battery_thresholds thresholds;
+
+       unsigned int charge_milliamp;
+       unsigned int charge_millivolt;
+
+       /* charger status */
+       bool chdet;
+       uint8_t fault;
+       int mA;
+       int mV;
+       bool is_on;
+
+       struct notifier_block nb;
+
+       /* platform callbacks for battery low and critical events */
+       void (*battery_low)(void);
+       void (*battery_critical)(void);
+
+       struct dentry *debug_file;
+};
+
+static inline int da9030_reg_to_mV(int reg)
+{
+       return ((reg * 2650) >> 8) + 2650;
+}
+
+static inline int da9030_millivolt_to_reg(int mV)
+{
+       return ((mV - 2650) << 8) / 2650;
+}
+
+static inline int da9030_reg_to_mA(int reg)
+{
+       return ((reg * 24000) >> 8) / 15;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int bat_debug_show(struct seq_file *s, void *data)
+{
+       struct da9030_charger *charger = s->private;
+
+       seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
+       if (charger->chdet) {
+               seq_printf(s, "iset = %dmA, vset = %dmV\n",
+                          charger->mA, charger->mV);
+       }
+
+       seq_printf(s, "vbat_res = %d (%dmV)\n",
+                  charger->adc.vbat_res,
+                  da9030_reg_to_mV(charger->adc.vbat_res));
+       seq_printf(s, "vbatmin_res = %d (%dmV)\n",
+                  charger->adc.vbatmin_res,
+                  da9030_reg_to_mV(charger->adc.vbatmin_res));
+       seq_printf(s, "vbatmintxon = %d (%dmV)\n",
+                  charger->adc.vbatmintxon,
+                  da9030_reg_to_mV(charger->adc.vbatmintxon));
+       seq_printf(s, "ichmax_res = %d (%dmA)\n",
+                  charger->adc.ichmax_res,
+                  da9030_reg_to_mV(charger->adc.ichmax_res));
+       seq_printf(s, "ichmin_res = %d (%dmA)\n",
+                  charger->adc.ichmin_res,
+                  da9030_reg_to_mA(charger->adc.ichmin_res));
+       seq_printf(s, "ichaverage_res = %d (%dmA)\n",
+                  charger->adc.ichaverage_res,
+                  da9030_reg_to_mA(charger->adc.ichaverage_res));
+       seq_printf(s, "vchmax_res = %d (%dmV)\n",
+                  charger->adc.vchmax_res,
+                  da9030_reg_to_mA(charger->adc.vchmax_res));
+       seq_printf(s, "vchmin_res = %d (%dmV)\n",
+                  charger->adc.vchmin_res,
+                  da9030_reg_to_mV(charger->adc.vchmin_res));
+
+       return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, bat_debug_show, inode->i_private);
+}
+
+static const struct file_operations bat_debug_fops = {
+       .open           = debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
+{
+       charger->debug_file = debugfs_create_file("charger", 0666, 0, charger,
+                                                &bat_debug_fops);
+       return charger->debug_file;
+}
+
+static void da9030_bat_remove_debugfs(struct da9030_charger *charger)
+{
+       debugfs_remove(charger->debug_file);
+}
+#else
+static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
+{
+       return NULL;
+}
+static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
+{
+}
+#endif
+
+static inline void da9030_read_adc(struct da9030_charger *charger,
+                                  struct da9030_adc_res *adc)
+{
+       da903x_reads(charger->master, DA9030_VBAT_RES,
+                    sizeof(*adc), (uint8_t *)adc);
+}
+
+static void da9030_charger_update_state(struct da9030_charger *charger)
+{
+       uint8_t val;
+
+       da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
+       charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
+       charger->mA = ((val >> 3) & 0xf) * 100;
+       charger->mV = (val & 0x7) * 50 + 4000;
+
+       da9030_read_adc(charger, &charger->adc);
+       da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
+       charger->chdet = da903x_query_status(charger->master,
+                                                    DA9030_STATUS_CHDET);
+}
+
+static void da9030_set_charge(struct da9030_charger *charger, int on)
+{
+       uint8_t val;
+
+       if (on) {
+               val = DA9030_CHRG_CHARGER_ENABLE;
+               val |= (charger->charge_milliamp / 100) << 3;
+               val |= (charger->charge_millivolt - 4000) / 50;
+               charger->is_on = 1;
+       } else {
+               val = 0;
+               charger->is_on = 0;
+       }
+
+       da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
+}
+
+static void da9030_charger_check_state(struct da9030_charger *charger)
+{
+       da9030_charger_update_state(charger);
+
+       /* we wake or boot with external power on */
+       if (!charger->is_on) {
+               if ((charger->chdet) &&
+                   (charger->adc.vbat_res <
+                    charger->thresholds.vbat_charge_start)) {
+                       da9030_set_charge(charger, 1);
+               }
+       } else {
+               if (charger->adc.vbat_res >=
+                   charger->thresholds.vbat_charge_stop) {
+                       da9030_set_charge(charger, 0);
+                       da903x_write(charger->master, DA9030_VBATMON,
+                                      charger->thresholds.vbat_charge_restart);
+               } else if (charger->adc.vbat_res >
+                          charger->thresholds.vbat_low) {
+                       /* we are charging and passed LOW_THRESH,
+                          so upate DA9030 VBAT threshold
+                        */
+                       da903x_write(charger->master, DA9030_VBATMON,
+                                    charger->thresholds.vbat_low);
+               }
+               if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
+                   charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
+                   /* Tempreture readings are negative */
+                   charger->adc.tbat_res < charger->thresholds.tbat_high ||
+                   charger->adc.tbat_res > charger->thresholds.tbat_low) {
+                       /* disable charger */
+                       da9030_set_charge(charger, 0);
+               }
+       }
+}
+
+static void da9030_charging_monitor(struct work_struct *work)
+{
+       struct da9030_charger *charger;
+
+       charger = container_of(work, struct da9030_charger, work.work);
+
+       da9030_charger_check_state(charger);
+
+       /* reschedule for the next time */
+       schedule_delayed_work(&charger->work, charger->interval);
+}
+
+static enum power_supply_property da9030_battery_props[] = {
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+};
+
+static void da9030_battery_check_status(struct da9030_charger *charger,
+                                   union power_supply_propval *val)
+{
+       if (charger->chdet) {
+               if (charger->is_on)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+}
+
+static void da9030_battery_check_health(struct da9030_charger *charger,
+                                   union power_supply_propval *val)
+{
+       if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
+               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+       else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
+               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+       else
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int da9030_battery_get_property(struct power_supply *psy,
+                                  enum power_supply_property psp,
+                                  union power_supply_propval *val)
+{
+       struct da9030_charger *charger;
+       charger = container_of(psy, struct da9030_charger, psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               da9030_battery_check_status(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               da9030_battery_check_health(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = charger->battery_info->technology;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = charger->battery_info->voltage_max_design;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = charger->battery_info->voltage_min_design;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               val->intval =
+                       da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = charger->battery_info->name;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static void da9030_battery_vbat_event(struct da9030_charger *charger)
+{
+       da9030_read_adc(charger, &charger->adc);
+
+       if (charger->is_on)
+               return;
+
+       if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
+               /* set VBAT threshold for critical */
+               da903x_write(charger->master, DA9030_VBATMON,
+                            charger->thresholds.vbat_crit);
+               if (charger->battery_low)
+                       charger->battery_low();
+       } else if (charger->adc.vbat_res <
+                  charger->thresholds.vbat_crit) {
+               /* notify the system of battery critical */
+               if (charger->battery_critical)
+                       charger->battery_critical();
+       }
+}
+
+static int da9030_battery_event(struct notifier_block *nb, unsigned long event,
+                               void *data)
+{
+       struct da9030_charger *charger =
+               container_of(nb, struct da9030_charger, nb);
+       int status;
+
+       switch (event) {
+       case DA9030_EVENT_CHDET:
+               status = da903x_query_status(charger->master,
+                                            DA9030_STATUS_CHDET);
+               da9030_set_charge(charger, status);
+               break;
+       case DA9030_EVENT_VBATMON:
+               da9030_battery_vbat_event(charger);
+               break;
+       case DA9030_EVENT_CHIOVER:
+       case DA9030_EVENT_TBAT:
+               da9030_set_charge(charger, 0);
+               break;
+       }
+
+       return 0;
+}
+
+static void da9030_battery_convert_thresholds(struct da9030_charger *charger,
+                                             struct da9030_battery_info *pdata)
+{
+       charger->thresholds.tbat_low = pdata->tbat_low;
+       charger->thresholds.tbat_high = pdata->tbat_high;
+       charger->thresholds.tbat_restart  = pdata->tbat_restart;
+
+       charger->thresholds.vbat_low =
+               da9030_millivolt_to_reg(pdata->vbat_low);
+       charger->thresholds.vbat_crit =
+               da9030_millivolt_to_reg(pdata->vbat_crit);
+       charger->thresholds.vbat_charge_start =
+               da9030_millivolt_to_reg(pdata->vbat_charge_start);
+       charger->thresholds.vbat_charge_stop =
+               da9030_millivolt_to_reg(pdata->vbat_charge_stop);
+       charger->thresholds.vbat_charge_restart =
+               da9030_millivolt_to_reg(pdata->vbat_charge_restart);
+
+       charger->thresholds.vcharge_min =
+               da9030_millivolt_to_reg(pdata->vcharge_min);
+       charger->thresholds.vcharge_max =
+               da9030_millivolt_to_reg(pdata->vcharge_max);
+}
+
+static void da9030_battery_setup_psy(struct da9030_charger *charger)
+{
+       struct power_supply *psy = &charger->psy;
+       struct power_supply_info *info = charger->battery_info;
+
+       psy->name = info->name;
+       psy->use_for_apm = info->use_for_apm;
+       psy->type = POWER_SUPPLY_TYPE_BATTERY;
+       psy->get_property = da9030_battery_get_property;
+
+       psy->properties = da9030_battery_props;
+       psy->num_properties = ARRAY_SIZE(da9030_battery_props);
+};
+
+static int da9030_battery_charger_init(struct da9030_charger *charger)
+{
+       char v[5];
+       int ret;
+
+       v[0] = v[1] = charger->thresholds.vbat_low;
+       v[2] = charger->thresholds.tbat_high;
+       v[3] = charger->thresholds.tbat_restart;
+       v[4] = charger->thresholds.tbat_low;
+
+       ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
+       if (ret)
+               return ret;
+
+       /*
+        * Enable reference voltage supply for ADC from the LDO_INTERNAL
+        * regulator. Must be set before ADC measurements can be made.
+        */
+       ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
+                          DA9030_ADC_LDO_INT_ENABLE |
+                          DA9030_ADC_TBATREF_ENABLE);
+       if (ret)
+               return ret;
+
+       /* enable auto ADC measuremnts */
+       return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
+                           DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
+                           DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
+                           DA9030_ADC_VBAT_ENABLE |
+                           DA9030_ADC_AUTO_SLEEP_ENABLE);
+}
+
+static int da9030_battery_probe(struct platform_device *pdev)
+{
+       struct da9030_charger *charger;
+       struct da9030_battery_info *pdata = pdev->dev.platform_data;
+       int ret;
+
+       if (pdata == NULL)
+               return -EINVAL;
+
+       if (pdata->charge_milliamp >= 1500 ||
+           pdata->charge_millivolt < 4000 ||
+           pdata->charge_millivolt > 4350)
+               return -EINVAL;
+
+       charger = kzalloc(sizeof(*charger), GFP_KERNEL);
+       if (charger == NULL)
+               return -ENOMEM;
+
+       charger->master = pdev->dev.parent;
+
+       /* 10 seconds between monotor runs unless platfrom defines other
+          interval */
+       charger->interval = msecs_to_jiffies(
+               (pdata->batmon_interval ? : 10) * 1000);
+
+       charger->charge_milliamp = pdata->charge_milliamp;
+       charger->charge_millivolt = pdata->charge_millivolt;
+       charger->battery_info = pdata->battery_info;
+       charger->battery_low = pdata->battery_low;
+       charger->battery_critical = pdata->battery_critical;
+
+       da9030_battery_convert_thresholds(charger, pdata);
+
+       ret = da9030_battery_charger_init(charger);
+       if (ret)
+               goto err_charger_init;
+
+       INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
+       schedule_delayed_work(&charger->work, charger->interval);
+
+       charger->nb.notifier_call = da9030_battery_event;
+       ret = da903x_register_notifier(charger->master, &charger->nb,
+                                      DA9030_EVENT_CHDET |
+                                      DA9030_EVENT_VBATMON |
+                                      DA9030_EVENT_CHIOVER |
+                                      DA9030_EVENT_TBAT);
+       if (ret)
+               goto err_notifier;
+
+       da9030_battery_setup_psy(charger);
+       ret = power_supply_register(&pdev->dev, &charger->psy);
+       if (ret)
+               goto err_ps_register;
+
+       charger->debug_file = da9030_bat_create_debugfs(charger);
+       platform_set_drvdata(pdev, charger);
+       return 0;
+
+err_ps_register:
+       da903x_unregister_notifier(charger->master, &charger->nb,
+                                  DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
+                                  DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
+err_notifier:
+       cancel_delayed_work(&charger->work);
+
+err_charger_init:
+       kfree(charger);
+
+       return ret;
+}
+
+static int da9030_battery_remove(struct platform_device *dev)
+{
+       struct da9030_charger *charger = platform_get_drvdata(dev);
+
+       da9030_bat_remove_debugfs(charger);
+
+       da903x_unregister_notifier(charger->master, &charger->nb,
+                                  DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
+                                  DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
+       cancel_delayed_work(&charger->work);
+       power_supply_unregister(&charger->psy);
+
+       kfree(charger);
+
+       return 0;
+}
+
+static struct platform_driver da903x_battery_driver = {
+       .driver = {
+               .name   = "da903x-battery",
+               .owner  = THIS_MODULE,
+       },
+       .probe = da9030_battery_probe,
+       .remove = da9030_battery_remove,
+};
+
+static int da903x_battery_init(void)
+{
+       return platform_driver_register(&da903x_battery_driver);
+}
+
+static void da903x_battery_exit(void)
+{
+       platform_driver_unregister(&da903x_battery_driver);
+}
+
+module_init(da903x_battery_init);
+module_exit(da903x_battery_exit);
+
+MODULE_DESCRIPTION("DA9030 battery charger driver");
+MODULE_AUTHOR("Mike Rapoport, CompuLab");
+MODULE_LICENSE("GPL");
index 23ae8460f5c1a71f7b794745837799ac1fc21f25..ac01e06817fb330539a18836fa570c16597de55d 100644 (file)
@@ -45,7 +45,7 @@ static ssize_t power_supply_show_property(struct device *dev,
        };
        static char *health_text[] = {
                "Unknown", "Good", "Overheat", "Dead", "Over voltage",
-               "Unspecified failure"
+               "Unspecified failure", "Cold",
        };
        static char *technology_text[] = {
                "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c
new file mode 100644 (file)
index 0000000..1b16bf3
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Battery driver for wm8350 PMIC
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Based on OLPC Battery Driver
+ *
+ * Copyright 2006  David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/comparator.h>
+
+static int wm8350_read_battery_uvolts(struct wm8350 *wm8350)
+{
+       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0)
+               * WM8350_AUX_COEFF;
+}
+
+static int wm8350_read_line_uvolts(struct wm8350 *wm8350)
+{
+       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0)
+               * WM8350_AUX_COEFF;
+}
+
+static int wm8350_read_usb_uvolts(struct wm8350 *wm8350)
+{
+       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0)
+               * WM8350_AUX_COEFF;
+}
+
+#define WM8350_BATT_SUPPLY     1
+#define WM8350_USB_SUPPLY      2
+#define WM8350_LINE_SUPPLY     4
+
+static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min)
+{
+       if (!wm8350->power.rev_g_coeff)
+               return (((min - 30) / 15) & 0xf) << 8;
+       else
+               return (((min - 30) / 30) & 0xf) << 8;
+}
+
+static int wm8350_get_supplies(struct wm8350 *wm8350)
+{
+       u16 sm, ov, co, chrg;
+       int supplies = 0;
+
+       sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS);
+       ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES);
+       co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES);
+       chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
+
+       /* USB_SM */
+       sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT;
+
+       /* CHG_ISEL */
+       chrg &= WM8350_CHG_ISEL_MASK;
+
+       /* If the USB state machine is active then we're using that with or
+        * without battery, otherwise check for wall supply */
+       if (((sm == WM8350_USB_SM_100_SLV) ||
+            (sm == WM8350_USB_SM_500_SLV) ||
+            (sm == WM8350_USB_SM_STDBY_SLV))
+           && !(ov & WM8350_USB_LIMIT_OVRDE))
+               supplies = WM8350_USB_SUPPLY;
+       else if (((sm == WM8350_USB_SM_100_SLV) ||
+                 (sm == WM8350_USB_SM_500_SLV) ||
+                 (sm == WM8350_USB_SM_STDBY_SLV))
+                && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0))
+               supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY;
+       else if (co & WM8350_WALL_FB_OVRDE)
+               supplies = WM8350_LINE_SUPPLY;
+       else
+               supplies = WM8350_BATT_SUPPLY;
+
+       return supplies;
+}
+
+static int wm8350_charger_config(struct wm8350 *wm8350,
+                                struct wm8350_charger_policy *policy)
+{
+       u16 reg, eoc_mA, fast_limit_mA;
+
+       if (!policy) {
+               dev_warn(wm8350->dev,
+                        "No charger policy, charger not configured.\n");
+               return -EINVAL;
+       }
+
+       /* make sure USB fast charge current is not > 500mA */
+       if (policy->fast_limit_USB_mA > 500) {
+               dev_err(wm8350->dev, "USB fast charge > 500mA\n");
+               return -EINVAL;
+       }
+
+       eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA);
+
+       wm8350_reg_unlock(wm8350);
+
+       reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1)
+               & WM8350_CHG_ENA_R168;
+       wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
+                        reg | eoc_mA | policy->trickle_start_mV |
+                        WM8350_CHG_TRICKLE_TEMP_CHOKE |
+                        WM8350_CHG_TRICKLE_USB_CHOKE |
+                        WM8350_CHG_FAST_USB_THROTTLE);
+
+       if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) {
+               fast_limit_mA =
+                       WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA);
+               wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
+                           policy->charge_mV | policy->trickle_charge_USB_mA |
+                           fast_limit_mA | wm8350_charge_time_min(wm8350,
+                                               policy->charge_timeout));
+
+       } else {
+               fast_limit_mA =
+                       WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA);
+               wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
+                           policy->charge_mV | policy->trickle_charge_mA |
+                           fast_limit_mA | wm8350_charge_time_min(wm8350,
+                                               policy->charge_timeout));
+       }
+
+       wm8350_reg_lock(wm8350);
+       return 0;
+}
+
+static int wm8350_batt_status(struct wm8350 *wm8350)
+{
+       u16 state;
+
+       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
+       state &= WM8350_CHG_STS_MASK;
+
+       switch (state) {
+       case WM8350_CHG_STS_OFF:
+               return POWER_SUPPLY_STATUS_DISCHARGING;
+
+       case WM8350_CHG_STS_TRICKLE:
+       case WM8350_CHG_STS_FAST:
+               return POWER_SUPPLY_STATUS_CHARGING;
+
+       default:
+               return POWER_SUPPLY_STATUS_UNKNOWN;
+       }
+}
+
+static ssize_t charger_state_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(dev);
+       char *charge;
+       int state;
+
+       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &
+           WM8350_CHG_STS_MASK;
+       switch (state) {
+       case WM8350_CHG_STS_OFF:
+               charge = "Charger Off";
+               break;
+       case WM8350_CHG_STS_TRICKLE:
+               charge = "Trickle Charging";
+               break;
+       case WM8350_CHG_STS_FAST:
+               charge = "Fast Charging";
+               break;
+       default:
+               return 0;
+       }
+
+       return sprintf(buf, "%s\n", charge);
+}
+
+static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL);
+
+static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+       struct wm8350_power *power = &wm8350->power;
+       struct wm8350_charger_policy *policy = power->policy;
+
+       switch (irq) {
+       case WM8350_IRQ_CHG_BAT_FAIL:
+               dev_err(wm8350->dev, "battery failed\n");
+               break;
+       case WM8350_IRQ_CHG_TO:
+               dev_err(wm8350->dev, "charger timeout\n");
+               power_supply_changed(&power->battery);
+               break;
+
+       case WM8350_IRQ_CHG_BAT_HOT:
+       case WM8350_IRQ_CHG_BAT_COLD:
+       case WM8350_IRQ_CHG_START:
+       case WM8350_IRQ_CHG_END:
+               power_supply_changed(&power->battery);
+               break;
+
+       case WM8350_IRQ_CHG_FAST_RDY:
+               dev_dbg(wm8350->dev, "fast charger ready\n");
+               wm8350_charger_config(wm8350, policy);
+               wm8350_reg_unlock(wm8350);
+               wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
+                               WM8350_CHG_FAST);
+               wm8350_reg_lock(wm8350);
+               break;
+
+       case WM8350_IRQ_CHG_VBATT_LT_3P9:
+               dev_warn(wm8350->dev, "battery < 3.9V\n");
+               break;
+       case WM8350_IRQ_CHG_VBATT_LT_3P1:
+               dev_warn(wm8350->dev, "battery < 3.1V\n");
+               break;
+       case WM8350_IRQ_CHG_VBATT_LT_2P85:
+               dev_warn(wm8350->dev, "battery < 2.85V\n");
+               break;
+
+               /* Supply change.  We will overnotify but it should do
+                * no harm. */
+       case WM8350_IRQ_EXT_USB_FB:
+       case WM8350_IRQ_EXT_WALL_FB:
+               wm8350_charger_config(wm8350, policy);
+       case WM8350_IRQ_EXT_BAT_FB:   /* Fall through */
+               power_supply_changed(&power->battery);
+               power_supply_changed(&power->usb);
+               power_supply_changed(&power->ac);
+               break;
+
+       default:
+               dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);
+       }
+}
+
+/*********************************************************************
+ *             AC Power
+ *********************************************************************/
+static int wm8350_ac_get_prop(struct power_supply *psy,
+                             enum power_supply_property psp,
+                             union power_supply_propval *val)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(wm8350_get_supplies(wm8350) &
+                                WM8350_LINE_SUPPLY);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = wm8350_read_line_uvolts(wm8350);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static enum power_supply_property wm8350_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *             USB Power
+ *********************************************************************/
+static int wm8350_usb_get_prop(struct power_supply *psy,
+                              enum power_supply_property psp,
+                              union power_supply_propval *val)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(wm8350_get_supplies(wm8350) &
+                                WM8350_USB_SUPPLY);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = wm8350_read_usb_uvolts(wm8350);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static enum power_supply_property wm8350_usb_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *             Battery properties
+ *********************************************************************/
+
+static int wm8350_bat_check_health(struct wm8350 *wm8350)
+{
+       u16 reg;
+
+       if (wm8350_read_battery_uvolts(wm8350) < 2850000)
+               return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+
+       reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES);
+       if (reg & WM8350_CHG_BATT_HOT_OVRDE)
+               return POWER_SUPPLY_HEALTH_OVERHEAT;
+
+       if (reg & WM8350_CHG_BATT_COLD_OVRDE)
+               return POWER_SUPPLY_HEALTH_COLD;
+
+       return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int wm8350_bat_get_property(struct power_supply *psy,
+                                  enum power_supply_property psp,
+                                  union power_supply_propval *val)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = wm8350_batt_status(wm8350);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(wm8350_get_supplies(wm8350) &
+                                WM8350_BATT_SUPPLY);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = wm8350_read_battery_uvolts(wm8350);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = wm8350_bat_check_health(wm8350);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property wm8350_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_HEALTH,
+};
+
+/*********************************************************************
+ *             Initialisation
+ *********************************************************************/
+
+static void wm8350_init_charger(struct wm8350 *wm8350)
+{
+       /* register our interest in charger events */
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_TO);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_END);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_START);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+
+       /* and supply change events */
+       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
+       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
+       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,
+                           wm8350_charger_handler, NULL);
+       wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
+}
+
+static void free_charger_irq(struct wm8350 *wm8350)
+{
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_TO);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_END);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_START);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
+       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
+       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
+       wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
+       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
+}
+
+static __devinit int wm8350_power_probe(struct platform_device *pdev)
+{
+       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+       struct wm8350_power *power = &wm8350->power;
+       struct wm8350_charger_policy *policy = power->policy;
+       struct power_supply *usb = &power->usb;
+       struct power_supply *battery = &power->battery;
+       struct power_supply *ac = &power->ac;
+       int ret;
+
+       ac->name = "wm8350-ac";
+       ac->type = POWER_SUPPLY_TYPE_MAINS;
+       ac->properties = wm8350_ac_props;
+       ac->num_properties = ARRAY_SIZE(wm8350_ac_props);
+       ac->get_property = wm8350_ac_get_prop;
+       ret = power_supply_register(&pdev->dev, ac);
+       if (ret)
+               return ret;
+
+       battery->name = "wm8350-battery";
+       battery->properties = wm8350_bat_props;
+       battery->num_properties = ARRAY_SIZE(wm8350_bat_props);
+       battery->get_property = wm8350_bat_get_property;
+       battery->use_for_apm = 1;
+       ret = power_supply_register(&pdev->dev, battery);
+       if (ret)
+               goto battery_failed;
+
+       usb->name = "wm8350-usb",
+       usb->type = POWER_SUPPLY_TYPE_USB;
+       usb->properties = wm8350_usb_props;
+       usb->num_properties = ARRAY_SIZE(wm8350_usb_props);
+       usb->get_property = wm8350_usb_get_prop;
+       ret = power_supply_register(&pdev->dev, usb);
+       if (ret)
+               goto usb_failed;
+
+       ret = device_create_file(&pdev->dev, &dev_attr_charger_state);
+       if (ret < 0)
+               dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret);
+       ret = 0;
+
+       wm8350_init_charger(wm8350);
+       if (wm8350_charger_config(wm8350, policy) == 0) {
+               wm8350_reg_unlock(wm8350);
+               wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);
+               wm8350_reg_lock(wm8350);
+       }
+
+       return ret;
+
+usb_failed:
+       power_supply_unregister(battery);
+battery_failed:
+       power_supply_unregister(ac);
+
+       return ret;
+}
+
+static __devexit int wm8350_power_remove(struct platform_device *pdev)
+{
+       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+       struct wm8350_power *power = &wm8350->power;
+
+       free_charger_irq(wm8350);
+       device_remove_file(&pdev->dev, &dev_attr_charger_state);
+       power_supply_unregister(&power->battery);
+       power_supply_unregister(&power->ac);
+       power_supply_unregister(&power->usb);
+       return 0;
+}
+
+static struct platform_driver wm8350_power_driver = {
+       .probe = wm8350_power_probe,
+       .remove = __devexit_p(wm8350_power_remove),
+       .driver = {
+               .name = "wm8350-power",
+       },
+};
+
+static int __init wm8350_power_init(void)
+{
+       return platform_driver_register(&wm8350_power_driver);
+}
+module_init(wm8350_power_init);
+
+static void __exit wm8350_power_exit(void)
+{
+       platform_driver_unregister(&wm8350_power_driver);
+}
+module_exit(wm8350_power_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Power supply driver for WM8350");
+MODULE_ALIAS("platform:wm8350-power");
index 1f44b17e23b197fb3d32322566ced21dfe515864..c68c496b2c499c30316e86bcb3f4237f79c9c4ff 100644 (file)
@@ -1380,6 +1380,13 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
        if (wm8350->pmic.pdev[reg])
                return -EBUSY;
 
+       if (reg >= WM8350_DCDC_1 && reg <= WM8350_DCDC_6 &&
+           reg > wm8350->pmic.max_dcdc)
+               return -ENODEV;
+       if (reg >= WM8350_ISINK_A && reg <= WM8350_ISINK_B &&
+           reg > wm8350->pmic.max_isink)
+               return -ENODEV;
+
        pdev = platform_device_alloc("wm8350-regulator", reg);
        if (!pdev)
                return -ENOMEM;
diff --git a/include/linux/i2c/dm355evm_msp.h b/include/linux/i2c/dm355evm_msp.h
new file mode 100644 (file)
index 0000000..3724703
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * dm355evm_msp.h - support MSP430 microcontroller on DM355EVM board
+ */
+#ifndef __LINUX_I2C_DM355EVM_MSP
+#define __LINUX_I2C_DM355EVM_MSP
+
+/*
+ * Written against Spectrum's writeup for the A4 firmware revision,
+ * and tweaked to match source and rev D2 schematics by removing CPLD
+ * and NOR flash hooks (which were last appropriate in rev B boards).
+ *
+ * Note that the firmware supports a flavor of write posting ... to be
+ * sure a write completes, issue another read or write.
+ */
+
+/* utilities to access "registers" emulated by msp430 firmware */
+extern int dm355evm_msp_write(u8 value, u8 reg);
+extern int dm355evm_msp_read(u8 reg);
+
+
+/* command/control registers */
+#define DM355EVM_MSP_COMMAND           0x00
+#      define MSP_COMMAND_NULL         0
+#      define MSP_COMMAND_RESET_COLD   1
+#      define MSP_COMMAND_RESET_WARM   2
+#      define MSP_COMMAND_RESET_WARM_I 3
+#      define MSP_COMMAND_POWEROFF     4
+#      define MSP_COMMAND_IR_REINIT    5
+#define DM355EVM_MSP_STATUS            0x01
+#      define MSP_STATUS_BAD_OFFSET    BIT(0)
+#      define MSP_STATUS_BAD_COMMAND   BIT(1)
+#      define MSP_STATUS_POWER_ERROR   BIT(2)
+#      define MSP_STATUS_RXBUF_OVERRUN BIT(3)
+#define DM355EVM_MSP_RESET             0x02    /* 0 bits == in reset */
+#      define MSP_RESET_DC5            BIT(0)
+#      define MSP_RESET_TVP5154        BIT(2)
+#      define MSP_RESET_IMAGER         BIT(3)
+#      define MSP_RESET_ETHERNET       BIT(4)
+#      define MSP_RESET_SYS            BIT(5)
+#      define MSP_RESET_AIC33          BIT(7)
+
+/* GPIO registers ... bit patterns mostly match the source MSP ports */
+#define DM355EVM_MSP_LED               0x03    /* active low (MSP P4) */
+#define DM355EVM_MSP_SWITCH1           0x04    /* (MSP P5, masked) */
+#      define MSP_SWITCH1_SW6_1        BIT(0)
+#      define MSP_SWITCH1_SW6_2        BIT(1)
+#      define MSP_SWITCH1_SW6_3        BIT(2)
+#      define MSP_SWITCH1_SW6_4        BIT(3)
+#      define MSP_SWITCH1_J1           BIT(4)  /* NTSC/PAL */
+#      define MSP_SWITCH1_MSP_INT      BIT(5)  /* active low */
+#define DM355EVM_MSP_SWITCH2           0x05    /* (MSP P6, masked) */
+#      define MSP_SWITCH2_SW10         BIT(3)
+#      define MSP_SWITCH2_SW11         BIT(4)
+#      define MSP_SWITCH2_SW12         BIT(5)
+#      define MSP_SWITCH2_SW13         BIT(6)
+#      define MSP_SWITCH2_SW14         BIT(7)
+#define DM355EVM_MSP_SDMMC             0x06    /* (MSP P2, masked) */
+#      define MSP_SDMMC_0_WP           BIT(1)
+#      define MSP_SDMMC_0_CD           BIT(2)  /* active low */
+#      define MSP_SDMMC_1_WP           BIT(3)
+#      define MSP_SDMMC_1_CD           BIT(4)  /* active low */
+#define DM355EVM_MSP_FIRMREV           0x07    /* not a GPIO (out of order) */
+#define DM355EVM_MSP_VIDEO_IN          0x08    /* (MSP P3, masked) */
+#      define MSP_VIDEO_IMAGER         BIT(7)  /* low == tvp5146 */
+
+/* power supply registers are currently omitted */
+
+/* RTC registers */
+#define DM355EVM_MSP_RTC_0             0x12    /* LSB */
+#define DM355EVM_MSP_RTC_1             0x13
+#define DM355EVM_MSP_RTC_2             0x14
+#define DM355EVM_MSP_RTC_3             0x15    /* MSB */
+
+/* input event queue registers; code == ((HIGH << 8) | LOW) */
+#define DM355EVM_MSP_INPUT_COUNT       0x16    /* decrement by reading LOW */
+#define DM355EVM_MSP_INPUT_HIGH                0x17
+#define DM355EVM_MSP_INPUT_LOW         0x18
+
+#endif /* __LINUX_I2C_DM355EVM_MSP */
index fb604dcd38f19d89a817c8bc2397aa09d3ece62c..a8f84c01f82ec03f8419a2bffa95e33daea9424a 100644 (file)
@@ -78,8 +78,8 @@ int twl4030_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
  * IMPORTANT:  For twl4030_i2c_write(), allocate num_bytes + 1
  * for the value, and populate your data starting at offset 1.
  */
-int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes);
-int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes);
+int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
+int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
 
 /*----------------------------------------------------------------------*/
 
@@ -278,6 +278,18 @@ struct twl4030_platform_data {
        struct twl4030_keypad_data              *keypad;
        struct twl4030_usb_data                 *usb;
 
+       /* LDO regulators */
+       struct regulator_init_data              *vdac;
+       struct regulator_init_data              *vpll1;
+       struct regulator_init_data              *vpll2;
+       struct regulator_init_data              *vmmc1;
+       struct regulator_init_data              *vmmc2;
+       struct regulator_init_data              *vsim;
+       struct regulator_init_data              *vaux1;
+       struct regulator_init_data              *vaux2;
+       struct regulator_init_data              *vaux3;
+       struct regulator_init_data              *vaux4;
+
        /* REVISIT more to come ... _nothing_ should be hard-wired */
 };
 
@@ -285,33 +297,6 @@ struct twl4030_platform_data {
 
 int twl4030_sih_setup(int module);
 
-/*
- * FIXME completely stop using TWL4030_IRQ_BASE ... instead, pass the
- * IRQ data to subsidiary devices using platform device resources.
- */
-
-/* IRQ information-need base */
-#include <mach/irqs.h>
-/* TWL4030 interrupts */
-
-/* #define TWL4030_MODIRQ_GPIO         (TWL4030_IRQ_BASE + 0) */
-#define TWL4030_MODIRQ_KEYPAD          (TWL4030_IRQ_BASE + 1)
-#define TWL4030_MODIRQ_BCI             (TWL4030_IRQ_BASE + 2)
-#define TWL4030_MODIRQ_MADC            (TWL4030_IRQ_BASE + 3)
-/* #define TWL4030_MODIRQ_USB          (TWL4030_IRQ_BASE + 4) */
-/* #define TWL4030_MODIRQ_PWR          (TWL4030_IRQ_BASE + 5) */
-
-#define TWL4030_PWRIRQ_PWRBTN          (TWL4030_PWR_IRQ_BASE + 0)
-/* #define TWL4030_PWRIRQ_CHG_PRES             (TWL4030_PWR_IRQ_BASE + 1) */
-/* #define TWL4030_PWRIRQ_USB_PRES             (TWL4030_PWR_IRQ_BASE + 2) */
-/* #define TWL4030_PWRIRQ_RTC          (TWL4030_PWR_IRQ_BASE + 3) */
-/* #define TWL4030_PWRIRQ_HOT_DIE              (TWL4030_PWR_IRQ_BASE + 4) */
-/* #define TWL4030_PWRIRQ_PWROK_TIMEOUT        (TWL4030_PWR_IRQ_BASE + 5) */
-/* #define TWL4030_PWRIRQ_MBCHG                (TWL4030_PWR_IRQ_BASE + 6) */
-/* #define TWL4030_PWRIRQ_SC_DETECT    (TWL4030_PWR_IRQ_BASE + 7) */
-
-/* Rest are unsued currently*/
-
 /* Offsets to Power Registers */
 #define TWL4030_VDAC_DEV_GRP           0x3B
 #define TWL4030_VDAC_DEDICATED         0x3E
@@ -322,10 +307,6 @@ int twl4030_sih_setup(int module);
 #define TWL4030_VAUX3_DEV_GRP          0x1F
 #define TWL4030_VAUX3_DEDICATED                0x22
 
-/* TWL4030 GPIO interrupt definitions */
-
-#define TWL4030_GPIO_IRQ_NO(n)         (TWL4030_GPIO_IRQ_BASE + (n))
-
 /*
  * Exported TWL4030 GPIO APIs
  *
@@ -340,4 +321,38 @@ int twl4030_set_gpio_debounce(int gpio, int enable);
        static inline int twl4030charger_usb_en(int enable) { return 0; }
 #endif
 
+/*----------------------------------------------------------------------*/
+
+/* Linux-specific regulator identifiers ... for now, we only support
+ * the LDOs, and leave the three buck converters alone.  VDD1 and VDD2
+ * need to tie into hardware based voltage scaling (cpufreq etc), while
+ * VIO is generally fixed.
+ */
+
+/* EXTERNAL dc-to-dc buck converters */
+#define TWL4030_REG_VDD1       0
+#define TWL4030_REG_VDD2       1
+#define TWL4030_REG_VIO                2
+
+/* EXTERNAL LDOs */
+#define TWL4030_REG_VDAC       3
+#define TWL4030_REG_VPLL1      4
+#define TWL4030_REG_VPLL2      5       /* not on all chips */
+#define TWL4030_REG_VMMC1      6
+#define TWL4030_REG_VMMC2      7       /* not on all chips */
+#define TWL4030_REG_VSIM       8       /* not on all chips */
+#define TWL4030_REG_VAUX1      9       /* not on all chips */
+#define TWL4030_REG_VAUX2_4030 10      /* (twl4030-specific) */
+#define TWL4030_REG_VAUX2      11      /* (twl5030 and newer) */
+#define TWL4030_REG_VAUX3      12      /* not on all chips */
+#define TWL4030_REG_VAUX4      13      /* not on all chips */
+
+/* INTERNAL LDOs */
+#define TWL4030_REG_VINTANA1   14
+#define TWL4030_REG_VINTANA2   15
+#define TWL4030_REG_VINTDIG    16
+#define TWL4030_REG_VUSB1V5    17
+#define TWL4030_REG_VUSB1V8    18
+#define TWL4030_REG_VUSB3V1    19
+
 #endif /* End of __TWL4030_H */
index cad314c124396bb57efcdcd9ab8823042c623532..115dbe965082f5e445f473f066bf23f4950b82a0 100644 (file)
@@ -32,6 +32,7 @@ enum {
        DA9030_ID_LDO18,
        DA9030_ID_LDO19,
        DA9030_ID_LDO_INT,      /* LDO Internal */
+       DA9030_ID_BAT,          /* battery charger */
 
        DA9034_ID_LED_1,
        DA9034_ID_LED_2,
@@ -93,6 +94,43 @@ struct da9034_touch_pdata {
        int     y_inverted;
 };
 
+/* DA9030 battery charger data */
+struct power_supply_info;
+
+struct da9030_battery_info {
+       /* battery parameters */
+       struct power_supply_info *battery_info;
+
+       /* current and voltage to use for battery charging */
+       unsigned int charge_milliamp;
+       unsigned int charge_millivolt;
+
+       /* voltage thresholds (in millivolts) */
+       int vbat_low;
+       int vbat_crit;
+       int vbat_charge_start;
+       int vbat_charge_stop;
+       int vbat_charge_restart;
+
+       /* battery nominal minimal and maximal voltages in millivolts */
+       int vcharge_min;
+       int vcharge_max;
+
+       /* Temperature thresholds. These are DA9030 register values
+          "as is" and should be measured for each battery type */
+       int tbat_low;
+       int tbat_high;
+       int tbat_restart;
+
+
+       /* battery monitor interval (seconds) */
+       unsigned int batmon_interval;
+
+       /* platform callbacks for battery low and critical events */
+       void (*battery_low)(void);
+       void (*battery_critical)(void);
+};
+
 struct da903x_subdev_info {
        int             id;
        const char      *name;
@@ -190,11 +228,13 @@ extern int da903x_unregister_notifier(struct device *dev,
 extern int da903x_query_status(struct device *dev, unsigned int status);
 
 
-/* NOTE: the two functions below are not intended for use outside
- * of the DA9034 sub-device drivers
+/* NOTE: the functions below are not intended for use outside
+ * of the DA903x sub-device drivers
  */
 extern int da903x_write(struct device *dev, int reg, uint8_t val);
+extern int da903x_writes(struct device *dev, int reg, int len, uint8_t *val);
 extern int da903x_read(struct device *dev, int reg, uint8_t *val);
+extern int da903x_reads(struct device *dev, int reg, int len, uint8_t *val);
 extern int da903x_update(struct device *dev, int reg, uint8_t val, uint8_t mask);
 extern int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask);
 extern int da903x_clr_bits(struct device *dev, int reg, uint8_t bit_mask);
index 053788649452a595dad95de896f548e6f4006c5a..54bc5d0fd5025011ba52f0a3156875157181ef13 100644 (file)
 #define WM8350_AUXADC_BATT                     6
 #define WM8350_AUXADC_TEMP                     7
 
+struct wm8350;
+
+/*
+ * AUX ADC Readback
+ */
+int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale,
+                      int vref);
+
 #endif
index 6ebf97f2a475f9eb198378b24b98d7cca2bbc9e8..980669d50dcaef1d450eec3d93412b7c3cdb38a1 100644 (file)
@@ -29,6 +29,7 @@
  */
 #define WM8350_RESET_ID                         0x00
 #define WM8350_ID                               0x01
+#define WM8350_REVISION                                0x02
 #define WM8350_SYSTEM_CONTROL_1                 0x03
 #define WM8350_SYSTEM_CONTROL_2                 0x04
 #define WM8350_SYSTEM_HIBERNATE                 0x05
 #define WM8350_OVER_CURRENT_INT_STATUS_MASK     0x25
 #define WM8350_GPIO_INT_STATUS_MASK             0x26
 #define WM8350_COMPARATOR_INT_STATUS_MASK       0x27
+#define WM8350_CHARGER_OVERRIDES               0xE2
+#define WM8350_MISC_OVERRIDES                  0xE3
+#define WM8350_COMPARATOR_OVERRIDES            0xE7
+#define WM8350_STATE_MACHINE_STATUS            0xE9
 
 #define WM8350_MAX_REGISTER                     0xFF
 
 #define WM8350_CONF_STS_MASK                    0x0C00
 #define WM8350_CUST_ID_MASK                     0x00FF
 
+/*
+ * R2 (0x02) - Revision
+ */
+#define WM8350_MASK_REV_MASK                   0x00FF
+
 /*
  * R3 (0x03) - System Control 1
  */
 #define WM8350_DC2_STS                          0x0002
 #define WM8350_DC1_STS                          0x0001
 
+/*
+ * R226 (0xE2) - Charger status
+ */
+#define WM8350_CHG_BATT_HOT_OVRDE              0x8000
+#define WM8350_CHG_BATT_COLD_OVRDE             0x4000
+
+/*
+ * R227 (0xE3) - Misc Overrides
+ */
+#define WM8350_USB_LIMIT_OVRDE                 0x0400
+
+/*
+ * R227 (0xE7) - Comparator Overrides
+ */
+#define WM8350_USB_FB_OVRDE                    0x8000
+#define WM8350_WALL_FB_OVRDE                   0x4000
+#define WM8350_BATT_FB_OVRDE                   0x2000
+
+
+/*
+ * R233 (0xE9) - State Machinine Status
+ */
+#define WM8350_USB_SM_MASK                     0x0700
+#define WM8350_USB_SM_SHIFT                    8
+
+#define WM8350_USB_SM_100_SLV   1
+#define WM8350_USB_SM_500_SLV   5
+#define WM8350_USB_SM_STDBY_SLV 7
+
 /* WM8350 wake up conditions */
 #define WM8350_IRQ_WKUP_OFF_STATE              43
 #define WM8350_IRQ_WKUP_HIB_STATE              44
 #define WM8350_REV_E                           0x4
 #define WM8350_REV_F                           0x5
 #define WM8350_REV_G                           0x6
+#define WM8350_REV_H                           0x7
 
 #define WM8350_NUM_IRQ                         63
 
@@ -549,6 +589,14 @@ extern const u16 wm8350_mode0_defaults[];
 extern const u16 wm8350_mode1_defaults[];
 extern const u16 wm8350_mode2_defaults[];
 extern const u16 wm8350_mode3_defaults[];
+extern const u16 wm8351_mode0_defaults[];
+extern const u16 wm8351_mode1_defaults[];
+extern const u16 wm8351_mode2_defaults[];
+extern const u16 wm8351_mode3_defaults[];
+extern const u16 wm8352_mode0_defaults[];
+extern const u16 wm8352_mode1_defaults[];
+extern const u16 wm8352_mode2_defaults[];
+extern const u16 wm8352_mode3_defaults[];
 
 struct wm8350;
 
@@ -558,8 +606,6 @@ struct wm8350_irq {
 };
 
 struct wm8350 {
-       int rev;                /* chip revision */
-
        struct device *dev;
 
        /* device IO */
@@ -572,6 +618,8 @@ struct wm8350 {
                         void *src);
        u16 *reg_cache;
 
+       struct mutex auxadc_mutex;
+
        /* Interrupt handling */
        struct work_struct irq_work;
        struct mutex irq_mutex; /* IRQ table mutex */
index 69b69e07f62fa4338303434e904439e45a65aac8..96acbfc8aa12f2c607a97e110af29ce370389297 100644 (file)
@@ -701,6 +701,10 @@ struct platform_device;
 struct regulator_init_data;
 
 struct wm8350_pmic {
+       /* Number of regulators of each type on this device */
+       int max_dcdc;
+       int max_isink;
+
        /* ISINK to DCDC mapping */
        int isink_A_dcdc;
        int isink_B_dcdc;
index 1c8f3cde79b05cc87452b3806c37a490a1a65609..2b9479310bbd32e81f54af29b51eff7bdf3922d4 100644 (file)
@@ -13,7 +13,8 @@
 #ifndef __LINUX_MFD_WM8350_SUPPLY_H_
 #define __LINUX_MFD_WM8350_SUPPLY_H_
 
-#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
 
 /*
  * Charger registers
 #define WM8350_IRQ_EXT_WALL_FB                 37
 #define WM8350_IRQ_EXT_BAT_FB                  38
 
+/*
+ * Policy to control charger state machine.
+ */
+struct wm8350_charger_policy {
+
+       /* charger state machine policy  - set in machine driver */
+       int eoc_mA;             /* end of charge current (mA)  */
+       int charge_mV;          /* charge voltage */
+       int fast_limit_mA;      /* fast charge current limit */
+       int fast_limit_USB_mA;  /* USB fast charge current limit */
+       int charge_timeout;     /* charge timeout (mins) */
+       int trickle_start_mV;   /* trickle charge starts at mV */
+       int trickle_charge_mA;  /* trickle charge current */
+       int trickle_charge_USB_mA;      /* USB trickle charge current */
+};
+
 struct wm8350_power {
        struct platform_device *pdev;
+       struct power_supply battery;
+       struct power_supply usb;
+       struct power_supply ac;
+       struct wm8350_charger_policy *policy;
+
+       int rev_g_coeff;
 };
 
 #endif
index f9348cba6dc11ec32ed425e4d271ba6100de43ab..8ff25e0e7f7a163f5240da47bc7bb0e850585de4 100644 (file)
@@ -45,6 +45,7 @@ enum {
        POWER_SUPPLY_HEALTH_DEAD,
        POWER_SUPPLY_HEALTH_OVERVOLTAGE,
        POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
+       POWER_SUPPLY_HEALTH_COLD,
 };
 
 enum {