summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/hwmon/wm831x37
-rw-r--r--Documentation/hwmon/wm835026
-rw-r--r--MAINTAINERS20
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c122
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/wm831x-gpio.c252
-rw-r--r--drivers/hwmon/Kconfig21
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/wm831x-hwmon.c226
-rw-r--r--drivers/hwmon/wm8350-hwmon.c151
-rw-r--r--drivers/input/misc/Kconfig20
-rw-r--r--drivers/input/misc/Makefile3
-rw-r--r--drivers/input/misc/pcap_keys.c144
-rw-r--r--drivers/input/misc/wm831x-on.c163
-rw-r--r--drivers/input/touchscreen/Kconfig9
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/pcap_ts.c271
-rw-r--r--drivers/mfd/Kconfig42
-rw-r--r--drivers/mfd/Makefile6
-rw-r--r--drivers/mfd/ab3100-core.c62
-rw-r--r--drivers/mfd/ab3100-otp.c268
-rw-r--r--drivers/mfd/dm355evm_msp.c12
-rw-r--r--drivers/mfd/ezx-pcap.c105
-rw-r--r--drivers/mfd/mc13783-core.c427
-rw-r--r--drivers/mfd/mfd-core.c2
-rw-r--r--drivers/mfd/pcf50633-adc.c32
-rw-r--r--drivers/mfd/pcf50633-core.c5
-rw-r--r--drivers/mfd/twl4030-core.c23
-rw-r--r--drivers/mfd/twl4030-irq.c2
-rw-r--r--drivers/mfd/twl4030-power.c472
-rw-r--r--drivers/mfd/wm831x-core.c1549
-rw-r--r--drivers/mfd/wm831x-irq.c559
-rw-r--r--drivers/mfd/wm831x-otp.c83
-rw-r--r--drivers/mfd/wm8350-core.c27
-rw-r--r--drivers/regulator/Kconfig31
-rw-r--r--drivers/regulator/Makefile6
-rw-r--r--drivers/regulator/ab3100.c700
-rw-r--r--drivers/regulator/core.c24
-rw-r--r--drivers/regulator/mc13783.c410
-rw-r--r--drivers/regulator/pcap-regulator.c318
-rw-r--r--drivers/regulator/wm831x-dcdc.c862
-rw-r--r--drivers/regulator/wm831x-isink.c260
-rw-r--r--drivers/regulator/wm831x-ldo.c852
-rw-r--r--drivers/rtc/Kconfig19
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/rtc-ab3100.c281
-rw-r--r--drivers/rtc/rtc-wm831x.c523
-rw-r--r--include/linux/i2c/twl4030.h94
-rw-r--r--include/linux/mfd/ab3100.h37
-rw-r--r--include/linux/mfd/core.h1
-rw-r--r--include/linux/mfd/ezx-pcap.h7
-rw-r--r--include/linux/mfd/mc13783-private.h396
-rw-r--r--include/linux/mfd/mc13783.h84
-rw-r--r--include/linux/mfd/pcf50633/adc.h3
-rw-r--r--include/linux/mfd/pcf50633/core.h1
-rw-r--r--include/linux/mfd/wm831x/auxadc.h216
-rw-r--r--include/linux/mfd/wm831x/core.h289
-rw-r--r--include/linux/mfd/wm831x/gpio.h55
-rw-r--r--include/linux/mfd/wm831x/irq.h764
-rw-r--r--include/linux/mfd/wm831x/otp.h162
-rw-r--r--include/linux/mfd/wm831x/pdata.h113
-rw-r--r--include/linux/mfd/wm831x/regulator.h1218
-rw-r--r--include/linux/mfd/wm8350/core.h7
-rw-r--r--include/linux/regulator/driver.h2
65 files changed, 12770 insertions, 119 deletions
diff --git a/Documentation/hwmon/wm831x b/Documentation/hwmon/wm831x
new file mode 100644
index 0000000..24f47d8
--- /dev/null
+++ b/Documentation/hwmon/wm831x
@@ -0,0 +1,37 @@
+Kernel driver wm831x-hwmon
+==========================
+
+Supported chips:
+ * Wolfson Microelectronics WM831x PMICs
+ Prefix: 'wm831x'
+ Datasheet:
+ http://www.wolfsonmicro.com/products/WM8310
+ http://www.wolfsonmicro.com/products/WM8311
+ http://www.wolfsonmicro.com/products/WM8312
+
+Authors: Mark Brown <broonie@opensource.wolfsonmicro.com>
+
+Description
+-----------
+
+The WM831x series of PMICs include an AUXADC which can be used to
+monitor a range of system operating parameters, including the voltages
+of the major supplies within the system. Currently the driver provides
+reporting of all the input values but does not provide any alarms.
+
+Voltage Monitoring
+------------------
+
+Voltages are sampled by a 12 bit ADC. Voltages in milivolts are 1.465
+times the ADC value.
+
+Temperature Monitoring
+----------------------
+
+Temperatures are sampled by a 12 bit ADC. Chip and battery temperatures
+are available. The chip temperature is calculated as:
+
+ Degrees celsius = (512.18 - data) / 1.0983
+
+while the battery temperature calculation will depend on the NTC
+thermistor component.
diff --git a/Documentation/hwmon/wm8350 b/Documentation/hwmon/wm8350
new file mode 100644
index 0000000..98f923bd
--- /dev/null
+++ b/Documentation/hwmon/wm8350
@@ -0,0 +1,26 @@
+Kernel driver wm8350-hwmon
+==========================
+
+Supported chips:
+ * Wolfson Microelectronics WM835x PMICs
+ Prefix: 'wm8350'
+ Datasheet:
+ http://www.wolfsonmicro.com/products/WM8350
+ http://www.wolfsonmicro.com/products/WM8351
+ http://www.wolfsonmicro.com/products/WM8352
+
+Authors: Mark Brown <broonie@opensource.wolfsonmicro.com>
+
+Description
+-----------
+
+The WM835x series of PMICs include an AUXADC which can be used to
+monitor a range of system operating parameters, including the voltages
+of the major supplies within the system. Currently the driver provides
+simple access to these major supplies.
+
+Voltage Monitoring
+------------------
+
+Voltages are sampled by a 12 bit ADC. For the internal supplies the ADC
+is referenced to the system VRTC.
diff --git a/MAINTAINERS b/MAINTAINERS
index e2b9486..bd3f026 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5683,6 +5683,26 @@ S: Supported
F: drivers/input/touchscreen/*wm97*
F: include/linux/wm97xx.h
+WOLFSON MICROELECTRONICS PMIC DRIVERS
+P: Mark Brown
+M: broonie@opensource.wolfsonmicro.com
+L: linux-kernel@vger.kernel.org
+T: git git://opensource.wolfsonmicro.com/linux-2.6-audioplus
+W: http://opensource.wolfsonmicro.com/node/8
+S: Supported
+F: drivers/leds/leds-wm83*.c
+F: drivers/mfd/wm8*.c
+F: drivers/power/wm83*.c
+F: drivers/rtc/rtc-wm83*.c
+F: drivers/regulator/wm8*.c
+F: drivers/video/backlight/wm83*_bl.c
+F: drivers/watchdog/wm83*_wdt.c
+F: include/linux/mfd/wm831x/
+F: include/linux/mfd/wm8350/
+F: include/linux/mfd/wm8400/
+F: sound/soc/codecs/wm8350.c
+F: sound/soc/codecs/wm8400.c
+
X.25 NETWORK LAYER
M: Henner Eisen <eis@baty.hanse.de>
L: linux-x25@vger.kernel.org
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index 56d931a..e70baa7 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -1,5 +1,5 @@
/*
- * linux/arch/arm/mach-omap2/board-rx51-flash.c
+ * linux/arch/arm/mach-omap2/board-rx51-peripherals.c
*
* Copyright (C) 2008-2009 Nokia
*
@@ -282,7 +282,124 @@ static struct twl4030_usb_data rx51_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
-static struct twl4030_platform_data rx51_twldata = {
+static struct twl4030_ins sleep_on_seq[] __initdata = {
+/*
+ * Turn off VDD1 and VDD2.
+ */
+ {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_OFF), 4},
+ {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_OFF), 2},
+/*
+ * And also turn off the OMAP3 PLLs and the sysclk output.
+ */
+ {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_OFF), 3},
+ {MSG_SINGULAR(DEV_GRP_P1, 0x17, RES_STATE_OFF), 3},
+};
+
+static struct twl4030_script sleep_on_script __initdata = {
+ .script = sleep_on_seq,
+ .size = ARRAY_SIZE(sleep_on_seq),
+ .flags = TWL4030_SLEEP_SCRIPT,
+};
+
+static struct twl4030_ins wakeup_seq[] __initdata = {
+/*
+ * Reenable the OMAP3 PLLs.
+ * Wakeup VDD1 and VDD2.
+ * Reenable sysclk output.
+ */
+ {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_ACTIVE), 0x30},
+ {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_ACTIVE), 0x30},
+ {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_ACTIVE), 0x37},
+ {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 3},
+};
+
+static struct twl4030_script wakeup_script __initdata = {
+ .script = wakeup_seq,
+ .size = ARRAY_SIZE(wakeup_seq),
+ .flags = TWL4030_WAKEUP12_SCRIPT,
+};
+
+static struct twl4030_ins wakeup_p3_seq[] __initdata = {
+/*
+ * Wakeup VDD1 (dummy to be able to insert a delay)
+ * Enable CLKEN
+ */
+ {MSG_SINGULAR(DEV_GRP_P1, 0x17, RES_STATE_ACTIVE), 3},
+};
+
+static struct twl4030_script wakeup_p3_script __initdata = {
+ .script = wakeup_p3_seq,
+ .size = ARRAY_SIZE(wakeup_p3_seq),
+ .flags = TWL4030_WAKEUP3_SCRIPT,
+};
+
+static struct twl4030_ins wrst_seq[] __initdata = {
+/*
+ * Reset twl4030.
+ * Reset VDD1 regulator.
+ * Reset VDD2 regulator.
+ * Reset VPLL1 regulator.
+ * Enable sysclk output.
+ * Reenable twl4030.
+ */
+ {MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_OFF), 2},
+ {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, 0, 1, RES_STATE_ACTIVE),
+ 0x13},
+ {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_PP, 0, 2, RES_STATE_WRST), 0x13},
+ {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_PP, 0, 3, RES_STATE_OFF), 0x13},
+ {MSG_SINGULAR(DEV_GRP_NULL, RES_VDD1, RES_STATE_WRST), 0x13},
+ {MSG_SINGULAR(DEV_GRP_NULL, RES_VDD2, RES_STATE_WRST), 0x13},
+ {MSG_SINGULAR(DEV_GRP_NULL, RES_VPLL1, RES_STATE_WRST), 0x35},
+ {MSG_SINGULAR(DEV_GRP_P1, RES_HFCLKOUT, RES_STATE_ACTIVE), 2},
+ {MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_ACTIVE), 2},
+};
+
+static struct twl4030_script wrst_script __initdata = {
+ .script = wrst_seq,
+ .size = ARRAY_SIZE(wrst_seq),
+ .flags = TWL4030_WRST_SCRIPT,
+};
+
+static struct twl4030_script *twl4030_scripts[] __initdata = {
+ /* wakeup12 script should be loaded before sleep script, otherwise a
+ board might hit retention before loading of wakeup script is
+ completed. This can cause boot failures depending on timing issues.
+ */
+ &wakeup_script,
+ &sleep_on_script,
+ &wakeup_p3_script,
+ &wrst_script,
+};
+
+static struct twl4030_resconfig twl4030_rconfig[] __initdata = {
+ { .resource = RES_VINTANA1, .devgroup = -1, .type = -1, .type2 = 1 },
+ { .resource = RES_VINTANA2, .devgroup = -1, .type = -1, .type2 = 1 },
+ { .resource = RES_VINTDIG, .devgroup = -1, .type = -1, .type2 = 1 },
+ { .resource = RES_VMMC1, .devgroup = -1, .type = -1, .type2 = 3},
+ { .resource = RES_VMMC2, .devgroup = DEV_GRP_NULL, .type = -1,
+ .type2 = 3},
+ { .resource = RES_VAUX1, .devgroup = -1, .type = -1, .type2 = 3},
+ { .resource = RES_VAUX2, .devgroup = -1, .type = -1, .type2 = 3},
+ { .resource = RES_VAUX3, .devgroup = -1, .type = -1, .type2 = 3},
+ { .resource = RES_VAUX4, .devgroup = -1, .type = -1, .type2 = 3},
+ { .resource = RES_VPLL2, .devgroup = -1, .type = -1, .type2 = 3},
+ { .resource = RES_VDAC, .devgroup = -1, .type = -1, .type2 = 3},
+ { .resource = RES_VSIM, .devgroup = DEV_GRP_NULL, .type = -1,
+ .type2 = 3},
+ { .resource = RES_CLKEN, .devgroup = DEV_GRP_P3, .type = -1,
+ .type2 = 1 },
+ { 0, 0},
+};
+
+static struct twl4030_power_data rx51_t2scripts_data __initdata = {
+ .scripts = twl4030_scripts,
+ .num = ARRAY_SIZE(twl4030_scripts),
+ .resource_config = twl4030_rconfig,
+};
+
+
+
+static struct twl4030_platform_data rx51_twldata __initdata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -291,6 +408,7 @@ static struct twl4030_platform_data rx51_twldata = {
.keypad = &rx51_kp_data,
.madc = &rx51_madc_data,
.usb = &rx51_usb_data,
+ .power = &rx51_t2scripts_data,
.vaux1 = &rx51_vaux1,
.vaux2 = &rx51_vaux2,
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 96dda81..6b4c484 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -155,6 +155,13 @@ config GPIO_TWL4030
Say yes here to access the GPIO signals of various multi-function
power management chips from Texas Instruments.
+config GPIO_WM831X
+ tristate "WM831x GPIOs"
+ depends on MFD_WM831X
+ help
+ Say yes here to access the GPIO signals of WM831x power management
+ chips from Wolfson Microelectronics.
+
comment "PCI GPIO expanders:"
config GPIO_BT8XX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9244c6f..ea7c745 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o
obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
+obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c
new file mode 100644
index 0000000..f9c09a5
--- /dev/null
+++ b/drivers/gpio/wm831x-gpio.c
@@ -0,0 +1,252 @@
+/*
+ * wm831x-gpio.c -- gpiolib support for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/gpio.h>
+
+#define WM831X_GPIO_MAX 16
+
+struct wm831x_gpio {
+ struct wm831x *wm831x;
+ struct gpio_chip gpio_chip;
+};
+
+static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm831x_gpio, gpio_chip);
+}
+
+static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+
+ return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
+ WM831X_GPN_DIR | WM831X_GPN_TRI,
+ WM831X_GPN_DIR);
+}
+
+static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
+ if (ret < 0)
+ return ret;
+
+ if (ret & 1 << offset)
+ return 1;
+ else
+ return 0;
+}
+
+static int wm831x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+
+ return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
+ WM831X_GPN_DIR | WM831X_GPN_TRI, 0);
+}
+
+static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+
+ wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset,
+ value << offset);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int i;
+
+ for (i = 0; i < chip->ngpio; i++) {
+ int gpio = i + chip->base;
+ int reg;
+ const char *label, *pull, *powerdomain;
+
+ /* We report the GPIO even if it's not requested since
+ * we're also reporting things like alternate
+ * functions which apply even when the GPIO is not in
+ * use as a GPIO.
+ */
+ label = gpiochip_is_requested(chip, i);
+ if (!label)
+ label = "Unrequested";
+
+ seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
+
+ reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i);
+ if (reg < 0) {
+ dev_err(wm831x->dev,
+ "GPIO control %d read failed: %d\n",
+ gpio, reg);
+ seq_printf(s, "\n");
+ continue;
+ }
+
+ switch (reg & WM831X_GPN_PULL_MASK) {
+ case WM831X_GPIO_PULL_NONE:
+ pull = "nopull";
+ break;
+ case WM831X_GPIO_PULL_DOWN:
+ pull = "pulldown";
+ break;
+ case WM831X_GPIO_PULL_UP:
+ pull = "pullup";
+ default:
+ pull = "INVALID PULL";
+ break;
+ }
+
+ switch (i + 1) {
+ case 1 ... 3:
+ case 7 ... 9:
+ if (reg & WM831X_GPN_PWR_DOM)
+ powerdomain = "VPMIC";
+ else
+ powerdomain = "DBVDD";
+ break;
+
+ case 4 ... 6:
+ case 10 ... 12:
+ if (reg & WM831X_GPN_PWR_DOM)
+ powerdomain = "SYSVDD";
+ else
+ powerdomain = "DBVDD";
+ break;
+
+ case 13 ... 16:
+ powerdomain = "TPVDD";
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ seq_printf(s, " %s %s %s %s%s\n"
+ " %s%s (0x%4x)\n",
+ reg & WM831X_GPN_DIR ? "in" : "out",
+ wm831x_gpio_get(chip, i) ? "high" : "low",
+ pull,
+ powerdomain,
+ reg & WM831X_GPN_POL ? " inverted" : "",
+ reg & WM831X_GPN_OD ? "open-drain" : "CMOS",
+ reg & WM831X_GPN_TRI ? " tristated" : "",
+ reg);
+ }
+}
+#else
+#define wm831x_gpio_dbg_show NULL
+#endif
+
+static struct gpio_chip template_chip = {
+ .label = "wm831x",
+ .owner = THIS_MODULE,
+ .direction_input = wm831x_gpio_direction_in,
+ .get = wm831x_gpio_get,
+ .direction_output = wm831x_gpio_direction_out,
+ .set = wm831x_gpio_set,
+ .dbg_show = wm831x_gpio_dbg_show,
+ .can_sleep = 1,
+};
+
+static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_gpio *wm831x_gpio;
+ int ret;
+
+ wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL);
+ if (wm831x_gpio == NULL)
+ return -ENOMEM;
+
+ wm831x_gpio->wm831x = wm831x;
+ wm831x_gpio->gpio_chip = template_chip;
+ wm831x_gpio->gpio_chip.ngpio = WM831X_GPIO_MAX;
+ wm831x_gpio->gpio_chip.dev = &pdev->dev;
+ if (pdata && pdata->gpio_base)
+ wm831x_gpio->gpio_chip.base = pdata->gpio_base;
+ else
+ wm831x_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm831x_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
+ ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, wm831x_gpio);
+
+ return ret;
+
+err:
+ kfree(wm831x_gpio);
+ return ret;
+}
+
+static int __devexit wm831x_gpio_remove(struct platform_device *pdev)
+{
+ struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&wm831x_gpio->gpio_chip);
+ if (ret == 0)
+ kfree(wm831x_gpio);
+
+ return ret;
+}
+
+static struct platform_driver wm831x_gpio_driver = {
+ .driver.name = "wm831x-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = wm831x_gpio_probe,
+ .remove = __devexit_p(wm831x_gpio_remove),
+};
+
+static int __init wm831x_gpio_init(void)
+{
+ return platform_driver_register(&wm831x_gpio_driver);
+}
+subsys_initcall(wm831x_gpio_init);
+
+static void __exit wm831x_gpio_exit(void)
+{
+ platform_driver_unregister(&wm831x_gpio_driver);
+}
+module_exit(wm831x_gpio_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("GPIO interface for WM831x PMICs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-gpio");
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 461abb1..ed7711d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -946,6 +946,27 @@ config SENSORS_W83627EHF
This driver can also be built as a module. If so, the module
will be called w83627ehf.
+config SENSORS_WM831X
+ tristate "WM831x PMICs"
+ depends on MFD_WM831X
+ help
+ If you say yes here you get support for the hardware
+ monitoring functionality of the Wolfson Microelectronics
+ WM831x series of PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called wm831x-hwmon.
+
+config SENSORS_WM8350
+ tristate "Wolfson Microelectronics WM835x"
+ depends on MFD_WM8350
+ help
+ If you say yes here you get support for the hardware
+ monitoring features of the WM835x series of PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called wm8350-hwmon.
+
config SENSORS_ULTRA45
tristate "Sun Ultra45 PIC16F747"
depends on SPARC64
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2e54788..bcf73a9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -93,6 +93,8 @@ obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
+obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
+obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/hwmon/wm831x-hwmon.c b/drivers/hwmon/wm831x-hwmon.c
new file mode 100644
index 0000000..c16e9e7
--- /dev/null
+++ b/drivers/hwmon/wm831x-hwmon.c
@@ -0,0 +1,226 @@
+/*
+ * drivers/hwmon/wm831x-hwmon.c - Wolfson Microelectronics WM831x PMIC
+ * hardware monitoring features.
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/auxadc.h>
+
+struct wm831x_hwmon {
+ struct wm831x *wm831x;
+ struct device *classdev;
+};
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "wm831x\n");
+}
+
+static const char *input_names[] = {
+ [WM831X_AUX_SYSVDD] = "SYSVDD",
+ [WM831X_AUX_USB] = "USB",
+ [WM831X_AUX_BKUP_BATT] = "Backup battery",
+ [WM831X_AUX_BATT] = "Battery",
+ [WM831X_AUX_WALL] = "WALL",
+ [WM831X_AUX_CHIP_TEMP] = "PMIC",
+ [WM831X_AUX_BATT_TEMP] = "Battery",
+};
+
+
+static ssize_t show_voltage(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wm831x_hwmon *hwmon = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(attr)->index;
+ int ret;
+
+ ret = wm831x_auxadc_read_uv(hwmon->wm831x, channel);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret, 1000));
+}
+
+static ssize_t show_chip_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wm831x_hwmon *hwmon = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(attr)->index;
+ int ret;
+
+ ret = wm831x_auxadc_read(hwmon->wm831x, channel);
+ if (ret < 0)
+ return ret;
+
+ /* Degrees celsius = (512.18-ret) / 1.0983 */
+ ret = 512180 - (ret * 1000);
+ ret = DIV_ROUND_CLOSEST(ret * 10000, 10983);
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int channel = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", input_names[channel]);
+}
+
+#define WM831X_VOLTAGE(id, name) \
+ static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage, \
+ NULL, name)
+
+#define WM831X_NAMED_VOLTAGE(id, name) \
+ WM831X_VOLTAGE(id, name); \
+ static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \
+ NULL, name)
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+WM831X_VOLTAGE(0, WM831X_AUX_AUX1);
+WM831X_VOLTAGE(1, WM831X_AUX_AUX2);
+WM831X_VOLTAGE(2, WM831X_AUX_AUX3);
+WM831X_VOLTAGE(3, WM831X_AUX_AUX4);
+
+WM831X_NAMED_VOLTAGE(4, WM831X_AUX_SYSVDD);
+WM831X_NAMED_VOLTAGE(5, WM831X_AUX_USB);
+WM831X_NAMED_VOLTAGE(6, WM831X_AUX_BATT);
+WM831X_NAMED_VOLTAGE(7, WM831X_AUX_WALL);
+WM831X_NAMED_VOLTAGE(8, WM831X_AUX_BKUP_BATT);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL,
+ WM831X_AUX_CHIP_TEMP);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL,
+ WM831X_AUX_CHIP_TEMP);
+/* Report as a voltage since conversion depends on external components
+ * and that's what the ABI wants. */
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL,
+ WM831X_AUX_BATT_TEMP);
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
+ WM831X_AUX_BATT_TEMP);
+
+static struct attribute *wm831x_attributes[] = {
+ &dev_attr_name.attr,
+
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_label.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_label.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_label.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+
+ NULL
+};
+
+static const struct attribute_group wm831x_attr_group = {
+ .attrs = wm831x_attributes,
+};
+
+static int __devinit wm831x_hwmon_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_hwmon *hwmon;
+ int ret;
+
+ hwmon = kzalloc(sizeof(struct wm831x_hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ hwmon->wm831x = wm831x;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &wm831x_attr_group);
+ if (ret)
+ goto err;
+
+ hwmon->classdev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(hwmon->classdev)) {
+ ret = PTR_ERR(hwmon->classdev);
+ goto err_sysfs;
+ }
+
+ platform_set_drvdata(pdev, hwmon);
+
+ return 0;
+
+err_sysfs:
+ sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group);
+err:
+ kfree(hwmon);
+ return ret;
+}
+
+static int __devexit wm831x_hwmon_remove(struct platform_device *pdev)
+{
+ struct wm831x_hwmon *hwmon = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(hwmon->classdev);
+ sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hwmon);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_hwmon_driver = {
+ .probe = wm831x_hwmon_probe,
+ .remove = __devexit_p(wm831x_hwmon_remove),
+ .driver = {
+ .name = "wm831x-hwmon",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wm831x_hwmon_init(void)
+{
+ return platform_driver_register(&wm831x_hwmon_driver);
+}
+module_init(wm831x_hwmon_init);
+
+static void __exit wm831x_hwmon_exit(void)
+{
+ platform_driver_unregister(&wm831x_hwmon_driver);
+}
+module_exit(wm831x_hwmon_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x Hardware Monitoring");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-hwmon");
diff --git a/drivers/hwmon/wm8350-hwmon.c b/drivers/hwmon/wm8350-hwmon.c
new file mode 100644
index 0000000..1329059
--- /dev/null
+++ b/drivers/hwmon/wm8350-hwmon.c
@@ -0,0 +1,151 @@
+/*
+ * drivers/hwmon/wm8350-hwmon.c - Wolfson Microelectronics WM8350 PMIC
+ * hardware monitoring features.
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/comparator.h>
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "wm8350\n");
+}
+
+static const char *input_names[] = {
+ [WM8350_AUXADC_USB] = "USB",
+ [WM8350_AUXADC_LINE] = "Line",
+ [WM8350_AUXADC_BATT] = "Battery",
+};
+
+
+static ssize_t show_voltage(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(attr)->index;
+ int val;
+
+ val = wm8350_read_auxadc(wm8350, channel, 0, 0) * WM8350_AUX_COEFF;
+ val = DIV_ROUND_CLOSEST(val, 1000);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int channel = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", input_names[channel]);
+}
+
+#define WM8350_NAMED_VOLTAGE(id, name) \
+ static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage,\
+ NULL, name); \
+ static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \
+ NULL, name)
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+WM8350_NAMED_VOLTAGE(0, WM8350_AUXADC_USB);
+WM8350_NAMED_VOLTAGE(1, WM8350_AUXADC_BATT);
+WM8350_NAMED_VOLTAGE(2, WM8350_AUXADC_LINE);
+
+static struct attribute *wm8350_attributes[] = {
+ &dev_attr_name.attr,
+
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+
+ NULL,
+};
+
+static const struct attribute_group wm8350_attr_group = {
+ .attrs = wm8350_attributes,
+};
+
+static int __devinit wm8350_hwmon_probe(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &wm8350_attr_group);
+ if (ret)
+ goto err;
+
+ wm8350->hwmon.classdev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(wm8350->hwmon.classdev)) {
+ ret = PTR_ERR(wm8350->hwmon.classdev);
+ goto err_group;
+ }
+
+ return 0;
+
+err_group:
+ sysfs_remove_group(&pdev->dev.kobj, &wm8350_attr_group);
+err:
+ return ret;
+}
+
+static int __devexit wm8350_hwmon_remove(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(wm8350->hwmon.classdev);
+ sysfs_remove_group(&pdev->dev.kobj, &wm8350_attr_group);
+
+ return 0;
+}
+
+static struct platform_driver wm8350_hwmon_driver = {
+ .probe = wm8350_hwmon_probe,
+ .remove = __devexit_p(wm8350_hwmon_remove),
+ .driver = {
+ .name = "wm8350-hwmon",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wm8350_hwmon_init(void)
+{
+ return platform_driver_register(&wm8350_hwmon_driver);
+}
+module_init(wm8350_hwmon_init);
+
+static void __exit wm8350_hwmon_exit(void)
+{
+ platform_driver_unregister(&wm8350_hwmon_driver);
+}
+module_exit(wm8350_hwmon_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8350 Hardware Monitoring");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-hwmon");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index cbe21bc..1a50be3 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -279,4 +279,24 @@ config INPUT_BFIN_ROTARY
To compile this driver as a module, choose M here: the
module will be called bfin-rotary.
+config INPUT_WM831X_ON
+ tristate "WM831X ON pin"
+ depends on MFD_WM831X
+ help
+ Support the ON pin of WM831X PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called wm831x_on.
+
+config INPUT_PCAP
+ tristate "Motorola EZX PCAP misc input events"
+ depends on EZX_PCAP
+ help
+ Say Y here if you want to use Power key and Headphone button
+ on Motorola EZX phones.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcap_keys.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 79c1e9a..bf4db62 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
+obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
@@ -26,4 +27,6 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
+obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
+
diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c
new file mode 100644
index 0000000..7ea9693
--- /dev/null
+++ b/drivers/input/misc/pcap_keys.c
@@ -0,0 +1,144 @@
+/*
+ * Input driver for PCAP events:
+ * * Power key
+ * * Headphone button
+ *
+ * Copyright (c) 2008,2009 Ilya Petrov <ilya.muromec@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/ezx-pcap.h>
+
+struct pcap_keys {
+ struct pcap_chip *pcap;
+ struct input_dev *input;
+};
+
+/* PCAP2 interrupts us on keypress */
+static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys)
+{
+ struct pcap_keys *pcap_keys = _pcap_keys;
+ int pirq = irq_to_pcap(pcap_keys->pcap, irq);
+ u32 pstat;
+
+ ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat);
+ pstat &= 1 << pirq;
+
+ switch (pirq) {
+ case PCAP_IRQ_ONOFF:
+ input_report_key(pcap_keys->input, KEY_POWER, !pstat);
+ break;
+ case PCAP_IRQ_MIC:
+ input_report_key(pcap_keys->input, KEY_HP, !pstat);
+ break;
+ }
+
+ input_sync(pcap_keys->input);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit pcap_keys_probe(struct platform_device *pdev)
+{
+ int err = -ENOMEM;
+ struct pcap_keys *pcap_keys;
+ struct input_dev *input_dev;
+
+ pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL);
+ if (!pcap_keys)
+ return err;
+
+ pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent);
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ pcap_keys->input = input_dev;
+
+ platform_set_drvdata(pdev, pcap_keys);
+ input_dev->name = pdev->name;
+ input_dev->phys = "pcap-keys/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &pdev->dev;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(KEY_POWER, input_dev->keybit);
+ __set_bit(KEY_HP, input_dev->keybit);
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto fail_allocate;
+
+ err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF),
+ pcap_keys_handler, 0, "Power key", pcap_keys);
+ if (err)
+ goto fail_register;
+
+ err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC),
+ pcap_keys_handler, 0, "Headphone button", pcap_keys);
+ if (err)
+ goto fail_pwrkey;
+
+ return 0;
+
+fail_pwrkey:
+ free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
+fail_register:
+ input_unregister_device(input_dev);
+ goto fail;
+fail_allocate:
+ input_free_device(input_dev);
+fail:
+ kfree(pcap_keys);
+ return err;
+}
+
+static int __devexit pcap_keys_remove(struct platform_device *pdev)
+{
+ struct pcap_keys *pcap_keys = platform_get_drvdata(pdev);
+
+ free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
+ free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys);
+
+ input_unregister_device(pcap_keys->input);
+ kfree(pcap_keys);
+
+ return 0;
+}
+
+static struct platform_driver pcap_keys_device_driver = {
+ .probe = pcap_keys_probe,
+ .remove = __devexit_p(pcap_keys_remove),
+ .driver = {
+ .name = "pcap-keys",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init pcap_keys_init(void)
+{
+ return platform_driver_register(&pcap_keys_device_driver);
+};
+
+static void __exit pcap_keys_exit(void)
+{
+ platform_driver_unregister(&pcap_keys_device_driver);
+};
+
+module_init(pcap_keys_init);
+module_exit(pcap_keys_exit);
+
+MODULE_DESCRIPTION("Motorola PCAP2 input events driver");
+MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcap_keys");
diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c
new file mode 100644
index 0000000..ba4f5dd
--- /dev/null
+++ b/drivers/input/misc/wm831x-on.c
@@ -0,0 +1,163 @@
+/**
+ * wm831x-on.c - WM831X ON pin driver
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics plc
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/wm831x/core.h>
+
+struct wm831x_on {
+ struct input_dev *dev;
+ struct delayed_work work;
+ struct wm831x *wm831x;
+};
+
+/*
+ * The chip gives us an interrupt when the ON pin is asserted but we
+ * then need to poll to see when the pin is deasserted.
+ */
+static void wm831x_poll_on(struct work_struct *work)
+{
+ struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on,
+ work.work);
+ struct wm831x *wm831x = wm831x_on->wm831x;
+ int poll, ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL);
+ if (ret >= 0) {
+ poll = !(ret & WM831X_ON_PIN_STS);
+
+ input_report_key(wm831x_on->dev, KEY_POWER, poll);
+ input_sync(wm831x_on->dev);
+ } else {
+ dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret);
+ poll = 1;
+ }
+
+ if (poll)
+ schedule_delayed_work(&wm831x_on->work, 100);
+}
+
+static irqreturn_t wm831x_on_irq(int irq, void *data)
+{
+ struct wm831x_on *wm831x_on = data;
+
+ schedule_delayed_work(&wm831x_on->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit wm831x_on_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_on *wm831x_on;
+ int irq = platform_get_irq(pdev, 0);
+ int ret;
+
+ wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL);
+ if (!wm831x_on) {
+ dev_err(&pdev->dev, "Can't allocate data\n");
+ return -ENOMEM;
+ }
+
+ wm831x_on->wm831x = wm831x;
+ INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
+
+ wm831x_on->dev = input_allocate_device();
+ if (!wm831x_on->dev) {
+ dev_err(&pdev->dev, "Can't allocate input dev\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY);
+ wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+ wm831x_on->dev->name = "wm831x_on";
+ wm831x_on->dev->phys = "wm831x_on/input0";
+ wm831x_on->dev->dev.parent = &pdev->dev;
+
+ ret = wm831x_request_irq(wm831x, irq, wm831x_on_irq,
+ IRQF_TRIGGER_RISING, "wm831x_on", wm831x_on);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret);
+ goto err_input_dev;
+ }
+ ret = input_register_device(wm831x_on->dev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret);
+ goto err_irq;
+ }
+
+ platform_set_drvdata(pdev, wm831x_on);
+
+ return 0;
+
+err_irq:
+ wm831x_free_irq(wm831x, irq, NULL);
+err_input_dev:
+ input_free_device(wm831x_on->dev);
+err:
+ kfree(wm831x_on);
+ return ret;
+}
+
+static int __devexit wm831x_on_remove(struct platform_device *pdev)
+{
+ struct wm831x_on *wm831x_on = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ wm831x_free_irq(wm831x_on->wm831x, irq, wm831x_on);
+ cancel_delayed_work_sync(&wm831x_on->work);
+ input_unregister_device(wm831x_on->dev);
+ kfree(wm831x_on);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_on_driver = {
+ .probe = wm831x_on_probe,
+ .remove = __devexit_p(wm831x_on_remove),
+ .driver = {
+ .name = "wm831x-on",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wm831x_on_init(void)
+{
+ return platform_driver_register(&wm831x_on_driver);
+}
+module_init(wm831x_on_init);
+
+static void __exit wm831x_on_exit(void)
+{
+ platform_driver_unregister(&wm831x_on_driver);
+}
+module_exit(wm831x_on_exit);
+
+MODULE_ALIAS("platform:wm831x-on");
+MODULE_DESCRIPTION("WM831x ON pin");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 87a1ae6..ab02d72 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -510,4 +510,13 @@ config TOUCHSCREEN_W90X900
To compile this driver as a module, choose M here: the
module will be called w90p910_ts.
+config TOUCHSCREEN_PCAP
+ tristate "Motorola PCAP touchscreen"
+ depends on EZX_PCAP
+ help
+ Say Y here if you have a Motorola EZX telephone and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcap_ts.
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 3e1c5e0..4599bf7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -40,3 +40,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c
new file mode 100644
index 0000000..67fcd33
--- /dev/null
+++ b/drivers/input/touchscreen/pcap_ts.c
@@ -0,0 +1,271 @@
+/*
+ * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform.
+ *
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/ezx-pcap.h>
+
+struct pcap_ts {
+ struct pcap_chip *pcap;
+ struct input_dev *input;
+ struct delayed_work work;
+ u16 x, y;
+ u16 pressure;
+ u8 read_state;
+};
+
+#define SAMPLE_DELAY 20 /* msecs */
+
+#define X_AXIS_MIN 0
+#define X_AXIS_MAX 1023
+#define Y_AXIS_MAX X_AXIS_MAX
+#define Y_AXIS_MIN X_AXIS_MIN
+#define PRESSURE_MAX X_AXIS_MAX
+#define PRESSURE_MIN X_AXIS_MIN
+
+static void pcap_ts_read_xy(void *data, u16 res[2])
+{
+ struct pcap_ts *pcap_ts = data;
+
+ switch (pcap_ts->read_state) {
+ case PCAP_ADC_TS_M_PRESSURE:
+ /* pressure reading is unreliable */
+ if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX)
+ pcap_ts->pressure = res[0];
+ pcap_ts->read_state = PCAP_ADC_TS_M_XY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ break;
+ case PCAP_ADC_TS_M_XY:
+ pcap_ts->y = res[0];
+ pcap_ts->x = res[1];
+ if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX ||
+ pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) {
+ /* pen has been released */
+ input_report_abs(pcap_ts->input, ABS_PRESSURE, 0);
+ input_report_key(pcap_ts->input, BTN_TOUCH, 0);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ } else {
+ /* pen is touching the screen */
+ input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x);
+ input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y);
+ input_report_key(pcap_ts->input, BTN_TOUCH, 1);
+ input_report_abs(pcap_ts->input, ABS_PRESSURE,
+ pcap_ts->pressure);
+
+ /* switch back to pressure read mode */
+ pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+ schedule_delayed_work(&pcap_ts->work,
+ msecs_to_jiffies(SAMPLE_DELAY));
+ }
+ input_sync(pcap_ts->input);
+ break;
+ default:
+ dev_warn(&pcap_ts->input->dev,
+ "pcap_ts: Warning, unhandled read_state %d\n",
+ pcap_ts->read_state);
+ break;
+ }
+}
+
+static void pcap_ts_work(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
+ u8 ch[2];
+
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+ if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY)
+ return;
+
+ /* start adc conversion */
+ ch[0] = PCAP_ADC_CH_TS_X1;
+ ch[1] = PCAP_ADC_CH_TS_Y1;
+ pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch,
+ pcap_ts_read_xy, pcap_ts);
+}
+
+static irqreturn_t pcap_ts_event_touch(int pirq, void *data)
+{
+ struct pcap_ts *pcap_ts = data;
+
+ if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) {
+ pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ }
+ return IRQ_HANDLED;
+}
+
+static int pcap_ts_open(struct input_dev *dev)
+{
+ struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+
+ return 0;
+}
+
+static void pcap_ts_close(struct input_dev *dev)
+{
+ struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&pcap_ts->work);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+}
+
+static int __devinit pcap_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *input_dev;
+ struct pcap_ts *pcap_ts;
+ int err = -ENOMEM;
+
+ pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL);
+ if (!pcap_ts)
+ return err;
+
+ pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, pcap_ts);
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+ pcap_ts->input = input_dev;
+ input_set_drvdata(input_dev, pcap_ts);
+
+ input_dev->name = "pcap-touchscreen";
+ input_dev->phys = "pcap_ts/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0002;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = pcap_ts_open;
+ input_dev->close = pcap_ts_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN,
+ PRESSURE_MAX, 0, 0);
+
+ err = input_register_device(pcap_ts->input);
+ if (err)
+ goto fail_allocate;
+
+ err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS),
+ pcap_ts_event_touch, 0, "Touch Screen", pcap_ts);
+ if (err)
+ goto fail_register;
+
+ return 0;
+
+fail_register:
+ input_unregister_device(input_dev);
+ goto fail;
+fail_allocate:
+ input_free_device(input_dev);
+fail:
+ kfree(pcap_ts);
+
+ return err;
+}
+
+static int __devexit pcap_ts_remove(struct platform_device *pdev)
+{
+ struct pcap_ts *pcap_ts = platform_get_drvdata(pdev);
+
+ free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts);
+ cancel_delayed_work_sync(&pcap_ts->work);
+
+ input_unregister_device(pcap_ts->input);
+
+ kfree(pcap_ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcap_ts_suspend(struct device *dev)
+{
+ struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+ pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR);
+ return 0;
+}
+
+static int pcap_ts_resume(struct device *dev)
+{
+ struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+ return 0;
+}
+
+static struct dev_pm_ops pcap_ts_pm_ops = {
+ .suspend = pcap_ts_suspend,
+ .resume = pcap_ts_resume,
+};
+#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops)
+#else
+#define PCAP_TS_PM_OPS NULL
+#endif
+
+static struct platform_driver pcap_ts_driver = {
+ .probe = pcap_ts_probe,
+ .remove = __devexit_p(pcap_ts_remove),
+ .driver = {
+ .name = "pcap-ts",
+ .owner = THIS_MODULE,
+ .pm = PCAP_TS_PM_OPS,
+ },
+};
+
+static int __init pcap_ts_init(void)
+{
+ return platform_driver_register(&pcap_ts_driver);
+}
+
+static void __exit pcap_ts_exit(void)
+{
+ platform_driver_unregister(&pcap_ts_driver);
+}
+
+module_init(pcap_ts_init);
+module_exit(pcap_ts_exit);
+
+MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");
+MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcap_ts");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 491ac0f..570be13 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -108,6 +108,19 @@ config TWL4030_CORE
high speed USB OTG transceiver, an audio codec (on most
versions) and many other features.
+config TWL4030_POWER
+ bool "Support power resources on TWL4030 family chips"
+ depends on TWL4030_CORE && ARM
+ help
+ Say yes here if you want to use the power resources on the
+ TWL4030 family chips. Most of these resources are regulators,
+ which have a separate driver; some are control signals, such
+ as clock request handshaking.
+
+ This driver uses board-specific data to initialize the resources
+ and load scripts controling which resources are switched off/on
+ or reset when a sleep, wakeup or warm reset event occurs.
+
config MFD_TMIO
bool
default n
@@ -157,6 +170,16 @@ config MFD_WM8400
the device, additional drivers must be enabled in order to use
the functionality of the device.
+config MFD_WM831X
+ tristate "Support Wolfson Microelectronics WM831x PMICs"
+ select MFD_CORE
+ depends on I2C
+ help
+ Support for the Wolfson Microelecronics WM831x PMICs. This
+ driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config MFD_WM8350
tristate
@@ -228,6 +251,16 @@ config MFD_PCF50633
facilities, and registers devices for the various functions
so that function-specific drivers can bind to them.
+config MFD_MC13783
+ tristate "Support Freescale MC13783"
+ depends on SPI_MASTER
+ select MFD_CORE
+ help
+ Support for the Freescale (Atlas) MC13783 PMIC and audio CODEC.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config PCF50633_ADC
tristate "Support for NXP PCF50633 ADC"
depends on MFD_PCF50633
@@ -256,6 +289,15 @@ config AB3100_CORE
LEDs, vibrator, system power and temperature, power management
and ALSA sound.
+config AB3100_OTP
+ tristate "ST-Ericsson AB3100 OTP functions"
+ depends on AB3100_CORE
+ default y if AB3100_CORE
+ help
+ Select this to enable the AB3100 Mixed Signal IC OTP (one-time
+ programmable memory) support. This exposes a sysfs file to read
+ out OTP values.
+
config EZX_PCAP
bool "PCAP Support"
depends on GENERIC_HARDIRQS && SPI_MASTER
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 6f8a9a1af..f3b277b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -15,6 +15,8 @@ obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
+wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
+obj-$(CONFIG_MFD_WM831X) += wm831x.o
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
@@ -23,6 +25,9 @@ obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
+obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
+
+obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
@@ -44,3 +49,4 @@ obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
+obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index 13e7d7b..c533f86 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -14,7 +14,6 @@
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/interrupt.h>
-#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
@@ -77,7 +76,7 @@ u8 ab3100_get_chip_type(struct ab3100 *ab3100)
}
EXPORT_SYMBOL(ab3100_get_chip_type);
-int ab3100_set_register(struct ab3100 *ab3100, u8 reg, u8 regval)
+int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
{
u8 regandval[2] = {reg, regval};
int err;
@@ -107,9 +106,10 @@ int ab3100_set_register(struct ab3100 *ab3100, u8 reg, u8 regval)
err = 0;
}
mutex_unlock(&ab3100->access_mutex);
- return 0;
+ return err;
}
-EXPORT_SYMBOL(ab3100_set_register);
+EXPORT_SYMBOL(ab3100_set_register_interruptible);
+
/*
* The test registers exist at an I2C bus address up one
@@ -118,7 +118,7 @@ EXPORT_SYMBOL(ab3100_set_register);
* anyway. It's currently only used from this file so declare
* it static and do not export.
*/
-static int ab3100_set_test_register(struct ab3100 *ab3100,
+static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 regval)
{
u8 regandval[2] = {reg, regval};
@@ -148,7 +148,8 @@ static int ab3100_set_test_register(struct ab3100 *ab3100,
return err;
}
-int ab3100_get_register(struct ab3100 *ab3100, u8 reg, u8 *regval)
+
+int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
{
int err;
@@ -202,9 +203,10 @@ int ab3100_get_register(struct ab3100 *ab3100, u8 reg, u8 *regval)
mutex_unlock(&ab3100->access_mutex);
return err;
}
-EXPORT_SYMBOL(ab3100_get_register);
+EXPORT_SYMBOL(ab3100_get_register_interruptible);
-int ab3100_get_register_page(struct ab3100 *ab3100,
+
+int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
u8 first_reg, u8 *regvals, u8 numregs)
{
int err;
@@ -258,9 +260,10 @@ int ab3100_get_register_page(struct ab3100 *ab3100,
mutex_unlock(&ab3100->access_mutex);
return err;
}
-EXPORT_SYMBOL(ab3100_get_register_page);
+EXPORT_SYMBOL(ab3100_get_register_page_interruptible);
+
-int ab3100_mask_and_set_register(struct ab3100 *ab3100,
+int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 andmask, u8 ormask)
{
u8 regandval[2] = {reg, 0};
@@ -328,7 +331,8 @@ int ab3100_mask_and_set_register(struct ab3100 *ab3100,
mutex_unlock(&ab3100->access_mutex);
return err;
}
-EXPORT_SYMBOL(ab3100_mask_and_set_register);
+EXPORT_SYMBOL(ab3100_mask_and_set_register_interruptible);
+
/*
* Register a simple callback for handling any AB3100 events.
@@ -371,7 +375,7 @@ static void ab3100_work(struct work_struct *work)
u32 fatevent;
int err;
- err = ab3100_get_register_page(ab3100, AB3100_EVENTA1,
+ err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1,
event_regs, 3);
if (err)
goto err_event_wq;
@@ -417,7 +421,7 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
* stuff and we will re-enable the interrupts once th
* worker has finished.
*/
- disable_irq(ab3100->i2c_client->irq);
+ disable_irq_nosync(irq);
schedule_work(&ab3100->work);
return IRQ_HANDLED;
}
@@ -435,7 +439,7 @@ static int ab3100_registers_print(struct seq_file *s, void *p)
seq_printf(s, "AB3100 registers:\n");
for (reg = 0; reg < 0xff; reg++) {
- ab3100_get_register(ab3100, reg, &value);
+ ab3100_get_register_interruptible(ab3100, reg, &value);
seq_printf(s, "[0x%x]: 0x%x\n", reg, value);
}
return 0;
@@ -465,14 +469,14 @@ static int ab3100_get_set_reg_open_file(struct inode *inode, struct file *file)
return 0;
}
-static int ab3100_get_set_reg(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
+static ssize_t ab3100_get_set_reg(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
struct ab3100_get_set_reg_priv *priv = file->private_data;
struct ab3100 *ab3100 = priv->ab3100;
char buf[32];
- int buf_size;
+ ssize_t buf_size;
int regp;
unsigned long user_reg;
int err;
@@ -515,7 +519,7 @@ static int ab3100_get_set_reg(struct file *file,
u8 reg = (u8) user_reg;
u8 regvalue;
- ab3100_get_register(ab3100, reg, &regvalue);
+ ab3100_get_register_interruptible(ab3100, reg, &regvalue);
dev_info(ab3100->dev,
"debug read AB3100 reg[0x%02x]: 0x%02x\n",
@@ -547,8 +551,8 @@ static int ab3100_get_set_reg(struct file *file,
return -EINVAL;
value = (u8) user_value;
- ab3100_set_register(ab3100, reg, value);
- ab3100_get_register(ab3100, reg, &regvalue);
+ ab3100_set_register_interruptible(ab3100, reg, value);
+ ab3100_get_register_interruptible(ab3100, reg, &regvalue);
dev_info(ab3100->dev,
"debug write reg[0x%02x] with 0x%02x, "
@@ -662,7 +666,7 @@ ab3100_init_settings[] = {
.setting = 0x01
}, {
.abreg = AB3100_IMRB1,
- .setting = 0xFF
+ .setting = 0xBF
}, {
.abreg = AB3100_IMRB2,
.setting = 0xFF
@@ -696,7 +700,7 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
int i;
for (i = 0; i < ARRAY_SIZE(ab3100_init_settings); i++) {
- err = ab3100_set_register(ab3100,
+ err = ab3100_set_register_interruptible(ab3100,
ab3100_init_settings[i].abreg,
ab3100_init_settings[i].setting);
if (err)
@@ -705,14 +709,14 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
/*
* Special trick to make the AB3100 use the 32kHz clock (RTC)
- * bit 3 in test registe 0x02 is a special, undocumented test
+ * bit 3 in test register 0x02 is a special, undocumented test
* register bit that only exist in AB3100 P1E
*/
if (ab3100->chip_id == 0xc4) {
dev_warn(ab3100->dev,
"AB3100 P1E variant detected, "
"forcing chip to 32KHz\n");
- err = ab3100_set_test_register(ab3100, 0x02, 0x08);
+ err = ab3100_set_test_register_interruptible(ab3100, 0x02, 0x08);
}
exit_no_setup:
@@ -833,6 +837,8 @@ static int __init ab3100_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ab3100 *ab3100;
+ struct ab3100_platform_data *ab3100_plf_data =
+ client->dev.platform_data;
int err;
int i;
@@ -852,8 +858,8 @@ static int __init ab3100_probe(struct i2c_client *client,
i2c_set_clientdata(client, ab3100);
/* Read chip ID register */
- err = ab3100_get_register(ab3100, AB3100_CID,
- &ab3100->chip_id);
+ err = ab3100_get_register_interruptible(ab3100, AB3100_CID,
+ &ab3100->chip_id);
if (err) {
dev_err(&client->dev,
"could not communicate with the AB3100 analog "
@@ -916,6 +922,8 @@ static int __init ab3100_probe(struct i2c_client *client,
for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) {
ab3100_platform_devs[i]->dev.parent =
&client->dev;
+ ab3100_platform_devs[i]->dev.platform_data =
+ ab3100_plf_data;
platform_set_drvdata(ab3100_platform_devs[i], ab3100);
}
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
new file mode 100644
index 0000000..0499b20
--- /dev/null
+++ b/drivers/mfd/ab3100-otp.c
@@ -0,0 +1,268 @@
+/*
+ * drivers/mfd/ab3100_otp.c
+ *
+ * Copyright (C) 2007-2009 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * Driver to read out OTP from the AB3100 Mixed-signal circuit
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ab3100.h>
+#include <linux/debugfs.h>
+
+/* The OTP registers */
+#define AB3100_OTP0 0xb0
+#define AB3100_OTP1 0xb1
+#define AB3100_OTP2 0xb2
+#define AB3100_OTP3 0xb3
+#define AB3100_OTP4 0xb4
+#define AB3100_OTP5 0xb5
+#define AB3100_OTP6 0xb6
+#define AB3100_OTP7 0xb7
+#define AB3100_OTPP 0xbf
+
+/**
+ * struct ab3100_otp
+ * @dev containing device
+ * @ab3100 a pointer to the parent ab3100 device struct
+ * @locked whether the OTP is locked, after locking, no more bits
+ * can be changed but before locking it is still possible
+ * to change bits from 1->0.
+ * @freq clocking frequency for the OTP, this frequency is either
+ * 32768Hz or 1MHz/30
+ * @paf product activation flag, indicates whether this is a real
+ * product (paf true) or a lab board etc (paf false)
+ * @imeich if this is set it is possible to override the
+ * IMEI number found in the tac, fac and svn fields with
+ * (secured) software
+ * @cid customer ID
+ * @tac type allocation code of the IMEI
+ * @fac final assembly code of the IMEI
+ * @svn software version number of the IMEI
+ * @debugfs a debugfs file used when dumping to file
+ */
+struct ab3100_otp {
+ struct device *dev;
+ struct ab3100 *ab3100;
+ bool locked;
+ u32 freq;
+ bool paf;
+ bool imeich;
+ u16 cid:14;
+ u32 tac:20;
+ u8 fac;
+ u32 svn:20;
+ struct dentry *debugfs;
+};
+
+static int __init ab3100_otp_read(struct ab3100_otp *otp)
+{
+ struct ab3100 *ab = otp->ab3100;
+ u8 otpval[8];
+ u8 otpp;
+ int err;
+
+ err = ab3100_get_register_interruptible(ab, AB3100_OTPP, &otpp);
+ if (err) {
+ dev_err(otp->dev, "unable to read OTPP register\n");
+ return err;
+ }
+
+ err = ab3100_get_register_page_interruptible(ab, AB3100_OTP0,
+ otpval, 8);
+ if (err) {
+ dev_err(otp->dev, "unable to read OTP register page\n");
+ return err;
+ }
+
+ /* Cache OTP properties, they never change by nature */
+ otp->locked = (otpp & 0x80);
+ otp->freq = (otpp & 0x40) ? 32768 : 34100;
+ otp->paf = (otpval[1] & 0x80);
+ otp->imeich = (otpval[1] & 0x40);
+ otp->cid = ((otpval[1] << 8) | otpval[0]) & 0x3fff;
+ otp->tac = ((otpval[4] & 0x0f) << 16) | (otpval[3] << 8) | otpval[2];
+ otp->fac = ((otpval[5] & 0x0f) << 4) | (otpval[4] >> 4);
+ otp->svn = (otpval[7] << 12) | (otpval[6] << 4) | (otpval[5] >> 4);
+ return 0;
+}
+
+/*
+ * This is a simple debugfs human-readable file that dumps out
+ * the contents of the OTP.
+ */
+#ifdef CONFIG_DEBUGFS
+static int show_otp(struct seq_file *s, void *v)
+{
+ struct ab3100_otp *otp = s->private;
+ int err;
+
+ seq_printf(s, "OTP is %s\n", otp->locked ? "LOCKED" : "UNLOCKED");
+ seq_printf(s, "OTP clock switch startup is %uHz\n", otp->freq);
+ seq_printf(s, "PAF is %s\n", otp->paf ? "SET" : "NOT SET");
+ seq_printf(s, "IMEI is %s\n", otp->imeich ?
+ "CHANGEABLE" : "NOT CHANGEABLE");
+ seq_printf(s, "CID: 0x%04x (decimal: %d)\n", otp->cid, otp->cid);
+ seq_printf(s, "IMEI: %u-%u-%u\n", otp->tac, otp->fac, otp->svn);
+ return 0;
+}
+
+static int ab3100_otp_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab3100_otp_show, inode->i_private);
+}
+
+static const struct file_operations ab3100_otp_operations = {
+ .open = ab3100_otp_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init ab3100_otp_init_debugfs(struct device *dev,
+ struct ab3100_otp *otp)
+{
+ otp->debugfs = debugfs_create_file("ab3100_otp", S_IFREG | S_IRUGO,
+ NULL, otp,
+ &ab3100_otp_operations);
+ if (!otp->debugfs) {
+ dev_err(dev, "AB3100 debugfs OTP file registration failed!\n");
+ return err;
+ }
+}
+
+static void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
+{
+ debugfs_remove_file(otp->debugfs);
+}
+#else
+/* Compile this out if debugfs not selected */
+static inline int __init ab3100_otp_init_debugfs(struct device *dev,
+ struct ab3100_otp *otp)
+{
+ return 0;
+}
+
+static inline void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
+{
+}
+#endif
+
+#define SHOW_AB3100_ATTR(name) \
+static ssize_t ab3100_otp_##name##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{\
+ struct ab3100_otp *otp = dev_get_drvdata(dev); \
+ return sprintf(buf, "%u\n", otp->name); \
+}
+
+SHOW_AB3100_ATTR(locked)
+SHOW_AB3100_ATTR(freq)
+SHOW_AB3100_ATTR(paf)
+SHOW_AB3100_ATTR(imeich)
+SHOW_AB3100_ATTR(cid)
+SHOW_AB3100_ATTR(fac)
+SHOW_AB3100_ATTR(tac)
+SHOW_AB3100_ATTR(svn)
+
+static struct device_attribute ab3100_otp_attrs[] = {
+ __ATTR(locked, S_IRUGO, ab3100_otp_locked_show, NULL),
+ __ATTR(freq, S_IRUGO, ab3100_otp_freq_show, NULL),
+ __ATTR(paf, S_IRUGO, ab3100_otp_paf_show, NULL),
+ __ATTR(imeich, S_IRUGO, ab3100_otp_imeich_show, NULL),
+ __ATTR(cid, S_IRUGO, ab3100_otp_cid_show, NULL),
+ __ATTR(fac, S_IRUGO, ab3100_otp_fac_show, NULL),
+ __ATTR(tac, S_IRUGO, ab3100_otp_tac_show, NULL),
+ __ATTR(svn, S_IRUGO, ab3100_otp_svn_show, NULL),
+};
+
+static int __init ab3100_otp_probe(struct platform_device *pdev)
+{
+ struct ab3100_otp *otp;
+ int err = 0;
+ int i;
+
+ otp = kzalloc(sizeof(struct ab3100_otp), GFP_KERNEL);
+ if (!otp) {
+ dev_err(&pdev->dev, "could not allocate AB3100 OTP device\n");
+ return -ENOMEM;
+ }
+ otp->dev = &pdev->dev;
+
+ /* Replace platform data coming in with a local struct */
+ otp->ab3100 = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, otp);
+
+ err = ab3100_otp_read(otp);
+ if (err)
+ return err;
+
+ dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
+
+ /* sysfs entries */
+ for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++) {
+ err = device_create_file(&pdev->dev,
+ &ab3100_otp_attrs[i]);
+ if (err)
+ goto out_no_sysfs;
+ }
+
+ /* debugfs entries */
+ err = ab3100_otp_init_debugfs(&pdev->dev, otp);
+ if (err)
+ goto out_no_debugfs;
+
+ return 0;
+
+out_no_sysfs:
+ for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
+ device_remove_file(&pdev->dev,
+ &ab3100_otp_attrs[i]);
+out_no_debugfs:
+ kfree(otp);
+ return err;
+}
+
+static int __exit ab3100_otp_remove(struct platform_device *pdev)
+{
+ struct ab3100_otp *otp = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
+ device_remove_file(&pdev->dev,
+ &ab3100_otp_attrs[i]);
+ ab3100_otp_exit_debugfs(otp);
+ kfree(otp);
+ return 0;
+}
+
+static struct platform_driver ab3100_otp_driver = {
+ .driver = {
+ .name = "ab3100-otp",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ab3100_otp_remove),
+};
+
+static int __init ab3100_otp_init(void)
+{
+ return platform_driver_probe(&ab3100_otp_driver,
+ ab3100_otp_probe);
+}
+
+static void __exit ab3100_otp_exit(void)
+{
+ platform_driver_unregister(&ab3100_otp_driver);
+}
+
+module_init(ab3100_otp_init);
+module_exit(ab3100_otp_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("AB3100 OTP Readout Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c
index 5b6e58a..3d4a861 100644
--- a/drivers/mfd/dm355evm_msp.c
+++ b/drivers/mfd/dm355evm_msp.c
@@ -107,8 +107,16 @@ static const u8 msp_gpios[] = {
MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1),
MSP_GPIO(4, SWITCH1),
/* switches on MMC/SD sockets */
- MSP_GPIO(1, SDMMC), MSP_GPIO(2, SDMMC), /* mmc0 WP, nCD */
- MSP_GPIO(3, SDMMC), MSP_GPIO(4, SDMMC), /* mmc1 WP, nCD */
+ /*
+ * Note: EVMDM355_ECP_VA4.pdf suggests that Bit 2 and 4 should be
+ * checked for card detection. However on the EVM bit 1 and 3 gives
+ * this status, for 0 and 1 instance respectively. The pdf also
+ * suggests that Bit 1 and 3 should be checked for write protection.
+ * However on the EVM bit 2 and 4 gives this status,for 0 and 1
+ * instance respectively.
+ */
+ MSP_GPIO(2, SDMMC), MSP_GPIO(1, SDMMC), /* mmc0 WP, nCD */
+ MSP_GPIO(4, SDMMC), MSP_GPIO(3, SDMMC), /* mmc1 WP, nCD */
};
#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3)
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
index c1de4af..016be49 100644
--- a/drivers/mfd/ezx-pcap.c
+++ b/drivers/mfd/ezx-pcap.c
@@ -17,6 +17,7 @@
#include <linux/irq.h>
#include <linux/mfd/ezx-pcap.h>
#include <linux/spi/spi.h>
+#include <linux/gpio.h>
#define PCAP_ADC_MAXQ 8
struct pcap_adc_request {
@@ -106,11 +107,35 @@ int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value)
}
EXPORT_SYMBOL_GPL(ezx_pcap_read);
+int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val)
+{
+ int ret;
+ u32 tmp = PCAP_REGISTER_READ_OP_BIT |
+ (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+
+ mutex_lock(&pcap->io_mutex);
+ ret = ezx_pcap_putget(pcap, &tmp);
+ if (ret)
+ goto out_unlock;
+
+ tmp &= (PCAP_REGISTER_VALUE_MASK & ~mask);
+ tmp |= (val & mask) | PCAP_REGISTER_WRITE_OP_BIT |
+ (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+
+ ret = ezx_pcap_putget(pcap, &tmp);
+out_unlock:
+ mutex_unlock(&pcap->io_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_set_bits);
+
/* IRQ */
-static inline unsigned int irq2pcap(struct pcap_chip *pcap, int irq)
+int irq_to_pcap(struct pcap_chip *pcap, int irq)
{
- return 1 << (irq - pcap->irq_base);
+ return irq - pcap->irq_base;
}
+EXPORT_SYMBOL_GPL(irq_to_pcap);
int pcap_to_irq(struct pcap_chip *pcap, int irq)
{
@@ -122,7 +147,7 @@ static void pcap_mask_irq(unsigned int irq)
{
struct pcap_chip *pcap = get_irq_chip_data(irq);
- pcap->msr |= irq2pcap(pcap, irq);
+ pcap->msr |= 1 << irq_to_pcap(pcap, irq);
queue_work(pcap->workqueue, &pcap->msr_work);
}
@@ -130,7 +155,7 @@ static void pcap_unmask_irq(unsigned int irq)
{
struct pcap_chip *pcap = get_irq_chip_data(irq);
- pcap->msr &= ~irq2pcap(pcap, irq);
+ pcap->msr &= ~(1 << irq_to_pcap(pcap, irq));
queue_work(pcap->workqueue, &pcap->msr_work);
}
@@ -154,34 +179,38 @@ static void pcap_isr_work(struct work_struct *work)
u32 msr, isr, int_sel, service;
int irq;
- ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
- ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);
+ do {
+ ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
+ ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);
- /* We cant service/ack irqs that are assigned to port 2 */
- if (!(pdata->config & PCAP_SECOND_PORT)) {
- ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
- isr &= ~int_sel;
- }
- ezx_pcap_write(pcap, PCAP_REG_ISR, isr);
+ /* We cant service/ack irqs that are assigned to port 2 */
+ if (!(pdata->config & PCAP_SECOND_PORT)) {
+ ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
+ isr &= ~int_sel;
+ }
- local_irq_disable();
- service = isr & ~msr;
+ ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
+ ezx_pcap_write(pcap, PCAP_REG_ISR, isr);
- for (irq = pcap->irq_base; service; service >>= 1, irq++) {
- if (service & 1) {
- struct irq_desc *desc = irq_to_desc(irq);
+ local_irq_disable();
+ service = isr & ~msr;
+ for (irq = pcap->irq_base; service; service >>= 1, irq++) {
+ if (service & 1) {
+ struct irq_desc *desc = irq_to_desc(irq);
- if (WARN(!desc, KERN_WARNING
- "Invalid PCAP IRQ %d\n", irq))
- break;
+ if (WARN(!desc, KERN_WARNING
+ "Invalid PCAP IRQ %d\n", irq))
+ break;
- if (desc->status & IRQ_DISABLED)
- note_interrupt(irq, desc, IRQ_NONE);
- else
- desc->handle_irq(irq, desc);
+ if (desc->status & IRQ_DISABLED)
+ note_interrupt(irq, desc, IRQ_NONE);
+ else
+ desc->handle_irq(irq, desc);
+ }
}
- }
- local_irq_enable();
+ local_irq_enable();
+ ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
+ } while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
}
static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc)
@@ -194,6 +223,19 @@ static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc)
}
/* ADC */
+void pcap_set_ts_bits(struct pcap_chip *pcap, u32 bits)
+{
+ u32 tmp;
+
+ mutex_lock(&pcap->adc_mutex);
+ ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
+ tmp &= ~(PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
+ tmp |= bits & (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
+ ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
+ mutex_unlock(&pcap->adc_mutex);
+}
+EXPORT_SYMBOL_GPL(pcap_set_ts_bits);
+
static void pcap_disable_adc(struct pcap_chip *pcap)
{
u32 tmp;
@@ -216,15 +258,16 @@ static void pcap_adc_trigger(struct pcap_chip *pcap)
mutex_unlock(&pcap->adc_mutex);
return;
}
- mutex_unlock(&pcap->adc_mutex);
-
- /* start conversion on requested bank */
- tmp = pcap->adc_queue[head]->flags | PCAP_ADC_ADEN;
+ /* start conversion on requested bank, save TS_M bits */
+ ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
+ tmp &= (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
+ tmp |= pcap->adc_queue[head]->flags | PCAP_ADC_ADEN;
if (pcap->adc_queue[head]->bank == PCAP_ADC_BANK_1)
tmp |= PCAP_ADC_AD_SEL1;
ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
+ mutex_unlock(&pcap->adc_mutex);
ezx_pcap_write(pcap, PCAP_REG_ADR, PCAP_ADR_ASC);
}
@@ -499,7 +542,7 @@ static void __exit ezx_pcap_exit(void)
spi_unregister_driver(&ezxpcap_driver);
}
-module_init(ezx_pcap_init);
+subsys_initcall(ezx_pcap_init);
module_exit(ezx_pcap_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
new file mode 100644
index 0000000..e354d29
--- /dev/null
+++ b/drivers/mfd/mc13783-core.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This code is in parts based on wm8350-core.c and pcf50633-core.c
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de
+ *
+ * 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/mfd/mc13783-private.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+
+#define MC13783_MAX_REG_NUM 0x3f
+#define MC13783_FRAME_MASK 0x00ffffff
+#define MC13783_MAX_REG_NUM 0x3f
+#define MC13783_REG_NUM_SHIFT 0x19
+#define MC13783_WRITE_BIT_SHIFT 31
+
+static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = (const void *)buf,
+ .rx_buf = buf,
+ .len = len,
+ .cs_change = 0,
+ .delay_usecs = 0,
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ if (spi_sync(spi, &m) != 0 || m.status != 0)
+ return -EINVAL;
+ return len - m.actual_length;
+}
+
+static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+{
+ unsigned int frame = 0;
+ int ret = 0;
+
+ if (reg_num > MC13783_MAX_REG_NUM)
+ return -EINVAL;
+
+ frame |= reg_num << MC13783_REG_NUM_SHIFT;
+
+ ret = spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+
+ *reg_val = frame & MC13783_FRAME_MASK;
+
+ return ret;
+}
+
+static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+{
+ unsigned int frame = 0;
+
+ if (reg_num > MC13783_MAX_REG_NUM)
+ return -EINVAL;
+
+ frame |= (1 << MC13783_WRITE_BIT_SHIFT);
+ frame |= reg_num << MC13783_REG_NUM_SHIFT;
+ frame |= reg_val & MC13783_FRAME_MASK;
+
+ return spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+}
+
+int mc13783_reg_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+{
+ int ret;
+
+ mutex_lock(&mc13783->io_lock);
+ ret = mc13783_read(mc13783, reg_num, reg_val);
+ mutex_unlock(&mc13783->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc13783_reg_read);
+
+int mc13783_reg_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+{
+ int ret;
+
+ mutex_lock(&mc13783->io_lock);
+ ret = mc13783_write(mc13783, reg_num, reg_val);
+ mutex_unlock(&mc13783->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc13783_reg_write);
+
+/**
+ * mc13783_set_bits - Bitmask write
+ *
+ * @mc13783: Pointer to mc13783 control structure
+ * @reg: Register to access
+ * @mask: Mask of bits to change
+ * @val: Value to set for masked bits
+ */
+int mc13783_set_bits(struct mc13783 *mc13783, int reg, u32 mask, u32 val)
+{
+ u32 tmp;
+ int ret;
+
+ mutex_lock(&mc13783->io_lock);
+
+ ret = mc13783_read(mc13783, reg, &tmp);
+ tmp = (tmp & ~mask) | val;
+ if (ret == 0)
+ ret = mc13783_write(mc13783, reg, tmp);
+
+ mutex_unlock(&mc13783->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc13783_set_bits);
+
+int mc13783_register_irq(struct mc13783 *mc13783, int irq,
+ void (*handler) (int, void *), void *data)
+{
+ if (irq < 0 || irq > MC13783_NUM_IRQ || !handler)
+ return -EINVAL;
+
+ if (WARN_ON(mc13783->irq_handler[irq].handler))
+ return -EBUSY;
+
+ mutex_lock(&mc13783->io_lock);
+ mc13783->irq_handler[irq].handler = handler;
+ mc13783->irq_handler[irq].data = data;
+ mutex_unlock(&mc13783->io_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mc13783_register_irq);
+
+int mc13783_free_irq(struct mc13783 *mc13783, int irq)
+{
+ if (irq < 0 || irq > MC13783_NUM_IRQ)
+ return -EINVAL;
+
+ mutex_lock(&mc13783->io_lock);
+ mc13783->irq_handler[irq].handler = NULL;
+ mutex_unlock(&mc13783->io_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mc13783_free_irq);
+
+static void mc13783_irq_work(struct work_struct *work)
+{
+ struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
+ int i;
+ unsigned int adc_sts;
+
+ /* check if the adc has finished any completion */
+ mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
+ mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
+ adc_sts & MC13783_INT_STAT_ADCDONEI);
+
+ if (adc_sts & MC13783_INT_STAT_ADCDONEI)
+ complete_all(&mc13783->adc_done);
+
+ for (i = 0; i < MC13783_NUM_IRQ; i++)
+ if (mc13783->irq_handler[i].handler)
+ mc13783->irq_handler[i].handler(i,
+ mc13783->irq_handler[i].data);
+ enable_irq(mc13783->irq);
+}
+
+static irqreturn_t mc13783_interrupt(int irq, void *dev_id)
+{
+ struct mc13783 *mc13783 = dev_id;
+
+ disable_irq_nosync(irq);
+
+ schedule_work(&mc13783->work);
+ return IRQ_HANDLED;
+}
+
+/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
+static inline void mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
+{
+ unsigned int reg_adc0, reg_adc1;
+
+ reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+ | MC13783_ADC0_TSMOD0;
+ reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
+
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+}
+
+int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+ unsigned int channel, unsigned int *sample)
+{
+ unsigned int reg_adc0, reg_adc1;
+ int i;
+
+ mutex_lock(&mc13783->adc_conv_lock);
+
+ /* set up auto incrementing anyway to make quick read */
+ reg_adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+ /* enable the adc, ignore external triggering and set ASC to trigger
+ * conversion */
+ reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
+ | MC13783_ADC1_ASC;
+
+ /* setup channel number */
+ if (channel > 7)
+ reg_adc1 |= MC13783_ADC1_ADSEL;
+
+ switch (mode) {
+ case MC13783_ADC_MODE_TS:
+ /* enables touch screen reference mode and set touchscreen mode
+ * to position mode */
+ reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+ | MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
+ reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+ case MC13783_ADC_MODE_SINGLE_CHAN:
+ reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+ reg_adc1 |= MC13783_ADC1_RAND;
+ break;
+ case MC13783_ADC_MODE_MULT_CHAN:
+ reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+
+ wait_for_completion_interruptible(&mc13783->adc_done);
+
+ for (i = 0; i < 4; i++)
+ mc13783_reg_read(mc13783, MC13783_REG_ADC_2, &sample[i]);
+
+ if (mc13783->ts_active)
+ mc13783_adc_set_ts_irq_mode(mc13783);
+
+ mutex_unlock(&mc13783->adc_conv_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
+
+void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
+{
+ mc13783->ts_active = status;
+}
+EXPORT_SYMBOL_GPL(mc13783_adc_set_ts_status);
+
+static int mc13783_check_revision(struct mc13783 *mc13783)
+{
+ u32 rev_id, rev1, rev2, finid, icid;
+
+ mc13783_read(mc13783, MC13783_REG_REVISION, &rev_id);
+
+ rev1 = (rev_id & 0x018) >> 3;
+ rev2 = (rev_id & 0x007);
+ icid = (rev_id & 0x01C0) >> 6;
+ finid = (rev_id & 0x01E00) >> 9;
+
+ /* Ver 0.2 is actually 3.2a. Report as 3.2 */
+ if ((rev1 == 0) && (rev2 == 2))
+ rev1 = 3;
+
+ if (rev1 == 0 || icid != 2) {
+ dev_err(mc13783->dev, "No MC13783 detected.\n");
+ return -ENODEV;
+ }
+
+ mc13783->revision = ((rev1 * 10) + rev2);
+ dev_info(mc13783->dev, "MC13783 Rev %d.%d FinVer %x detected\n", rev1,
+ rev2, finid);
+
+ return 0;
+}
+
+/*
+ * Register a client device. This is non-fatal since there is no need to
+ * fail the entire device init due to a single platform device failing.
+ */
+static void mc13783_client_dev_register(struct mc13783 *mc13783,
+ const char *name)
+{
+ struct mfd_cell cell = {};
+
+ cell.name = name;
+
+ mfd_add_devices(mc13783->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int __devinit mc13783_probe(struct spi_device *spi)
+{
+ struct mc13783 *mc13783;
+ struct mc13783_platform_data *pdata = spi->dev.platform_data;
+ int ret;
+
+ mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
+ if (!mc13783)
+ return -ENOMEM;
+
+ dev_set_drvdata(&spi->dev, mc13783);
+ spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+ spi->bits_per_word = 32;
+ spi_setup(spi);
+
+ mc13783->spi_device = spi;
+ mc13783->dev = &spi->dev;
+ mc13783->irq = spi->irq;
+
+ INIT_WORK(&mc13783->work, mc13783_irq_work);
+ mutex_init(&mc13783->io_lock);
+ mutex_init(&mc13783->adc_conv_lock);
+ init_completion(&mc13783->adc_done);
+
+ if (pdata) {
+ mc13783->flags = pdata->flags;
+ mc13783->regulators = pdata->regulators;
+ mc13783->num_regulators = pdata->num_regulators;
+ }
+
+ if (mc13783_check_revision(mc13783)) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* clear and mask all interrupts */
+ mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0, 0x00ffffff);
+ mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_0, 0x00ffffff);
+ mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_1, 0x00ffffff);
+ mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_1, 0x00ffffff);
+
+ /* unmask adcdone interrupts */
+ mc13783_set_bits(mc13783, MC13783_REG_INTERRUPT_MASK_0,
+ MC13783_INT_MASK_ADCDONEM, 0);
+
+ ret = request_irq(mc13783->irq, mc13783_interrupt,
+ IRQF_DISABLED | IRQF_TRIGGER_HIGH, "mc13783",
+ mc13783);
+ if (ret)
+ goto err_out;
+
+ if (mc13783->flags & MC13783_USE_CODEC)
+ mc13783_client_dev_register(mc13783, "mc13783-codec");
+ if (mc13783->flags & MC13783_USE_ADC)
+ mc13783_client_dev_register(mc13783, "mc13783-adc");
+ if (mc13783->flags & MC13783_USE_RTC)
+ mc13783_client_dev_register(mc13783, "mc13783-rtc");
+ if (mc13783->flags & MC13783_USE_REGULATOR)
+ mc13783_client_dev_register(mc13783, "mc13783-regulator");
+ if (mc13783->flags & MC13783_USE_TOUCHSCREEN)
+ mc13783_client_dev_register(mc13783, "mc13783-ts");
+
+ return 0;
+
+err_out:
+ kfree(mc13783);
+ return ret;
+}
+
+static int __devexit mc13783_remove(struct spi_device *spi)
+{
+ struct mc13783 *mc13783;
+
+ mc13783 = dev_get_drvdata(&spi->dev);
+
+ free_irq(mc13783->irq, mc13783);
+
+ mfd_remove_devices(&spi->dev);
+
+ return 0;
+}
+
+static struct spi_driver pmic_driver = {
+ .driver = {
+ .name = "mc13783",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mc13783_probe,
+ .remove = __devexit_p(mc13783_remove),
+};
+
+static int __init pmic_init(void)
+{
+ return spi_register_driver(&pmic_driver);
+}
+subsys_initcall(pmic_init);
+
+static void __exit pmic_exit(void)
+{
+ spi_unregister_driver(&pmic_driver);
+}
+module_exit(pmic_exit);
+
+MODULE_DESCRIPTION("Core/Protocol driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 54ddf37..ae15e49 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -25,7 +25,7 @@ static int mfd_add_device(struct device *parent, int id,
int ret = -ENOMEM;
int r;
- pdev = platform_device_alloc(cell->name, id);
+ pdev = platform_device_alloc(cell->name, id + cell->id);
if (!pdev)
goto fail_alloc;
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
index c2d05be..3d31e97 100644
--- a/drivers/mfd/pcf50633-adc.c
+++ b/drivers/mfd/pcf50633-adc.c
@@ -73,15 +73,10 @@ static void trigger_next_adc_job_if_any(struct pcf50633 *pcf)
struct pcf50633_adc *adc = __to_adc(pcf);
int head;
- mutex_lock(&adc->queue_mutex);
-
head = adc->queue_head;
- if (!adc->queue[head]) {
- mutex_unlock(&adc->queue_mutex);
+ if (!adc->queue[head])
return;
- }
- mutex_unlock(&adc->queue_mutex);
adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg);
}
@@ -99,16 +94,17 @@ adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
if (adc->queue[tail]) {
mutex_unlock(&adc->queue_mutex);
+ dev_err(pcf->dev, "ADC queue is full, dropping request\n");
return -EBUSY;
}
adc->queue[tail] = req;
+ if (head == tail)
+ trigger_next_adc_job_if_any(pcf);
adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
mutex_unlock(&adc->queue_mutex);
- trigger_next_adc_job_if_any(pcf);
-
return 0;
}
@@ -124,6 +120,7 @@ pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result)
int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
{
struct pcf50633_adc_request *req;
+ int err;
/* req is freed when the result is ready, in interrupt handler */
req = kzalloc(sizeof(*req), GFP_KERNEL);
@@ -136,9 +133,13 @@ int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
req->callback_param = req;
init_completion(&req->completion);
- adc_enqueue_request(pcf, req);
+ err = adc_enqueue_request(pcf, req);
+ if (err)
+ return err;
+
wait_for_completion(&req->completion);
+ /* FIXME by this time req might be already freed */
return req->result;
}
EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
@@ -159,9 +160,7 @@ int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
req->callback = callback;
req->callback_param = callback_param;
- adc_enqueue_request(pcf, req);
-
- return 0;
+ return adc_enqueue_request(pcf, req);
}
EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);
@@ -184,7 +183,7 @@ static void pcf50633_adc_irq(int irq, void *data)
struct pcf50633_adc *adc = data;
struct pcf50633 *pcf = adc->pcf;
struct pcf50633_adc_request *req;
- int head;
+ int head, res;
mutex_lock(&adc->queue_mutex);
head = adc->queue_head;
@@ -199,12 +198,13 @@ static void pcf50633_adc_irq(int irq, void *data)
adc->queue_head = (head + 1) &
(PCF50633_MAX_ADC_FIFO_DEPTH - 1);
+ res = adc_result(pcf);
+ trigger_next_adc_job_if_any(pcf);
+
mutex_unlock(&adc->queue_mutex);
- req->callback(pcf, req->callback_param, adc_result(pcf));
+ req->callback(pcf, req->callback_param, res);
kfree(req);
-
- trigger_next_adc_job_if_any(pcf);
}
static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 8d3c38b..d26d774 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -444,7 +444,7 @@ static irqreturn_t pcf50633_irq(int irq, void *data)
get_device(pcf->dev);
disable_irq_nosync(pcf->irq);
- schedule_work(&pcf->irq_work);
+ queue_work(pcf->work_queue, &pcf->irq_work);
return IRQ_HANDLED;
}
@@ -575,6 +575,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
pcf->dev = &client->dev;
pcf->i2c_client = client;
pcf->irq = client->irq;
+ pcf->work_queue = create_singlethread_workqueue("pcf50633");
INIT_WORK(&pcf->irq_work, pcf50633_irq_worker);
@@ -651,6 +652,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
return 0;
err:
+ destroy_workqueue(pcf->work_queue);
kfree(pcf);
return ret;
}
@@ -661,6 +663,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
int i;
free_irq(pcf->irq, pcf);
+ destroy_workqueue(pcf->work_queue);
platform_device_unregister(pcf->input_pdev);
platform_device_unregister(pcf->rtc_pdev);
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
index ca54996..e424cf6 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl4030-core.c
@@ -89,6 +89,12 @@
#define twl_has_madc() false
#endif
+#ifdef CONFIG_TWL4030_POWER
+#define twl_has_power() true
+#else
+#define twl_has_power() false
+#endif
+
#if defined(CONFIG_RTC_DRV_TWL4030) || defined(CONFIG_RTC_DRV_TWL4030_MODULE)
#define twl_has_rtc() true
#else
@@ -115,6 +121,12 @@
#define TWL4030_NUM_SLAVES 4
+#if defined(CONFIG_INPUT_TWL4030_PWRBUTTON) \
+ || defined(CONFIG_INPUT_TWL4030_PWBUTTON_MODULE)
+#define twl_has_pwrbutton() true
+#else
+#define twl_has_pwrbutton() false
+#endif
/* Base Address defns for twl4030_map[] */
@@ -538,6 +550,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
+ if (twl_has_pwrbutton()) {
+ child = add_child(1, "twl4030_pwrbutton",
+ NULL, 0, true, pdata->irq_base + 8 + 0, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
if (twl_has_regulator()) {
/*
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
@@ -788,6 +807,10 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* setup clock framework */
clocks_init(&client->dev);
+ /* load power event scripts */
+ if (twl_has_power() && pdata->power)
+ twl4030_power_init(pdata->power);
+
/* Maybe init the T2 Interrupt subsystem */
if (client->irq
&& pdata->irq_base
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 7d43083..fb194fe 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -424,7 +424,7 @@ static void twl4030_sih_do_edge(struct work_struct *work)
/* see what work we have */
spin_lock_irq(&sih_agent_lock);
edge_change = agent->edge_change;
- agent->edge_change = 0;;
+ agent->edge_change = 0;
sih = edge_change ? agent->sih : NULL;
spin_unlock_irq(&sih_agent_lock);
if (!sih)
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
new file mode 100644
index 0000000..d423e0c
--- /dev/null
+++ b/drivers/mfd/twl4030-power.c
@@ -0,0 +1,472 @@
+/*
+ * linux/drivers/i2c/chips/twl4030-power.c
+ *
+ * Handle TWL4030 Power initialization
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2006 Texas Instruments, Inc
+ *
+ * Written by Kalle Jokiniemi
+ * Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Several fixes by Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/pm.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+
+static u8 twl4030_start_script_address = 0x2b;
+
+#define PWR_P1_SW_EVENTS 0x10
+#define PWR_DEVOFF (1<<0)
+
+#define PHY_TO_OFF_PM_MASTER(p) (p - 0x36)
+#define PHY_TO_OFF_PM_RECEIVER(p) (p - 0x5b)
+
+/* resource - hfclk */
+#define R_HFCLKOUT_DEV_GRP PHY_TO_OFF_PM_RECEIVER(0xe6)
+
+/* PM events */
+#define R_P1_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x46)
+#define R_P2_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x47)
+#define R_P3_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x48)
+#define R_CFG_P1_TRANSITION PHY_TO_OFF_PM_MASTER(0x36)
+#define R_CFG_P2_TRANSITION PHY_TO_OFF_PM_MASTER(0x37)
+#define R_CFG_P3_TRANSITION PHY_TO_OFF_PM_MASTER(0x38)
+
+#define LVL_WAKEUP 0x08
+
+#define ENABLE_WARMRESET (1<<4)
+
+#define END_OF_SCRIPT 0x3f
+
+#define R_SEQ_ADD_A2S PHY_TO_OFF_PM_MASTER(0x55)
+#define R_SEQ_ADD_S2A12 PHY_TO_OFF_PM_MASTER(0x56)
+#define R_SEQ_ADD_S2A3 PHY_TO_OFF_PM_MASTER(0x57)
+#define R_SEQ_ADD_WARM PHY_TO_OFF_PM_MASTER(0x58)
+#define R_MEMORY_ADDRESS PHY_TO_OFF_PM_MASTER(0x59)
+#define R_MEMORY_DATA PHY_TO_OFF_PM_MASTER(0x5a)
+
+#define R_PROTECT_KEY 0x0E
+#define R_KEY_1 0xC0
+#define R_KEY_2 0x0C
+
+/* resource configuration registers */
+
+#define DEVGROUP_OFFSET 0
+#define TYPE_OFFSET 1
+
+/* Bit positions */
+#define DEVGROUP_SHIFT 5
+#define DEVGROUP_MASK (7 << DEVGROUP_SHIFT)
+#define TYPE_SHIFT 0
+#define TYPE_MASK (7 << TYPE_SHIFT)
+#define TYPE2_SHIFT 3
+#define TYPE2_MASK (3 << TYPE2_SHIFT)
+
+static u8 res_config_addrs[] = {
+ [RES_VAUX1] = 0x17,
+ [RES_VAUX2] = 0x1b,
+ [RES_VAUX3] = 0x1f,
+ [RES_VAUX4] = 0x23,
+ [RES_VMMC1] = 0x27,
+ [RES_VMMC2] = 0x2b,
+ [RES_VPLL1] = 0x2f,
+ [RES_VPLL2] = 0x33,
+ [RES_VSIM] = 0x37,
+ [RES_VDAC] = 0x3b,
+ [RES_VINTANA1] = 0x3f,
+ [RES_VINTANA2] = 0x43,
+ [RES_VINTDIG] = 0x47,
+ [RES_VIO] = 0x4b,
+ [RES_VDD1] = 0x55,
+ [RES_VDD2] = 0x63,
+ [RES_VUSB_1V5] = 0x71,
+ [RES_VUSB_1V8] = 0x74,
+ [RES_VUSB_3V1] = 0x77,
+ [RES_VUSBCP] = 0x7a,
+ [RES_REGEN] = 0x7f,
+ [RES_NRES_PWRON] = 0x82,
+ [RES_CLKEN] = 0x85,
+ [RES_SYSEN] = 0x88,
+ [RES_HFCLKOUT] = 0x8b,
+ [RES_32KCLKOUT] = 0x8e,
+ [RES_RESET] = 0x91,
+ [RES_Main_Ref] = 0x94,
+};
+
+static int __init twl4030_write_script_byte(u8 address, u8 byte)
+{
+ int err;
+
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ R_MEMORY_ADDRESS);
+ if (err)
+ goto out;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
+ R_MEMORY_DATA);
+out:
+ return err;
+}
+
+static int __init twl4030_write_script_ins(u8 address, u16 pmb_message,
+ u8 delay, u8 next)
+{
+ int err;
+
+ address *= 4;
+ err = twl4030_write_script_byte(address++, pmb_message >> 8);
+ if (err)
+ goto out;
+ err = twl4030_write_script_byte(address++, pmb_message & 0xff);
+ if (err)
+ goto out;
+ err = twl4030_write_script_byte(address++, delay);
+ if (err)
+ goto out;
+ err = twl4030_write_script_byte(address++, next);
+out:
+ return err;
+}
+
+static int __init twl4030_write_script(u8 address, struct twl4030_ins *script,
+ int len)
+{
+ int err;
+
+ for (; len; len--, address++, script++) {
+ if (len == 1) {
+ err = twl4030_write_script_ins(address,
+ script->pmb_message,
+ script->delay,
+ END_OF_SCRIPT);
+ if (err)
+ break;
+ } else {
+ err = twl4030_write_script_ins(address,
+ script->pmb_message,
+ script->delay,
+ address + 1);
+ if (err)
+ break;
+ }
+ }
+ return err;
+}
+
+static int __init twl4030_config_wakeup3_sequence(u8 address)
+{
+ int err;
+ u8 data;
+
+ /* Set SLEEP to ACTIVE SEQ address for P3 */
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ R_SEQ_ADD_S2A3);
+ if (err)
+ goto out;
+
+ /* P3 LVL_WAKEUP should be on LEVEL */
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ R_P3_SW_EVENTS);
+ if (err)
+ goto out;
+ data |= LVL_WAKEUP;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+ R_P3_SW_EVENTS);
+out:
+ if (err)
+ pr_err("TWL4030 wakeup sequence for P3 config error\n");
+ return err;
+}
+
+static int __init twl4030_config_wakeup12_sequence(u8 address)
+{
+ int err = 0;
+ u8 data;
+
+ /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ R_SEQ_ADD_S2A12);
+ if (err)
+ goto out;
+
+ /* P1/P2 LVL_WAKEUP should be on LEVEL */
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ R_P1_SW_EVENTS);
+ if (err)
+ goto out;
+
+ data |= LVL_WAKEUP;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+ R_P1_SW_EVENTS);
+ if (err)
+ goto out;
+
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ R_P2_SW_EVENTS);
+ if (err)
+ goto out;
+
+ data |= LVL_WAKEUP;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+ R_P2_SW_EVENTS);
+ if (err)
+ goto out;
+
+ if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
+ /* Disabling AC charger effect on sleep-active transitions */
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ R_CFG_P1_TRANSITION);
+ if (err)
+ goto out;
+ data &= ~(1<<1);
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
+ R_CFG_P1_TRANSITION);
+ if (err)
+ goto out;
+ }
+
+out:
+ if (err)
+ pr_err("TWL4030 wakeup sequence for P1 and P2" \
+ "config error\n");
+ return err;
+}
+
+static int __init twl4030_config_sleep_sequence(u8 address)
+{
+ int err;
+
+ /* Set ACTIVE to SLEEP SEQ address in T2 memory*/
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ R_SEQ_ADD_A2S);
+
+ if (err)
+ pr_err("TWL4030 sleep sequence config error\n");
+
+ return err;
+}
+
+static int __init twl4030_config_warmreset_sequence(u8 address)
+{
+ int err;
+ u8 rd_data;
+
+ /* Set WARM RESET SEQ address for P1 */
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ R_SEQ_ADD_WARM);
+ if (err)
+ goto out;
+
+ /* P1/P2/P3 enable WARMRESET */
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+ R_P1_SW_EVENTS);
+ if (err)
+ goto out;
+
+ rd_data |= ENABLE_WARMRESET;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+ R_P1_SW_EVENTS);
+ if (err)
+ goto out;
+
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+ R_P2_SW_EVENTS);
+ if (err)
+ goto out;
+
+ rd_data |= ENABLE_WARMRESET;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+ R_P2_SW_EVENTS);
+ if (err)
+ goto out;
+
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+ R_P3_SW_EVENTS);
+ if (err)
+ goto out;
+
+ rd_data |= ENABLE_WARMRESET;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+ R_P3_SW_EVENTS);
+out:
+ if (err)
+ pr_err("TWL4030 warmreset seq config error\n");
+ return err;
+}
+
+static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
+{
+ int rconfig_addr;
+ int err;
+ u8 type;
+ u8 grp;
+
+ if (rconfig->resource > TOTAL_RESOURCES) {
+ pr_err("TWL4030 Resource %d does not exist\n",
+ rconfig->resource);
+ return -EINVAL;
+ }
+
+ rconfig_addr = res_config_addrs[rconfig->resource];
+
+ /* Set resource group */
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
+ rconfig_addr + DEVGROUP_OFFSET);
+ if (err) {
+ pr_err("TWL4030 Resource %d group could not be read\n",
+ rconfig->resource);
+ return err;
+ }
+
+ if (rconfig->devgroup >= 0) {
+ grp &= ~DEVGROUP_MASK;
+ grp |= rconfig->devgroup << DEVGROUP_SHIFT;
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ grp, rconfig_addr + DEVGROUP_OFFSET);
+ if (err < 0) {
+ pr_err("TWL4030 failed to program devgroup\n");
+ return err;
+ }
+ }
+
+ /* Set resource types */
+ err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
+ rconfig_addr + TYPE_OFFSET);
+ if (err < 0) {
+ pr_err("TWL4030 Resource %d type could not be read\n",
+ rconfig->resource);
+ return err;
+ }
+
+ if (rconfig->type >= 0) {
+ type &= ~TYPE_MASK;
+ type |= rconfig->type << TYPE_SHIFT;
+ }
+
+ if (rconfig->type2 >= 0) {
+ type &= ~TYPE2_MASK;
+ type |= rconfig->type2 << TYPE2_SHIFT;
+ }
+
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ type, rconfig_addr + TYPE_OFFSET);
+ if (err < 0) {
+ pr_err("TWL4030 failed to program resource type\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int __init load_twl4030_script(struct twl4030_script *tscript,
+ u8 address)
+{
+ int err;
+ static int order;
+
+ /* Make sure the script isn't going beyond last valid address (0x3f) */
+ if ((address + tscript->size) > END_OF_SCRIPT) {
+ pr_err("TWL4030 scripts too big error\n");
+ return -EINVAL;
+ }
+
+ err = twl4030_write_script(address, tscript->script, tscript->size);
+ if (err)
+ goto out;
+
+ if (tscript->flags & TWL4030_WRST_SCRIPT) {
+ err = twl4030_config_warmreset_sequence(address);
+ if (err)
+ goto out;
+ }
+ if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) {
+ err = twl4030_config_wakeup12_sequence(address);
+ if (err)
+ goto out;
+ order = 1;
+ }
+ if (tscript->flags & TWL4030_WAKEUP3_SCRIPT) {
+ err = twl4030_config_wakeup3_sequence(address);
+ if (err)
+ goto out;
+ }
+ if (tscript->flags & TWL4030_SLEEP_SCRIPT)
+ if (order)
+ pr_warning("TWL4030: Bad order of scripts (sleep "\
+ "script before wakeup) Leads to boot"\
+ "failure on some boards\n");
+ err = twl4030_config_sleep_sequence(address);
+out:
+ return err;
+}
+
+void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
+{
+ int err = 0;
+ int i;
+ struct twl4030_resconfig *resconfig;
+ u8 address = twl4030_start_script_address;
+
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
+ R_PROTECT_KEY);
+ if (err)
+ goto unlock;
+
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
+ R_PROTECT_KEY);
+ if (err)
+ goto unlock;
+
+ for (i = 0; i < twl4030_scripts->num; i++) {
+ err = load_twl4030_script(twl4030_scripts->scripts[i], address);
+ if (err)
+ goto load;
+ address += twl4030_scripts->scripts[i]->size;
+ }
+
+ resconfig = twl4030_scripts->resource_config;
+ if (resconfig) {
+ while (resconfig->resource) {
+ err = twl4030_configure_resource(resconfig);
+ if (err)
+ goto resource;
+ resconfig++;
+
+ }
+ }
+
+ err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
+ if (err)
+ pr_err("TWL4030 Unable to relock registers\n");
+ return;
+
+unlock:
+ if (err)
+ pr_err("TWL4030 Unable to unlock registers\n");
+ return;
+load:
+ if (err)
+ pr_err("TWL4030 failed to load scripts\n");
+ return;
+resource:
+ if (err)
+ pr_err("TWL4030 failed to configure resource\n");
+ return;
+}
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
new file mode 100644
index 0000000..49b7885
--- /dev/null
+++ b/drivers/mfd/wm831x-core.c
@@ -0,0 +1,1549 @@
+/*
+ * wm831x-core.c -- Device access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/regulator.h>
+
+/* Current settings - values are 2*2^(reg_val/4) microamps. These are
+ * exported since they are used by multiple drivers.
+ */
+int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL] = {
+ 2,
+ 2,
+ 3,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 10,
+ 11,
+ 13,
+ 16,
+ 19,
+ 23,
+ 27,
+ 32,
+ 38,
+ 45,
+ 54,
+ 64,
+ 76,
+ 91,
+ 108,
+ 128,
+ 152,
+ 181,
+ 215,
+ 256,
+ 304,
+ 362,
+ 431,
+ 512,
+ 609,
+ 724,
+ 861,
+ 1024,
+ 1218,
+ 1448,
+ 1722,
+ 2048,
+ 2435,
+ 2896,
+ 3444,
+ 4096,
+ 4871,
+ 5793,
+ 6889,
+ 8192,
+ 9742,
+ 11585,
+ 13777,
+ 16384,
+ 19484,
+ 23170,
+ 27554,
+};
+EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
+
+enum wm831x_parent {
+ WM8310 = 0,
+ WM8311 = 1,
+ WM8312 = 2,
+};
+
+static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
+{
+ if (!wm831x->locked)
+ return 0;
+
+ switch (reg) {
+ case WM831X_WATCHDOG:
+ case WM831X_DC4_CONTROL:
+ case WM831X_ON_PIN_CONTROL:
+ case WM831X_BACKUP_CHARGER_CONTROL:
+ case WM831X_CHARGER_CONTROL_1:
+ case WM831X_CHARGER_CONTROL_2:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * wm831x_reg_unlock: Unlock user keyed registers
+ *
+ * The WM831x has a user key preventing writes to particularly
+ * critical registers. This function locks those registers,
+ * allowing writes to them.
+ */
+void wm831x_reg_lock(struct wm831x *wm831x)
+{
+ int ret;
+
+ ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
+ if (ret == 0) {
+ dev_vdbg(wm831x->dev, "Registers locked\n");
+
+ mutex_lock(&wm831x->io_lock);
+ WARN_ON(wm831x->locked);
+ wm831x->locked = 1;
+ mutex_unlock(&wm831x->io_lock);
+ } else {
+ dev_err(wm831x->dev, "Failed to lock registers: %d\n", ret);
+ }
+
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_lock);
+
+/**
+ * wm831x_reg_unlock: Unlock user keyed registers
+ *
+ * The WM831x has a user key preventing writes to particularly
+ * critical registers. This function locks those registers,
+ * preventing spurious writes.
+ */
+int wm831x_reg_unlock(struct wm831x *wm831x)
+{
+ int ret;
+
+ /* 0x9716 is the value required to unlock the registers */
+ ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0x9716);
+ if (ret == 0) {
+ dev_vdbg(wm831x->dev, "Registers unlocked\n");
+
+ mutex_lock(&wm831x->io_lock);
+ WARN_ON(!wm831x->locked);
+ wm831x->locked = 0;
+ mutex_unlock(&wm831x->io_lock);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
+
+static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *dest)
+{
+ int ret, i;
+ u16 *buf = dest;
+
+ BUG_ON(bytes % 2);
+ BUG_ON(bytes <= 0);
+
+ ret = wm831x->read_dev(wm831x, reg, bytes, dest);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < bytes / 2; i++) {
+ buf[i] = be16_to_cpu(buf[i]);
+
+ dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n",
+ buf[i], reg + i, reg + i);
+ }
+
+ return 0;
+}
+
+/**
+ * wm831x_reg_read: Read a single WM831x register.
+ *
+ * @wm831x: Device to read from.
+ * @reg: Register to read.
+ */
+int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
+{
+ unsigned short val;
+ int ret;
+
+ mutex_lock(&wm831x->io_lock);
+
+ ret = wm831x_read(wm831x, reg, 2, &val);
+
+ mutex_unlock(&wm831x->io_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return val;
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_read);
+
+/**
+ * wm831x_bulk_read: Read multiple WM831x registers
+ *
+ * @wm831x: Device to read from
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to fill.
+ */
+int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
+ int count, u16 *buf)
+{
+ int ret;
+
+ mutex_lock(&wm831x->io_lock);
+
+ ret = wm831x_read(wm831x, reg, count * 2, buf);
+
+ mutex_unlock(&wm831x->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_bulk_read);
+
+static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *src)
+{
+ u16 *buf = src;
+ int i;
+
+ BUG_ON(bytes % 2);
+ BUG_ON(bytes <= 0);
+
+ for (i = 0; i < bytes / 2; i++) {
+ if (wm831x_reg_locked(wm831x, reg))
+ return -EPERM;
+
+ dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
+ buf[i], reg + i, reg + i);
+
+ buf[i] = cpu_to_be16(buf[i]);
+ }
+
+ return wm831x->write_dev(wm831x, reg, bytes, src);
+}
+
+/**
+ * wm831x_reg_write: Write a single WM831x register.
+ *
+ * @wm831x: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ */
+int wm831x_reg_write(struct wm831x *wm831x, unsigned short reg,
+ unsigned short val)
+{
+ int ret;
+
+ mutex_lock(&wm831x->io_lock);
+
+ ret = wm831x_write(wm831x, reg, 2, &val);
+
+ mutex_unlock(&wm831x->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_write);
+
+/**
+ * wm831x_set_bits: Set the value of a bitfield in a WM831x register
+ *
+ * @wm831x: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ */
+int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
+ unsigned short mask, unsigned short val)
+{
+ int ret;
+ u16 r;
+
+ mutex_lock(&wm831x->io_lock);
+
+ ret = wm831x_read(wm831x, reg, 2, &r);
+ if (ret < 0)
+ goto out;
+
+ r &= ~mask;
+ r |= val;
+
+ ret = wm831x_write(wm831x, reg, 2, &r);
+
+out:
+ mutex_unlock(&wm831x->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_set_bits);
+
+/**
+ * wm831x_auxadc_read: Read a value from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+ int tries = 10;
+ int ret, src;
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_ENA, WM831X_AUX_ENA);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
+ goto out;
+ }
+
+ /* We force a single source at present */
+ src = input;
+ ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
+ 1 << src);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
+ goto out;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
+ goto disable;
+ }
+
+ do {
+ msleep(1);
+
+ ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
+ if (ret < 0)
+ ret = WM831X_AUX_CVT_ENA;
+ } while ((ret & WM831X_AUX_CVT_ENA) && --tries);
+
+ if (ret & WM831X_AUX_CVT_ENA) {
+ dev_err(wm831x->dev, "Timed out reading AUXADC\n");
+ ret = -EBUSY;
+ goto disable;
+ }
+
+ ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret);
+ } else {
+ src = ((ret & WM831X_AUX_DATA_SRC_MASK)
+ >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+ if (src == 14)
+ src = WM831X_AUX_CAL;
+
+ if (src != input) {
+ dev_err(wm831x->dev, "Data from source %d not %d\n",
+ src, input);
+ ret = -EINVAL;
+ } else {
+ ret &= WM831X_AUX_DATA_MASK;
+ }
+ }
+
+disable:
+ wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
+out:
+ mutex_unlock(&wm831x->auxadc_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
+
+/**
+ * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+ int ret;
+
+ ret = wm831x_auxadc_read(wm831x, input);
+ if (ret < 0)
+ return ret;
+
+ ret *= 1465;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
+
+static struct resource wm831x_dcdc1_resources[] = {
+ {
+ .start = WM831X_DC1_CONTROL_1,
+ .end = WM831X_DC1_DVS_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_DC1,
+ .end = WM831X_IRQ_UV_DC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "HC",
+ .start = WM831X_IRQ_HC_DC1,
+ .end = WM831X_IRQ_HC_DC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static struct resource wm831x_dcdc2_resources[] = {
+ {
+ .start = WM831X_DC2_CONTROL_1,
+ .end = WM831X_DC2_DVS_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_DC2,
+ .end = WM831X_IRQ_UV_DC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "HC",
+ .start = WM831X_IRQ_HC_DC2,
+ .end = WM831X_IRQ_HC_DC2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_dcdc3_resources[] = {
+ {
+ .start = WM831X_DC3_CONTROL_1,
+ .end = WM831X_DC3_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_DC3,
+ .end = WM831X_IRQ_UV_DC3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_dcdc4_resources[] = {
+ {
+ .start = WM831X_DC4_CONTROL,
+ .end = WM831X_DC4_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_DC4,
+ .end = WM831X_IRQ_UV_DC4,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_gpio_resources[] = {
+ {
+ .start = WM831X_IRQ_GPIO_1,
+ .end = WM831X_IRQ_GPIO_16,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_isink1_resources[] = {
+ {
+ .start = WM831X_CURRENT_SINK_1,
+ .end = WM831X_CURRENT_SINK_1,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .start = WM831X_IRQ_CS1,
+ .end = WM831X_IRQ_CS1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_isink2_resources[] = {
+ {
+ .start = WM831X_CURRENT_SINK_2,
+ .end = WM831X_CURRENT_SINK_2,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .start = WM831X_IRQ_CS2,
+ .end = WM831X_IRQ_CS2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo1_resources[] = {
+ {
+ .start = WM831X_LDO1_CONTROL,
+ .end = WM831X_LDO1_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO1,
+ .end = WM831X_IRQ_UV_LDO1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo2_resources[] = {
+ {
+ .start = WM831X_LDO2_CONTROL,
+ .end = WM831X_LDO2_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO2,
+ .end = WM831X_IRQ_UV_LDO2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo3_resources[] = {
+ {
+ .start = WM831X_LDO3_CONTROL,
+ .end = WM831X_LDO3_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO3,
+ .end = WM831X_IRQ_UV_LDO3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo4_resources[] = {
+ {
+ .start = WM831X_LDO4_CONTROL,
+ .end = WM831X_LDO4_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO4,
+ .end = WM831X_IRQ_UV_LDO4,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo5_resources[] = {
+ {
+ .start = WM831X_LDO5_CONTROL,
+ .end = WM831X_LDO5_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO5,
+ .end = WM831X_IRQ_UV_LDO5,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo6_resources[] = {
+ {
+ .start = WM831X_LDO6_CONTROL,
+ .end = WM831X_LDO6_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO6,
+ .end = WM831X_IRQ_UV_LDO6,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo7_resources[] = {
+ {
+ .start = WM831X_LDO7_CONTROL,
+ .end = WM831X_LDO7_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO7,
+ .end = WM831X_IRQ_UV_LDO7,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo8_resources[] = {
+ {
+ .start = WM831X_LDO8_CONTROL,
+ .end = WM831X_LDO8_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO8,
+ .end = WM831X_IRQ_UV_LDO8,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo9_resources[] = {
+ {
+ .start = WM831X_LDO9_CONTROL,
+ .end = WM831X_LDO9_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO9,
+ .end = WM831X_IRQ_UV_LDO9,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo10_resources[] = {
+ {
+ .start = WM831X_LDO10_CONTROL,
+ .end = WM831X_LDO10_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_LDO10,
+ .end = WM831X_IRQ_UV_LDO10,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_ldo11_resources[] = {
+ {
+ .start = WM831X_LDO11_ON_CONTROL,
+ .end = WM831X_LDO11_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct resource wm831x_on_resources[] = {
+ {
+ .start = WM831X_IRQ_ON,
+ .end = WM831X_IRQ_ON,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static struct resource wm831x_power_resources[] = {
+ {
+ .name = "SYSLO",
+ .start = WM831X_IRQ_PPM_SYSLO,
+ .end = WM831X_IRQ_PPM_SYSLO,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "PWR SRC",
+ .start = WM831X_IRQ_PPM_PWR_SRC,
+ .end = WM831X_IRQ_PPM_PWR_SRC,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB CURR",
+ .start = WM831X_IRQ_PPM_USB_CURR,
+ .end = WM831X_IRQ_PPM_USB_CURR,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BATT HOT",
+ .start = WM831X_IRQ_CHG_BATT_HOT,
+ .end = WM831X_IRQ_CHG_BATT_HOT,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BATT COLD",
+ .start = WM831X_IRQ_CHG_BATT_COLD,
+ .end = WM831X_IRQ_CHG_BATT_COLD,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BATT FAIL",
+ .start = WM831X_IRQ_CHG_BATT_FAIL,
+ .end = WM831X_IRQ_CHG_BATT_FAIL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "OV",
+ .start = WM831X_IRQ_CHG_OV,
+ .end = WM831X_IRQ_CHG_OV,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "END",
+ .start = WM831X_IRQ_CHG_END,
+ .end = WM831X_IRQ_CHG_END,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "TO",
+ .start = WM831X_IRQ_CHG_TO,
+ .end = WM831X_IRQ_CHG_TO,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MODE",
+ .start = WM831X_IRQ_CHG_MODE,
+ .end = WM831X_IRQ_CHG_MODE,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "START",
+ .start = WM831X_IRQ_CHG_START,
+ .end = WM831X_IRQ_CHG_START,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_rtc_resources[] = {
+ {
+ .name = "PER",
+ .start = WM831X_IRQ_RTC_PER,
+ .end = WM831X_IRQ_RTC_PER,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ALM",
+ .start = WM831X_IRQ_RTC_ALM,
+ .end = WM831X_IRQ_RTC_ALM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_status1_resources[] = {
+ {
+ .start = WM831X_STATUS_LED_1,
+ .end = WM831X_STATUS_LED_1,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct resource wm831x_status2_resources[] = {
+ {
+ .start = WM831X_STATUS_LED_2,
+ .end = WM831X_STATUS_LED_2,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct resource wm831x_touch_resources[] = {
+ {
+ .name = "TCHPD",
+ .start = WM831X_IRQ_TCHPD,
+ .end = WM831X_IRQ_TCHPD,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "TCHDATA",
+ .start = WM831X_IRQ_TCHDATA,
+ .end = WM831X_IRQ_TCHDATA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource wm831x_wdt_resources[] = {
+ {
+ .start = WM831X_IRQ_WDOG_TO,
+ .end = WM831X_IRQ_WDOG_TO,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell wm8310_devs[] = {
+ {
+ .name = "wm831x-buckv",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+ .resources = wm831x_dcdc1_resources,
+ },
+ {
+ .name = "wm831x-buckv",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+ .resources = wm831x_dcdc2_resources,
+ },
+ {
+ .name = "wm831x-buckp",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+ .resources = wm831x_dcdc3_resources,
+ },
+ {
+ .name = "wm831x-boostp",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
+ .resources = wm831x_dcdc4_resources,
+ },
+ {
+ .name = "wm831x-epe",
+ .id = 1,
+ },
+ {
+ .name = "wm831x-epe",
+ .id = 2,
+ },
+ {
+ .name = "wm831x-gpio",
+ .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+ .resources = wm831x_gpio_resources,
+ },
+ {
+ .name = "wm831x-hwmon",
+ },
+ {
+ .name = "wm831x-isink",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
+ .resources = wm831x_isink1_resources,
+ },
+ {
+ .name = "wm831x-isink",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
+ .resources = wm831x_isink2_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+ .resources = wm831x_ldo1_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+ .resources = wm831x_ldo2_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+ .resources = wm831x_ldo3_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+ .resources = wm831x_ldo4_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 5,
+ .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+ .resources = wm831x_ldo5_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 6,
+ .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
+ .resources = wm831x_ldo6_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 7,
+ .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+ .resources = wm831x_ldo7_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 8,
+ .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
+ .resources = wm831x_ldo8_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 9,
+ .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
+ .resources = wm831x_ldo9_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 10,
+ .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
+ .resources = wm831x_ldo10_resources,
+ },
+ {
+ .name = "wm831x-alive-ldo",
+ .id = 11,
+ .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+ .resources = wm831x_ldo11_resources,
+ },
+ {
+ .name = "wm831x-on",
+ .num_resources = ARRAY_SIZE(wm831x_on_resources),
+ .resources = wm831x_on_resources,
+ },
+ {
+ .name = "wm831x-power",
+ .num_resources = ARRAY_SIZE(wm831x_power_resources),
+ .resources = wm831x_power_resources,
+ },
+ {
+ .name = "wm831x-rtc",
+ .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+ .resources = wm831x_rtc_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_status1_resources),
+ .resources = wm831x_status1_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_status2_resources),
+ .resources = wm831x_status2_resources,
+ },
+ {
+ .name = "wm831x-watchdog",
+ .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+ .resources = wm831x_wdt_resources,
+ },
+};
+
+static struct mfd_cell wm8311_devs[] = {
+ {
+ .name = "wm831x-buckv",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+ .resources = wm831x_dcdc1_resources,
+ },
+ {
+ .name = "wm831x-buckv",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+ .resources = wm831x_dcdc2_resources,
+ },
+ {
+ .name = "wm831x-buckp",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+ .resources = wm831x_dcdc3_resources,
+ },
+ {
+ .name = "wm831x-boostp",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
+ .resources = wm831x_dcdc4_resources,
+ },
+ {
+ .name = "wm831x-epe",
+ .id = 1,
+ },
+ {
+ .name = "wm831x-epe",
+ .id = 2,
+ },
+ {
+ .name = "wm831x-gpio",
+ .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+ .resources = wm831x_gpio_resources,
+ },
+ {
+ .name = "wm831x-hwmon",
+ },
+ {
+ .name = "wm831x-isink",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
+ .resources = wm831x_isink1_resources,
+ },
+ {
+ .name = "wm831x-isink",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
+ .resources = wm831x_isink2_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+ .resources = wm831x_ldo1_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+ .resources = wm831x_ldo2_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+ .resources = wm831x_ldo3_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+ .resources = wm831x_ldo4_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 5,
+ .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+ .resources = wm831x_ldo5_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 7,
+ .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+ .resources = wm831x_ldo7_resources,
+ },
+ {
+ .name = "wm831x-alive-ldo",
+ .id = 11,
+ .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+ .resources = wm831x_ldo11_resources,
+ },
+ {
+ .name = "wm831x-on",
+ .num_resources = ARRAY_SIZE(wm831x_on_resources),
+ .resources = wm831x_on_resources,
+ },
+ {
+ .name = "wm831x-power",
+ .num_resources = ARRAY_SIZE(wm831x_power_resources),
+ .resources = wm831x_power_resources,
+ },
+ {
+ .name = "wm831x-rtc",
+ .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+ .resources = wm831x_rtc_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_status1_resources),
+ .resources = wm831x_status1_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_status2_resources),
+ .resources = wm831x_status2_resources,
+ },
+ {
+ .name = "wm831x-touch",
+ .num_resources = ARRAY_SIZE(wm831x_touch_resources),
+ .resources = wm831x_touch_resources,
+ },
+ {
+ .name = "wm831x-watchdog",
+ .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+ .resources = wm831x_wdt_resources,
+ },
+};
+
+static struct mfd_cell wm8312_devs[] = {
+ {
+ .name = "wm831x-buckv",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+ .resources = wm831x_dcdc1_resources,
+ },
+ {
+ .name = "wm831x-buckv",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+ .resources = wm831x_dcdc2_resources,
+ },
+ {
+ .name = "wm831x-buckp",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+ .resources = wm831x_dcdc3_resources,
+ },
+ {
+ .name = "wm831x-boostp",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
+ .resources = wm831x_dcdc4_resources,
+ },
+ {
+ .name = "wm831x-epe",
+ .id = 1,
+ },
+ {
+ .name = "wm831x-epe",
+ .id = 2,
+ },
+ {
+ .name = "wm831x-gpio",
+ .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+ .resources = wm831x_gpio_resources,
+ },
+ {
+ .name = "wm831x-hwmon",
+ },
+ {
+ .name = "wm831x-isink",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
+ .resources = wm831x_isink1_resources,
+ },
+ {
+ .name = "wm831x-isink",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
+ .resources = wm831x_isink2_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+ .resources = wm831x_ldo1_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+ .resources = wm831x_ldo2_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+ .resources = wm831x_ldo3_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+ .resources = wm831x_ldo4_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 5,
+ .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+ .resources = wm831x_ldo5_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 6,
+ .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
+ .resources = wm831x_ldo6_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 7,
+ .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+ .resources = wm831x_ldo7_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 8,
+ .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
+ .resources = wm831x_ldo8_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 9,
+ .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
+ .resources = wm831x_ldo9_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 10,
+ .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
+ .resources = wm831x_ldo10_resources,
+ },
+ {
+ .name = "wm831x-alive-ldo",
+ .id = 11,
+ .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+ .resources = wm831x_ldo11_resources,
+ },
+ {
+ .name = "wm831x-on",
+ .num_resources = ARRAY_SIZE(wm831x_on_resources),
+ .resources = wm831x_on_resources,
+ },
+ {
+ .name = "wm831x-power",
+ .num_resources = ARRAY_SIZE(wm831x_power_resources),
+ .resources = wm831x_power_resources,
+ },
+ {
+ .name = "wm831x-rtc",
+ .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+ .resources = wm831x_rtc_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_status1_resources),
+ .resources = wm831x_status1_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_status2_resources),
+ .resources = wm831x_status2_resources,
+ },
+ {
+ .name = "wm831x-touch",
+ .num_resources = ARRAY_SIZE(wm831x_touch_resources),
+ .resources = wm831x_touch_resources,
+ },
+ {
+ .name = "wm831x-watchdog",
+ .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+ .resources = wm831x_wdt_resources,
+ },
+};
+
+static struct mfd_cell backlight_devs[] = {
+ {
+ .name = "wm831x-backlight",
+ },
+};
+
+/*
+ * Instantiate the generic non-control parts of the device.
+ */
+static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
+{
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int rev;
+ enum wm831x_parent parent;
+ int ret;
+
+ mutex_init(&wm831x->io_lock);
+ mutex_init(&wm831x->key_lock);
+ mutex_init(&wm831x->auxadc_lock);
+ dev_set_drvdata(wm831x->dev, wm831x);
+
+ ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
+ goto err;
+ }
+ if (ret != 0x6204) {
+ dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = wm831x_reg_read(wm831x, WM831X_REVISION);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
+ goto err;
+ }
+ rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
+
+ ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
+ goto err;
+ }
+
+ switch (ret) {
+ case 0x8310:
+ parent = WM8310;
+ switch (rev) {
+ case 0:
+ dev_info(wm831x->dev, "WM8310 revision %c\n",
+ 'A' + rev);
+ break;
+ }
+ break;
+
+ case 0x8311:
+ parent = WM8311;
+ switch (rev) {
+ case 0:
+ dev_info(wm831x->dev, "WM8311 revision %c\n",
+ 'A' + rev);
+ break;
+ }
+ break;
+
+ case 0x8312:
+ parent = WM8312;
+ switch (rev) {
+ case 0:
+ dev_info(wm831x->dev, "WM8312 revision %c\n",
+ 'A' + rev);
+ break;
+ }
+ break;
+
+ case 0:
+ /* Some engineering samples do not have the ID set,
+ * rely on the device being registered correctly.
+ * This will need revisiting for future devices with
+ * multiple dies.
+ */
+ parent = id;
+ switch (rev) {
+ case 0:
+ dev_info(wm831x->dev, "WM831%d ES revision %c\n",
+ parent, 'A' + rev);
+ break;
+ }
+ break;
+
+ default:
+ dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* This will need revisiting in future but is OK for all
+ * current parts.
+ */
+ if (parent != id)
+ dev_warn(wm831x->dev, "Device was registered as a WM831%lu\n",
+ id);
+
+ /* Bootstrap the user key */
+ ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
+ goto err;
+ }
+ if (ret != 0) {
+ dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
+ ret);
+ wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
+ }
+ wm831x->locked = 1;
+
+ if (pdata && pdata->pre_init) {
+ ret = pdata->pre_init(wm831x);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ ret = wm831x_irq_init(wm831x, irq);
+ if (ret != 0)
+ goto err;
+
+ /* The core device is up, instantiate the subdevices. */
+ switch (parent) {
+ case WM8310:
+ ret = mfd_add_devices(wm831x->dev, -1,
+ wm8310_devs, ARRAY_SIZE(wm8310_devs),
+ NULL, 0);
+ break;
+
+ case WM8311:
+ ret = mfd_add_devices(wm831x->dev, -1,
+ wm8311_devs, ARRAY_SIZE(wm8311_devs),
+ NULL, 0);
+ break;
+
+ case WM8312:
+ ret = mfd_add_devices(wm831x->dev, -1,
+ wm8312_devs, ARRAY_SIZE(wm8312_devs),
+ NULL, 0);
+ break;
+
+ default:
+ /* If this happens the bus probe function is buggy */
+ BUG();
+ }
+
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to add children\n");
+ goto err_irq;
+ }
+
+ if (pdata && pdata->backlight) {
+ /* Treat errors as non-critical */
+ ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
+ ARRAY_SIZE(backlight_devs), NULL, 0);
+ if (ret < 0)
+ dev_err(wm831x->dev, "Failed to add backlight: %d\n",
+ ret);
+ }
+
+ wm831x_otp_init(wm831x);
+
+ if (pdata && pdata->post_init) {
+ ret = pdata->post_init(wm831x);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "post_init() failed: %d\n", ret);
+ goto err_irq;
+ }
+ }
+
+ return 0;
+
+err_irq:
+ wm831x_irq_exit(wm831x);
+err:
+ mfd_remove_devices(wm831x->dev);
+ kfree(wm831x);
+ return ret;
+}
+
+static void wm831x_device_exit(struct wm831x *wm831x)
+{
+ wm831x_otp_exit(wm831x);
+ mfd_remove_devices(wm831x->dev);
+ wm831x_irq_exit(wm831x);
+ kfree(wm831x);
+}
+
+static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *dest)
+{
+ struct i2c_client *i2c = wm831x->control_data;
+ int ret;
+ u16 r = cpu_to_be16(reg);
+
+ ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ ret = i2c_master_recv(i2c, dest, bytes);
+ if (ret < 0)
+ return ret;
+ if (ret != bytes)
+ return -EIO;
+ return 0;
+}
+
+/* Currently we allocate the write buffer on the stack; this is OK for
+ * small writes - if we need to do large writes this will need to be
+ * revised.
+ */
+static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *src)
+{
+ struct i2c_client *i2c = wm831x->control_data;
+ unsigned char msg[bytes + 2];
+ int ret;
+
+ reg = cpu_to_be16(reg);
+ memcpy(&msg[0], &reg, 2);
+ memcpy(&msg[2], src, bytes);
+
+ ret = i2c_master_send(i2c, msg, bytes + 2);
+ if (ret < 0)
+ return ret;
+ if (ret < bytes + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int wm831x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm831x *wm831x;
+
+ wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
+ if (wm831x == NULL) {
+ kfree(i2c);
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c, wm831x);
+ wm831x->dev = &i2c->dev;
+ wm831x->control_data = i2c;
+ wm831x->read_dev = wm831x_i2c_read_device;
+ wm831x->write_dev = wm831x_i2c_write_device;
+
+ return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
+}
+
+static int wm831x_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+ wm831x_device_exit(wm831x);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm831x_i2c_id[] = {
+ { "wm8310", WM8310 },
+ { "wm8311", WM8311 },
+ { "wm8312", WM8312 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
+
+
+static struct i2c_driver wm831x_i2c_driver = {
+ .driver = {
+ .name = "wm831x",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_i2c_probe,
+ .remove = wm831x_i2c_remove,
+ .id_table = wm831x_i2c_id,
+};
+
+static int __init wm831x_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm831x_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register wm831x I2C driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(wm831x_i2c_init);
+
+static void __exit wm831x_i2c_exit(void)
+{
+ i2c_del_driver(&wm831x_i2c_driver);
+}
+module_exit(wm831x_i2c_exit);
+
+MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown");
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
new file mode 100644
index 0000000..d3015df
--- /dev/null
+++ b/drivers/mfd/wm831x-irq.c
@@ -0,0 +1,559 @@
+/*
+ * wm831x-irq.c -- Interrupt controller support for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/interrupt.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+
+#include <linux/delay.h>
+
+/*
+ * Since generic IRQs don't currently support interrupt controllers on
+ * interrupt driven buses we don't use genirq but instead provide an
+ * interface that looks very much like the standard ones. This leads
+ * to some bodges, including storing interrupt handler information in
+ * the static irq_data table we use to look up the data for individual
+ * interrupts, but hopefully won't last too long.
+ */
+
+struct wm831x_irq_data {
+ int primary;
+ int reg;
+ int mask;
+ irq_handler_t handler;
+ void *handler_data;
+};
+
+static struct wm831x_irq_data wm831x_irqs[] = {
+ [WM831X_IRQ_TEMP_THW] = {
+ .primary = WM831X_TEMP_INT,
+ .reg = 1,
+ .mask = WM831X_TEMP_THW_EINT,
+ },
+ [WM831X_IRQ_GPIO_1] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP1_EINT,
+ },
+ [WM831X_IRQ_GPIO_2] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP2_EINT,
+ },
+ [WM831X_IRQ_GPIO_3] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP3_EINT,
+ },
+ [WM831X_IRQ_GPIO_4] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP4_EINT,
+ },
+ [WM831X_IRQ_GPIO_5] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP5_EINT,
+ },
+ [WM831X_IRQ_GPIO_6] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP6_EINT,
+ },
+ [WM831X_IRQ_GPIO_7] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP7_EINT,
+ },
+ [WM831X_IRQ_GPIO_8] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP8_EINT,
+ },
+ [WM831X_IRQ_GPIO_9] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP9_EINT,
+ },
+ [WM831X_IRQ_GPIO_10] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP10_EINT,
+ },
+ [WM831X_IRQ_GPIO_11] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP11_EINT,
+ },
+ [WM831X_IRQ_GPIO_12] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP12_EINT,
+ },
+ [WM831X_IRQ_GPIO_13] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP13_EINT,
+ },
+ [WM831X_IRQ_GPIO_14] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP14_EINT,
+ },
+ [WM831X_IRQ_GPIO_15] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP15_EINT,
+ },
+ [WM831X_IRQ_GPIO_16] = {
+ .primary = WM831X_GP_INT,
+ .reg = 5,
+ .mask = WM831X_GP16_EINT,
+ },
+ [WM831X_IRQ_ON] = {
+ .primary = WM831X_ON_PIN_INT,
+ .reg = 1,
+ .mask = WM831X_ON_PIN_EINT,
+ },
+ [WM831X_IRQ_PPM_SYSLO] = {
+ .primary = WM831X_PPM_INT,
+ .reg = 1,
+ .mask = WM831X_PPM_SYSLO_EINT,
+ },
+ [WM831X_IRQ_PPM_PWR_SRC] = {
+ .primary = WM831X_PPM_INT,
+ .reg = 1,
+ .mask = WM831X_PPM_PWR_SRC_EINT,
+ },
+ [WM831X_IRQ_PPM_USB_CURR] = {
+ .primary = WM831X_PPM_INT,
+ .reg = 1,
+ .mask = WM831X_PPM_USB_CURR_EINT,
+ },
+ [WM831X_IRQ_WDOG_TO] = {
+ .primary = WM831X_WDOG_INT,
+ .reg = 1,
+ .mask = WM831X_WDOG_TO_EINT,
+ },
+ [WM831X_IRQ_RTC_PER] = {
+ .primary = WM831X_RTC_INT,
+ .reg = 1,
+ .mask = WM831X_RTC_PER_EINT,
+ },
+ [WM831X_IRQ_RTC_ALM] = {
+ .primary = WM831X_RTC_INT,
+ .reg = 1,
+ .mask = WM831X_RTC_ALM_EINT,
+ },
+ [WM831X_IRQ_CHG_BATT_HOT] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_BATT_HOT_EINT,
+ },
+ [WM831X_IRQ_CHG_BATT_COLD] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_BATT_COLD_EINT,
+ },
+ [WM831X_IRQ_CHG_BATT_FAIL] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_BATT_FAIL_EINT,
+ },
+ [WM831X_IRQ_CHG_OV] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_OV_EINT,
+ },
+ [WM831X_IRQ_CHG_END] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_END_EINT,
+ },
+ [WM831X_IRQ_CHG_TO] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_TO_EINT,
+ },
+ [WM831X_IRQ_CHG_MODE] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_MODE_EINT,
+ },
+ [WM831X_IRQ_CHG_START] = {
+ .primary = WM831X_CHG_INT,
+ .reg = 2,
+ .mask = WM831X_CHG_START_EINT,
+ },
+ [WM831X_IRQ_TCHDATA] = {
+ .primary = WM831X_TCHDATA_INT,
+ .reg = 1,
+ .mask = WM831X_TCHDATA_EINT,
+ },
+ [WM831X_IRQ_TCHPD] = {
+ .primary = WM831X_TCHPD_INT,
+ .reg = 1,
+ .mask = WM831X_TCHPD_EINT,
+ },
+ [WM831X_IRQ_AUXADC_DATA] = {
+ .primary = WM831X_AUXADC_INT,
+ .reg = 1,
+ .mask = WM831X_AUXADC_DATA_EINT,
+ },
+ [WM831X_IRQ_AUXADC_DCOMP1] = {
+ .primary = WM831X_AUXADC_INT,
+ .reg = 1,
+ .mask = WM831X_AUXADC_DCOMP1_EINT,
+ },
+ [WM831X_IRQ_AUXADC_DCOMP2] = {
+ .primary = WM831X_AUXADC_INT,
+ .reg = 1,
+ .mask = WM831X_AUXADC_DCOMP2_EINT,
+ },
+ [WM831X_IRQ_AUXADC_DCOMP3] = {
+ .primary = WM831X_AUXADC_INT,
+ .reg = 1,
+ .mask = WM831X_AUXADC_DCOMP3_EINT,
+ },
+ [WM831X_IRQ_AUXADC_DCOMP4] = {
+ .primary = WM831X_AUXADC_INT,
+ .reg = 1,
+ .mask = WM831X_AUXADC_DCOMP4_EINT,
+ },
+ [WM831X_IRQ_CS1] = {
+ .primary = WM831X_CS_INT,
+ .reg = 2,
+ .mask = WM831X_CS1_EINT,
+ },
+ [WM831X_IRQ_CS2] = {
+ .primary = WM831X_CS_INT,
+ .reg = 2,
+ .mask = WM831X_CS2_EINT,
+ },
+ [WM831X_IRQ_HC_DC1] = {
+ .primary = WM831X_HC_INT,
+ .reg = 4,
+ .mask = WM831X_HC_DC1_EINT,
+ },
+ [WM831X_IRQ_HC_DC2] = {
+ .primary = WM831X_HC_INT,
+ .reg = 4,
+ .mask = WM831X_HC_DC2_EINT,
+ },
+ [WM831X_IRQ_UV_LDO1] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO1_EINT,
+ },
+ [WM831X_IRQ_UV_LDO2] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO2_EINT,
+ },
+ [WM831X_IRQ_UV_LDO3] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO3_EINT,
+ },
+ [WM831X_IRQ_UV_LDO4] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO4_EINT,
+ },
+ [WM831X_IRQ_UV_LDO5] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO5_EINT,
+ },
+ [WM831X_IRQ_UV_LDO6] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO6_EINT,
+ },
+ [WM831X_IRQ_UV_LDO7] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO7_EINT,
+ },
+ [WM831X_IRQ_UV_LDO8] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO8_EINT,
+ },
+ [WM831X_IRQ_UV_LDO9] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO9_EINT,
+ },
+ [WM831X_IRQ_UV_LDO10] = {
+ .primary = WM831X_UV_INT,
+ .reg = 3,
+ .mask = WM831X_UV_LDO10_EINT,
+ },
+ [WM831X_IRQ_UV_DC1] = {
+ .primary = WM831X_UV_INT,
+ .reg = 4,
+ .mask = WM831X_UV_DC1_EINT,
+ },
+ [WM831X_IRQ_UV_DC2] = {
+ .primary = WM831X_UV_INT,
+ .reg = 4,
+ .mask = WM831X_UV_DC2_EINT,
+ },
+ [WM831X_IRQ_UV_DC3] = {
+ .primary = WM831X_UV_INT,
+ .reg = 4,
+ .mask = WM831X_UV_DC3_EINT,
+ },
+ [WM831X_IRQ_UV_DC4] = {
+ .primary = WM831X_UV_INT,
+ .reg = 4,
+ .mask = WM831X_UV_DC4_EINT,
+ },
+};
+
+static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data)
+{
+ return WM831X_INTERRUPT_STATUS_1 - 1 + irq_data->reg;
+}
+
+static inline int irq_data_to_mask_reg(struct wm831x_irq_data *irq_data)
+{
+ return WM831X_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg;
+}
+
+static void __wm831x_enable_irq(struct wm831x *wm831x, int irq)
+{
+ struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
+
+ wm831x->irq_masks[irq_data->reg - 1] &= ~irq_data->mask;
+ wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data),
+ wm831x->irq_masks[irq_data->reg - 1]);
+}
+
+void wm831x_enable_irq(struct wm831x *wm831x, int irq)
+{
+ mutex_lock(&wm831x->irq_lock);
+ __wm831x_enable_irq(wm831x, irq);
+ mutex_unlock(&wm831x->irq_lock);
+}
+EXPORT_SYMBOL_GPL(wm831x_enable_irq);
+
+static void __wm831x_disable_irq(struct wm831x *wm831x, int irq)
+{
+ struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
+
+ wm831x->irq_masks[irq_data->reg - 1] |= irq_data->mask;
+ wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data),
+ wm831x->irq_masks[irq_data->reg - 1]);
+}
+
+void wm831x_disable_irq(struct wm831x *wm831x, int irq)
+{
+ mutex_lock(&wm831x->irq_lock);
+ __wm831x_disable_irq(wm831x, irq);
+ mutex_unlock(&wm831x->irq_lock);
+}
+EXPORT_SYMBOL_GPL(wm831x_disable_irq);
+
+int wm831x_request_irq(struct wm831x *wm831x,
+ unsigned int irq, irq_handler_t handler,
+ unsigned long flags, const char *name,
+ void *dev)
+{
+ int ret = 0;
+
+ if (irq < 0 || irq >= WM831X_NUM_IRQS)
+ return -EINVAL;
+
+ mutex_lock(&wm831x->irq_lock);
+
+ if (wm831x_irqs[irq].handler) {
+ dev_err(wm831x->dev, "Already have handler for IRQ %d\n", irq);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ wm831x_irqs[irq].handler = handler;
+ wm831x_irqs[irq].handler_data = dev;
+
+ __wm831x_enable_irq(wm831x, irq);
+
+out:
+ mutex_unlock(&wm831x->irq_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_request_irq);
+
+void wm831x_free_irq(struct wm831x *wm831x, unsigned int irq, void *data)
+{
+ if (irq < 0 || irq >= WM831X_NUM_IRQS)
+ return;
+
+ mutex_lock(&wm831x->irq_lock);
+
+ wm831x_irqs[irq].handler = NULL;
+ wm831x_irqs[irq].handler_data = NULL;
+
+ __wm831x_disable_irq(wm831x, irq);
+
+ mutex_unlock(&wm831x->irq_lock);
+}
+EXPORT_SYMBOL_GPL(wm831x_free_irq);
+
+
+static void wm831x_handle_irq(struct wm831x *wm831x, int irq, int status)
+{
+ struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
+
+ if (irq_data->handler) {
+ irq_data->handler(irq, irq_data->handler_data);
+ wm831x_reg_write(wm831x, irq_data_to_status_reg(irq_data),
+ irq_data->mask);
+ } else {
+ dev_err(wm831x->dev, "Unhandled IRQ %d, masking\n", irq);
+ __wm831x_disable_irq(wm831x, irq);
+ }
+}
+
+/* Main interrupt handling occurs in a workqueue since we need
+ * interrupts enabled to interact with the chip. */
+static void wm831x_irq_worker(struct work_struct *work)
+{
+ struct wm831x *wm831x = container_of(work, struct wm831x, irq_work);
+ unsigned int i;
+ int primary;
+ int status_regs[5];
+ int read[5] = { 0 };
+ int *status;
+
+ primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS);
+ if (primary < 0) {
+ dev_err(wm831x->dev, "Failed to read system interrupt: %d\n",
+ primary);
+ goto out;
+ }
+
+ mutex_lock(&wm831x->irq_lock);
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
+ int offset = wm831x_irqs[i].reg - 1;
+
+ if (!(primary & wm831x_irqs[i].primary))
+ continue;
+
+ status = &status_regs[offset];
+
+ /* Hopefully there should only be one register to read
+ * each time otherwise we ought to do a block read. */
+ if (!read[offset]) {
+ *status = wm831x_reg_read(wm831x,
+ irq_data_to_status_reg(&wm831x_irqs[i]));
+ if (*status < 0) {
+ dev_err(wm831x->dev,
+ "Failed to read IRQ status: %d\n",
+ *status);
+ goto out_lock;
+ }
+
+ /* Mask out the disabled IRQs */
+ *status &= ~wm831x->irq_masks[offset];
+ read[offset] = 1;
+ }
+
+ if (*status & wm831x_irqs[i].mask)
+ wm831x_handle_irq(wm831x, i, *status);
+ }
+
+out_lock:
+ mutex_unlock(&wm831x->irq_lock);
+out:
+ enable_irq(wm831x->irq);
+}
+
+
+static irqreturn_t wm831x_cpu_irq(int irq, void *data)
+{
+ struct wm831x *wm831x = data;
+
+ /* Shut the interrupt to the CPU up and schedule the actual
+ * handler; we can't check that the IRQ is asserted. */
+ disable_irq_nosync(irq);
+
+ queue_work(wm831x->irq_wq, &wm831x->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+int wm831x_irq_init(struct wm831x *wm831x, int irq)
+{
+ int i, ret;
+
+ if (!irq) {
+ dev_warn(wm831x->dev,
+ "No interrupt specified - functionality limited\n");
+ return 0;
+ }
+
+
+ wm831x->irq_wq = create_singlethread_workqueue("wm831x-irq");
+ if (!wm831x->irq_wq) {
+ dev_err(wm831x->dev, "Failed to allocate IRQ worker\n");
+ return -ESRCH;
+ }
+
+ wm831x->irq = irq;
+ mutex_init(&wm831x->irq_lock);
+ INIT_WORK(&wm831x->irq_work, wm831x_irq_worker);
+
+ /* Mask the individual interrupt sources */
+ for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks); i++) {
+ wm831x->irq_masks[i] = 0xffff;
+ wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
+ 0xffff);
+ }
+
+ /* Enable top level interrupts, we mask at secondary level */
+ wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
+
+ /* We're good to go. We set IRQF_SHARED since there's a
+ * chance the driver will interoperate with another driver but
+ * the need to disable the IRQ while handing via I2C/SPI means
+ * that this may break and performance will be impacted. If
+ * this does happen it's a hardware design issue and the only
+ * other alternative would be polling.
+ */
+ ret = request_irq(irq, wm831x_cpu_irq, IRQF_TRIGGER_LOW | IRQF_SHARED,
+ "wm831x", wm831x);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
+ irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void wm831x_irq_exit(struct wm831x *wm831x)
+{
+ if (wm831x->irq)
+ free_irq(wm831x->irq, wm831x);
+}
diff --git a/drivers/mfd/wm831x-otp.c b/drivers/mfd/wm831x-otp.c
new file mode 100644
index 0000000..f742745
--- /dev/null
+++ b/drivers/mfd/wm831x-otp.c
@@ -0,0 +1,83 @@
+/*
+ * wm831x-otp.c -- OTP for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/otp.h>
+
+/* In bytes */
+#define WM831X_UNIQUE_ID_LEN 16
+
+/* Read the unique ID from the chip into id */
+static int wm831x_unique_id_read(struct wm831x *wm831x, char *id)
+{
+ int i, val;
+
+ for (i = 0; i < WM831X_UNIQUE_ID_LEN / 2; i++) {
+ val = wm831x_reg_read(wm831x, WM831X_UNIQUE_ID_1 + i);
+ if (val < 0)
+ return val;
+
+ id[i * 2] = (val >> 8) & 0xff;
+ id[(i * 2) + 1] = val & 0xff;
+ }
+
+ return 0;
+}
+
+static ssize_t wm831x_unique_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wm831x *wm831x = dev_get_drvdata(dev);
+ int i, rval;
+ char id[WM831X_UNIQUE_ID_LEN];
+ ssize_t ret = 0;
+
+ rval = wm831x_unique_id_read(wm831x, id);
+ if (rval < 0)
+ return 0;
+
+ for (i = 0; i < WM831X_UNIQUE_ID_LEN; i++)
+ ret += sprintf(&buf[ret], "%02x", buf[i]);
+
+ ret += sprintf(&buf[ret], "\n");
+
+ return ret;
+}
+
+static DEVICE_ATTR(unique_id, 0444, wm831x_unique_id_show, NULL);
+
+int wm831x_otp_init(struct wm831x *wm831x)
+{
+ int ret;
+
+ ret = device_create_file(wm831x->dev, &dev_attr_unique_id);
+ if (ret != 0)
+ dev_err(wm831x->dev, "Unique ID attribute not created: %d\n",
+ ret);
+
+ return ret;
+}
+
+void wm831x_otp_exit(struct wm831x *wm831x)
+{
+ device_remove_file(wm831x->dev, &dev_attr_unique_id);
+}
+
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index fe24079..ba27c9d 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -353,15 +353,15 @@ static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
}
/*
- * wm8350_irq_worker actually handles the interrupts. Since all
+ * This is a threaded IRQ handler so can access I2C/SPI. Since all
* interrupts are clear on read the IRQ line will be reasserted and
* the physical IRQ will be handled again if another interrupt is
* asserted while we run - in the normal course of events this is a
* rare occurrence so we save I2C/SPI reads.
*/
-static void wm8350_irq_worker(struct work_struct *work)
+static irqreturn_t wm8350_irq(int irq, void *data)
{
- struct wm8350 *wm8350 = container_of(work, struct wm8350, irq_work);
+ struct wm8350 *wm8350 = data;
u16 level_one, status1, status2, comp;
/* TODO: Use block reads to improve performance? */
@@ -552,16 +552,6 @@ static void wm8350_irq_worker(struct work_struct *work)
}
}
- enable_irq(wm8350->chip_irq);
-}
-
-static irqreturn_t wm8350_irq(int irq, void *data)
-{
- struct wm8350 *wm8350 = data;
-
- disable_irq_nosync(irq);
- schedule_work(&wm8350->irq_work);
-
return IRQ_HANDLED;
}
@@ -1428,9 +1418,8 @@ 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) {
- int flags = 0;
+ int flags = IRQF_ONESHOT;
if (pdata && pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;
@@ -1444,8 +1433,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
WM8350_IRQ_POL);
}
- ret = request_irq(irq, wm8350_irq, flags,
- "wm8350", wm8350);
+ ret = request_threaded_irq(irq, NULL, wm8350_irq, flags,
+ "wm8350", wm8350);
if (ret != 0) {
dev_err(wm8350->dev, "Failed to request IRQ: %d\n",
ret);
@@ -1472,6 +1461,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
&(wm8350->codec.pdev));
wm8350_client_dev_register(wm8350, "wm8350-gpio",
&(wm8350->gpio.pdev));
+ wm8350_client_dev_register(wm8350, "wm8350-hwmon",
+ &(wm8350->hwmon.pdev));
wm8350_client_dev_register(wm8350, "wm8350-power",
&(wm8350->power.pdev));
wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev));
@@ -1498,11 +1489,11 @@ void wm8350_device_exit(struct wm8350 *wm8350)
platform_device_unregister(wm8350->wdt.pdev);
platform_device_unregister(wm8350->rtc.pdev);
platform_device_unregister(wm8350->power.pdev);
+ platform_device_unregister(wm8350->hwmon.pdev);
platform_device_unregister(wm8350->gpio.pdev);
platform_device_unregister(wm8350->codec.pdev);
free_irq(wm8350->chip_irq, wm8350);
- flush_work(&wm8350->irq_work);
kfree(wm8350->reg_cache);
}
EXPORT_SYMBOL_GPL(wm8350_device_exit);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f431779..2dc42bb 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -82,6 +82,13 @@ config REGULATOR_TWL4030
This driver supports the voltage regulators provided by
this family of companion chips.
+config REGULATOR_WM831X
+ tristate "Wolfson Microelcronics WM831x PMIC regulators"
+ depends on MFD_WM831X
+ help
+ Support the voltage and current regulators of the WM831x series
+ of PMIC devices.
+
config REGULATOR_WM8350
tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC"
depends on MFD_WM8350
@@ -117,4 +124,28 @@ config REGULATOR_LP3971
Say Y here to support the voltage regulators and convertors
on National Semiconductors LP3971 PMIC
+config REGULATOR_PCAP
+ tristate "PCAP2 regulator driver"
+ depends on EZX_PCAP
+ help
+ This driver provides support for the voltage regulators of the
+ PCAP2 PMIC.
+
+config REGULATOR_MC13783
+ tristate "Support regulators on Freescale MC13783 PMIC"
+ depends on MFD_MC13783
+ help
+ Say y here to support the regulators found on the Freescale MC13783
+ PMIC.
+
+config REGULATOR_AB3100
+ tristate "ST-Ericsson AB3100 Regulator functions"
+ depends on AB3100_CORE
+ default y if AB3100_CORE
+ help
+ These regulators correspond to functionality in the
+ AB3100 analog baseband dealing with power regulators
+ for the system.
+
endif
+
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4d762c4..768b331 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -12,9 +12,15 @@ obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o
+obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
+obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
+obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
+obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
+obj-$(CONFIG_REGULATOR_MC13783) += mc13783.o
+obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
new file mode 100644
index 0000000..49aeee8
--- /dev/null
+++ b/drivers/regulator/ab3100.c
@@ -0,0 +1,700 @@
+/*
+ * drivers/regulator/ab3100.c
+ *
+ * Copyright (C) 2008-2009 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * Low-level control of the AB3100 IC Low Dropout (LDO)
+ * regulators, external regulator and buck converter
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/ab3100.h>
+
+/* LDO registers and some handy masking definitions for AB3100 */
+#define AB3100_LDO_A 0x40
+#define AB3100_LDO_C 0x41
+#define AB3100_LDO_D 0x42
+#define AB3100_LDO_E 0x43
+#define AB3100_LDO_E_SLEEP 0x44
+#define AB3100_LDO_F 0x45
+#define AB3100_LDO_G 0x46
+#define AB3100_LDO_H 0x47
+#define AB3100_LDO_H_SLEEP_MODE 0
+#define AB3100_LDO_H_SLEEP_EN 2
+#define AB3100_LDO_ON 4
+#define AB3100_LDO_H_VSEL_AC 5
+#define AB3100_LDO_K 0x48
+#define AB3100_LDO_EXT 0x49
+#define AB3100_BUCK 0x4A
+#define AB3100_BUCK_SLEEP 0x4B
+#define AB3100_REG_ON_MASK 0x10
+
+/**
+ * struct ab3100_regulator
+ * A struct passed around the individual regulator functions
+ * @platform_device: platform device holding this regulator
+ * @ab3100: handle to the AB3100 parent chip
+ * @plfdata: AB3100 platform data passed in at probe time
+ * @regreg: regulator register number in the AB3100
+ * @fixed_voltage: a fixed voltage for this regulator, if this
+ * 0 the voltages array is used instead.
+ * @typ_voltages: an array of available typical voltages for
+ * this regulator
+ * @voltages_len: length of the array of available voltages
+ */
+struct ab3100_regulator {
+ struct regulator_dev *rdev;
+ struct ab3100 *ab3100;
+ struct ab3100_platform_data *plfdata;
+ u8 regreg;
+ int fixed_voltage;
+ int const *typ_voltages;
+ u8 voltages_len;
+};
+
+/* The order in which registers are initialized */
+static const u8 ab3100_reg_init_order[AB3100_NUM_REGULATORS+2] = {
+ AB3100_LDO_A,
+ AB3100_LDO_C,
+ AB3100_LDO_E,
+ AB3100_LDO_E_SLEEP,
+ AB3100_LDO_F,
+ AB3100_LDO_G,
+ AB3100_LDO_H,
+ AB3100_LDO_K,
+ AB3100_LDO_EXT,
+ AB3100_BUCK,
+ AB3100_BUCK_SLEEP,
+ AB3100_LDO_D,
+};
+
+/* Preset (hardware defined) voltages for these regulators */
+#define LDO_A_VOLTAGE 2750000
+#define LDO_C_VOLTAGE 2650000
+#define LDO_D_VOLTAGE 2650000
+
+static const int const ldo_e_buck_typ_voltages[] = {
+ 1800000,
+ 1400000,
+ 1300000,
+ 1200000,
+ 1100000,
+ 1050000,
+ 900000,
+};
+
+static const int const ldo_f_typ_voltages[] = {
+ 1800000,
+ 1400000,
+ 1300000,
+ 1200000,
+ 1100000,
+ 1050000,
+ 2500000,
+ 2650000,
+};
+
+static const int const ldo_g_typ_voltages[] = {
+ 2850000,
+ 2750000,
+ 1800000,
+ 1500000,
+};
+
+static const int const ldo_h_typ_voltages[] = {
+ 2750000,
+ 1800000,
+ 1500000,
+ 1200000,
+};
+
+static const int const ldo_k_typ_voltages[] = {
+ 2750000,
+ 1800000,
+};
+
+
+/* The regulator devices */
+static struct ab3100_regulator
+ab3100_regulators[AB3100_NUM_REGULATORS] = {
+ {
+ .regreg = AB3100_LDO_A,
+ .fixed_voltage = LDO_A_VOLTAGE,
+ },
+ {
+ .regreg = AB3100_LDO_C,
+ .fixed_voltage = LDO_C_VOLTAGE,
+ },
+ {
+ .regreg = AB3100_LDO_D,
+ .fixed_voltage = LDO_D_VOLTAGE,
+ },
+ {
+ .regreg = AB3100_LDO_E,
+ .typ_voltages = ldo_e_buck_typ_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_e_buck_typ_voltages),
+ },
+ {
+ .regreg = AB3100_LDO_F,
+ .typ_voltages = ldo_f_typ_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_f_typ_voltages),
+ },
+ {
+ .regreg = AB3100_LDO_G,
+ .typ_voltages = ldo_g_typ_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_g_typ_voltages),
+ },
+ {
+ .regreg = AB3100_LDO_H,
+ .typ_voltages = ldo_h_typ_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_h_typ_voltages),
+ },
+ {
+ .regreg = AB3100_LDO_K,
+ .typ_voltages = ldo_k_typ_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_k_typ_voltages),
+ },
+ {
+ .regreg = AB3100_LDO_EXT,
+ /* No voltages for the external regulator */
+ },
+ {
+ .regreg = AB3100_BUCK,
+ .typ_voltages = ldo_e_buck_typ_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_e_buck_typ_voltages),
+ },
+};
+
+/*
+ * General functions for enable, disable and is_enabled used for
+ * LDO: A,C,E,F,G,H,K,EXT and BUCK
+ */
+static int ab3100_enable_regulator(struct regulator_dev *reg)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+ int err;
+ u8 regval;
+
+ err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
+ &regval);
+ if (err) {
+ dev_warn(&reg->dev, "failed to get regid %d value\n",
+ abreg->regreg);
+ return err;
+ }
+
+ /* The regulator is already on, no reason to go further */
+ if (regval & AB3100_REG_ON_MASK)
+ return 0;
+
+ regval |= AB3100_REG_ON_MASK;
+
+ err = ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg,
+ regval);
+ if (err) {
+ dev_warn(&reg->dev, "failed to set regid %d value\n",
+ abreg->regreg);
+ return err;
+ }
+
+ /* Per-regulator power on delay from spec */
+ switch (abreg->regreg) {
+ case AB3100_LDO_A: /* Fallthrough */
+ case AB3100_LDO_C: /* Fallthrough */
+ case AB3100_LDO_D: /* Fallthrough */
+ case AB3100_LDO_E: /* Fallthrough */
+ case AB3100_LDO_H: /* Fallthrough */
+ case AB3100_LDO_K:
+ udelay(200);
+ break;
+ case AB3100_LDO_F:
+ udelay(600);
+ break;
+ case AB3100_LDO_G:
+ udelay(400);
+ break;
+ case AB3100_BUCK:
+ mdelay(1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ab3100_disable_regulator(struct regulator_dev *reg)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+ int err;
+ u8 regval;
+
+ /*
+ * LDO D is a special regulator. When it is disabled, the entire
+ * system is shut down. So this is handled specially.
+ */
+ if (abreg->regreg == AB3100_LDO_D) {
+ int i;
+
+ dev_info(&reg->dev, "disabling LDO D - shut down system\n");
+ /*
+ * Set regulators to default values, ignore any errors,
+ * we're going DOWN
+ */
+ for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
+ (void) ab3100_set_register_interruptible(abreg->ab3100,
+ ab3100_reg_init_order[i],
+ abreg->plfdata->reg_initvals[i]);
+ }
+
+ /* Setting LDO D to 0x00 cuts the power to the SoC */
+ return ab3100_set_register_interruptible(abreg->ab3100,
+ AB3100_LDO_D, 0x00U);
+
+ }
+
+ /*
+ * All other regulators are handled here
+ */
+ err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
+ &regval);
+ if (err) {
+ dev_err(&reg->dev, "unable to get register 0x%x\n",
+ abreg->regreg);
+ return err;
+ }
+ regval &= ~AB3100_REG_ON_MASK;
+ return ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg,
+ regval);
+}
+
+static int ab3100_is_enabled_regulator(struct regulator_dev *reg)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+ u8 regval;
+ int err;
+
+ err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
+ &regval);
+ if (err) {
+ dev_err(&reg->dev, "unable to get register 0x%x\n",
+ abreg->regreg);
+ return err;
+ }
+
+ return regval & AB3100_REG_ON_MASK;
+}
+
+static int ab3100_list_voltage_regulator(struct regulator_dev *reg,
+ unsigned selector)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+
+ if (selector > abreg->voltages_len)
+ return -EINVAL;
+ return abreg->typ_voltages[selector];
+}
+
+static int ab3100_get_voltage_regulator(struct regulator_dev *reg)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+ u8 regval;
+ int err;
+
+ /* Return the voltage for fixed regulators immediately */
+ if (abreg->fixed_voltage)
+ return abreg->fixed_voltage;
+
+ /*
+ * For variable types, read out setting and index into
+ * supplied voltage list.
+ */
+ err = ab3100_get_register_interruptible(abreg->ab3100,
+ abreg->regreg, &regval);
+ if (err) {
+ dev_warn(&reg->dev,
+ "failed to get regulator value in register %02x\n",
+ abreg->regreg);
+ return err;
+ }
+
+ /* The 3 highest bits index voltages */
+ regval &= 0xE0;
+ regval >>= 5;
+
+ if (regval > abreg->voltages_len) {
+ dev_err(&reg->dev,
+ "regulator register %02x contains an illegal voltage setting\n",
+ abreg->regreg);
+ return -EINVAL;
+ }
+
+ return abreg->typ_voltages[regval];
+}
+
+static int ab3100_get_best_voltage_index(struct regulator_dev *reg,
+ int min_uV, int max_uV)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+ int i;
+ int bestmatch;
+ int bestindex;
+
+ /*
+ * Locate the minimum voltage fitting the criteria on
+ * this regulator. The switchable voltages are not
+ * in strict falling order so we need to check them
+ * all for the best match.
+ */
+ bestmatch = INT_MAX;
+ bestindex = -1;
+ for (i = 0; i < abreg->voltages_len; i++) {
+ if (abreg->typ_voltages[i] <= max_uV &&
+ abreg->typ_voltages[i] >= min_uV &&
+ abreg->typ_voltages[i] < bestmatch) {
+ bestmatch = abreg->typ_voltages[i];
+ bestindex = i;
+ }
+ }
+
+ if (bestindex < 0) {
+ dev_warn(&reg->dev, "requested %d<=x<=%d uV, out of range!\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+ return bestindex;
+}
+
+static int ab3100_set_voltage_regulator(struct regulator_dev *reg,
+ int min_uV, int max_uV)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+ u8 regval;
+ int err;
+ int bestindex;
+
+ bestindex = ab3100_get_best_voltage_index(reg, min_uV, max_uV);
+ if (bestindex < 0)
+ return bestindex;
+
+ err = ab3100_get_register_interruptible(abreg->ab3100,
+ abreg->regreg, &regval);
+ if (err) {
+ dev_warn(&reg->dev,
+ "failed to get regulator register %02x\n",
+ abreg->regreg);
+ return err;
+ }
+
+ /* The highest three bits control the variable regulators */
+ regval &= ~0xE0;
+ regval |= (bestindex << 5);
+
+ err = ab3100_set_register_interruptible(abreg->ab3100,
+ abreg->regreg, regval);
+ if (err)
+ dev_warn(&reg->dev, "failed to set regulator register %02x\n",
+ abreg->regreg);
+
+ return err;
+}
+
+static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg,
+ int uV)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+ u8 regval;
+ int err;
+ int bestindex;
+ u8 targetreg;
+
+ if (abreg->regreg == AB3100_LDO_E)
+ targetreg = AB3100_LDO_E_SLEEP;
+ else if (abreg->regreg == AB3100_BUCK)
+ targetreg = AB3100_BUCK_SLEEP;
+ else
+ return -EINVAL;
+
+ /* LDO E and BUCK have special suspend voltages you can set */
+ bestindex = ab3100_get_best_voltage_index(reg, uV, uV);
+
+ err = ab3100_get_register_interruptible(abreg->ab3100,
+ targetreg, &regval);
+ if (err) {
+ dev_warn(&reg->dev,
+ "failed to get regulator register %02x\n",
+ targetreg);
+ return err;
+ }
+
+ /* The highest three bits control the variable regulators */
+ regval &= ~0xE0;
+ regval |= (bestindex << 5);
+
+ err = ab3100_set_register_interruptible(abreg->ab3100,
+ targetreg, regval);
+ if (err)
+ dev_warn(&reg->dev, "failed to set regulator register %02x\n",
+ abreg->regreg);
+
+ return err;
+}
+
+/*
+ * The external regulator can just define a fixed voltage.
+ */
+static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg)
+{
+ struct ab3100_regulator *abreg = reg->reg_data;
+
+ return abreg->plfdata->external_voltage;
+}
+
+static struct regulator_ops regulator_ops_fixed = {
+ .enable = ab3100_enable_regulator,
+ .disable = ab3100_disable_regulator,
+ .is_enabled = ab3100_is_enabled_regulator,
+ .get_voltage = ab3100_get_voltage_regulator,
+};
+
+static struct regulator_ops regulator_ops_variable = {
+ .enable = ab3100_enable_regulator,
+ .disable = ab3100_disable_regulator,
+ .is_enabled = ab3100_is_enabled_regulator,
+ .get_voltage = ab3100_get_voltage_regulator,
+ .set_voltage = ab3100_set_voltage_regulator,
+ .list_voltage = ab3100_list_voltage_regulator,
+};
+
+static struct regulator_ops regulator_ops_variable_sleepable = {
+ .enable = ab3100_enable_regulator,
+ .disable = ab3100_disable_regulator,
+ .is_enabled = ab3100_is_enabled_regulator,
+ .get_voltage = ab3100_get_voltage_regulator,
+ .set_voltage = ab3100_set_voltage_regulator,
+ .set_suspend_voltage = ab3100_set_suspend_voltage_regulator,
+ .list_voltage = ab3100_list_voltage_regulator,
+};
+
+/*
+ * LDO EXT is an external regulator so it is really
+ * not possible to set any voltage locally here, AB3100
+ * is an on/off switch plain an simple. The external
+ * voltage is defined in the board set-up if any.
+ */
+static struct regulator_ops regulator_ops_external = {
+ .enable = ab3100_enable_regulator,
+ .disable = ab3100_disable_regulator,
+ .is_enabled = ab3100_is_enabled_regulator,
+ .get_voltage = ab3100_get_voltage_regulator_external,
+};
+
+static struct regulator_desc
+ab3100_regulator_desc[AB3100_NUM_REGULATORS] = {
+ {
+ .name = "LDO_A",
+ .id = AB3100_LDO_A,
+ .ops = &regulator_ops_fixed,
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_C",
+ .id = AB3100_LDO_C,
+ .ops = &regulator_ops_fixed,
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_D",
+ .id = AB3100_LDO_D,
+ .ops = &regulator_ops_fixed,
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_E",
+ .id = AB3100_LDO_E,
+ .ops = &regulator_ops_variable_sleepable,
+ .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages),
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_F",
+ .id = AB3100_LDO_F,
+ .ops = &regulator_ops_variable,
+ .n_voltages = ARRAY_SIZE(ldo_f_typ_voltages),
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_G",
+ .id = AB3100_LDO_G,
+ .ops = &regulator_ops_variable,
+ .n_voltages = ARRAY_SIZE(ldo_g_typ_voltages),
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_H",
+ .id = AB3100_LDO_H,
+ .ops = &regulator_ops_variable,
+ .n_voltages = ARRAY_SIZE(ldo_h_typ_voltages),
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_K",
+ .id = AB3100_LDO_K,
+ .ops = &regulator_ops_variable,
+ .n_voltages = ARRAY_SIZE(ldo_k_typ_voltages),
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "LDO_EXT",
+ .id = AB3100_LDO_EXT,
+ .ops = &regulator_ops_external,
+ .type = REGULATOR_VOLTAGE,
+ },
+ {
+ .name = "BUCK",
+ .id = AB3100_BUCK,
+ .ops = &regulator_ops_variable_sleepable,
+ .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages),
+ .type = REGULATOR_VOLTAGE,
+ },
+};
+
+/*
+ * NOTE: the following functions are regulators pluralis - it is the
+ * binding to the AB3100 core driver and the parent platform device
+ * for all the different regulators.
+ */
+
+static int __init ab3100_regulators_probe(struct platform_device *pdev)
+{
+ struct ab3100_platform_data *plfdata = pdev->dev.platform_data;
+ struct ab3100 *ab3100 = platform_get_drvdata(pdev);
+ int err = 0;
+ u8 data;
+ int i;
+
+ /* Check chip state */
+ err = ab3100_get_register_interruptible(ab3100,
+ AB3100_LDO_D, &data);
+ if (err) {
+ dev_err(&pdev->dev, "could not read initial status of LDO_D\n");
+ return err;
+ }
+ if (data & 0x10)
+ dev_notice(&pdev->dev,
+ "chip is already in active mode (Warm start)\n");
+ else
+ dev_notice(&pdev->dev,
+ "chip is in inactive mode (Cold start)\n");
+
+ /* Set up regulators */
+ for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
+ err = ab3100_set_register_interruptible(ab3100,
+ ab3100_reg_init_order[i],
+ plfdata->reg_initvals[i]);
+ if (err) {
+ dev_err(&pdev->dev, "regulator initialization failed with error %d\n",
+ err);
+ return err;
+ }
+ }
+
+ if (err) {
+ dev_err(&pdev->dev,
+ "LDO D regulator initialization failed with error %d\n",
+ err);
+ return err;
+ }
+
+ /* Register the regulators */
+ for (i = 0; i < AB3100_NUM_REGULATORS; i++) {
+ struct ab3100_regulator *reg = &ab3100_regulators[i];
+ struct regulator_dev *rdev;
+
+ /*
+ * Initialize per-regulator struct.
+ * Inherit platform data, this comes down from the
+ * i2c boarddata, from the machine. So if you want to
+ * see what it looks like for a certain machine, go
+ * into the machine I2C setup.
+ */
+ reg->ab3100 = ab3100;
+ reg->plfdata = plfdata;
+
+ /*
+ * Register the regulator, pass around
+ * the ab3100_regulator struct
+ */
+ rdev = regulator_register(&ab3100_regulator_desc[i],
+ &pdev->dev,
+ &plfdata->reg_constraints[i],
+ reg);
+
+ if (IS_ERR(rdev)) {
+ err = PTR_ERR(rdev);
+ dev_err(&pdev->dev,
+ "%s: failed to register regulator %s err %d\n",
+ __func__, ab3100_regulator_desc[i].name,
+ err);
+ i--;
+ /* remove the already registered regulators */
+ while (i > 0) {
+ regulator_unregister(ab3100_regulators[i].rdev);
+ i--;
+ }
+ return err;
+ }
+
+ /* Then set a pointer back to the registered regulator */
+ reg->rdev = rdev;
+ }
+
+ return 0;
+}
+
+static int __exit ab3100_regulators_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < AB3100_NUM_REGULATORS; i++) {
+ struct ab3100_regulator *reg = &ab3100_regulators[i];
+
+ regulator_unregister(reg->rdev);
+ }
+ return 0;
+}
+
+static struct platform_driver ab3100_regulators_driver = {
+ .driver = {
+ .name = "ab3100-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab3100_regulators_probe,
+ .remove = __exit_p(ab3100_regulators_remove),
+};
+
+static __init int ab3100_regulators_init(void)
+{
+ return platform_driver_register(&ab3100_regulators_driver);
+}
+
+static __exit void ab3100_regulators_exit(void)
+{
+ platform_driver_register(&ab3100_regulators_driver);
+}
+
+subsys_initcall(ab3100_regulators_init);
+module_exit(ab3100_regulators_exit);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("AB3100 Regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ab3100-regulators");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 98c3a74..91ba9bf 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1864,6 +1864,30 @@ int regulator_notifier_call_chain(struct regulator_dev *rdev,
}
EXPORT_SYMBOL_GPL(regulator_notifier_call_chain);
+/**
+ * regulator_mode_to_status - convert a regulator mode into a status
+ *
+ * @mode: Mode to convert
+ *
+ * Convert a regulator mode into a status.
+ */
+int regulator_mode_to_status(unsigned int mode)
+{
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ return REGULATOR_STATUS_FAST;
+ case REGULATOR_MODE_NORMAL:
+ return REGULATOR_STATUS_NORMAL;
+ case REGULATOR_MODE_IDLE:
+ return REGULATOR_STATUS_IDLE;
+ case REGULATOR_STATUS_STANDBY:
+ return REGULATOR_STATUS_STANDBY;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(regulator_mode_to_status);
+
/*
* To avoid cluttering sysfs (and memory) with useless state, only
* create attributes that can be meaningfully displayed.
diff --git a/drivers/regulator/mc13783.c b/drivers/regulator/mc13783.c
new file mode 100644
index 0000000..710211f
--- /dev/null
+++ b/drivers/regulator/mc13783.c
@@ -0,0 +1,410 @@
+/*
+ * Regulator Driver for Freescale MC13783 PMIC
+ *
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ * 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/mfd/mc13783-private.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+
+struct mc13783_regulator {
+ struct regulator_desc desc;
+ int reg;
+ int enable_bit;
+};
+
+static struct regulator_ops mc13783_regulator_ops;
+
+static struct mc13783_regulator mc13783_regulators[] = {
+ [MC13783_SW_SW3] = {
+ .desc = {
+ .name = "SW_SW3",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_SW_SW3,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_SWITCHERS_5,
+ .enable_bit = MC13783_SWCTRL_SW3_EN,
+ },
+ [MC13783_SW_PLL] = {
+ .desc = {
+ .name = "SW_PLL",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_SW_PLL,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_SWITCHERS_4,
+ .enable_bit = MC13783_SWCTRL_PLL_EN,
+ },
+ [MC13783_REGU_VAUDIO] = {
+ .desc = {
+ .name = "REGU_VAUDIO",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VAUDIO,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VAUDIO_EN,
+ },
+ [MC13783_REGU_VIOHI] = {
+ .desc = {
+ .name = "REGU_VIOHI",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VIOHI,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VIOHI_EN,
+ },
+ [MC13783_REGU_VIOLO] = {
+ .desc = {
+ .name = "REGU_VIOLO",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VIOLO,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VIOLO_EN,
+ },
+ [MC13783_REGU_VDIG] = {
+ .desc = {
+ .name = "REGU_VDIG",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VDIG,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VDIG_EN,
+ },
+ [MC13783_REGU_VGEN] = {
+ .desc = {
+ .name = "REGU_VGEN",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VGEN,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VGEN_EN,
+ },
+ [MC13783_REGU_VRFDIG] = {
+ .desc = {
+ .name = "REGU_VRFDIG",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VRFDIG,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VRFDIG_EN,
+ },
+ [MC13783_REGU_VRFREF] = {
+ .desc = {
+ .name = "REGU_VRFREF",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VRFREF,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VRFREF_EN,
+ },
+ [MC13783_REGU_VRFCP] = {
+ .desc = {
+ .name = "REGU_VRFCP",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VRFCP,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_0,
+ .enable_bit = MC13783_REGCTRL_VRFCP_EN,
+ },
+ [MC13783_REGU_VSIM] = {
+ .desc = {
+ .name = "REGU_VSIM",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VSIM,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VSIM_EN,
+ },
+ [MC13783_REGU_VESIM] = {
+ .desc = {
+ .name = "REGU_VESIM",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VESIM,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VESIM_EN,
+ },
+ [MC13783_REGU_VCAM] = {
+ .desc = {
+ .name = "REGU_VCAM",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VCAM,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VCAM_EN,
+ },
+ [MC13783_REGU_VRFBG] = {
+ .desc = {
+ .name = "REGU_VRFBG",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VRFBG,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VRFBG_EN,
+ },
+ [MC13783_REGU_VVIB] = {
+ .desc = {
+ .name = "REGU_VVIB",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VVIB,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VVIB_EN,
+ },
+ [MC13783_REGU_VRF1] = {
+ .desc = {
+ .name = "REGU_VRF1",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VRF1,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VRF1_EN,
+ },
+ [MC13783_REGU_VRF2] = {
+ .desc = {
+ .name = "REGU_VRF2",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VRF2,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VRF2_EN,
+ },
+ [MC13783_REGU_VMMC1] = {
+ .desc = {
+ .name = "REGU_VMMC1",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VMMC1,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VMMC1_EN,
+ },
+ [MC13783_REGU_VMMC2] = {
+ .desc = {
+ .name = "REGU_VMMC2",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_VMMC2,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_REGULATOR_MODE_1,
+ .enable_bit = MC13783_REGCTRL_VMMC2_EN,
+ },
+ [MC13783_REGU_GPO1] = {
+ .desc = {
+ .name = "REGU_GPO1",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_GPO1,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_POWER_MISCELLANEOUS,
+ .enable_bit = MC13783_REGCTRL_GPO1_EN,
+ },
+ [MC13783_REGU_GPO2] = {
+ .desc = {
+ .name = "REGU_GPO2",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_GPO2,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_POWER_MISCELLANEOUS,
+ .enable_bit = MC13783_REGCTRL_GPO2_EN,
+ },
+ [MC13783_REGU_GPO3] = {
+ .desc = {
+ .name = "REGU_GPO3",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_GPO3,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_POWER_MISCELLANEOUS,
+ .enable_bit = MC13783_REGCTRL_GPO3_EN,
+ },
+ [MC13783_REGU_GPO4] = {
+ .desc = {
+ .name = "REGU_GPO4",
+ .ops = &mc13783_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = MC13783_REGU_GPO4,
+ .owner = THIS_MODULE,
+ },
+ .reg = MC13783_REG_POWER_MISCELLANEOUS,
+ .enable_bit = MC13783_REGCTRL_GPO4_EN,
+ },
+};
+
+struct mc13783_priv {
+ struct regulator_desc desc[ARRAY_SIZE(mc13783_regulators)];
+ struct mc13783 *mc13783;
+ struct regulator_dev *regulators[0];
+};
+
+static int mc13783_enable(struct regulator_dev *rdev)
+{
+ struct mc13783_priv *priv = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
+ mc13783_regulators[id].enable_bit,
+ mc13783_regulators[id].enable_bit);
+}
+
+static int mc13783_disable(struct regulator_dev *rdev)
+{
+ struct mc13783_priv *priv = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
+ mc13783_regulators[id].enable_bit, 0);
+}
+
+static int mc13783_is_enabled(struct regulator_dev *rdev)
+{
+ struct mc13783_priv *priv = rdev_get_drvdata(rdev);
+ int ret, id = rdev_get_id(rdev);
+ unsigned int val;
+
+ ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
+ if (ret)
+ return ret;
+
+ return (val & mc13783_regulators[id].enable_bit) != 0;
+}
+
+static struct regulator_ops mc13783_regulator_ops = {
+ .enable = mc13783_enable,
+ .disable = mc13783_disable,
+ .is_enabled = mc13783_is_enabled,
+};
+
+static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
+{
+ struct mc13783_priv *priv;
+ struct mc13783 *mc13783 = dev_get_drvdata(pdev->dev.parent);
+ struct mc13783_regulator_init_data *init_data;
+ int i, ret;
+
+ dev_dbg(&pdev->dev, "mc13783_regulator_probe id %d\n", pdev->id);
+
+ priv = kzalloc(sizeof(*priv) + mc13783->num_regulators * sizeof(void *),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mc13783 = mc13783;
+
+ for (i = 0; i < mc13783->num_regulators; i++) {
+ init_data = &mc13783->regulators[i];
+ priv->regulators[i] = regulator_register(
+ &mc13783_regulators[init_data->id].desc,
+ &pdev->dev, init_data->init_data, priv);
+
+ if (IS_ERR(priv->regulators[i])) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ mc13783_regulators[i].desc.name);
+ ret = PTR_ERR(priv->regulators[i]);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+err:
+ while (--i >= 0)
+ regulator_unregister(priv->regulators[i]);
+
+ kfree(priv);
+
+ return ret;
+}
+
+static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
+{
+ struct mc13783_priv *priv = platform_get_drvdata(pdev);
+ struct mc13783 *mc13783 = priv->mc13783;
+ int i;
+
+ for (i = 0; i < mc13783->num_regulators; i++)
+ regulator_unregister(priv->regulators[i]);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_regulator_driver = {
+ .driver = {
+ .name = "mc13783-regulator",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(mc13783_regulator_remove),
+};
+
+static int __init mc13783_regulator_init(void)
+{
+ return platform_driver_probe(&mc13783_regulator_driver,
+ mc13783_regulator_probe);
+}
+subsys_initcall(mc13783_regulator_init);
+
+static void __exit mc13783_regulator_exit(void)
+{
+ platform_driver_unregister(&mc13783_regulator_driver);
+}
+module_exit(mc13783_regulator_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC");
+MODULE_ALIAS("platform:mc13783-regulator");
diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c
new file mode 100644
index 0000000..33d7d89
--- /dev/null
+++ b/drivers/regulator/pcap-regulator.c
@@ -0,0 +1,318 @@
+/*
+ * PCAP2 Regulator Driver
+ *
+ * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/ezx-pcap.h>
+
+static const u16 V1_table[] = {
+ 2775, 1275, 1600, 1725, 1825, 1925, 2075, 2275,
+};
+
+static const u16 V2_table[] = {
+ 2500, 2775,
+};
+
+static const u16 V3_table[] = {
+ 1075, 1275, 1550, 1725, 1876, 1950, 2075, 2275,
+};
+
+static const u16 V4_table[] = {
+ 1275, 1550, 1725, 1875, 1950, 2075, 2275, 2775,
+};
+
+static const u16 V5_table[] = {
+ 1875, 2275, 2475, 2775,
+};
+
+static const u16 V6_table[] = {
+ 2475, 2775,
+};
+
+static const u16 V7_table[] = {
+ 1875, 2775,
+};
+
+#define V8_table V4_table
+
+static const u16 V9_table[] = {
+ 1575, 1875, 2475, 2775,
+};
+
+static const u16 V10_table[] = {
+ 5000,
+};
+
+static const u16 VAUX1_table[] = {
+ 1875, 2475, 2775, 3000,
+};
+
+#define VAUX2_table VAUX1_table
+
+static const u16 VAUX3_table[] = {
+ 1200, 1200, 1200, 1200, 1400, 1600, 1800, 2000,
+ 2200, 2400, 2600, 2800, 3000, 3200, 3400, 3600,
+};
+
+static const u16 VAUX4_table[] = {
+ 1800, 1800, 3000, 5000,
+};
+
+static const u16 VSIM_table[] = {
+ 1875, 3000,
+};
+
+static const u16 VSIM2_table[] = {
+ 1875,
+};
+
+static const u16 VVIB_table[] = {
+ 1300, 1800, 2000, 3000,
+};
+
+static const u16 SW1_table[] = {
+ 900, 950, 1000, 1050, 1100, 1150, 1200, 1250,
+ 1300, 1350, 1400, 1450, 1500, 1600, 1875, 2250,
+};
+
+#define SW2_table SW1_table
+
+static const u16 SW3_table[] = {
+ 4000, 4500, 5000, 5500,
+};
+
+struct pcap_regulator {
+ const u8 reg;
+ const u8 en;
+ const u8 index;
+ const u8 stby;
+ const u8 lowpwr;
+ const u8 n_voltages;
+ const u16 *voltage_table;
+};
+
+#define NA 0xff
+
+#define VREG_INFO(_vreg, _reg, _en, _index, _stby, _lowpwr) \
+ [_vreg] = { \
+ .reg = _reg, \
+ .en = _en, \
+ .index = _index, \
+ .stby = _stby, \
+ .lowpwr = _lowpwr, \
+ .n_voltages = ARRAY_SIZE(_vreg##_table), \
+ .voltage_table = _vreg##_table, \
+ }
+
+static struct pcap_regulator vreg_table[] = {
+ VREG_INFO(V1, PCAP_REG_VREG1, 1, 2, 18, 0),
+ VREG_INFO(V2, PCAP_REG_VREG1, 5, 6, 19, 22),
+ VREG_INFO(V3, PCAP_REG_VREG1, 7, 8, 20, 23),
+ VREG_INFO(V4, PCAP_REG_VREG1, 11, 12, 21, 24),
+ /* V5 STBY and LOWPWR are on PCAP_REG_VREG2 */
+ VREG_INFO(V5, PCAP_REG_VREG1, 15, 16, 12, 19),
+
+ VREG_INFO(V6, PCAP_REG_VREG2, 1, 2, 14, 20),
+ VREG_INFO(V7, PCAP_REG_VREG2, 3, 4, 15, 21),
+ VREG_INFO(V8, PCAP_REG_VREG2, 5, 6, 16, 22),
+ VREG_INFO(V9, PCAP_REG_VREG2, 9, 10, 17, 23),
+ VREG_INFO(V10, PCAP_REG_VREG2, 10, NA, 18, 24),
+
+ VREG_INFO(VAUX1, PCAP_REG_AUXVREG, 1, 2, 22, 23),
+ /* VAUX2 ... VSIM2 STBY and LOWPWR are on PCAP_REG_LOWPWR */
+ VREG_INFO(VAUX2, PCAP_REG_AUXVREG, 4, 5, 0, 1),
+ VREG_INFO(VAUX3, PCAP_REG_AUXVREG, 7, 8, 2, 3),
+ VREG_INFO(VAUX4, PCAP_REG_AUXVREG, 12, 13, 4, 5),
+ VREG_INFO(VSIM, PCAP_REG_AUXVREG, 17, 18, NA, 6),
+ VREG_INFO(VSIM2, PCAP_REG_AUXVREG, 16, NA, NA, 7),
+ VREG_INFO(VVIB, PCAP_REG_AUXVREG, 19, 20, NA, NA),
+
+ VREG_INFO(SW1, PCAP_REG_SWCTRL, 1, 2, NA, NA),
+ VREG_INFO(SW2, PCAP_REG_SWCTRL, 6, 7, NA, NA),
+ /* SW3 STBY is on PCAP_REG_AUXVREG */
+ VREG_INFO(SW3, PCAP_REG_SWCTRL, 11, 12, 24, NA),
+
+ /* SWxS used to control SWx voltage on standby */
+/* VREG_INFO(SW1S, PCAP_REG_LOWPWR, NA, 12, NA, NA),
+ VREG_INFO(SW2S, PCAP_REG_LOWPWR, NA, 20, NA, NA), */
+};
+
+static int pcap_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)];
+ void *pcap = rdev_get_drvdata(rdev);
+ int uV;
+ u8 i;
+
+ /* the regulator doesn't support voltage switching */
+ if (vreg->n_voltages == 1)
+ return -EINVAL;
+
+ for (i = 0; i < vreg->n_voltages; i++) {
+ /* For V1 the first is not the best match */
+ if (i == 0 && rdev_get_id(rdev) == V1)
+ i = 1;
+ else if (i + 1 == vreg->n_voltages && rdev_get_id(rdev) == V1)
+ i = 0;
+
+ uV = vreg->voltage_table[i] * 1000;
+ if (min_uV <= uV && uV <= max_uV)
+ return ezx_pcap_set_bits(pcap, vreg->reg,
+ (vreg->n_voltages - 1) << vreg->index,
+ i << vreg->index);
+
+ if (i == 0 && rdev_get_id(rdev) == V1)
+ i = vreg->n_voltages - 1;
+ }
+
+ /* the requested voltage range is not supported by this regulator */
+ return -EINVAL;
+}
+
+static int pcap_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)];
+ void *pcap = rdev_get_drvdata(rdev);
+ u32 tmp;
+ int mV;
+
+ if (vreg->n_voltages == 1)
+ return vreg->voltage_table[0] * 1000;
+
+ ezx_pcap_read(pcap, vreg->reg, &tmp);
+ tmp = ((tmp >> vreg->index) & (vreg->n_voltages - 1));
+ mV = vreg->voltage_table[tmp];
+
+ return mV * 1000;
+}
+
+static int pcap_regulator_enable(struct regulator_dev *rdev)
+{
+ struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)];
+ void *pcap = rdev_get_drvdata(rdev);
+
+ if (vreg->en == NA)
+ return -EINVAL;
+
+ return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 1 << vreg->en);
+}
+
+static int pcap_regulator_disable(struct regulator_dev *rdev)
+{
+ struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)];
+ void *pcap = rdev_get_drvdata(rdev);
+
+ if (vreg->en == NA)
+ return -EINVAL;
+
+ return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 0);
+}
+
+static int pcap_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)];
+ void *pcap = rdev_get_drvdata(rdev);
+ u32 tmp;
+
+ if (vreg->en == NA)
+ return -EINVAL;
+
+ ezx_pcap_read(pcap, vreg->reg, &tmp);
+ return (tmp >> vreg->en) & 1;
+}
+
+static int pcap_regulator_list_voltage(struct regulator_dev *rdev,
+ unsigned int index)
+{
+ struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)];
+
+ return vreg->voltage_table[index] * 1000;
+}
+
+static struct regulator_ops pcap_regulator_ops = {
+ .list_voltage = pcap_regulator_list_voltage,
+ .set_voltage = pcap_regulator_set_voltage,
+ .get_voltage = pcap_regulator_get_voltage,
+ .enable = pcap_regulator_enable,
+ .disable = pcap_regulator_disable,
+ .is_enabled = pcap_regulator_is_enabled,
+};
+
+#define VREG(_vreg) \
+ [_vreg] = { \
+ .name = #_vreg, \
+ .id = _vreg, \
+ .n_voltages = ARRAY_SIZE(_vreg##_table), \
+ .ops = &pcap_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+
+static struct regulator_desc pcap_regulators[] = {
+ VREG(V1), VREG(V2), VREG(V3), VREG(V4), VREG(V5), VREG(V6), VREG(V7),
+ VREG(V8), VREG(V9), VREG(V10), VREG(VAUX1), VREG(VAUX2), VREG(VAUX3),
+ VREG(VAUX4), VREG(VSIM), VREG(VSIM2), VREG(VVIB), VREG(SW1), VREG(SW2),
+};
+
+static int __devinit pcap_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+ void *pcap = dev_get_drvdata(pdev->dev.parent);
+
+ rdev = regulator_register(&pcap_regulators[pdev->id], &pdev->dev,
+ pdev->dev.platform_data, pcap);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int __devexit pcap_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+static struct platform_driver pcap_regulator_driver = {
+ .driver = {
+ .name = "pcap-regulator",
+ },
+ .probe = pcap_regulator_probe,
+ .remove = __devexit_p(pcap_regulator_remove),
+};
+
+static int __init pcap_regulator_init(void)
+{
+ return platform_driver_register(&pcap_regulator_driver);
+}
+
+static void __exit pcap_regulator_exit(void)
+{
+ platform_driver_unregister(&pcap_regulator_driver);
+}
+
+subsys_initcall(pcap_regulator_init);
+module_exit(pcap_regulator_exit);
+
+MODULE_AUTHOR("Daniel Ribeiro <drwyrm@gmail.com>");
+MODULE_DESCRIPTION("PCAP2 Regulator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
new file mode 100644
index 0000000..2eefc1a
--- /dev/null
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -0,0 +1,862 @@
+/*
+ * wm831x-dcdc.c -- DC-DC buck convertor driver for the WM831x series
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/regulator.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+#define WM831X_BUCKV_MAX_SELECTOR 0x68
+#define WM831X_BUCKP_MAX_SELECTOR 0x66
+
+#define WM831X_DCDC_MODE_FAST 0
+#define WM831X_DCDC_MODE_NORMAL 1
+#define WM831X_DCDC_MODE_IDLE 2
+#define WM831X_DCDC_MODE_STANDBY 3
+
+#define WM831X_DCDC_MAX_NAME 6
+
+/* Register offsets in control block */
+#define WM831X_DCDC_CONTROL_1 0
+#define WM831X_DCDC_CONTROL_2 1
+#define WM831X_DCDC_ON_CONFIG 2
+#define WM831X_DCDC_SLEEP_CONTROL 3
+
+/*
+ * Shared
+ */
+
+struct wm831x_dcdc {
+ char name[WM831X_DCDC_MAX_NAME];
+ struct regulator_desc desc;
+ int base;
+ struct wm831x *wm831x;
+ struct regulator_dev *regulator;
+};
+
+static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+ int reg;
+
+ reg = wm831x_reg_read(wm831x, WM831X_DCDC_ENABLE);
+ if (reg < 0)
+ return reg;
+
+ if (reg & mask)
+ return 1;
+ else
+ return 0;
+}
+
+static int wm831x_dcdc_enable(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+
+ return wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, mask, mask);
+}
+
+static int wm831x_dcdc_disable(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+
+ return wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, mask, 0);
+}
+
+static unsigned int wm831x_dcdc_get_mode(struct regulator_dev *rdev)
+
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+ int val;
+
+ val = wm831x_reg_read(wm831x, reg);
+ if (val < 0)
+ return val;
+
+ val = (val & WM831X_DC1_ON_MODE_MASK) >> WM831X_DC1_ON_MODE_SHIFT;
+
+ switch (val) {
+ case WM831X_DCDC_MODE_FAST:
+ return REGULATOR_MODE_FAST;
+ case WM831X_DCDC_MODE_NORMAL:
+ return REGULATOR_MODE_NORMAL;
+ case WM831X_DCDC_MODE_STANDBY:
+ return REGULATOR_MODE_STANDBY;
+ case WM831X_DCDC_MODE_IDLE:
+ return REGULATOR_MODE_IDLE;
+ default:
+ BUG();
+ }
+}
+
+static int wm831x_dcdc_set_mode_int(struct wm831x *wm831x, int reg,
+ unsigned int mode)
+{
+ int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = WM831X_DCDC_MODE_FAST;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = WM831X_DCDC_MODE_NORMAL;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = WM831X_DCDC_MODE_STANDBY;
+ break;
+ case REGULATOR_MODE_IDLE:
+ val = WM831X_DCDC_MODE_IDLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_MODE_MASK,
+ val << WM831X_DC1_ON_MODE_SHIFT);
+}
+
+static int wm831x_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+
+ return wm831x_dcdc_set_mode_int(wm831x, reg, mode);
+}
+
+static int wm831x_dcdc_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
+
+ return wm831x_dcdc_set_mode_int(wm831x, reg, mode);
+}
+
+static int wm831x_dcdc_get_status(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ int ret;
+
+ /* First, check for errors */
+ ret = wm831x_reg_read(wm831x, WM831X_DCDC_UV_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (ret & (1 << rdev_get_id(rdev))) {
+ dev_dbg(wm831x->dev, "DCDC%d under voltage\n",
+ rdev_get_id(rdev) + 1);
+ return REGULATOR_STATUS_ERROR;
+ }
+
+ /* DCDC1 and DCDC2 can additionally detect high voltage/current */
+ if (rdev_get_id(rdev) < 2) {
+ if (ret & (WM831X_DC1_OV_STS << rdev_get_id(rdev))) {
+ dev_dbg(wm831x->dev, "DCDC%d over voltage\n",
+ rdev_get_id(rdev) + 1);
+ return REGULATOR_STATUS_ERROR;
+ }
+
+ if (ret & (WM831X_DC1_HC_STS << rdev_get_id(rdev))) {
+ dev_dbg(wm831x->dev, "DCDC%d over current\n",
+ rdev_get_id(rdev) + 1);
+ return REGULATOR_STATUS_ERROR;
+ }
+ }
+
+ /* Is the regulator on? */
+ ret = wm831x_reg_read(wm831x, WM831X_DCDC_STATUS);
+ if (ret < 0)
+ return ret;
+ if (!(ret & (1 << rdev_get_id(rdev))))
+ return REGULATOR_STATUS_OFF;
+
+ /* TODO: When we handle hardware control modes so we can report the
+ * current mode. */
+ return REGULATOR_STATUS_ON;
+}
+
+static irqreturn_t wm831x_dcdc_uv_irq(int irq, void *data)
+{
+ struct wm831x_dcdc *dcdc = data;
+
+ regulator_notifier_call_chain(dcdc->regulator,
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_dcdc_oc_irq(int irq, void *data)
+{
+ struct wm831x_dcdc *dcdc = data;
+
+ regulator_notifier_call_chain(dcdc->regulator,
+ REGULATOR_EVENT_OVER_CURRENT,
+ NULL);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * BUCKV specifics
+ */
+
+static int wm831x_buckv_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ if (selector <= 0x8)
+ return 600000;
+ if (selector <= WM831X_BUCKV_MAX_SELECTOR)
+ return 600000 + ((selector - 0x8) * 12500);
+ return -EINVAL;
+}
+
+static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
+ int min_uV, int max_uV)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 vsel;
+
+ if (min_uV < 600000)
+ vsel = 0;
+ else if (min_uV <= 1800000)
+ vsel = ((min_uV - 600000) / 12500) + 8;
+ else
+ return -EINVAL;
+
+ if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV)
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_VSEL_MASK, vsel);
+}
+
+static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+
+ return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV);
+}
+
+static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev,
+ int uV)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
+
+ return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV);
+}
+
+static int wm831x_buckv_get_voltage(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+ int val;
+
+ val = wm831x_reg_read(wm831x, reg);
+ if (val < 0)
+ return val;
+
+ return wm831x_buckv_list_voltage(rdev, val & WM831X_DC1_ON_VSEL_MASK);
+}
+
+/* Current limit options */
+static u16 wm831x_dcdc_ilim[] = {
+ 125, 250, 375, 500, 625, 750, 875, 1000
+};
+
+static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_dcdc_ilim); i++) {
+ if (max_uA <= wm831x_dcdc_ilim[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(wm831x_dcdc_ilim))
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_DC1_HC_THR_MASK, i);
+}
+
+static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2;
+ int val;
+
+ val = wm831x_reg_read(wm831x, reg);
+ if (val < 0)
+ return val;
+
+ return wm831x_dcdc_ilim[val & WM831X_DC1_HC_THR_MASK];
+}
+
+static struct regulator_ops wm831x_buckv_ops = {
+ .set_voltage = wm831x_buckv_set_voltage,
+ .get_voltage = wm831x_buckv_get_voltage,
+ .list_voltage = wm831x_buckv_list_voltage,
+ .set_suspend_voltage = wm831x_buckv_set_suspend_voltage,
+ .set_current_limit = wm831x_buckv_set_current_limit,
+ .get_current_limit = wm831x_buckv_get_current_limit,
+
+ .is_enabled = wm831x_dcdc_is_enabled,
+ .enable = wm831x_dcdc_enable,
+ .disable = wm831x_dcdc_disable,
+ .get_status = wm831x_dcdc_get_status,
+ .get_mode = wm831x_dcdc_get_mode,
+ .set_mode = wm831x_dcdc_set_mode,
+ .set_suspend_mode = wm831x_dcdc_set_suspend_mode,
+};
+
+static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
+ struct wm831x_dcdc *dcdc;
+ struct resource *res;
+ int ret, irq;
+
+ dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
+
+ if (pdata == NULL || pdata->dcdc[id] == NULL)
+ return -ENODEV;
+
+ dcdc = kzalloc(sizeof(struct wm831x_dcdc), GFP_KERNEL);
+ if (dcdc == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ dcdc->wm831x = wm831x;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ dcdc->base = res->start;
+
+ snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1);
+ dcdc->desc.name = dcdc->name;
+ dcdc->desc.id = id;
+ dcdc->desc.type = REGULATOR_VOLTAGE;
+ dcdc->desc.n_voltages = WM831X_BUCKV_MAX_SELECTOR + 1;
+ dcdc->desc.ops = &wm831x_buckv_ops;
+ dcdc->desc.owner = THIS_MODULE;
+
+ dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
+ pdata->dcdc[id], dcdc);
+ if (IS_ERR(dcdc->regulator)) {
+ ret = PTR_ERR(dcdc->regulator);
+ dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ irq = platform_get_irq_byname(pdev, "UV");
+ ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq,
+ IRQF_TRIGGER_RISING, dcdc->name,
+ dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err_regulator;
+ }
+
+ irq = platform_get_irq_byname(pdev, "HC");
+ ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_oc_irq,
+ IRQF_TRIGGER_RISING, dcdc->name,
+ dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
+ irq, ret);
+ goto err_uv;
+ }
+
+ platform_set_drvdata(pdev, dcdc);
+
+ return 0;
+
+err_uv:
+ wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
+err_regulator:
+ regulator_unregister(dcdc->regulator);
+err:
+ kfree(dcdc);
+ return ret;
+}
+
+static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
+{
+ struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+
+ wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc);
+ wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
+ regulator_unregister(dcdc->regulator);
+ kfree(dcdc);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_buckv_driver = {
+ .probe = wm831x_buckv_probe,
+ .remove = __devexit_p(wm831x_buckv_remove),
+ .driver = {
+ .name = "wm831x-buckv",
+ },
+};
+
+/*
+ * BUCKP specifics
+ */
+
+static int wm831x_buckp_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ if (selector <= WM831X_BUCKP_MAX_SELECTOR)
+ return 850000 + (selector * 25000);
+ else
+ return -EINVAL;
+}
+
+static int wm831x_buckp_set_voltage_int(struct regulator_dev *rdev, int reg,
+ int min_uV, int max_uV)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 vsel;
+
+ if (min_uV <= 34000000)
+ vsel = (min_uV - 850000) / 25000;
+ else
+ return -EINVAL;
+
+ if (wm831x_buckp_list_voltage(rdev, vsel) > max_uV)
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, vsel);
+}
+
+static int wm831x_buckp_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+
+ return wm831x_buckp_set_voltage_int(rdev, reg, min_uV, max_uV);
+}
+
+static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev,
+ int uV)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
+
+ return wm831x_buckp_set_voltage_int(rdev, reg, uV, uV);
+}
+
+static int wm831x_buckp_get_voltage(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+ int val;
+
+ val = wm831x_reg_read(wm831x, reg);
+ if (val < 0)
+ return val;
+
+ return wm831x_buckp_list_voltage(rdev, val & WM831X_DC3_ON_VSEL_MASK);
+}
+
+static struct regulator_ops wm831x_buckp_ops = {
+ .set_voltage = wm831x_buckp_set_voltage,
+ .get_voltage = wm831x_buckp_get_voltage,
+ .list_voltage = wm831x_buckp_list_voltage,
+ .set_suspend_voltage = wm831x_buckp_set_suspend_voltage,
+
+ .is_enabled = wm831x_dcdc_is_enabled,
+ .enable = wm831x_dcdc_enable,
+ .disable = wm831x_dcdc_disable,
+ .get_status = wm831x_dcdc_get_status,
+ .get_mode = wm831x_dcdc_get_mode,
+ .set_mode = wm831x_dcdc_set_mode,
+ .set_suspend_mode = wm831x_dcdc_set_suspend_mode,
+};
+
+static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
+ struct wm831x_dcdc *dcdc;
+ struct resource *res;
+ int ret, irq;
+
+ dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
+
+ if (pdata == NULL || pdata->dcdc[id] == NULL)
+ return -ENODEV;
+
+ dcdc = kzalloc(sizeof(struct wm831x_dcdc), GFP_KERNEL);
+ if (dcdc == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ dcdc->wm831x = wm831x;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ dcdc->base = res->start;
+
+ snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1);
+ dcdc->desc.name = dcdc->name;
+ dcdc->desc.id = id;
+ dcdc->desc.type = REGULATOR_VOLTAGE;
+ dcdc->desc.n_voltages = WM831X_BUCKP_MAX_SELECTOR + 1;
+ dcdc->desc.ops = &wm831x_buckp_ops;
+ dcdc->desc.owner = THIS_MODULE;
+
+ dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
+ pdata->dcdc[id], dcdc);
+ if (IS_ERR(dcdc->regulator)) {
+ ret = PTR_ERR(dcdc->regulator);
+ dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ irq = platform_get_irq_byname(pdev, "UV");
+ ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq,
+ IRQF_TRIGGER_RISING, dcdc->name,
+ dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err_regulator;
+ }
+
+ platform_set_drvdata(pdev, dcdc);
+
+ return 0;
+
+err_regulator:
+ regulator_unregister(dcdc->regulator);
+err:
+ kfree(dcdc);
+ return ret;
+}
+
+static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
+{
+ struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+
+ wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
+ regulator_unregister(dcdc->regulator);
+ kfree(dcdc);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_buckp_driver = {
+ .probe = wm831x_buckp_probe,
+ .remove = __devexit_p(wm831x_buckp_remove),
+ .driver = {
+ .name = "wm831x-buckp",
+ },
+};
+
+/*
+ * DCDC boost convertors
+ */
+
+static int wm831x_boostp_get_status(struct regulator_dev *rdev)
+{
+ struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+ int ret;
+
+ /* First, check for errors */
+ ret = wm831x_reg_read(wm831x, WM831X_DCDC_UV_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (ret & (1 << rdev_get_id(rdev))) {
+ dev_dbg(wm831x->dev, "DCDC%d under voltage\n",
+ rdev_get_id(rdev) + 1);
+ return REGULATOR_STATUS_ERROR;
+ }
+
+ /* Is the regulator on? */
+ ret = wm831x_reg_read(wm831x, WM831X_DCDC_STATUS);
+ if (ret < 0)
+ return ret;
+ if (ret & (1 << rdev_get_id(rdev)))
+ return REGULATOR_STATUS_ON;
+ else
+ return REGULATOR_STATUS_OFF;
+}
+
+static struct regulator_ops wm831x_boostp_ops = {
+ .get_status = wm831x_boostp_get_status,
+
+ .is_enabled = wm831x_dcdc_is_enabled,
+ .enable = wm831x_dcdc_enable,
+ .disable = wm831x_dcdc_disable,
+};
+
+static __devinit int wm831x_boostp_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
+ struct wm831x_dcdc *dcdc;
+ struct resource *res;
+ int ret, irq;
+
+ dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
+
+ if (pdata == NULL || pdata->dcdc[id] == NULL)
+ return -ENODEV;
+
+ dcdc = kzalloc(sizeof(struct wm831x_dcdc), GFP_KERNEL);
+ if (dcdc == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ dcdc->wm831x = wm831x;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ dcdc->base = res->start;
+
+ snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1);
+ dcdc->desc.name = dcdc->name;
+ dcdc->desc.id = id;
+ dcdc->desc.type = REGULATOR_VOLTAGE;
+ dcdc->desc.ops = &wm831x_boostp_ops;
+ dcdc->desc.owner = THIS_MODULE;
+
+ dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
+ pdata->dcdc[id], dcdc);
+ if (IS_ERR(dcdc->regulator)) {
+ ret = PTR_ERR(dcdc->regulator);
+ dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ irq = platform_get_irq_byname(pdev, "UV");
+ ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq,
+ IRQF_TRIGGER_RISING, dcdc->name,
+ dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err_regulator;
+ }
+
+ platform_set_drvdata(pdev, dcdc);
+
+ return 0;
+
+err_regulator:
+ regulator_unregister(dcdc->regulator);
+err:
+ kfree(dcdc);
+ return ret;
+}
+
+static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
+{
+ struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
+ struct wm831x *wm831x = dcdc->wm831x;
+
+ wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
+ regulator_unregister(dcdc->regulator);
+ kfree(dcdc);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_boostp_driver = {
+ .probe = wm831x_boostp_probe,
+ .remove = __devexit_p(wm831x_boostp_remove),
+ .driver = {
+ .name = "wm831x-boostp",
+ },
+};
+
+/*
+ * External Power Enable
+ *
+ * These aren't actually DCDCs but look like them in hardware so share
+ * code.
+ */
+
+#define WM831X_EPE_BASE 6
+
+static struct regulator_ops wm831x_epe_ops = {
+ .is_enabled = wm831x_dcdc_is_enabled,
+ .enable = wm831x_dcdc_enable,
+ .disable = wm831x_dcdc_disable,
+ .get_status = wm831x_dcdc_get_status,
+};
+
+static __devinit int wm831x_epe_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int id = pdev->id % ARRAY_SIZE(pdata->epe);
+ struct wm831x_dcdc *dcdc;
+ int ret;
+
+ dev_dbg(&pdev->dev, "Probing EPE%d\n", id + 1);
+
+ if (pdata == NULL || pdata->epe[id] == NULL)
+ return -ENODEV;
+
+ dcdc = kzalloc(sizeof(struct wm831x_dcdc), GFP_KERNEL);
+ if (dcdc == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ dcdc->wm831x = wm831x;
+
+ /* For current parts this is correct; probably need to revisit
+ * in future.
+ */
+ snprintf(dcdc->name, sizeof(dcdc->name), "EPE%d", id + 1);
+ dcdc->desc.name = dcdc->name;
+ dcdc->desc.id = id + WM831X_EPE_BASE; /* Offset in DCDC registers */
+ dcdc->desc.ops = &wm831x_epe_ops;
+ dcdc->desc.type = REGULATOR_VOLTAGE;
+ dcdc->desc.owner = THIS_MODULE;
+
+ dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
+ pdata->epe[id], dcdc);
+ if (IS_ERR(dcdc->regulator)) {
+ ret = PTR_ERR(dcdc->regulator);
+ dev_err(wm831x->dev, "Failed to register EPE%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, dcdc);
+
+ return 0;
+
+err:
+ kfree(dcdc);
+ return ret;
+}
+
+static __devexit int wm831x_epe_remove(struct platform_device *pdev)
+{
+ struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
+
+ regulator_unregister(dcdc->regulator);
+ kfree(dcdc);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_epe_driver = {
+ .probe = wm831x_epe_probe,
+ .remove = __devexit_p(wm831x_epe_remove),
+ .driver = {
+ .name = "wm831x-epe",
+ },
+};
+
+static int __init wm831x_dcdc_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&wm831x_buckv_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x BUCKV driver: %d\n", ret);
+
+ ret = platform_driver_register(&wm831x_buckp_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x BUCKP driver: %d\n", ret);
+
+ ret = platform_driver_register(&wm831x_boostp_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x BOOST driver: %d\n", ret);
+
+ ret = platform_driver_register(&wm831x_epe_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x EPE driver: %d\n", ret);
+
+ return 0;
+}
+subsys_initcall(wm831x_dcdc_init);
+
+static void __exit wm831x_dcdc_exit(void)
+{
+ platform_driver_unregister(&wm831x_epe_driver);
+ platform_driver_unregister(&wm831x_boostp_driver);
+ platform_driver_unregister(&wm831x_buckp_driver);
+ platform_driver_unregister(&wm831x_buckv_driver);
+}
+module_exit(wm831x_dcdc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM831x DC-DC convertor driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-buckv");
+MODULE_ALIAS("platform:wm831x-buckp");
diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c
new file mode 100644
index 0000000..1d8d987
--- /dev/null
+++ b/drivers/regulator/wm831x-isink.c
@@ -0,0 +1,260 @@
+/*
+ * wm831x-isink.c -- Current sink driver for the WM831x series
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/regulator.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+#define WM831X_ISINK_MAX_NAME 7
+
+struct wm831x_isink {
+ char name[WM831X_ISINK_MAX_NAME];
+ struct regulator_desc desc;
+ int reg;
+ struct wm831x *wm831x;
+ struct regulator_dev *regulator;
+};
+
+static int wm831x_isink_enable(struct regulator_dev *rdev)
+{
+ struct wm831x_isink *isink = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = isink->wm831x;
+ int ret;
+
+ /* We have a two stage enable: first start the ISINK... */
+ ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA,
+ WM831X_CS1_ENA);
+ if (ret != 0)
+ return ret;
+
+ /* ...then enable drive */
+ ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE,
+ WM831X_CS1_DRIVE);
+ if (ret != 0)
+ wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
+
+ return ret;
+
+}
+
+static int wm831x_isink_disable(struct regulator_dev *rdev)
+{
+ struct wm831x_isink *isink = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = isink->wm831x;
+ int ret;
+
+ ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+
+}
+
+static int wm831x_isink_is_enabled(struct regulator_dev *rdev)
+{
+ struct wm831x_isink *isink = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = isink->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, isink->reg);
+ if (ret < 0)
+ return ret;
+
+ if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) ==
+ (WM831X_CS1_ENA | WM831X_CS1_DRIVE))
+ return 1;
+ else
+ return 0;
+}
+
+static int wm831x_isink_set_current(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct wm831x_isink *isink = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = isink->wm831x;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_isinkv_values); i++) {
+ int val = wm831x_isinkv_values[i];
+ if (min_uA >= val && val <= max_uA) {
+ ret = wm831x_set_bits(wm831x, isink->reg,
+ WM831X_CS1_ISEL_MASK, i);
+ return ret;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int wm831x_isink_get_current(struct regulator_dev *rdev)
+{
+ struct wm831x_isink *isink = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = isink->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, isink->reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= WM831X_CS1_ISEL_MASK;
+ if (ret > WM831X_ISINK_MAX_ISEL)
+ ret = WM831X_ISINK_MAX_ISEL;
+
+ return wm831x_isinkv_values[ret];
+}
+
+static struct regulator_ops wm831x_isink_ops = {
+ .is_enabled = wm831x_isink_is_enabled,
+ .enable = wm831x_isink_enable,
+ .disable = wm831x_isink_disable,
+ .set_current_limit = wm831x_isink_set_current,
+ .get_current_limit = wm831x_isink_get_current,
+};
+
+static irqreturn_t wm831x_isink_irq(int irq, void *data)
+{
+ struct wm831x_isink *isink = data;
+
+ regulator_notifier_call_chain(isink->regulator,
+ REGULATOR_EVENT_OVER_CURRENT,
+ NULL);
+
+ return IRQ_HANDLED;
+}
+
+
+static __devinit int wm831x_isink_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_isink *isink;
+ int id = pdev->id % ARRAY_SIZE(pdata->isink);
+ struct resource *res;
+ int ret, irq;
+
+ dev_dbg(&pdev->dev, "Probing ISINK%d\n", id + 1);
+
+ if (pdata == NULL || pdata->isink[id] == NULL)
+ return -ENODEV;
+
+ isink = kzalloc(sizeof(struct wm831x_isink), GFP_KERNEL);
+ if (isink == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ isink->reg = res->start;
+
+ /* For current parts this is correct; probably need to revisit
+ * in future.
+ */
+ snprintf(isink->name, sizeof(isink->name), "ISINK%d", id + 1);
+ isink->desc.name = isink->name;
+ isink->desc.id = id;
+ isink->desc.ops = &wm831x_isink_ops;
+ isink->desc.type = REGULATOR_CURRENT;
+ isink->desc.owner = THIS_MODULE;
+
+ isink->regulator = regulator_register(&isink->desc, &pdev->dev,
+ pdata->isink[id], isink);
+ if (IS_ERR(isink->regulator)) {
+ ret = PTR_ERR(isink->regulator);
+ dev_err(wm831x->dev, "Failed to register ISINK%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = wm831x_request_irq(wm831x, irq, wm831x_isink_irq,
+ IRQF_TRIGGER_RISING, isink->name,
+ isink);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
+ irq, ret);
+ goto err_regulator;
+ }
+
+ platform_set_drvdata(pdev, isink);
+
+ return 0;
+
+err_regulator:
+ regulator_unregister(isink->regulator);
+err:
+ kfree(isink);
+ return ret;
+}
+
+static __devexit int wm831x_isink_remove(struct platform_device *pdev)
+{
+ struct wm831x_isink *isink = platform_get_drvdata(pdev);
+ struct wm831x *wm831x = isink->wm831x;
+
+ wm831x_free_irq(wm831x, platform_get_irq(pdev, 0), isink);
+
+ regulator_unregister(isink->regulator);
+ kfree(isink);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_isink_driver = {
+ .probe = wm831x_isink_probe,
+ .remove = __devexit_p(wm831x_isink_remove),
+ .driver = {
+ .name = "wm831x-isink",
+ },
+};
+
+static int __init wm831x_isink_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&wm831x_isink_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x ISINK driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(wm831x_isink_init);
+
+static void __exit wm831x_isink_exit(void)
+{
+ platform_driver_unregister(&wm831x_isink_driver);
+}
+module_exit(wm831x_isink_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM831x current sink driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-isink");
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
new file mode 100644
index 0000000..bb61aed
--- /dev/null
+++ b/drivers/regulator/wm831x-ldo.c
@@ -0,0 +1,852 @@
+/*
+ * wm831x-ldo.c -- LDO driver for the WM831x series
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/regulator.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+#define WM831X_LDO_MAX_NAME 6
+
+#define WM831X_LDO_CONTROL 0
+#define WM831X_LDO_ON_CONTROL 1
+#define WM831X_LDO_SLEEP_CONTROL 2
+
+#define WM831X_ALIVE_LDO_ON_CONTROL 0
+#define WM831X_ALIVE_LDO_SLEEP_CONTROL 1
+
+struct wm831x_ldo {
+ char name[WM831X_LDO_MAX_NAME];
+ struct regulator_desc desc;
+ int base;
+ struct wm831x *wm831x;
+ struct regulator_dev *regulator;
+};
+
+/*
+ * Shared
+ */
+
+static int wm831x_ldo_is_enabled(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+ int reg;
+
+ reg = wm831x_reg_read(wm831x, WM831X_LDO_ENABLE);
+ if (reg < 0)
+ return reg;
+
+ if (reg & mask)
+ return 1;
+ else
+ return 0;
+}
+
+static int wm831x_ldo_enable(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+
+ return wm831x_set_bits(wm831x, WM831X_LDO_ENABLE, mask, mask);
+}
+
+static int wm831x_ldo_disable(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+
+ return wm831x_set_bits(wm831x, WM831X_LDO_ENABLE, mask, 0);
+}
+
+static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data)
+{
+ struct wm831x_ldo *ldo = data;
+
+ regulator_notifier_call_chain(ldo->regulator,
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * General purpose LDOs
+ */
+
+#define WM831X_GP_LDO_SELECTOR_LOW 0xe
+#define WM831X_GP_LDO_MAX_SELECTOR 0x1f
+
+static int wm831x_gp_ldo_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ /* 0.9-1.6V in 50mV steps */
+ if (selector <= WM831X_GP_LDO_SELECTOR_LOW)
+ return 900000 + (selector * 50000);
+ /* 1.7-3.3V in 50mV steps */
+ if (selector <= WM831X_GP_LDO_MAX_SELECTOR)
+ return 1600000 + ((selector - WM831X_GP_LDO_SELECTOR_LOW)
+ * 100000);
+ return -EINVAL;
+}
+
+static int wm831x_gp_ldo_set_voltage_int(struct regulator_dev *rdev, int reg,
+ int min_uV, int max_uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int vsel, ret;
+
+ if (min_uV < 900000)
+ vsel = 0;
+ else if (min_uV < 1700000)
+ vsel = ((min_uV - 900000) / 50000);
+ else
+ vsel = ((min_uV - 1700000) / 100000)
+ + WM831X_GP_LDO_SELECTOR_LOW + 1;
+
+ ret = wm831x_gp_ldo_list_voltage(rdev, vsel);
+ if (ret < 0)
+ return ret;
+ if (ret < min_uV || ret > max_uV)
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_LDO1_ON_VSEL_MASK, vsel);
+}
+
+static int wm831x_gp_ldo_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ int reg = ldo->base + WM831X_LDO_ON_CONTROL;
+
+ return wm831x_gp_ldo_set_voltage_int(rdev, reg, min_uV, max_uV);
+}
+
+static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev,
+ int uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ int reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
+
+ return wm831x_gp_ldo_set_voltage_int(rdev, reg, uV, uV);
+}
+
+static int wm831x_gp_ldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int reg = ldo->base + WM831X_LDO_ON_CONTROL;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= WM831X_LDO1_ON_VSEL_MASK;
+
+ return wm831x_gp_ldo_list_voltage(rdev, ret);
+}
+
+static unsigned int wm831x_gp_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int ctrl_reg = ldo->base + WM831X_LDO_CONTROL;
+ int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
+ unsigned int ret;
+
+ ret = wm831x_reg_read(wm831x, on_reg);
+ if (ret < 0)
+ return 0;
+
+ if (!(ret & WM831X_LDO1_ON_MODE))
+ return REGULATOR_MODE_NORMAL;
+
+ ret = wm831x_reg_read(wm831x, ctrl_reg);
+ if (ret < 0)
+ return 0;
+
+ if (ret & WM831X_LDO1_LP_MODE)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_IDLE;
+}
+
+static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int ctrl_reg = ldo->base + WM831X_LDO_CONTROL;
+ int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
+ int ret;
+
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ ret = wm831x_set_bits(wm831x, on_reg,
+ WM831X_LDO1_ON_MODE, 0);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case REGULATOR_MODE_IDLE:
+ ret = wm831x_set_bits(wm831x, ctrl_reg,
+ WM831X_LDO1_LP_MODE,
+ WM831X_LDO1_LP_MODE);
+ if (ret < 0)
+ return ret;
+
+ ret = wm831x_set_bits(wm831x, on_reg,
+ WM831X_LDO1_ON_MODE,
+ WM831X_LDO1_ON_MODE);
+ if (ret < 0)
+ return ret;
+
+ case REGULATOR_MODE_STANDBY:
+ ret = wm831x_set_bits(wm831x, ctrl_reg,
+ WM831X_LDO1_LP_MODE, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = wm831x_set_bits(wm831x, on_reg,
+ WM831X_LDO1_ON_MODE,
+ WM831X_LDO1_ON_MODE);
+ if (ret < 0)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm831x_gp_ldo_get_status(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+ int ret;
+
+ /* Is the regulator on? */
+ ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS);
+ if (ret < 0)
+ return ret;
+ if (!(ret & mask))
+ return REGULATOR_STATUS_OFF;
+
+ /* Is it reporting under voltage? */
+ ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+ if (ret & mask)
+ return REGULATOR_STATUS_ERROR;
+
+ ret = wm831x_gp_ldo_get_mode(rdev);
+ if (ret < 0)
+ return ret;
+ else
+ return regulator_mode_to_status(ret);
+}
+
+static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV,
+ int output_uV, int load_uA)
+{
+ if (load_uA < 20000)
+ return REGULATOR_MODE_STANDBY;
+ if (load_uA < 50000)
+ return REGULATOR_MODE_IDLE;
+ return REGULATOR_MODE_NORMAL;
+}
+
+
+static struct regulator_ops wm831x_gp_ldo_ops = {
+ .list_voltage = wm831x_gp_ldo_list_voltage,
+ .get_voltage = wm831x_gp_ldo_get_voltage,
+ .set_voltage = wm831x_gp_ldo_set_voltage,
+ .set_suspend_voltage = wm831x_gp_ldo_set_suspend_voltage,
+ .get_mode = wm831x_gp_ldo_get_mode,
+ .set_mode = wm831x_gp_ldo_set_mode,
+ .get_status = wm831x_gp_ldo_get_status,
+ .get_optimum_mode = wm831x_gp_ldo_get_optimum_mode,
+
+ .is_enabled = wm831x_ldo_is_enabled,
+ .enable = wm831x_ldo_enable,
+ .disable = wm831x_ldo_disable,
+};
+
+static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+ struct wm831x_ldo *ldo;
+ struct resource *res;
+ int ret, irq;
+
+ dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
+
+ if (pdata == NULL || pdata->ldo[id] == NULL)
+ return -ENODEV;
+
+ ldo = kzalloc(sizeof(struct wm831x_ldo), GFP_KERNEL);
+ if (ldo == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ ldo->wm831x = wm831x;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ ldo->base = res->start;
+
+ snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1);
+ ldo->desc.name = ldo->name;
+ ldo->desc.id = id;
+ ldo->desc.type = REGULATOR_VOLTAGE;
+ ldo->desc.n_voltages = WM831X_GP_LDO_MAX_SELECTOR + 1;
+ ldo->desc.ops = &wm831x_gp_ldo_ops;
+ ldo->desc.owner = THIS_MODULE;
+
+ ldo->regulator = regulator_register(&ldo->desc, &pdev->dev,
+ pdata->ldo[id], ldo);
+ if (IS_ERR(ldo->regulator)) {
+ ret = PTR_ERR(ldo->regulator);
+ dev_err(wm831x->dev, "Failed to register LDO%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ irq = platform_get_irq_byname(pdev, "UV");
+ ret = wm831x_request_irq(wm831x, irq, wm831x_ldo_uv_irq,
+ IRQF_TRIGGER_RISING, ldo->name,
+ ldo);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err_regulator;
+ }
+
+ platform_set_drvdata(pdev, ldo);
+
+ return 0;
+
+err_regulator:
+ regulator_unregister(ldo->regulator);
+err:
+ kfree(ldo);
+ return ret;
+}
+
+static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
+{
+ struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
+ struct wm831x *wm831x = ldo->wm831x;
+
+ wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo);
+ regulator_unregister(ldo->regulator);
+ kfree(ldo);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_gp_ldo_driver = {
+ .probe = wm831x_gp_ldo_probe,
+ .remove = __devexit_p(wm831x_gp_ldo_remove),
+ .driver = {
+ .name = "wm831x-ldo",
+ },
+};
+
+/*
+ * Analogue LDOs
+ */
+
+
+#define WM831X_ALDO_SELECTOR_LOW 0xc
+#define WM831X_ALDO_MAX_SELECTOR 0x1f
+
+static int wm831x_aldo_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ /* 1-1.6V in 50mV steps */
+ if (selector <= WM831X_ALDO_SELECTOR_LOW)
+ return 1000000 + (selector * 50000);
+ /* 1.7-3.5V in 50mV steps */
+ if (selector <= WM831X_ALDO_MAX_SELECTOR)
+ return 1600000 + ((selector - WM831X_ALDO_SELECTOR_LOW)
+ * 100000);
+ return -EINVAL;
+}
+
+static int wm831x_aldo_set_voltage_int(struct regulator_dev *rdev, int reg,
+ int min_uV, int max_uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int vsel, ret;
+
+ if (min_uV < 1000000)
+ vsel = 0;
+ else if (min_uV < 1700000)
+ vsel = ((min_uV - 1000000) / 50000);
+ else
+ vsel = ((min_uV - 1700000) / 100000)
+ + WM831X_ALDO_SELECTOR_LOW + 1;
+
+ ret = wm831x_aldo_list_voltage(rdev, vsel);
+ if (ret < 0)
+ return ret;
+ if (ret < min_uV || ret > max_uV)
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_LDO7_ON_VSEL_MASK, vsel);
+}
+
+static int wm831x_aldo_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ int reg = ldo->base + WM831X_LDO_ON_CONTROL;
+
+ return wm831x_aldo_set_voltage_int(rdev, reg, min_uV, max_uV);
+}
+
+static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev,
+ int uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ int reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
+
+ return wm831x_aldo_set_voltage_int(rdev, reg, uV, uV);
+}
+
+static int wm831x_aldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int reg = ldo->base + WM831X_LDO_ON_CONTROL;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= WM831X_LDO7_ON_VSEL_MASK;
+
+ return wm831x_aldo_list_voltage(rdev, ret);
+}
+
+static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
+ unsigned int ret;
+
+ ret = wm831x_reg_read(wm831x, on_reg);
+ if (ret < 0)
+ return 0;
+
+ if (ret & WM831X_LDO7_ON_MODE)
+ return REGULATOR_MODE_IDLE;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int wm831x_aldo_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int ctrl_reg = ldo->base + WM831X_LDO_CONTROL;
+ int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
+ int ret;
+
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ ret = wm831x_set_bits(wm831x, on_reg,
+ WM831X_LDO7_ON_MODE, 0);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case REGULATOR_MODE_IDLE:
+ ret = wm831x_set_bits(wm831x, ctrl_reg,
+ WM831X_LDO7_ON_MODE,
+ WM831X_LDO7_ON_MODE);
+ if (ret < 0)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm831x_aldo_get_status(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+ int ret;
+
+ /* Is the regulator on? */
+ ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS);
+ if (ret < 0)
+ return ret;
+ if (!(ret & mask))
+ return REGULATOR_STATUS_OFF;
+
+ /* Is it reporting under voltage? */
+ ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+ if (ret & mask)
+ return REGULATOR_STATUS_ERROR;
+
+ ret = wm831x_aldo_get_mode(rdev);
+ if (ret < 0)
+ return ret;
+ else
+ return regulator_mode_to_status(ret);
+}
+
+static struct regulator_ops wm831x_aldo_ops = {
+ .list_voltage = wm831x_aldo_list_voltage,
+ .get_voltage = wm831x_aldo_get_voltage,
+ .set_voltage = wm831x_aldo_set_voltage,
+ .set_suspend_voltage = wm831x_aldo_set_suspend_voltage,
+ .get_mode = wm831x_aldo_get_mode,
+ .set_mode = wm831x_aldo_set_mode,
+ .get_status = wm831x_aldo_get_status,
+
+ .is_enabled = wm831x_ldo_is_enabled,
+ .enable = wm831x_ldo_enable,
+ .disable = wm831x_ldo_disable,
+};
+
+static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+ struct wm831x_ldo *ldo;
+ struct resource *res;
+ int ret, irq;
+
+ dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
+
+ if (pdata == NULL || pdata->ldo[id] == NULL)
+ return -ENODEV;
+
+ ldo = kzalloc(sizeof(struct wm831x_ldo), GFP_KERNEL);
+ if (ldo == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ ldo->wm831x = wm831x;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ ldo->base = res->start;
+
+ snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1);
+ ldo->desc.name = ldo->name;
+ ldo->desc.id = id;
+ ldo->desc.type = REGULATOR_VOLTAGE;
+ ldo->desc.n_voltages = WM831X_ALDO_MAX_SELECTOR + 1;
+ ldo->desc.ops = &wm831x_aldo_ops;
+ ldo->desc.owner = THIS_MODULE;
+
+ ldo->regulator = regulator_register(&ldo->desc, &pdev->dev,
+ pdata->ldo[id], ldo);
+ if (IS_ERR(ldo->regulator)) {
+ ret = PTR_ERR(ldo->regulator);
+ dev_err(wm831x->dev, "Failed to register LDO%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ irq = platform_get_irq_byname(pdev, "UV");
+ ret = wm831x_request_irq(wm831x, irq, wm831x_ldo_uv_irq,
+ IRQF_TRIGGER_RISING, ldo->name,
+ ldo);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err_regulator;
+ }
+
+ platform_set_drvdata(pdev, ldo);
+
+ return 0;
+
+err_regulator:
+ regulator_unregister(ldo->regulator);
+err:
+ kfree(ldo);
+ return ret;
+}
+
+static __devexit int wm831x_aldo_remove(struct platform_device *pdev)
+{
+ struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
+ struct wm831x *wm831x = ldo->wm831x;
+
+ wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo);
+ regulator_unregister(ldo->regulator);
+ kfree(ldo);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_aldo_driver = {
+ .probe = wm831x_aldo_probe,
+ .remove = __devexit_p(wm831x_aldo_remove),
+ .driver = {
+ .name = "wm831x-aldo",
+ },
+};
+
+/*
+ * Alive LDO
+ */
+
+#define WM831X_ALIVE_LDO_MAX_SELECTOR 0xf
+
+static int wm831x_alive_ldo_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ /* 0.8-1.55V in 50mV steps */
+ if (selector <= WM831X_ALIVE_LDO_MAX_SELECTOR)
+ return 800000 + (selector * 50000);
+ return -EINVAL;
+}
+
+static int wm831x_alive_ldo_set_voltage_int(struct regulator_dev *rdev,
+ int reg,
+ int min_uV, int max_uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int vsel, ret;
+
+ vsel = (min_uV - 800000) / 50000;
+
+ ret = wm831x_alive_ldo_list_voltage(rdev, vsel);
+ if (ret < 0)
+ return ret;
+ if (ret < min_uV || ret > max_uV)
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_LDO11_ON_VSEL_MASK, vsel);
+}
+
+static int wm831x_alive_ldo_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ int reg = ldo->base + WM831X_ALIVE_LDO_ON_CONTROL;
+
+ return wm831x_alive_ldo_set_voltage_int(rdev, reg, min_uV, max_uV);
+}
+
+static int wm831x_alive_ldo_set_suspend_voltage(struct regulator_dev *rdev,
+ int uV)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ int reg = ldo->base + WM831X_ALIVE_LDO_SLEEP_CONTROL;
+
+ return wm831x_alive_ldo_set_voltage_int(rdev, reg, uV, uV);
+}
+
+static int wm831x_alive_ldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int reg = ldo->base + WM831X_ALIVE_LDO_ON_CONTROL;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= WM831X_LDO11_ON_VSEL_MASK;
+
+ return wm831x_alive_ldo_list_voltage(rdev, ret);
+}
+
+static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev)
+{
+ struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
+ struct wm831x *wm831x = ldo->wm831x;
+ int mask = 1 << rdev_get_id(rdev);
+ int ret;
+
+ /* Is the regulator on? */
+ ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS);
+ if (ret < 0)
+ return ret;
+ if (ret & mask)
+ return REGULATOR_STATUS_ON;
+ else
+ return REGULATOR_STATUS_OFF;
+}
+
+static struct regulator_ops wm831x_alive_ldo_ops = {
+ .list_voltage = wm831x_alive_ldo_list_voltage,
+ .get_voltage = wm831x_alive_ldo_get_voltage,
+ .set_voltage = wm831x_alive_ldo_set_voltage,
+ .set_suspend_voltage = wm831x_alive_ldo_set_suspend_voltage,
+ .get_status = wm831x_alive_ldo_get_status,
+
+ .is_enabled = wm831x_ldo_is_enabled,
+ .enable = wm831x_ldo_enable,
+ .disable = wm831x_ldo_disable,
+};
+
+static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+ struct wm831x_ldo *ldo;
+ struct resource *res;
+ int ret;
+
+ dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
+
+ if (pdata == NULL || pdata->ldo[id] == NULL)
+ return -ENODEV;
+
+ ldo = kzalloc(sizeof(struct wm831x_ldo), GFP_KERNEL);
+ if (ldo == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ ldo->wm831x = wm831x;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ ldo->base = res->start;
+
+ snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1);
+ ldo->desc.name = ldo->name;
+ ldo->desc.id = id;
+ ldo->desc.type = REGULATOR_VOLTAGE;
+ ldo->desc.n_voltages = WM831X_ALIVE_LDO_MAX_SELECTOR + 1;
+ ldo->desc.ops = &wm831x_alive_ldo_ops;
+ ldo->desc.owner = THIS_MODULE;
+
+ ldo->regulator = regulator_register(&ldo->desc, &pdev->dev,
+ pdata->ldo[id], ldo);
+ if (IS_ERR(ldo->regulator)) {
+ ret = PTR_ERR(ldo->regulator);
+ dev_err(wm831x->dev, "Failed to register LDO%d: %d\n",
+ id + 1, ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, ldo);
+
+ return 0;
+
+err:
+ kfree(ldo);
+ return ret;
+}
+
+static __devexit int wm831x_alive_ldo_remove(struct platform_device *pdev)
+{
+ struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
+
+ regulator_unregister(ldo->regulator);
+ kfree(ldo);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_alive_ldo_driver = {
+ .probe = wm831x_alive_ldo_probe,
+ .remove = __devexit_p(wm831x_alive_ldo_remove),
+ .driver = {
+ .name = "wm831x-alive-ldo",
+ },
+};
+
+static int __init wm831x_ldo_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&wm831x_gp_ldo_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x GP LDO driver: %d\n", ret);
+
+ ret = platform_driver_register(&wm831x_aldo_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x ALDO driver: %d\n", ret);
+
+ ret = platform_driver_register(&wm831x_alive_ldo_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x alive LDO driver: %d\n",
+ ret);
+
+ return 0;
+}
+subsys_initcall(wm831x_ldo_init);
+
+static void __exit wm831x_ldo_exit(void)
+{
+ platform_driver_unregister(&wm831x_alive_ldo_driver);
+ platform_driver_unregister(&wm831x_aldo_driver);
+ platform_driver_unregister(&wm831x_gp_ldo_driver);
+}
+module_exit(wm831x_ldo_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x LDO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-ldo");
+MODULE_ALIAS("platform:wm831x-aldo");
+MODULE_ALIAS("platform:wm831x-aliveldo");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 81adbdb..73771b0 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -518,6 +518,16 @@ config RTC_DRV_V3020
This driver can also be built as a module. If so, the module
will be called rtc-v3020.
+config RTC_DRV_WM831X
+ tristate "Wolfson Microelectronics WM831x RTC"
+ depends on MFD_WM831X
+ help
+ If you say yes here you will get support for the RTC subsystem
+ of the Wolfson Microelectronics WM831X series PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called "rtc-wm831x".
+
config RTC_DRV_WM8350
tristate "Wolfson Microelectronics WM8350 RTC"
depends on MFD_WM8350
@@ -535,6 +545,15 @@ config RTC_DRV_PCF50633
If you say yes here you get support for the RTC subsystem of the
NXP PCF50633 used in embedded systems.
+config RTC_DRV_AB3100
+ tristate "ST-Ericsson AB3100 RTC"
+ depends on AB3100_CORE
+ default y if AB3100_CORE
+ help
+ Select this to enable the ST-Ericsson AB3100 Mixed Signal IC RTC
+ support. This chip contains a battery- and capacitor-backed RTC.
+
+
comment "on-CPU RTC drivers"
config RTC_DRV_OMAP
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 3c0f2b2..5e152ff 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -17,6 +17,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
# Keep the list ordered.
+obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
@@ -74,6 +75,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c
new file mode 100644
index 0000000..4704aac
--- /dev/null
+++ b/drivers/rtc/rtc-ab3100.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007-2009 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * RTC clock driver for the AB3100 Analog Baseband Chip
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/mfd/ab3100.h>
+
+/* Clock rate in Hz */
+#define AB3100_RTC_CLOCK_RATE 32768
+
+/*
+ * The AB3100 RTC registers. These are the same for
+ * AB3000 and AB3100.
+ * Control register:
+ * Bit 0: RTC Monitor cleared=0, active=1, if you set it
+ * to 1 it remains active until RTC power is lost.
+ * Bit 1: 32 kHz Oscillator, 0 = on, 1 = bypass
+ * Bit 2: Alarm on, 0 = off, 1 = on
+ * Bit 3: 32 kHz buffer disabling, 0 = enabled, 1 = disabled
+ */
+#define AB3100_RTC 0x53
+/* default setting, buffer disabled, alarm on */
+#define RTC_SETTING 0x30
+/* Alarm when AL0-AL3 == TI0-TI3 */
+#define AB3100_AL0 0x56
+#define AB3100_AL1 0x57
+#define AB3100_AL2 0x58
+#define AB3100_AL3 0x59
+/* This 48-bit register that counts up at 32768 Hz */
+#define AB3100_TI0 0x5a
+#define AB3100_TI1 0x5b
+#define AB3100_TI2 0x5c
+#define AB3100_TI3 0x5d
+#define AB3100_TI4 0x5e
+#define AB3100_TI5 0x5f
+
+/*
+ * RTC clock functions and device struct declaration
+ */
+static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct ab3100 *ab3100_data = dev_get_drvdata(dev);
+ u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2,
+ AB3100_TI3, AB3100_TI4, AB3100_TI5};
+ unsigned char buf[6];
+ u64 fat_time = (u64) secs * AB3100_RTC_CLOCK_RATE * 2;
+ int err = 0;
+ int i;
+
+ buf[0] = (fat_time) & 0xFF;
+ buf[1] = (fat_time >> 8) & 0xFF;
+ buf[2] = (fat_time >> 16) & 0xFF;
+ buf[3] = (fat_time >> 24) & 0xFF;
+ buf[4] = (fat_time >> 32) & 0xFF;
+ buf[5] = (fat_time >> 40) & 0xFF;
+
+ for (i = 0; i < 6; i++) {
+ err = ab3100_set_register_interruptible(ab3100_data,
+ regs[i], buf[i]);
+ if (err)
+ return err;
+ }
+
+ /* Set the flag to mark that the clock is now set */
+ return ab3100_mask_and_set_register_interruptible(ab3100_data,
+ AB3100_RTC,
+ 0xFE, 0x01);
+
+}
+
+static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct ab3100 *ab3100_data = dev_get_drvdata(dev);
+ unsigned long time;
+ u8 rtcval;
+ int err;
+
+ err = ab3100_get_register_interruptible(ab3100_data,
+ AB3100_RTC, &rtcval);
+ if (err)
+ return err;
+
+ if (!(rtcval & 0x01)) {
+ dev_info(dev, "clock not set (lost power)");
+ return -EINVAL;
+ } else {
+ u64 fat_time;
+ u8 buf[6];
+
+ /* Read out time registers */
+ err = ab3100_get_register_page_interruptible(ab3100_data,
+ AB3100_TI0,
+ buf, 6);
+ if (err != 0)
+ return err;
+
+ fat_time = ((u64) buf[5] << 40) | ((u64) buf[4] << 32) |
+ ((u64) buf[3] << 24) | ((u64) buf[2] << 16) |
+ ((u64) buf[1] << 8) | (u64) buf[0];
+ time = (unsigned long) (fat_time /
+ (u64) (AB3100_RTC_CLOCK_RATE * 2));
+ }
+
+ rtc_time_to_tm(time, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct ab3100 *ab3100_data = dev_get_drvdata(dev);
+ unsigned long time;
+ u64 fat_time;
+ u8 buf[6];
+ u8 rtcval;
+ int err;
+
+ /* Figure out if alarm is enabled or not */
+ err = ab3100_get_register_interruptible(ab3100_data,
+ AB3100_RTC, &rtcval);
+ if (err)
+ return err;
+ if (rtcval & 0x04)
+ alarm->enabled = 1;
+ else
+ alarm->enabled = 0;
+ /* No idea how this could be represented */
+ alarm->pending = 0;
+ /* Read out alarm registers, only 4 bytes */
+ err = ab3100_get_register_page_interruptible(ab3100_data,
+ AB3100_AL0, buf, 4);
+ if (err)
+ return err;
+ fat_time = ((u64) buf[3] << 40) | ((u64) buf[2] << 32) |
+ ((u64) buf[1] << 24) | ((u64) buf[0] << 16);
+ time = (unsigned long) (fat_time / (u64) (AB3100_RTC_CLOCK_RATE * 2));
+
+ rtc_time_to_tm(time, &alarm->time);
+
+ return rtc_valid_tm(&alarm->time);
+}
+
+static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct ab3100 *ab3100_data = dev_get_drvdata(dev);
+ u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3};
+ unsigned char buf[4];
+ unsigned long secs;
+ u64 fat_time;
+ int err;
+ int i;
+
+ rtc_tm_to_time(&alarm->time, &secs);
+ fat_time = (u64) secs * AB3100_RTC_CLOCK_RATE * 2;
+ buf[0] = (fat_time >> 16) & 0xFF;
+ buf[1] = (fat_time >> 24) & 0xFF;
+ buf[2] = (fat_time >> 32) & 0xFF;
+ buf[3] = (fat_time >> 40) & 0xFF;
+
+ /* Set the alarm */
+ for (i = 0; i < 4; i++) {
+ err = ab3100_set_register_interruptible(ab3100_data,
+ regs[i], buf[i]);
+ if (err)
+ return err;
+ }
+ /* Then enable the alarm */
+ return ab3100_mask_and_set_register_interruptible(ab3100_data,
+ AB3100_RTC, ~(1 << 2),
+ alarm->enabled << 2);
+}
+
+static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ab3100 *ab3100_data = dev_get_drvdata(dev);
+
+ /*
+ * It's not possible to enable/disable the alarm IRQ for this RTC.
+ * It does not actually trigger any IRQ: instead its only function is
+ * to power up the system, if it wasn't on. This will manifest as
+ * a "power up cause" in the AB3100 power driver (battery charging etc)
+ * and need to be handled there instead.
+ */
+ if (enabled)
+ return ab3100_mask_and_set_register_interruptible(ab3100_data,
+ AB3100_RTC, ~(1 << 2),
+ 1 << 2);
+ else
+ return ab3100_mask_and_set_register_interruptible(ab3100_data,
+ AB3100_RTC, ~(1 << 2),
+ 0);
+}
+
+static const struct rtc_class_ops ab3100_rtc_ops = {
+ .read_time = ab3100_rtc_read_time,
+ .set_mmss = ab3100_rtc_set_mmss,
+ .read_alarm = ab3100_rtc_read_alarm,
+ .set_alarm = ab3100_rtc_set_alarm,
+ .alarm_irq_enable = ab3100_rtc_irq_enable,
+};
+
+static int __init ab3100_rtc_probe(struct platform_device *pdev)
+{
+ int err;
+ u8 regval;
+ struct rtc_device *rtc;
+ struct ab3100 *ab3100_data = platform_get_drvdata(pdev);
+
+ /* The first RTC register needs special treatment */
+ err = ab3100_get_register_interruptible(ab3100_data,
+ AB3100_RTC, &regval);
+ if (err) {
+ dev_err(&pdev->dev, "unable to read RTC register\n");
+ return -ENODEV;
+ }
+
+ if ((regval & 0xFE) != RTC_SETTING) {
+ dev_warn(&pdev->dev, "not default value in RTC reg 0x%x\n",
+ regval);
+ }
+
+ if ((regval & 1) == 0) {
+ /*
+ * Set bit to detect power loss.
+ * This bit remains until RTC power is lost.
+ */
+ regval = 1 | RTC_SETTING;
+ err = ab3100_set_register_interruptible(ab3100_data,
+ AB3100_RTC, regval);
+ /* Ignore any error on this write */
+ }
+
+ rtc = rtc_device_register("ab3100-rtc", &pdev->dev, &ab3100_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ err = PTR_ERR(rtc);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __exit ab3100_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(rtc);
+ return 0;
+}
+
+static struct platform_driver ab3100_rtc_driver = {
+ .driver = {
+ .name = "ab3100-rtc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ab3100_rtc_remove),
+};
+
+static int __init ab3100_rtc_init(void)
+{
+ return platform_driver_probe(&ab3100_rtc_driver,
+ ab3100_rtc_probe);
+}
+
+static void __exit ab3100_rtc_exit(void)
+{
+ platform_driver_unregister(&ab3100_rtc_driver);
+}
+
+module_init(ab3100_rtc_init);
+module_exit(ab3100_rtc_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("AB3100 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
new file mode 100644
index 0000000..79795cd
--- /dev/null
+++ b/drivers/rtc/rtc-wm831x.c
@@ -0,0 +1,523 @@
+/*
+ * Real Time Clock driver for Wolfson Microelectronics WM831x
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+
+/*
+ * R16416 (0x4020) - RTC Write Counter
+ */
+#define WM831X_RTC_WR_CNT_MASK 0xFFFF /* RTC_WR_CNT - [15:0] */
+#define WM831X_RTC_WR_CNT_SHIFT 0 /* RTC_WR_CNT - [15:0] */
+#define WM831X_RTC_WR_CNT_WIDTH 16 /* RTC_WR_CNT - [15:0] */
+
+/*
+ * R16417 (0x4021) - RTC Time 1
+ */
+#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */
+
+/*
+ * R16418 (0x4022) - RTC Time 2
+ */
+#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */
+
+/*
+ * R16419 (0x4023) - RTC Alarm 1
+ */
+#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */
+
+/*
+ * R16420 (0x4024) - RTC Alarm 2
+ */
+#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */
+
+/*
+ * R16421 (0x4025) - RTC Control
+ */
+#define WM831X_RTC_VALID 0x8000 /* RTC_VALID */
+#define WM831X_RTC_VALID_MASK 0x8000 /* RTC_VALID */
+#define WM831X_RTC_VALID_SHIFT 15 /* RTC_VALID */
+#define WM831X_RTC_VALID_WIDTH 1 /* RTC_VALID */
+#define WM831X_RTC_SYNC_BUSY 0x4000 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_SYNC_BUSY_MASK 0x4000 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_SYNC_BUSY_SHIFT 14 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_SYNC_BUSY_WIDTH 1 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_ALM_ENA 0x0400 /* RTC_ALM_ENA */
+#define WM831X_RTC_ALM_ENA_MASK 0x0400 /* RTC_ALM_ENA */
+#define WM831X_RTC_ALM_ENA_SHIFT 10 /* RTC_ALM_ENA */
+#define WM831X_RTC_ALM_ENA_WIDTH 1 /* RTC_ALM_ENA */
+#define WM831X_RTC_PINT_FREQ_MASK 0x0070 /* RTC_PINT_FREQ - [6:4] */
+#define WM831X_RTC_PINT_FREQ_SHIFT 4 /* RTC_PINT_FREQ - [6:4] */
+#define WM831X_RTC_PINT_FREQ_WIDTH 3 /* RTC_PINT_FREQ - [6:4] */
+
+/*
+ * R16422 (0x4026) - RTC Trim
+ */
+#define WM831X_RTC_TRIM_MASK 0x03FF /* RTC_TRIM - [9:0] */
+#define WM831X_RTC_TRIM_SHIFT 0 /* RTC_TRIM - [9:0] */
+#define WM831X_RTC_TRIM_WIDTH 10 /* RTC_TRIM - [9:0] */
+
+#define WM831X_SET_TIME_RETRIES 5
+#define WM831X_GET_TIME_RETRIES 5
+
+struct wm831x_rtc {
+ struct wm831x *wm831x;
+ struct rtc_device *rtc;
+ unsigned int alarm_enabled:1;
+};
+
+/*
+ * Read current time and date in RTC
+ */
+static int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ struct wm831x *wm831x = wm831x_rtc->wm831x;
+ u16 time1[2], time2[2];
+ int ret;
+ int count = 0;
+
+ /* Has the RTC been programmed? */
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read RTC control: %d\n", ret);
+ return ret;
+ }
+ if (!(ret & WM831X_RTC_VALID)) {
+ dev_dbg(dev, "RTC not yet configured\n");
+ return -EINVAL;
+ }
+
+ /* Read twice to make sure we don't read a corrupt, partially
+ * incremented, value.
+ */
+ do {
+ ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
+ 2, time1);
+ if (ret != 0)
+ continue;
+
+ ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
+ 2, time2);
+ if (ret != 0)
+ continue;
+
+ if (memcmp(time1, time2, sizeof(time1)) == 0) {
+ u32 time = (time1[0] << 16) | time1[1];
+
+ rtc_time_to_tm(time, tm);
+ return rtc_valid_tm(tm);
+ }
+
+ } while (++count < WM831X_GET_TIME_RETRIES);
+
+ dev_err(dev, "Timed out reading current time\n");
+
+ return -EIO;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int wm831x_rtc_set_mmss(struct device *dev, unsigned long time)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ struct wm831x *wm831x = wm831x_rtc->wm831x;
+ struct rtc_time new_tm;
+ unsigned long new_time;
+ int ret;
+ int count = 0;
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1,
+ (time >> 16) & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write TIME_1: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write TIME_2: %d\n", ret);
+ return ret;
+ }
+
+ /* Wait for the update to complete - should happen first time
+ * round but be conservative.
+ */
+ do {
+ msleep(1);
+
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0)
+ ret = WM831X_RTC_SYNC_BUSY;
+ } while (!(ret & WM831X_RTC_SYNC_BUSY) &&
+ ++count < WM831X_SET_TIME_RETRIES);
+
+ if (ret & WM831X_RTC_SYNC_BUSY) {
+ dev_err(dev, "Timed out writing RTC update\n");
+ return -EIO;
+ }
+
+ /* Check that the update was accepted; security features may
+ * have caused the update to be ignored.
+ */
+ ret = wm831x_rtc_readtime(dev, &new_tm);
+ if (ret < 0)
+ return ret;
+
+ ret = rtc_tm_to_time(&new_tm, &new_time);
+ if (ret < 0) {
+ dev_err(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ /* Allow a second of change in case of tick */
+ if (new_time - time > 1) {
+ dev_err(dev, "RTC update not permitted by hardware\n");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ int ret;
+ u16 data[2];
+ u32 time;
+
+ ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1,
+ 2, data);
+ if (ret != 0) {
+ dev_err(dev, "Failed to read alarm time: %d\n", ret);
+ return ret;
+ }
+
+ time = (data[0] << 16) | data[1];
+
+ rtc_time_to_tm(time, &alrm->time);
+
+ ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read RTC control: %d\n", ret);
+ return ret;
+ }
+
+ if (ret & WM831X_RTC_ALM_ENA)
+ alrm->enabled = 1;
+ else
+ alrm->enabled = 0;
+
+ return 0;
+}
+
+static int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc)
+{
+ wm831x_rtc->alarm_enabled = 0;
+
+ return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, 0);
+}
+
+static int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc)
+{
+ wm831x_rtc->alarm_enabled = 1;
+
+ return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA);
+}
+
+static int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ struct wm831x *wm831x = wm831x_rtc->wm831x;
+ int ret;
+ unsigned long time;
+
+ ret = rtc_tm_to_time(&alrm->time, &time);
+ if (ret < 0) {
+ dev_err(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_rtc_stop_alarm(wm831x_rtc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to stop alarm: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1,
+ (time >> 16) & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write ALARM_1: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write ALARM_2: %d\n", ret);
+ return ret;
+ }
+
+ if (alrm->enabled) {
+ ret = wm831x_rtc_start_alarm(wm831x_rtc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to start alarm: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int wm831x_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ return wm831x_rtc_start_alarm(wm831x_rtc);
+ else
+ return wm831x_rtc_stop_alarm(wm831x_rtc);
+}
+
+static int wm831x_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ int val;
+
+ if (enabled)
+ val = 1 << WM831X_RTC_PINT_FREQ_SHIFT;
+ else
+ val = 0;
+
+ return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_PINT_FREQ_MASK, val);
+}
+
+static irqreturn_t wm831x_alm_irq(int irq, void *data)
+{
+ struct wm831x_rtc *wm831x_rtc = data;
+
+ rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_per_irq(int irq, void *data)
+{
+ struct wm831x_rtc *wm831x_rtc = data;
+
+ rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_UF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops wm831x_rtc_ops = {
+ .read_time = wm831x_rtc_readtime,
+ .set_mmss = wm831x_rtc_set_mmss,
+ .read_alarm = wm831x_rtc_readalarm,
+ .set_alarm = wm831x_rtc_setalarm,
+ .alarm_irq_enable = wm831x_rtc_alarm_irq_enable,
+ .update_irq_enable = wm831x_rtc_update_irq_enable,
+};
+
+#ifdef CONFIG_PM
+/* Turn off the alarm if it should not be a wake source. */
+static int wm831x_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ int ret, enable;
+
+ if (wm831x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev))
+ enable = WM831X_RTC_ALM_ENA;
+ else
+ enable = 0;
+
+ ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, enable);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret);
+
+ return 0;
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+static int wm831x_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (wm831x_rtc->alarm_enabled) {
+ ret = wm831x_rtc_start_alarm(wm831x_rtc);
+ if (ret != 0)
+ dev_err(&pdev->dev,
+ "Failed to restart RTC alarm: %d\n", ret);
+ }
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int wm831x_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, 0);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret);
+
+ return 0;
+}
+#else
+#define wm831x_rtc_suspend NULL
+#define wm831x_rtc_resume NULL
+#define wm831x_rtc_freeze NULL
+#endif
+
+static int wm831x_rtc_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_rtc *wm831x_rtc;
+ int per_irq = platform_get_irq_byname(pdev, "PER");
+ int alm_irq = platform_get_irq_byname(pdev, "ALM");
+ int ret = 0;
+
+ wm831x_rtc = kzalloc(sizeof(*wm831x_rtc), GFP_KERNEL);
+ if (wm831x_rtc == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, wm831x_rtc);
+ wm831x_rtc->wm831x = wm831x;
+
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
+ goto err;
+ }
+ if (ret & WM831X_RTC_ALM_ENA)
+ wm831x_rtc->alarm_enabled = 1;
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ wm831x_rtc->rtc = rtc_device_register("wm831x", &pdev->dev,
+ &wm831x_rtc_ops, THIS_MODULE);
+ if (IS_ERR(wm831x_rtc->rtc)) {
+ ret = PTR_ERR(wm831x_rtc->rtc);
+ goto err;
+ }
+
+ ret = wm831x_request_irq(wm831x, per_irq, wm831x_per_irq,
+ IRQF_TRIGGER_RISING, "wm831x_rtc_per",
+ wm831x_rtc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request periodic IRQ %d: %d\n",
+ per_irq, ret);
+ }
+
+ ret = wm831x_request_irq(wm831x, alm_irq, wm831x_alm_irq,
+ IRQF_TRIGGER_RISING, "wm831x_rtc_alm",
+ wm831x_rtc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+ alm_irq, ret);
+ }
+
+ return 0;
+
+err:
+ kfree(wm831x_rtc);
+ return ret;
+}
+
+static int __devexit wm831x_rtc_remove(struct platform_device *pdev)
+{
+ struct wm831x_rtc *wm831x_rtc = platform_get_drvdata(pdev);
+ int per_irq = platform_get_irq_byname(pdev, "PER");
+ int alm_irq = platform_get_irq_byname(pdev, "ALM");
+
+ wm831x_free_irq(wm831x_rtc->wm831x, alm_irq, wm831x_rtc);
+ wm831x_free_irq(wm831x_rtc->wm831x, per_irq, wm831x_rtc);
+ rtc_device_unregister(wm831x_rtc->rtc);
+ kfree(wm831x_rtc);
+
+ return 0;
+}
+
+static struct dev_pm_ops wm831x_rtc_pm_ops = {
+ .suspend = wm831x_rtc_suspend,
+ .resume = wm831x_rtc_resume,
+
+ .freeze = wm831x_rtc_freeze,
+ .thaw = wm831x_rtc_resume,
+ .restore = wm831x_rtc_resume,
+
+ .poweroff = wm831x_rtc_suspend,
+};
+
+static struct platform_driver wm831x_rtc_driver = {
+ .probe = wm831x_rtc_probe,
+ .remove = __devexit_p(wm831x_rtc_remove),
+ .driver = {
+ .name = "wm831x-rtc",
+ .pm = &wm831x_rtc_pm_ops,
+ },
+};
+
+static int __init wm831x_rtc_init(void)
+{
+ return platform_driver_register(&wm831x_rtc_driver);
+}
+module_init(wm831x_rtc_init);
+
+static void __exit wm831x_rtc_exit(void)
+{
+ platform_driver_unregister(&wm831x_rtc_driver);
+}
+module_exit(wm831x_rtc_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("RTC driver for the WM831x series PMICs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-rtc");
diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
index 3fd21d7..2d02dfd 100644
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -223,19 +223,28 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
/* Power bus message definitions */
-#define DEV_GRP_NULL 0x0
-#define DEV_GRP_P1 0x1
-#define DEV_GRP_P2 0x2
-#define DEV_GRP_P3 0x4
+/* The TWL4030/5030 splits its power-management resources (the various
+ * regulators, clock and reset lines) into 3 processor groups - P1, P2 and
+ * P3. These groups can then be configured to transition between sleep, wait-on
+ * and active states by sending messages to the power bus. See Section 5.4.2
+ * Power Resources of TWL4030 TRM
+ */
-#define RES_GRP_RES 0x0
-#define RES_GRP_PP 0x1
-#define RES_GRP_RC 0x2
+/* Processor groups */
+#define DEV_GRP_NULL 0x0
+#define DEV_GRP_P1 0x1 /* P1: all OMAP devices */
+#define DEV_GRP_P2 0x2 /* P2: all Modem devices */
+#define DEV_GRP_P3 0x4 /* P3: all peripheral devices */
+
+/* Resource groups */
+#define RES_GRP_RES 0x0 /* Reserved */
+#define RES_GRP_PP 0x1 /* Power providers */
+#define RES_GRP_RC 0x2 /* Reset and control */
#define RES_GRP_PP_RC 0x3
-#define RES_GRP_PR 0x4
+#define RES_GRP_PR 0x4 /* Power references */
#define RES_GRP_PP_PR 0x5
#define RES_GRP_RC_PR 0x6
-#define RES_GRP_ALL 0x7
+#define RES_GRP_ALL 0x7 /* All resource groups */
#define RES_TYPE2_R0 0x0
@@ -246,6 +255,41 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
#define RES_STATE_SLEEP 0x8
#define RES_STATE_OFF 0x0
+/* Power resources */
+
+/* Power providers */
+#define RES_VAUX1 1
+#define RES_VAUX2 2
+#define RES_VAUX3 3
+#define RES_VAUX4 4
+#define RES_VMMC1 5
+#define RES_VMMC2 6
+#define RES_VPLL1 7
+#define RES_VPLL2 8
+#define RES_VSIM 9
+#define RES_VDAC 10
+#define RES_VINTANA1 11
+#define RES_VINTANA2 12
+#define RES_VINTDIG 13
+#define RES_VIO 14
+#define RES_VDD1 15
+#define RES_VDD2 16
+#define RES_VUSB_1V5 17
+#define RES_VUSB_1V8 18
+#define RES_VUSB_3V1 19
+#define RES_VUSBCP 20
+#define RES_REGEN 21
+/* Reset and control */
+#define RES_NRES_PWRON 22
+#define RES_CLKEN 23
+#define RES_SYSEN 24
+#define RES_HFCLKOUT 25
+#define RES_32KCLKOUT 26
+#define RES_RESET 27
+/* Power Reference */
+#define RES_Main_Ref 28
+
+#define TOTAL_RESOURCES 28
/*
* Power Bus Message Format ... these can be sent individually by Linux,
* but are usually part of downloaded scripts that are run when various
@@ -327,6 +371,36 @@ struct twl4030_usb_data {
enum twl4030_usb_mode usb_mode;
};
+struct twl4030_ins {
+ u16 pmb_message;
+ u8 delay;
+};
+
+struct twl4030_script {
+ struct twl4030_ins *script;
+ unsigned size;
+ u8 flags;
+#define TWL4030_WRST_SCRIPT (1<<0)
+#define TWL4030_WAKEUP12_SCRIPT (1<<1)
+#define TWL4030_WAKEUP3_SCRIPT (1<<2)
+#define TWL4030_SLEEP_SCRIPT (1<<3)
+};
+
+struct twl4030_resconfig {
+ u8 resource;
+ u8 devgroup; /* Processor group that Power resource belongs to */
+ u8 type; /* Power resource addressed, 6 / broadcast message */
+ u8 type2; /* Power resource addressed, 3 / broadcast message */
+};
+
+struct twl4030_power_data {
+ struct twl4030_script **scripts;
+ unsigned num;
+ struct twl4030_resconfig *resource_config;
+};
+
+extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
+
struct twl4030_platform_data {
unsigned irq_base, irq_end;
struct twl4030_bci_platform_data *bci;
@@ -334,6 +408,7 @@ struct twl4030_platform_data {
struct twl4030_madc_platform_data *madc;
struct twl4030_keypad_data *keypad;
struct twl4030_usb_data *usb;
+ struct twl4030_power_data *power;
/* LDO regulators */
struct regulator_init_data *vdac;
@@ -364,7 +439,6 @@ int twl4030_sih_setup(int module);
#define TWL4030_VAUX3_DEV_GRP 0x1F
#define TWL4030_VAUX3_DEDICATED 0x22
-
#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
extern int twl4030charger_usb_en(int enable);
diff --git a/include/linux/mfd/ab3100.h b/include/linux/mfd/ab3100.h
index 7a3f316..e9aa4c9 100644
--- a/include/linux/mfd/ab3100.h
+++ b/include/linux/mfd/ab3100.h
@@ -6,6 +6,8 @@
*/
#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/machine.h>
#ifndef MFD_AB3100_H
#define MFD_AB3100_H
@@ -56,6 +58,14 @@
#define AB3100_STR_BATT_REMOVAL (0x40)
#define AB3100_STR_VBUS (0x80)
+/*
+ * AB3100 contains 8 regulators, one external regulator controller
+ * and a buck converter, further the LDO E and buck converter can
+ * have separate settings if they are in sleep mode, this is
+ * modeled as a separate regulator.
+ */
+#define AB3100_NUM_REGULATORS 10
+
/**
* struct ab3100
* @access_mutex: lock out concurrent accesses to the AB3100 registers
@@ -86,11 +96,30 @@ struct ab3100 {
bool startup_events_read;
};
-int ab3100_set_register(struct ab3100 *ab3100, u8 reg, u8 regval);
-int ab3100_get_register(struct ab3100 *ab3100, u8 reg, u8 *regval);
-int ab3100_get_register_page(struct ab3100 *ab3100,
+/**
+ * struct ab3100_platform_data
+ * Data supplied to initialize board connections to the AB3100
+ * @reg_constraints: regulator constraints for target board
+ * the order of these constraints are: LDO A, C, D, E,
+ * F, G, H, K, EXT and BUCK.
+ * @reg_initvals: initial values for the regulator registers
+ * plus two sleep settings for LDO E and the BUCK converter.
+ * exactly AB3100_NUM_REGULATORS+2 values must be sent in.
+ * Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK,
+ * BUCK sleep, LDO D. (LDO D need to be initialized last.)
+ * @external_voltage: voltage level of the external regulator.
+ */
+struct ab3100_platform_data {
+ struct regulator_init_data reg_constraints[AB3100_NUM_REGULATORS];
+ u8 reg_initvals[AB3100_NUM_REGULATORS+2];
+ int external_voltage;
+};
+
+int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval);
+int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval);
+int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
u8 first_reg, u8 *regvals, u8 numregs);
-int ab3100_mask_and_set_register(struct ab3100 *ab3100,
+int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 andmask, u8 ormask);
u8 ab3100_get_chip_type(struct ab3100 *ab3100);
int ab3100_event_register(struct ab3100 *ab3100,
diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
index 49ef857..11d740b 100644
--- a/include/linux/mfd/core.h
+++ b/include/linux/mfd/core.h
@@ -23,6 +23,7 @@
*/
struct mfd_cell {
const char *name;
+ int id;
int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev);
diff --git a/include/linux/mfd/ezx-pcap.h b/include/linux/mfd/ezx-pcap.h
index c12c3c0..e5124ce 100644
--- a/include/linux/mfd/ezx-pcap.h
+++ b/include/linux/mfd/ezx-pcap.h
@@ -25,9 +25,12 @@ struct pcap_chip;
int ezx_pcap_write(struct pcap_chip *, u8, u32);
int ezx_pcap_read(struct pcap_chip *, u8, u32 *);
+int ezx_pcap_set_bits(struct pcap_chip *, u8, u32, u32);
int pcap_to_irq(struct pcap_chip *, int);
+int irq_to_pcap(struct pcap_chip *, int);
int pcap_adc_async(struct pcap_chip *, u8, u32, u8[], void *, void *);
int pcap_adc_sync(struct pcap_chip *, u8, u32, u8[], u16[]);
+void pcap_set_ts_bits(struct pcap_chip *, u32);
#define PCAP_SECOND_PORT 1
#define PCAP_CS_AH 2
@@ -224,7 +227,6 @@ int pcap_adc_sync(struct pcap_chip *, u8, u32, u8[], u16[]);
#define PCAP_LED1 1
#define PCAP_BL0 2
#define PCAP_BL1 3
-#define PCAP_VIB 4
#define PCAP_LED_3MA 0
#define PCAP_LED_4MA 1
#define PCAP_LED_5MA 2
@@ -243,9 +245,6 @@ int pcap_adc_sync(struct pcap_chip *, u8, u32, u8[], u16[]);
#define PCAP_LED0_C_SHIFT 15
#define PCAP_LED1_C_SHIFT 17
#define PCAP_BL1_SHIFT 20
-#define PCAP_VIB_MASK 0x3
-#define PCAP_VIB_SHIFT 20
-#define PCAP_VIB_EN (1 << 19)
/* RTC */
#define PCAP_RTC_DAY_MASK 0x3fff
diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
new file mode 100644
index 0000000..47e698c
--- /dev/null
+++ b/include/linux/mfd/mc13783-private.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_MC13783_PRIV_H
+#define __LINUX_MFD_MC13783_PRIV_H
+
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+struct mc13783_irq {
+ void (*handler)(int, void *);
+ void *data;
+};
+
+#define MC13783_NUM_IRQ 2
+#define MC13783_IRQ_TS 0
+#define MC13783_IRQ_REGULATOR 1
+
+#define MC13783_ADC_MODE_TS 1
+#define MC13783_ADC_MODE_SINGLE_CHAN 2
+#define MC13783_ADC_MODE_MULT_CHAN 3
+
+struct mc13783 {
+ int revision;
+ struct device *dev;
+ struct spi_device *spi_device;
+
+ int (*read_dev)(void *data, char reg, int count, u32 *dst);
+ int (*write_dev)(void *data, char reg, int count, const u32 *src);
+
+ struct mutex io_lock;
+ void *io_data;
+ int irq;
+ unsigned int flags;
+
+ struct mc13783_irq irq_handler[MC13783_NUM_IRQ];
+ struct work_struct work;
+ struct completion adc_done;
+ unsigned int ts_active;
+ struct mutex adc_conv_lock;
+
+ struct mc13783_regulator_init_data *regulators;
+ int num_regulators;
+};
+
+int mc13783_reg_read(struct mc13783 *, int reg_num, u32 *);
+int mc13783_reg_write(struct mc13783 *, int, u32);
+int mc13783_set_bits(struct mc13783 *, int, u32, u32);
+int mc13783_free_irq(struct mc13783 *mc13783, int irq);
+int mc13783_register_irq(struct mc13783 *mc13783, int irq,
+ void (*handler) (int, void *), void *data);
+
+#define MC13783_REG_INTERRUPT_STATUS_0 0
+#define MC13783_REG_INTERRUPT_MASK_0 1
+#define MC13783_REG_INTERRUPT_SENSE_0 2
+#define MC13783_REG_INTERRUPT_STATUS_1 3
+#define MC13783_REG_INTERRUPT_MASK_1 4
+#define MC13783_REG_INTERRUPT_SENSE_1 5
+#define MC13783_REG_POWER_UP_MODE_SENSE 6
+#define MC13783_REG_REVISION 7
+#define MC13783_REG_SEMAPHORE 8
+#define MC13783_REG_ARBITRATION_PERIPHERAL_AUDIO 9
+#define MC13783_REG_ARBITRATION_SWITCHERS 10
+#define MC13783_REG_ARBITRATION_REGULATORS_0 11
+#define MC13783_REG_ARBITRATION_REGULATORS_1 12
+#define MC13783_REG_POWER_CONTROL_0 13
+#define MC13783_REG_POWER_CONTROL_1 14
+#define MC13783_REG_POWER_CONTROL_2 15
+#define MC13783_REG_REGEN_ASSIGNMENT 16
+#define MC13783_REG_CONTROL_SPARE 17
+#define MC13783_REG_MEMORY_A 18
+#define MC13783_REG_MEMORY_B 19
+#define MC13783_REG_RTC_TIME 20
+#define MC13783_REG_RTC_ALARM 21
+#define MC13783_REG_RTC_DAY 22
+#define MC13783_REG_RTC_DAY_ALARM 23
+#define MC13783_REG_SWITCHERS_0 24
+#define MC13783_REG_SWITCHERS_1 25
+#define MC13783_REG_SWITCHERS_2 26
+#define MC13783_REG_SWITCHERS_3 27
+#define MC13783_REG_SWITCHERS_4 28
+#define MC13783_REG_SWITCHERS_5 29
+#define MC13783_REG_REGULATOR_SETTING_0 30
+#define MC13783_REG_REGULATOR_SETTING_1 31
+#define MC13783_REG_REGULATOR_MODE_0 32
+#define MC13783_REG_REGULATOR_MODE_1 33
+#define MC13783_REG_POWER_MISCELLANEOUS 34
+#define MC13783_REG_POWER_SPARE 35
+#define MC13783_REG_AUDIO_RX_0 36
+#define MC13783_REG_AUDIO_RX_1 37
+#define MC13783_REG_AUDIO_TX 38
+#define MC13783_REG_AUDIO_SSI_NETWORK 39
+#define MC13783_REG_AUDIO_CODEC 40
+#define MC13783_REG_AUDIO_STEREO_DAC 41
+#define MC13783_REG_AUDIO_SPARE 42
+#define MC13783_REG_ADC_0 43
+#define MC13783_REG_ADC_1 44
+#define MC13783_REG_ADC_2 45
+#define MC13783_REG_ADC_3 46
+#define MC13783_REG_ADC_4 47
+#define MC13783_REG_CHARGER 48
+#define MC13783_REG_USB 49
+#define MC13783_REG_CHARGE_USB_SPARE 50
+#define MC13783_REG_LED_CONTROL_0 51
+#define MC13783_REG_LED_CONTROL_1 52
+#define MC13783_REG_LED_CONTROL_2 53
+#define MC13783_REG_LED_CONTROL_3 54
+#define MC13783_REG_LED_CONTROL_4 55
+#define MC13783_REG_LED_CONTROL_5 56
+#define MC13783_REG_SPARE 57
+#define MC13783_REG_TRIM_0 58
+#define MC13783_REG_TRIM_1 59
+#define MC13783_REG_TEST_0 60
+#define MC13783_REG_TEST_1 61
+#define MC13783_REG_TEST_2 62
+#define MC13783_REG_TEST_3 63
+#define MC13783_REG_NB 64
+
+
+/*
+ * Interrupt Status
+ */
+#define MC13783_INT_STAT_ADCDONEI (1 << 0)
+#define MC13783_INT_STAT_ADCBISDONEI (1 << 1)
+#define MC13783_INT_STAT_TSI (1 << 2)
+#define MC13783_INT_STAT_WHIGHI (1 << 3)
+#define MC13783_INT_STAT_WLOWI (1 << 4)
+#define MC13783_INT_STAT_CHGDETI (1 << 6)
+#define MC13783_INT_STAT_CHGOVI (1 << 7)
+#define MC13783_INT_STAT_CHGREVI (1 << 8)
+#define MC13783_INT_STAT_CHGSHORTI (1 << 9)
+#define MC13783_INT_STAT_CCCVI (1 << 10)
+#define MC13783_INT_STAT_CHGCURRI (1 << 11)
+#define MC13783_INT_STAT_BPONI (1 << 12)
+#define MC13783_INT_STAT_LOBATLI (1 << 13)
+#define MC13783_INT_STAT_LOBATHI (1 << 14)
+#define MC13783_INT_STAT_UDPI (1 << 15)
+#define MC13783_INT_STAT_USBI (1 << 16)
+#define MC13783_INT_STAT_IDI (1 << 19)
+#define MC13783_INT_STAT_Unused (1 << 20)
+#define MC13783_INT_STAT_SE1I (1 << 21)
+#define MC13783_INT_STAT_CKDETI (1 << 22)
+#define MC13783_INT_STAT_UDMI (1 << 23)
+
+/*
+ * Interrupt Mask
+ */
+#define MC13783_INT_MASK_ADCDONEM (1 << 0)
+#define MC13783_INT_MASK_ADCBISDONEM (1 << 1)
+#define MC13783_INT_MASK_TSM (1 << 2)
+#define MC13783_INT_MASK_WHIGHM (1 << 3)
+#define MC13783_INT_MASK_WLOWM (1 << 4)
+#define MC13783_INT_MASK_CHGDETM (1 << 6)
+#define MC13783_INT_MASK_CHGOVM (1 << 7)
+#define MC13783_INT_MASK_CHGREVM (1 << 8)
+#define MC13783_INT_MASK_CHGSHORTM (1 << 9)
+#define MC13783_INT_MASK_CCCVM (1 << 10)
+#define MC13783_INT_MASK_CHGCURRM (1 << 11)
+#define MC13783_INT_MASK_BPONM (1 << 12)
+#define MC13783_INT_MASK_LOBATLM (1 << 13)
+#define MC13783_INT_MASK_LOBATHM (1 << 14)
+#define MC13783_INT_MASK_UDPM (1 << 15)
+#define MC13783_INT_MASK_USBM (1 << 16)
+#define MC13783_INT_MASK_IDM (1 << 19)
+#define MC13783_INT_MASK_SE1M (1 << 21)
+#define MC13783_INT_MASK_CKDETM (1 << 22)
+
+/*
+ * Reg Regulator Mode 0
+ */
+#define MC13783_REGCTRL_VAUDIO_EN (1 << 0)
+#define MC13783_REGCTRL_VAUDIO_STBY (1 << 1)
+#define MC13783_REGCTRL_VAUDIO_MODE (1 << 2)
+#define MC13783_REGCTRL_VIOHI_EN (1 << 3)
+#define MC13783_REGCTRL_VIOHI_STBY (1 << 4)
+#define MC13783_REGCTRL_VIOHI_MODE (1 << 5)
+#define MC13783_REGCTRL_VIOLO_EN (1 << 6)
+#define MC13783_REGCTRL_VIOLO_STBY (1 << 7)
+#define MC13783_REGCTRL_VIOLO_MODE (1 << 8)
+#define MC13783_REGCTRL_VDIG_EN (1 << 9)
+#define MC13783_REGCTRL_VDIG_STBY (1 << 10)
+#define MC13783_REGCTRL_VDIG_MODE (1 << 11)
+#define MC13783_REGCTRL_VGEN_EN (1 << 12)
+#define MC13783_REGCTRL_VGEN_STBY (1 << 13)
+#define MC13783_REGCTRL_VGEN_MODE (1 << 14)
+#define MC13783_REGCTRL_VRFDIG_EN (1 << 15)
+#define MC13783_REGCTRL_VRFDIG_STBY (1 << 16)
+#define MC13783_REGCTRL_VRFDIG_MODE (1 << 17)
+#define MC13783_REGCTRL_VRFREF_EN (1 << 18)
+#define MC13783_REGCTRL_VRFREF_STBY (1 << 19)
+#define MC13783_REGCTRL_VRFREF_MODE (1 << 20)
+#define MC13783_REGCTRL_VRFCP_EN (1 << 21)
+#define MC13783_REGCTRL_VRFCP_STBY (1 << 22)
+#define MC13783_REGCTRL_VRFCP_MODE (1 << 23)
+
+/*
+ * Reg Regulator Mode 1
+ */
+#define MC13783_REGCTRL_VSIM_EN (1 << 0)
+#define MC13783_REGCTRL_VSIM_STBY (1 << 1)
+#define MC13783_REGCTRL_VSIM_MODE (1 << 2)
+#define MC13783_REGCTRL_VESIM_EN (1 << 3)
+#define MC13783_REGCTRL_VESIM_STBY (1 << 4)
+#define MC13783_REGCTRL_VESIM_MODE (1 << 5)
+#define MC13783_REGCTRL_VCAM_EN (1 << 6)
+#define MC13783_REGCTRL_VCAM_STBY (1 << 7)
+#define MC13783_REGCTRL_VCAM_MODE (1 << 8)
+#define MC13783_REGCTRL_VRFBG_EN (1 << 9)
+#define MC13783_REGCTRL_VRFBG_STBY (1 << 10)
+#define MC13783_REGCTRL_VVIB_EN (1 << 11)
+#define MC13783_REGCTRL_VRF1_EN (1 << 12)
+#define MC13783_REGCTRL_VRF1_STBY (1 << 13)
+#define MC13783_REGCTRL_VRF1_MODE (1 << 14)
+#define MC13783_REGCTRL_VRF2_EN (1 << 15)
+#define MC13783_REGCTRL_VRF2_STBY (1 << 16)
+#define MC13783_REGCTRL_VRF2_MODE (1 << 17)
+#define MC13783_REGCTRL_VMMC1_EN (1 << 18)
+#define MC13783_REGCTRL_VMMC1_STBY (1 << 19)
+#define MC13783_REGCTRL_VMMC1_MODE (1 << 20)
+#define MC13783_REGCTRL_VMMC2_EN (1 << 21)
+#define MC13783_REGCTRL_VMMC2_STBY (1 << 22)
+#define MC13783_REGCTRL_VMMC2_MODE (1 << 23)
+
+/*
+ * Reg Regulator Misc.
+ */
+#define MC13783_REGCTRL_GPO1_EN (1 << 6)
+#define MC13783_REGCTRL_GPO2_EN (1 << 8)
+#define MC13783_REGCTRL_GPO3_EN (1 << 10)
+#define MC13783_REGCTRL_GPO4_EN (1 << 12)
+#define MC13783_REGCTRL_VIBPINCTRL (1 << 14)
+
+/*
+ * Reg Switcher 4
+ */
+#define MC13783_SWCTRL_SW1A_MODE (1 << 0)
+#define MC13783_SWCTRL_SW1A_STBY_MODE (1 << 2)
+#define MC13783_SWCTRL_SW1A_DVS_SPEED (1 << 6)
+#define MC13783_SWCTRL_SW1A_PANIC_MODE (1 << 8)
+#define MC13783_SWCTRL_SW1A_SOFTSTART (1 << 9)
+#define MC13783_SWCTRL_SW1B_MODE (1 << 10)
+#define MC13783_SWCTRL_SW1B_STBY_MODE (1 << 12)
+#define MC13783_SWCTRL_SW1B_DVS_SPEED (1 << 14)
+#define MC13783_SWCTRL_SW1B_PANIC_MODE (1 << 16)
+#define MC13783_SWCTRL_SW1B_SOFTSTART (1 << 17)
+#define MC13783_SWCTRL_PLL_EN (1 << 18)
+#define MC13783_SWCTRL_PLL_FACTOR (1 << 19)
+
+/*
+ * Reg Switcher 5
+ */
+#define MC13783_SWCTRL_SW2A_MODE (1 << 0)
+#define MC13783_SWCTRL_SW2A_STBY_MODE (1 << 2)
+#define MC13783_SWCTRL_SW2A_DVS_SPEED (1 << 6)
+#define MC13783_SWCTRL_SW2A_PANIC_MODE (1 << 8)
+#define MC13783_SWCTRL_SW2A_SOFTSTART (1 << 9)
+#define MC13783_SWCTRL_SW2B_MODE (1 << 10)
+#define MC13783_SWCTRL_SW2B_STBY_MODE (1 << 12)
+#define MC13783_SWCTRL_SW2B_DVS_SPEED (1 << 14)
+#define MC13783_SWCTRL_SW2B_PANIC_MODE (1 << 16)
+#define MC13783_SWCTRL_SW2B_SOFTSTART (1 << 17)
+#define MC13783_SWSET_SW3 (1 << 18)
+#define MC13783_SWCTRL_SW3_EN (1 << 20)
+#define MC13783_SWCTRL_SW3_STBY (1 << 21)
+#define MC13783_SWCTRL_SW3_MODE (1 << 22)
+
+/*
+ * ADC/Touch
+ */
+#define MC13783_ADC0_LICELLCON (1 << 0)
+#define MC13783_ADC0_CHRGICON (1 << 1)
+#define MC13783_ADC0_BATICON (1 << 2)
+#define MC13783_ADC0_RTHEN (1 << 3)
+#define MC13783_ADC0_DTHEN (1 << 4)
+#define MC13783_ADC0_UIDEN (1 << 5)
+#define MC13783_ADC0_ADOUTEN (1 << 6)
+#define MC13783_ADC0_ADOUTPER (1 << 7)
+#define MC13783_ADC0_ADREFEN (1 << 10)
+#define MC13783_ADC0_ADREFMODE (1 << 11)
+#define MC13783_ADC0_TSMOD0 (1 << 12)
+#define MC13783_ADC0_TSMOD1 (1 << 13)
+#define MC13783_ADC0_TSMOD2 (1 << 14)
+#define MC13783_ADC0_CHRGRAWDIV (1 << 15)
+#define MC13783_ADC0_ADINC1 (1 << 16)
+#define MC13783_ADC0_ADINC2 (1 << 17)
+#define MC13783_ADC0_WCOMP (1 << 18)
+#define MC13783_ADC0_ADCBIS0 (1 << 23)
+
+#define MC13783_ADC1_ADEN (1 << 0)
+#define MC13783_ADC1_RAND (1 << 1)
+#define MC13783_ADC1_ADSEL (1 << 3)
+#define MC13783_ADC1_TRIGMASK (1 << 4)
+#define MC13783_ADC1_ADA10 (1 << 5)
+#define MC13783_ADC1_ADA11 (1 << 6)
+#define MC13783_ADC1_ADA12 (1 << 7)
+#define MC13783_ADC1_ADA20 (1 << 8)
+#define MC13783_ADC1_ADA21 (1 << 9)
+#define MC13783_ADC1_ADA22 (1 << 10)
+#define MC13783_ADC1_ATO0 (1 << 11)
+#define MC13783_ADC1_ATO1 (1 << 12)
+#define MC13783_ADC1_ATO2 (1 << 13)
+#define MC13783_ADC1_ATO3 (1 << 14)
+#define MC13783_ADC1_ATO4 (1 << 15)
+#define MC13783_ADC1_ATO5 (1 << 16)
+#define MC13783_ADC1_ATO6 (1 << 17)
+#define MC13783_ADC1_ATO7 (1 << 18)
+#define MC13783_ADC1_ATOX (1 << 19)
+#define MC13783_ADC1_ASC (1 << 20)
+#define MC13783_ADC1_ADTRIGIGN (1 << 21)
+#define MC13783_ADC1_ADONESHOT (1 << 22)
+#define MC13783_ADC1_ADCBIS1 (1 << 23)
+
+#define MC13783_ADC1_CHAN0_SHIFT 5
+#define MC13783_ADC1_CHAN1_SHIFT 8
+
+#define MC13783_ADC2_ADD10 (1 << 2)
+#define MC13783_ADC2_ADD11 (1 << 3)
+#define MC13783_ADC2_ADD12 (1 << 4)
+#define MC13783_ADC2_ADD13 (1 << 5)
+#define MC13783_ADC2_ADD14 (1 << 6)
+#define MC13783_ADC2_ADD15 (1 << 7)
+#define MC13783_ADC2_ADD16 (1 << 8)
+#define MC13783_ADC2_ADD17 (1 << 9)
+#define MC13783_ADC2_ADD18 (1 << 10)
+#define MC13783_ADC2_ADD19 (1 << 11)
+#define MC13783_ADC2_ADD20 (1 << 14)
+#define MC13783_ADC2_ADD21 (1 << 15)
+#define MC13783_ADC2_ADD22 (1 << 16)
+#define MC13783_ADC2_ADD23 (1 << 17)
+#define MC13783_ADC2_ADD24 (1 << 18)
+#define MC13783_ADC2_ADD25 (1 << 19)
+#define MC13783_ADC2_ADD26 (1 << 20)
+#define MC13783_ADC2_ADD27 (1 << 21)
+#define MC13783_ADC2_ADD28 (1 << 22)
+#define MC13783_ADC2_ADD29 (1 << 23)
+
+#define MC13783_ADC3_WHIGH0 (1 << 0)
+#define MC13783_ADC3_WHIGH1 (1 << 1)
+#define MC13783_ADC3_WHIGH2 (1 << 2)
+#define MC13783_ADC3_WHIGH3 (1 << 3)
+#define MC13783_ADC3_WHIGH4 (1 << 4)
+#define MC13783_ADC3_WHIGH5 (1 << 5)
+#define MC13783_ADC3_ICID0 (1 << 6)
+#define MC13783_ADC3_ICID1 (1 << 7)
+#define MC13783_ADC3_ICID2 (1 << 8)
+#define MC13783_ADC3_WLOW0 (1 << 9)
+#define MC13783_ADC3_WLOW1 (1 << 10)
+#define MC13783_ADC3_WLOW2 (1 << 11)
+#define MC13783_ADC3_WLOW3 (1 << 12)
+#define MC13783_ADC3_WLOW4 (1 << 13)
+#define MC13783_ADC3_WLOW5 (1 << 14)
+#define MC13783_ADC3_ADCBIS2 (1 << 23)
+
+#define MC13783_ADC4_ADDBIS10 (1 << 2)
+#define MC13783_ADC4_ADDBIS11 (1 << 3)
+#define MC13783_ADC4_ADDBIS12 (1 << 4)
+#define MC13783_ADC4_ADDBIS13 (1 << 5)
+#define MC13783_ADC4_ADDBIS14 (1 << 6)
+#define MC13783_ADC4_ADDBIS15 (1 << 7)
+#define MC13783_ADC4_ADDBIS16 (1 << 8)
+#define MC13783_ADC4_ADDBIS17 (1 << 9)
+#define MC13783_ADC4_ADDBIS18 (1 << 10)
+#define MC13783_ADC4_ADDBIS19 (1 << 11)
+#define MC13783_ADC4_ADDBIS20 (1 << 14)
+#define MC13783_ADC4_ADDBIS21 (1 << 15)
+#define MC13783_ADC4_ADDBIS22 (1 << 16)
+#define MC13783_ADC4_ADDBIS23 (1 << 17)
+#define MC13783_ADC4_ADDBIS24 (1 << 18)
+#define MC13783_ADC4_ADDBIS25 (1 << 19)
+#define MC13783_ADC4_ADDBIS26 (1 << 20)
+#define MC13783_ADC4_ADDBIS27 (1 << 21)
+#define MC13783_ADC4_ADDBIS28 (1 << 22)
+#define MC13783_ADC4_ADDBIS29 (1 << 23)
+
+#endif /* __LINUX_MFD_MC13783_PRIV_H */
+
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
new file mode 100644
index 0000000..b3a2a72
--- /dev/null
+++ b/include/linux/mfd/mc13783.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de
+ *
+ * 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.
+ */
+
+#ifndef __INCLUDE_LINUX_MFD_MC13783_H
+#define __INCLUDE_LINUX_MFD_MC13783_H
+
+struct mc13783;
+struct regulator_init_data;
+
+struct mc13783_regulator_init_data {
+ int id;
+ struct regulator_init_data *init_data;
+};
+
+struct mc13783_platform_data {
+ struct mc13783_regulator_init_data *regulators;
+ int num_regulators;
+ unsigned int flags;
+};
+
+/* mc13783_platform_data flags */
+#define MC13783_USE_TOUCHSCREEN (1 << 0)
+#define MC13783_USE_CODEC (1 << 1)
+#define MC13783_USE_ADC (1 << 2)
+#define MC13783_USE_RTC (1 << 3)
+#define MC13783_USE_REGULATOR (1 << 4)
+
+int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+ unsigned int channel, unsigned int *sample);
+
+void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
+
+#define MC13783_SW_SW1A 0
+#define MC13783_SW_SW1B 1
+#define MC13783_SW_SW2A 2
+#define MC13783_SW_SW2B 3
+#define MC13783_SW_SW3 4
+#define MC13783_SW_PLL 5
+#define MC13783_REGU_VAUDIO 6
+#define MC13783_REGU_VIOHI 7
+#define MC13783_REGU_VIOLO 8
+#define MC13783_REGU_VDIG 9
+#define MC13783_REGU_VGEN 10
+#define MC13783_REGU_VRFDIG 11
+#define MC13783_REGU_VRFREF 12
+#define MC13783_REGU_VRFCP 13
+#define MC13783_REGU_VSIM 14
+#define MC13783_REGU_VESIM 15
+#define MC13783_REGU_VCAM 16
+#define MC13783_REGU_VRFBG 17
+#define MC13783_REGU_VVIB 18
+#define MC13783_REGU_VRF1 19
+#define MC13783_REGU_VRF2 20
+#define MC13783_REGU_VMMC1 21
+#define MC13783_REGU_VMMC2 22
+#define MC13783_REGU_GPO1 23
+#define MC13783_REGU_GPO2 24
+#define MC13783_REGU_GPO3 25
+#define MC13783_REGU_GPO4 26
+#define MC13783_REGU_V1 27
+#define MC13783_REGU_V2 28
+#define MC13783_REGU_V3 29
+#define MC13783_REGU_V4 30
+
+#endif /* __INCLUDE_LINUX_MFD_MC13783_H */
+
diff --git a/include/linux/mfd/pcf50633/adc.h b/include/linux/mfd/pcf50633/adc.h
index 56669b4..b35e628 100644
--- a/include/linux/mfd/pcf50633/adc.h
+++ b/include/linux/mfd/pcf50633/adc.h
@@ -25,7 +25,8 @@
#define PCF50633_REG_ADCS3 0x57
#define PCF50633_ADCC1_ADCSTART 0x01
-#define PCF50633_ADCC1_RES_10BIT 0x02
+#define PCF50633_ADCC1_RES_8BIT 0x02
+#define PCF50633_ADCC1_RES_10BIT 0x00
#define PCF50633_ADCC1_AVERAGE_NO 0x00
#define PCF50633_ADCC1_AVERAGE_4 0x04
#define PCF50633_ADCC1_AVERAGE_8 0x08
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
index c8f51c3..9aba7b7 100644
--- a/include/linux/mfd/pcf50633/core.h
+++ b/include/linux/mfd/pcf50633/core.h
@@ -136,6 +136,7 @@ struct pcf50633 {
int irq;
struct pcf50633_irq irq_handler[PCF50633_NUM_IRQ];
struct work_struct irq_work;
+ struct workqueue_struct *work_queue;
struct mutex lock;
u8 mask_regs[5];
diff --git a/include/linux/mfd/wm831x/auxadc.h b/include/linux/mfd/wm831x/auxadc.h
new file mode 100644
index 0000000..b132067
--- /dev/null
+++ b/include/linux/mfd/wm831x/auxadc.h
@@ -0,0 +1,216 @@
+/*
+ * include/linux/mfd/wm831x/auxadc.h -- Auxiliary ADC interface for WM831x
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM831X_AUXADC_H__
+#define __MFD_WM831X_AUXADC_H__
+
+/*
+ * R16429 (0x402D) - AuxADC Data
+ */
+#define WM831X_AUX_DATA_SRC_MASK 0xF000 /* AUX_DATA_SRC - [15:12] */
+#define WM831X_AUX_DATA_SRC_SHIFT 12 /* AUX_DATA_SRC - [15:12] */
+#define WM831X_AUX_DATA_SRC_WIDTH 4 /* AUX_DATA_SRC - [15:12] */
+#define WM831X_AUX_DATA_MASK 0x0FFF /* AUX_DATA - [11:0] */
+#define WM831X_AUX_DATA_SHIFT 0 /* AUX_DATA - [11:0] */
+#define WM831X_AUX_DATA_WIDTH 12 /* AUX_DATA - [11:0] */
+
+/*
+ * R16430 (0x402E) - AuxADC Control
+ */
+#define WM831X_AUX_ENA 0x8000 /* AUX_ENA */
+#define WM831X_AUX_ENA_MASK 0x8000 /* AUX_ENA */
+#define WM831X_AUX_ENA_SHIFT 15 /* AUX_ENA */
+#define WM831X_AUX_ENA_WIDTH 1 /* AUX_ENA */
+#define WM831X_AUX_CVT_ENA 0x4000 /* AUX_CVT_ENA */
+#define WM831X_AUX_CVT_ENA_MASK 0x4000 /* AUX_CVT_ENA */
+#define WM831X_AUX_CVT_ENA_SHIFT 14 /* AUX_CVT_ENA */
+#define WM831X_AUX_CVT_ENA_WIDTH 1 /* AUX_CVT_ENA */
+#define WM831X_AUX_SLPENA 0x1000 /* AUX_SLPENA */
+#define WM831X_AUX_SLPENA_MASK 0x1000 /* AUX_SLPENA */
+#define WM831X_AUX_SLPENA_SHIFT 12 /* AUX_SLPENA */
+#define WM831X_AUX_SLPENA_WIDTH 1 /* AUX_SLPENA */
+#define WM831X_AUX_FRC_ENA 0x0800 /* AUX_FRC_ENA */
+#define WM831X_AUX_FRC_ENA_MASK 0x0800 /* AUX_FRC_ENA */
+#define WM831X_AUX_FRC_ENA_SHIFT 11 /* AUX_FRC_ENA */
+#define WM831X_AUX_FRC_ENA_WIDTH 1 /* AUX_FRC_ENA */
+#define WM831X_AUX_RATE_MASK 0x003F /* AUX_RATE - [5:0] */
+#define WM831X_AUX_RATE_SHIFT 0 /* AUX_RATE - [5:0] */
+#define WM831X_AUX_RATE_WIDTH 6 /* AUX_RATE - [5:0] */
+
+/*
+ * R16431 (0x402F) - AuxADC Source
+ */
+#define WM831X_AUX_CAL_SEL 0x8000 /* AUX_CAL_SEL */
+#define WM831X_AUX_CAL_SEL_MASK 0x8000 /* AUX_CAL_SEL */
+#define WM831X_AUX_CAL_SEL_SHIFT 15 /* AUX_CAL_SEL */
+#define WM831X_AUX_CAL_SEL_WIDTH 1 /* AUX_CAL_SEL */
+#define WM831X_AUX_BKUP_BATT_SEL 0x0400 /* AUX_BKUP_BATT_SEL */
+#define WM831X_AUX_BKUP_BATT_SEL_MASK 0x0400 /* AUX_BKUP_BATT_SEL */
+#define WM831X_AUX_BKUP_BATT_SEL_SHIFT 10 /* AUX_BKUP_BATT_SEL */
+#define WM831X_AUX_BKUP_BATT_SEL_WIDTH 1 /* AUX_BKUP_BATT_SEL */
+#define WM831X_AUX_WALL_SEL 0x0200 /* AUX_WALL_SEL */
+#define WM831X_AUX_WALL_SEL_MASK 0x0200 /* AUX_WALL_SEL */
+#define WM831X_AUX_WALL_SEL_SHIFT 9 /* AUX_WALL_SEL */
+#define WM831X_AUX_WALL_SEL_WIDTH 1 /* AUX_WALL_SEL */
+#define WM831X_AUX_BATT_SEL 0x0100 /* AUX_BATT_SEL */
+#define WM831X_AUX_BATT_SEL_MASK 0x0100 /* AUX_BATT_SEL */
+#define WM831X_AUX_BATT_SEL_SHIFT 8 /* AUX_BATT_SEL */
+#define WM831X_AUX_BATT_SEL_WIDTH 1 /* AUX_BATT_SEL */
+#define WM831X_AUX_USB_SEL 0x0080 /* AUX_USB_SEL */
+#define WM831X_AUX_USB_SEL_MASK 0x0080 /* AUX_USB_SEL */
+#define WM831X_AUX_USB_SEL_SHIFT 7 /* AUX_USB_SEL */
+#define WM831X_AUX_USB_SEL_WIDTH 1 /* AUX_USB_SEL */
+#define WM831X_AUX_SYSVDD_SEL 0x0040 /* AUX_SYSVDD_SEL */
+#define WM831X_AUX_SYSVDD_SEL_MASK 0x0040 /* AUX_SYSVDD_SEL */
+#define WM831X_AUX_SYSVDD_SEL_SHIFT 6 /* AUX_SYSVDD_SEL */
+#define WM831X_AUX_SYSVDD_SEL_WIDTH 1 /* AUX_SYSVDD_SEL */
+#define WM831X_AUX_BATT_TEMP_SEL 0x0020 /* AUX_BATT_TEMP_SEL */
+#define WM831X_AUX_BATT_TEMP_SEL_MASK 0x0020 /* AUX_BATT_TEMP_SEL */
+#define WM831X_AUX_BATT_TEMP_SEL_SHIFT 5 /* AUX_BATT_TEMP_SEL */
+#define WM831X_AUX_BATT_TEMP_SEL_WIDTH 1 /* AUX_BATT_TEMP_SEL */
+#define WM831X_AUX_CHIP_TEMP_SEL 0x0010 /* AUX_CHIP_TEMP_SEL */
+#define WM831X_AUX_CHIP_TEMP_SEL_MASK 0x0010 /* AUX_CHIP_TEMP_SEL */
+#define WM831X_AUX_CHIP_TEMP_SEL_SHIFT 4 /* AUX_CHIP_TEMP_SEL */
+#define WM831X_AUX_CHIP_TEMP_SEL_WIDTH 1 /* AUX_CHIP_TEMP_SEL */
+#define WM831X_AUX_AUX4_SEL 0x0008 /* AUX_AUX4_SEL */
+#define WM831X_AUX_AUX4_SEL_MASK 0x0008 /* AUX_AUX4_SEL */
+#define WM831X_AUX_AUX4_SEL_SHIFT 3 /* AUX_AUX4_SEL */
+#define WM831X_AUX_AUX4_SEL_WIDTH 1 /* AUX_AUX4_SEL */
+#define WM831X_AUX_AUX3_SEL 0x0004 /* AUX_AUX3_SEL */
+#define WM831X_AUX_AUX3_SEL_MASK 0x0004 /* AUX_AUX3_SEL */
+#define WM831X_AUX_AUX3_SEL_SHIFT 2 /* AUX_AUX3_SEL */
+#define WM831X_AUX_AUX3_SEL_WIDTH 1 /* AUX_AUX3_SEL */
+#define WM831X_AUX_AUX2_SEL 0x0002 /* AUX_AUX2_SEL */
+#define WM831X_AUX_AUX2_SEL_MASK 0x0002 /* AUX_AUX2_SEL */
+#define WM831X_AUX_AUX2_SEL_SHIFT 1 /* AUX_AUX2_SEL */
+#define WM831X_AUX_AUX2_SEL_WIDTH 1 /* AUX_AUX2_SEL */
+#define WM831X_AUX_AUX1_SEL 0x0001 /* AUX_AUX1_SEL */
+#define WM831X_AUX_AUX1_SEL_MASK 0x0001 /* AUX_AUX1_SEL */
+#define WM831X_AUX_AUX1_SEL_SHIFT 0 /* AUX_AUX1_SEL */
+#define WM831X_AUX_AUX1_SEL_WIDTH 1 /* AUX_AUX1_SEL */
+
+/*
+ * R16432 (0x4030) - Comparator Control
+ */
+#define WM831X_DCOMP4_STS 0x0800 /* DCOMP4_STS */
+#define WM831X_DCOMP4_STS_MASK 0x0800 /* DCOMP4_STS */
+#define WM831X_DCOMP4_STS_SHIFT 11 /* DCOMP4_STS */
+#define WM831X_DCOMP4_STS_WIDTH 1 /* DCOMP4_STS */
+#define WM831X_DCOMP3_STS 0x0400 /* DCOMP3_STS */
+#define WM831X_DCOMP3_STS_MASK 0x0400 /* DCOMP3_STS */
+#define WM831X_DCOMP3_STS_SHIFT 10 /* DCOMP3_STS */
+#define WM831X_DCOMP3_STS_WIDTH 1 /* DCOMP3_STS */
+#define WM831X_DCOMP2_STS 0x0200 /* DCOMP2_STS */
+#define WM831X_DCOMP2_STS_MASK 0x0200 /* DCOMP2_STS */
+#define WM831X_DCOMP2_STS_SHIFT 9 /* DCOMP2_STS */
+#define WM831X_DCOMP2_STS_WIDTH 1 /* DCOMP2_STS */
+#define WM831X_DCOMP1_STS 0x0100 /* DCOMP1_STS */
+#define WM831X_DCOMP1_STS_MASK 0x0100 /* DCOMP1_STS */
+#define WM831X_DCOMP1_STS_SHIFT 8 /* DCOMP1_STS */
+#define WM831X_DCOMP1_STS_WIDTH 1 /* DCOMP1_STS */
+#define WM831X_DCMP4_ENA 0x0008 /* DCMP4_ENA */
+#define WM831X_DCMP4_ENA_MASK 0x0008 /* DCMP4_ENA */
+#define WM831X_DCMP4_ENA_SHIFT 3 /* DCMP4_ENA */
+#define WM831X_DCMP4_ENA_WIDTH 1 /* DCMP4_ENA */
+#define WM831X_DCMP3_ENA 0x0004 /* DCMP3_ENA */
+#define WM831X_DCMP3_ENA_MASK 0x0004 /* DCMP3_ENA */
+#define WM831X_DCMP3_ENA_SHIFT 2 /* DCMP3_ENA */
+#define WM831X_DCMP3_ENA_WIDTH 1 /* DCMP3_ENA */
+#define WM831X_DCMP2_ENA 0x0002 /* DCMP2_ENA */
+#define WM831X_DCMP2_ENA_MASK 0x0002 /* DCMP2_ENA */
+#define WM831X_DCMP2_ENA_SHIFT 1 /* DCMP2_ENA */
+#define WM831X_DCMP2_ENA_WIDTH 1 /* DCMP2_ENA */
+#define WM831X_DCMP1_ENA 0x0001 /* DCMP1_ENA */
+#define WM831X_DCMP1_ENA_MASK 0x0001 /* DCMP1_ENA */
+#define WM831X_DCMP1_ENA_SHIFT 0 /* DCMP1_ENA */
+#define WM831X_DCMP1_ENA_WIDTH 1 /* DCMP1_ENA */
+
+/*
+ * R16433 (0x4031) - Comparator 1
+ */
+#define WM831X_DCMP1_SRC_MASK 0xE000 /* DCMP1_SRC - [15:13] */
+#define WM831X_DCMP1_SRC_SHIFT 13 /* DCMP1_SRC - [15:13] */
+#define WM831X_DCMP1_SRC_WIDTH 3 /* DCMP1_SRC - [15:13] */
+#define WM831X_DCMP1_GT 0x1000 /* DCMP1_GT */
+#define WM831X_DCMP1_GT_MASK 0x1000 /* DCMP1_GT */
+#define WM831X_DCMP1_GT_SHIFT 12 /* DCMP1_GT */
+#define WM831X_DCMP1_GT_WIDTH 1 /* DCMP1_GT */
+#define WM831X_DCMP1_THR_MASK 0x0FFF /* DCMP1_THR - [11:0] */
+#define WM831X_DCMP1_THR_SHIFT 0 /* DCMP1_THR - [11:0] */
+#define WM831X_DCMP1_THR_WIDTH 12 /* DCMP1_THR - [11:0] */
+
+/*
+ * R16434 (0x4032) - Comparator 2
+ */
+#define WM831X_DCMP2_SRC_MASK 0xE000 /* DCMP2_SRC - [15:13] */
+#define WM831X_DCMP2_SRC_SHIFT 13 /* DCMP2_SRC - [15:13] */
+#define WM831X_DCMP2_SRC_WIDTH 3 /* DCMP2_SRC - [15:13] */
+#define WM831X_DCMP2_GT 0x1000 /* DCMP2_GT */
+#define WM831X_DCMP2_GT_MASK 0x1000 /* DCMP2_GT */
+#define WM831X_DCMP2_GT_SHIFT 12 /* DCMP2_GT */
+#define WM831X_DCMP2_GT_WIDTH 1 /* DCMP2_GT */
+#define WM831X_DCMP2_THR_MASK 0x0FFF /* DCMP2_THR - [11:0] */
+#define WM831X_DCMP2_THR_SHIFT 0 /* DCMP2_THR - [11:0] */
+#define WM831X_DCMP2_THR_WIDTH 12 /* DCMP2_THR - [11:0] */
+
+/*
+ * R16435 (0x4033) - Comparator 3
+ */
+#define WM831X_DCMP3_SRC_MASK 0xE000 /* DCMP3_SRC - [15:13] */
+#define WM831X_DCMP3_SRC_SHIFT 13 /* DCMP3_SRC - [15:13] */
+#define WM831X_DCMP3_SRC_WIDTH 3 /* DCMP3_SRC - [15:13] */
+#define WM831X_DCMP3_GT 0x1000 /* DCMP3_GT */
+#define WM831X_DCMP3_GT_MASK 0x1000 /* DCMP3_GT */
+#define WM831X_DCMP3_GT_SHIFT 12 /* DCMP3_GT */
+#define WM831X_DCMP3_GT_WIDTH 1 /* DCMP3_GT */
+#define WM831X_DCMP3_THR_MASK 0x0FFF /* DCMP3_THR - [11:0] */
+#define WM831X_DCMP3_THR_SHIFT 0 /* DCMP3_THR - [11:0] */
+#define WM831X_DCMP3_THR_WIDTH 12 /* DCMP3_THR - [11:0] */
+
+/*
+ * R16436 (0x4034) - Comparator 4
+ */
+#define WM831X_DCMP4_SRC_MASK 0xE000 /* DCMP4_SRC - [15:13] */
+#define WM831X_DCMP4_SRC_SHIFT 13 /* DCMP4_SRC - [15:13] */
+#define WM831X_DCMP4_SRC_WIDTH 3 /* DCMP4_SRC - [15:13] */
+#define WM831X_DCMP4_GT 0x1000 /* DCMP4_GT */
+#define WM831X_DCMP4_GT_MASK 0x1000 /* DCMP4_GT */
+#define WM831X_DCMP4_GT_SHIFT 12 /* DCMP4_GT */
+#define WM831X_DCMP4_GT_WIDTH 1 /* DCMP4_GT */
+#define WM831X_DCMP4_THR_MASK 0x0FFF /* DCMP4_THR - [11:0] */
+#define WM831X_DCMP4_THR_SHIFT 0 /* DCMP4_THR - [11:0] */
+#define WM831X_DCMP4_THR_WIDTH 12 /* DCMP4_THR - [11:0] */
+
+#define WM831X_AUX_CAL_FACTOR 0xfff
+#define WM831X_AUX_CAL_NOMINAL 0x222
+
+enum wm831x_auxadc {
+ WM831X_AUX_CAL = 15,
+ WM831X_AUX_BKUP_BATT = 10,
+ WM831X_AUX_WALL = 9,
+ WM831X_AUX_BATT = 8,
+ WM831X_AUX_USB = 7,
+ WM831X_AUX_SYSVDD = 6,
+ WM831X_AUX_BATT_TEMP = 5,
+ WM831X_AUX_CHIP_TEMP = 4,
+ WM831X_AUX_AUX4 = 3,
+ WM831X_AUX_AUX3 = 2,
+ WM831X_AUX_AUX2 = 1,
+ WM831X_AUX_AUX1 = 0,
+};
+
+int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input);
+int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input);
+
+#endif
diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h
new file mode 100644
index 0000000..91eb493
--- /dev/null
+++ b/include/linux/mfd/wm831x/core.h
@@ -0,0 +1,289 @@
+/*
+ * include/linux/mfd/wm831x/core.h -- Core interface for WM831x
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM831X_CORE_H__
+#define __MFD_WM831X_CORE_H__
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+/*
+ * Register values.
+ */
+#define WM831X_RESET_ID 0x00
+#define WM831X_REVISION 0x01
+#define WM831X_PARENT_ID 0x4000
+#define WM831X_SYSVDD_CONTROL 0x4001
+#define WM831X_THERMAL_MONITORING 0x4002
+#define WM831X_POWER_STATE 0x4003
+#define WM831X_WATCHDOG 0x4004
+#define WM831X_ON_PIN_CONTROL 0x4005
+#define WM831X_RESET_CONTROL 0x4006
+#define WM831X_CONTROL_INTERFACE 0x4007
+#define WM831X_SECURITY_KEY 0x4008
+#define WM831X_SOFTWARE_SCRATCH 0x4009
+#define WM831X_OTP_CONTROL 0x400A
+#define WM831X_GPIO_LEVEL 0x400C
+#define WM831X_SYSTEM_STATUS 0x400D
+#define WM831X_ON_SOURCE 0x400E
+#define WM831X_OFF_SOURCE 0x400F
+#define WM831X_SYSTEM_INTERRUPTS 0x4010
+#define WM831X_INTERRUPT_STATUS_1 0x4011
+#define WM831X_INTERRUPT_STATUS_2 0x4012
+#define WM831X_INTERRUPT_STATUS_3 0x4013
+#define WM831X_INTERRUPT_STATUS_4 0x4014
+#define WM831X_INTERRUPT_STATUS_5 0x4015
+#define WM831X_IRQ_CONFIG 0x4017
+#define WM831X_SYSTEM_INTERRUPTS_MASK 0x4018
+#define WM831X_INTERRUPT_STATUS_1_MASK 0x4019
+#define WM831X_INTERRUPT_STATUS_2_MASK 0x401A
+#define WM831X_INTERRUPT_STATUS_3_MASK 0x401B
+#define WM831X_INTERRUPT_STATUS_4_MASK 0x401C
+#define WM831X_INTERRUPT_STATUS_5_MASK 0x401D
+#define WM831X_RTC_WRITE_COUNTER 0x4020
+#define WM831X_RTC_TIME_1 0x4021
+#define WM831X_RTC_TIME_2 0x4022
+#define WM831X_RTC_ALARM_1 0x4023
+#define WM831X_RTC_ALARM_2 0x4024
+#define WM831X_RTC_CONTROL 0x4025
+#define WM831X_RTC_TRIM 0x4026
+#define WM831X_TOUCH_CONTROL_1 0x4028
+#define WM831X_TOUCH_CONTROL_2 0x4029
+#define WM831X_TOUCH_DATA_X 0x402A
+#define WM831X_TOUCH_DATA_Y 0x402B
+#define WM831X_TOUCH_DATA_Z 0x402C
+#define WM831X_AUXADC_DATA 0x402D
+#define WM831X_AUXADC_CONTROL 0x402E
+#define WM831X_AUXADC_SOURCE 0x402F
+#define WM831X_COMPARATOR_CONTROL 0x4030
+#define WM831X_COMPARATOR_1 0x4031
+#define WM831X_COMPARATOR_2 0x4032
+#define WM831X_COMPARATOR_3 0x4033
+#define WM831X_COMPARATOR_4 0x4034
+#define WM831X_GPIO1_CONTROL 0x4038
+#define WM831X_GPIO2_CONTROL 0x4039
+#define WM831X_GPIO3_CONTROL 0x403A
+#define WM831X_GPIO4_CONTROL 0x403B
+#define WM831X_GPIO5_CONTROL 0x403C
+#define WM831X_GPIO6_CONTROL 0x403D
+#define WM831X_GPIO7_CONTROL 0x403E
+#define WM831X_GPIO8_CONTROL 0x403F
+#define WM831X_GPIO9_CONTROL 0x4040
+#define WM831X_GPIO10_CONTROL 0x4041
+#define WM831X_GPIO11_CONTROL 0x4042
+#define WM831X_GPIO12_CONTROL 0x4043
+#define WM831X_GPIO13_CONTROL 0x4044
+#define WM831X_GPIO14_CONTROL 0x4045
+#define WM831X_GPIO15_CONTROL 0x4046
+#define WM831X_GPIO16_CONTROL 0x4047
+#define WM831X_CHARGER_CONTROL_1 0x4048
+#define WM831X_CHARGER_CONTROL_2 0x4049
+#define WM831X_CHARGER_STATUS 0x404A
+#define WM831X_BACKUP_CHARGER_CONTROL 0x404B
+#define WM831X_STATUS_LED_1 0x404C
+#define WM831X_STATUS_LED_2 0x404D
+#define WM831X_CURRENT_SINK_1 0x404E
+#define WM831X_CURRENT_SINK_2 0x404F
+#define WM831X_DCDC_ENABLE 0x4050
+#define WM831X_LDO_ENABLE 0x4051
+#define WM831X_DCDC_STATUS 0x4052
+#define WM831X_LDO_STATUS 0x4053
+#define WM831X_DCDC_UV_STATUS 0x4054
+#define WM831X_LDO_UV_STATUS 0x4055
+#define WM831X_DC1_CONTROL_1 0x4056
+#define WM831X_DC1_CONTROL_2 0x4057
+#define WM831X_DC1_ON_CONFIG 0x4058
+#define WM831X_DC1_SLEEP_CONTROL 0x4059
+#define WM831X_DC1_DVS_CONTROL 0x405A
+#define WM831X_DC2_CONTROL_1 0x405B
+#define WM831X_DC2_CONTROL_2 0x405C
+#define WM831X_DC2_ON_CONFIG 0x405D
+#define WM831X_DC2_SLEEP_CONTROL 0x405E
+#define WM831X_DC2_DVS_CONTROL 0x405F
+#define WM831X_DC3_CONTROL_1 0x4060
+#define WM831X_DC3_CONTROL_2 0x4061
+#define WM831X_DC3_ON_CONFIG 0x4062
+#define WM831X_DC3_SLEEP_CONTROL 0x4063
+#define WM831X_DC4_CONTROL 0x4064
+#define WM831X_DC4_SLEEP_CONTROL 0x4065
+#define WM831X_EPE1_CONTROL 0x4066
+#define WM831X_EPE2_CONTROL 0x4067
+#define WM831X_LDO1_CONTROL 0x4068
+#define WM831X_LDO1_ON_CONTROL 0x4069
+#define WM831X_LDO1_SLEEP_CONTROL 0x406A
+#define WM831X_LDO2_CONTROL 0x406B
+#define WM831X_LDO2_ON_CONTROL 0x406C
+#define WM831X_LDO2_SLEEP_CONTROL 0x406D
+#define WM831X_LDO3_CONTROL 0x406E
+#define WM831X_LDO3_ON_CONTROL 0x406F
+#define WM831X_LDO3_SLEEP_CONTROL 0x4070
+#define WM831X_LDO4_CONTROL 0x4071
+#define WM831X_LDO4_ON_CONTROL 0x4072
+#define WM831X_LDO4_SLEEP_CONTROL 0x4073
+#define WM831X_LDO5_CONTROL 0x4074
+#define WM831X_LDO5_ON_CONTROL 0x4075
+#define WM831X_LDO5_SLEEP_CONTROL 0x4076
+#define WM831X_LDO6_CONTROL 0x4077
+#define WM831X_LDO6_ON_CONTROL 0x4078
+#define WM831X_LDO6_SLEEP_CONTROL 0x4079
+#define WM831X_LDO7_CONTROL 0x407A
+#define WM831X_LDO7_ON_CONTROL 0x407B
+#define WM831X_LDO7_SLEEP_CONTROL 0x407C
+#define WM831X_LDO8_CONTROL 0x407D
+#define WM831X_LDO8_ON_CONTROL 0x407E
+#define WM831X_LDO8_SLEEP_CONTROL 0x407F
+#define WM831X_LDO9_CONTROL 0x4080
+#define WM831X_LDO9_ON_CONTROL 0x4081
+#define WM831X_LDO9_SLEEP_CONTROL 0x4082
+#define WM831X_LDO10_CONTROL 0x4083
+#define WM831X_LDO10_ON_CONTROL 0x4084
+#define WM831X_LDO10_SLEEP_CONTROL 0x4085
+#define WM831X_LDO11_ON_CONTROL 0x4087
+#define WM831X_LDO11_SLEEP_CONTROL 0x4088
+#define WM831X_POWER_GOOD_SOURCE_1 0x408E
+#define WM831X_POWER_GOOD_SOURCE_2 0x408F
+#define WM831X_CLOCK_CONTROL_1 0x4090
+#define WM831X_CLOCK_CONTROL_2 0x4091
+#define WM831X_FLL_CONTROL_1 0x4092
+#define WM831X_FLL_CONTROL_2 0x4093
+#define WM831X_FLL_CONTROL_3 0x4094
+#define WM831X_FLL_CONTROL_4 0x4095
+#define WM831X_FLL_CONTROL_5 0x4096
+#define WM831X_UNIQUE_ID_1 0x7800
+#define WM831X_UNIQUE_ID_2 0x7801
+#define WM831X_UNIQUE_ID_3 0x7802
+#define WM831X_UNIQUE_ID_4 0x7803
+#define WM831X_UNIQUE_ID_5 0x7804
+#define WM831X_UNIQUE_ID_6 0x7805
+#define WM831X_UNIQUE_ID_7 0x7806
+#define WM831X_UNIQUE_ID_8 0x7807
+#define WM831X_FACTORY_OTP_ID 0x7808
+#define WM831X_FACTORY_OTP_1 0x7809
+#define WM831X_FACTORY_OTP_2 0x780A
+#define WM831X_FACTORY_OTP_3 0x780B
+#define WM831X_FACTORY_OTP_4 0x780C
+#define WM831X_FACTORY_OTP_5 0x780D
+#define WM831X_CUSTOMER_OTP_ID 0x7810
+#define WM831X_DC1_OTP_CONTROL 0x7811
+#define WM831X_DC2_OTP_CONTROL 0x7812
+#define WM831X_DC3_OTP_CONTROL 0x7813
+#define WM831X_LDO1_2_OTP_CONTROL 0x7814
+#define WM831X_LDO3_4_OTP_CONTROL 0x7815
+#define WM831X_LDO5_6_OTP_CONTROL 0x7816
+#define WM831X_LDO7_8_OTP_CONTROL 0x7817
+#define WM831X_LDO9_10_OTP_CONTROL 0x7818
+#define WM831X_LDO11_EPE_CONTROL 0x7819
+#define WM831X_GPIO1_OTP_CONTROL 0x781A
+#define WM831X_GPIO2_OTP_CONTROL 0x781B
+#define WM831X_GPIO3_OTP_CONTROL 0x781C
+#define WM831X_GPIO4_OTP_CONTROL 0x781D
+#define WM831X_GPIO5_OTP_CONTROL 0x781E
+#define WM831X_GPIO6_OTP_CONTROL 0x781F
+#define WM831X_DBE_CHECK_DATA 0x7827
+
+/*
+ * R0 (0x00) - Reset ID
+ */
+#define WM831X_CHIP_ID_MASK 0xFFFF /* CHIP_ID - [15:0] */
+#define WM831X_CHIP_ID_SHIFT 0 /* CHIP_ID - [15:0] */
+#define WM831X_CHIP_ID_WIDTH 16 /* CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - Revision
+ */
+#define WM831X_PARENT_REV_MASK 0xFF00 /* PARENT_REV - [15:8] */
+#define WM831X_PARENT_REV_SHIFT 8 /* PARENT_REV - [15:8] */
+#define WM831X_PARENT_REV_WIDTH 8 /* PARENT_REV - [15:8] */
+#define WM831X_CHILD_REV_MASK 0x00FF /* CHILD_REV - [7:0] */
+#define WM831X_CHILD_REV_SHIFT 0 /* CHILD_REV - [7:0] */
+#define WM831X_CHILD_REV_WIDTH 8 /* CHILD_REV - [7:0] */
+
+/*
+ * R16384 (0x4000) - Parent ID
+ */
+#define WM831X_PARENT_ID_MASK 0xFFFF /* PARENT_ID - [15:0] */
+#define WM831X_PARENT_ID_SHIFT 0 /* PARENT_ID - [15:0] */
+#define WM831X_PARENT_ID_WIDTH 16 /* PARENT_ID - [15:0] */
+
+/*
+ * R16389 (0x4005) - ON Pin Control
+ */
+#define WM831X_ON_PIN_SECACT_MASK 0x0300 /* ON_PIN_SECACT - [9:8] */
+#define WM831X_ON_PIN_SECACT_SHIFT 8 /* ON_PIN_SECACT - [9:8] */
+#define WM831X_ON_PIN_SECACT_WIDTH 2 /* ON_PIN_SECACT - [9:8] */
+#define WM831X_ON_PIN_PRIMACT_MASK 0x0030 /* ON_PIN_PRIMACT - [5:4] */
+#define WM831X_ON_PIN_PRIMACT_SHIFT 4 /* ON_PIN_PRIMACT - [5:4] */
+#define WM831X_ON_PIN_PRIMACT_WIDTH 2 /* ON_PIN_PRIMACT - [5:4] */
+#define WM831X_ON_PIN_STS 0x0008 /* ON_PIN_STS */
+#define WM831X_ON_PIN_STS_MASK 0x0008 /* ON_PIN_STS */
+#define WM831X_ON_PIN_STS_SHIFT 3 /* ON_PIN_STS */
+#define WM831X_ON_PIN_STS_WIDTH 1 /* ON_PIN_STS */
+#define WM831X_ON_PIN_TO_MASK 0x0003 /* ON_PIN_TO - [1:0] */
+#define WM831X_ON_PIN_TO_SHIFT 0 /* ON_PIN_TO - [1:0] */
+#define WM831X_ON_PIN_TO_WIDTH 2 /* ON_PIN_TO - [1:0] */
+
+struct regulator_dev;
+
+struct wm831x {
+ struct mutex io_lock;
+
+ struct device *dev;
+ int (*read_dev)(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *dest);
+ int (*write_dev)(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *src);
+
+ void *control_data;
+
+ int irq; /* Our chip IRQ */
+ struct mutex irq_lock;
+ struct workqueue_struct *irq_wq;
+ struct work_struct irq_work;
+ unsigned int irq_base;
+ int irq_masks[5];
+
+ struct mutex auxadc_lock;
+
+ /* The WM831x has a security key blocking access to certain
+ * registers. The mutex is taken by the accessors for locking
+ * and unlocking the security key, locked is used to fail
+ * writes if the lock is held.
+ */
+ struct mutex key_lock;
+ unsigned int locked:1;
+};
+
+/* Device I/O API */
+int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg);
+int wm831x_reg_write(struct wm831x *wm831x, unsigned short reg,
+ unsigned short val);
+void wm831x_reg_lock(struct wm831x *wm831x);
+int wm831x_reg_unlock(struct wm831x *wm831x);
+int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
+ unsigned short mask, unsigned short val);
+int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
+ int count, u16 *buf);
+
+int wm831x_irq_init(struct wm831x *wm831x, int irq);
+void wm831x_irq_exit(struct wm831x *wm831x);
+
+int __must_check wm831x_request_irq(struct wm831x *wm831x,
+ unsigned int irq, irq_handler_t handler,
+ unsigned long flags, const char *name,
+ void *dev);
+void wm831x_free_irq(struct wm831x *wm831x, unsigned int, void *);
+void wm831x_disable_irq(struct wm831x *wm831x, int irq);
+void wm831x_enable_irq(struct wm831x *wm831x, int irq);
+
+#endif
diff --git a/include/linux/mfd/wm831x/gpio.h b/include/linux/mfd/wm831x/gpio.h
new file mode 100644
index 0000000..2835614
--- /dev/null
+++ b/include/linux/mfd/wm831x/gpio.h
@@ -0,0 +1,55 @@
+/*
+ * include/linux/mfd/wm831x/gpio.h -- GPIO for WM831x
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM831X_GPIO_H__
+#define __MFD_WM831X_GPIO_H__
+
+/*
+ * R16440-16455 (0x4038-0x4047) - GPIOx Control
+ */
+#define WM831X_GPN_DIR 0x8000 /* GPN_DIR */
+#define WM831X_GPN_DIR_MASK 0x8000 /* GPN_DIR */
+#define WM831X_GPN_DIR_SHIFT 15 /* GPN_DIR */
+#define WM831X_GPN_DIR_WIDTH 1 /* GPN_DIR */
+#define WM831X_GPN_PULL_MASK 0x6000 /* GPN_PULL - [14:13] */
+#define WM831X_GPN_PULL_SHIFT 13 /* GPN_PULL - [14:13] */
+#define WM831X_GPN_PULL_WIDTH 2 /* GPN_PULL - [14:13] */
+#define WM831X_GPN_INT_MODE 0x1000 /* GPN_INT_MODE */
+#define WM831X_GPN_INT_MODE_MASK 0x1000 /* GPN_INT_MODE */
+#define WM831X_GPN_INT_MODE_SHIFT 12 /* GPN_INT_MODE */
+#define WM831X_GPN_INT_MODE_WIDTH 1 /* GPN_INT_MODE */
+#define WM831X_GPN_PWR_DOM 0x0800 /* GPN_PWR_DOM */
+#define WM831X_GPN_PWR_DOM_MASK 0x0800 /* GPN_PWR_DOM */
+#define WM831X_GPN_PWR_DOM_SHIFT 11 /* GPN_PWR_DOM */
+#define WM831X_GPN_PWR_DOM_WIDTH 1 /* GPN_PWR_DOM */
+#define WM831X_GPN_POL 0x0400 /* GPN_POL */
+#define WM831X_GPN_POL_MASK 0x0400 /* GPN_POL */
+#define WM831X_GPN_POL_SHIFT 10 /* GPN_POL */
+#define WM831X_GPN_POL_WIDTH 1 /* GPN_POL */
+#define WM831X_GPN_OD 0x0200 /* GPN_OD */
+#define WM831X_GPN_OD_MASK 0x0200 /* GPN_OD */
+#define WM831X_GPN_OD_SHIFT 9 /* GPN_OD */
+#define WM831X_GPN_OD_WIDTH 1 /* GPN_OD */
+#define WM831X_GPN_TRI 0x0080 /* GPN_TRI */
+#define WM831X_GPN_TRI_MASK 0x0080 /* GPN_TRI */
+#define WM831X_GPN_TRI_SHIFT 7 /* GPN_TRI */
+#define WM831X_GPN_TRI_WIDTH 1 /* GPN_TRI */
+#define WM831X_GPN_FN_MASK 0x000F /* GPN_FN - [3:0] */
+#define WM831X_GPN_FN_SHIFT 0 /* GPN_FN - [3:0] */
+#define WM831X_GPN_FN_WIDTH 4 /* GPN_FN - [3:0] */
+
+#define WM831X_GPIO_PULL_NONE (0 << WM831X_GPN_PULL_SHIFT)
+#define WM831X_GPIO_PULL_DOWN (1 << WM831X_GPN_PULL_SHIFT)
+#define WM831X_GPIO_PULL_UP (2 << WM831X_GPN_PULL_SHIFT)
+#endif
diff --git a/include/linux/mfd/wm831x/irq.h b/include/linux/mfd/wm831x/irq.h
new file mode 100644
index 0000000..3a8c976
--- /dev/null
+++ b/include/linux/mfd/wm831x/irq.h
@@ -0,0 +1,764 @@
+/*
+ * include/linux/mfd/wm831x/irq.h -- Interrupt controller for WM831x
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM831X_IRQ_H__
+#define __MFD_WM831X_IRQ_H__
+
+/* Interrupt number assignments within Linux */
+#define WM831X_IRQ_TEMP_THW 0
+#define WM831X_IRQ_GPIO_1 1
+#define WM831X_IRQ_GPIO_2 2
+#define WM831X_IRQ_GPIO_3 3
+#define WM831X_IRQ_GPIO_4 4
+#define WM831X_IRQ_GPIO_5 5
+#define WM831X_IRQ_GPIO_6 6
+#define WM831X_IRQ_GPIO_7 7
+#define WM831X_IRQ_GPIO_8 8
+#define WM831X_IRQ_GPIO_9 9
+#define WM831X_IRQ_GPIO_10 10
+#define WM831X_IRQ_GPIO_11 11
+#define WM831X_IRQ_GPIO_12 12
+#define WM831X_IRQ_GPIO_13 13
+#define WM831X_IRQ_GPIO_14 14
+#define WM831X_IRQ_GPIO_15 15
+#define WM831X_IRQ_GPIO_16 16
+#define WM831X_IRQ_ON 17
+#define WM831X_IRQ_PPM_SYSLO 18
+#define WM831X_IRQ_PPM_PWR_SRC 19
+#define WM831X_IRQ_PPM_USB_CURR 20
+#define WM831X_IRQ_WDOG_TO 21
+#define WM831X_IRQ_RTC_PER 22
+#define WM831X_IRQ_RTC_ALM 23
+#define WM831X_IRQ_CHG_BATT_HOT 24
+#define WM831X_IRQ_CHG_BATT_COLD 25
+#define WM831X_IRQ_CHG_BATT_FAIL 26
+#define WM831X_IRQ_CHG_OV 27
+#define WM831X_IRQ_CHG_END 29
+#define WM831X_IRQ_CHG_TO 30
+#define WM831X_IRQ_CHG_MODE 31
+#define WM831X_IRQ_CHG_START 32
+#define WM831X_IRQ_TCHDATA 33
+#define WM831X_IRQ_TCHPD 34
+#define WM831X_IRQ_AUXADC_DATA 35
+#define WM831X_IRQ_AUXADC_DCOMP1 36
+#define WM831X_IRQ_AUXADC_DCOMP2 37
+#define WM831X_IRQ_AUXADC_DCOMP3 38
+#define WM831X_IRQ_AUXADC_DCOMP4 39
+#define WM831X_IRQ_CS1 40
+#define WM831X_IRQ_CS2 41
+#define WM831X_IRQ_HC_DC1 42
+#define WM831X_IRQ_HC_DC2 43
+#define WM831X_IRQ_UV_LDO1 44
+#define WM831X_IRQ_UV_LDO2 45
+#define WM831X_IRQ_UV_LDO3 46
+#define WM831X_IRQ_UV_LDO4 47
+#define WM831X_IRQ_UV_LDO5 48
+#define WM831X_IRQ_UV_LDO6 49
+#define WM831X_IRQ_UV_LDO7 50
+#define WM831X_IRQ_UV_LDO8 51
+#define WM831X_IRQ_UV_LDO9 52
+#define WM831X_IRQ_UV_LDO10 53
+#define WM831X_IRQ_UV_DC1 54
+#define WM831X_IRQ_UV_DC2 55
+#define WM831X_IRQ_UV_DC3 56
+#define WM831X_IRQ_UV_DC4 57
+
+#define WM831X_NUM_IRQS 58
+
+/*
+ * R16400 (0x4010) - System Interrupts
+ */
+#define WM831X_PS_INT 0x8000 /* PS_INT */
+#define WM831X_PS_INT_MASK 0x8000 /* PS_INT */
+#define WM831X_PS_INT_SHIFT 15 /* PS_INT */
+#define WM831X_PS_INT_WIDTH 1 /* PS_INT */
+#define WM831X_TEMP_INT 0x4000 /* TEMP_INT */
+#define WM831X_TEMP_INT_MASK 0x4000 /* TEMP_INT */
+#define WM831X_TEMP_INT_SHIFT 14 /* TEMP_INT */
+#define WM831X_TEMP_INT_WIDTH 1 /* TEMP_INT */
+#define WM831X_GP_INT 0x2000 /* GP_INT */
+#define WM831X_GP_INT_MASK 0x2000 /* GP_INT */
+#define WM831X_GP_INT_SHIFT 13 /* GP_INT */
+#define WM831X_GP_INT_WIDTH 1 /* GP_INT */
+#define WM831X_ON_PIN_INT 0x1000 /* ON_PIN_INT */
+#define WM831X_ON_PIN_INT_MASK 0x1000 /* ON_PIN_INT */
+#define WM831X_ON_PIN_INT_SHIFT 12 /* ON_PIN_INT */
+#define WM831X_ON_PIN_INT_WIDTH 1 /* ON_PIN_INT */
+#define WM831X_WDOG_INT 0x0800 /* WDOG_INT */
+#define WM831X_WDOG_INT_MASK 0x0800 /* WDOG_INT */
+#define WM831X_WDOG_INT_SHIFT 11 /* WDOG_INT */
+#define WM831X_WDOG_INT_WIDTH 1 /* WDOG_INT */
+#define WM831X_TCHDATA_INT 0x0400 /* TCHDATA_INT */
+#define WM831X_TCHDATA_INT_MASK 0x0400 /* TCHDATA_INT */
+#define WM831X_TCHDATA_INT_SHIFT 10 /* TCHDATA_INT */
+#define WM831X_TCHDATA_INT_WIDTH 1 /* TCHDATA_INT */
+#define WM831X_TCHPD_INT 0x0200 /* TCHPD_INT */
+#define WM831X_TCHPD_INT_MASK 0x0200 /* TCHPD_INT */
+#define WM831X_TCHPD_INT_SHIFT 9 /* TCHPD_INT */
+#define WM831X_TCHPD_INT_WIDTH 1 /* TCHPD_INT */
+#define WM831X_AUXADC_INT 0x0100 /* AUXADC_INT */
+#define WM831X_AUXADC_INT_MASK 0x0100 /* AUXADC_INT */
+#define WM831X_AUXADC_INT_SHIFT 8 /* AUXADC_INT */
+#define WM831X_AUXADC_INT_WIDTH 1 /* AUXADC_INT */
+#define WM831X_PPM_INT 0x0080 /* PPM_INT */
+#define WM831X_PPM_INT_MASK 0x0080 /* PPM_INT */
+#define WM831X_PPM_INT_SHIFT 7 /* PPM_INT */
+#define WM831X_PPM_INT_WIDTH 1 /* PPM_INT */
+#define WM831X_CS_INT 0x0040 /* CS_INT */
+#define WM831X_CS_INT_MASK 0x0040 /* CS_INT */
+#define WM831X_CS_INT_SHIFT 6 /* CS_INT */
+#define WM831X_CS_INT_WIDTH 1 /* CS_INT */
+#define WM831X_RTC_INT 0x0020 /* RTC_INT */
+#define WM831X_RTC_INT_MASK 0x0020 /* RTC_INT */
+#define WM831X_RTC_INT_SHIFT 5 /* RTC_INT */
+#define WM831X_RTC_INT_WIDTH 1 /* RTC_INT */
+#define WM831X_OTP_INT 0x0010 /* OTP_INT */
+#define WM831X_OTP_INT_MASK 0x0010 /* OTP_INT */
+#define WM831X_OTP_INT_SHIFT 4 /* OTP_INT */
+#define WM831X_OTP_INT_WIDTH 1 /* OTP_INT */
+#define WM831X_CHILD_INT 0x0008 /* CHILD_INT */
+#define WM831X_CHILD_INT_MASK 0x0008 /* CHILD_INT */
+#define WM831X_CHILD_INT_SHIFT 3 /* CHILD_INT */
+#define WM831X_CHILD_INT_WIDTH 1 /* CHILD_INT */
+#define WM831X_CHG_INT 0x0004 /* CHG_INT */
+#define WM831X_CHG_INT_MASK 0x0004 /* CHG_INT */
+#define WM831X_CHG_INT_SHIFT 2 /* CHG_INT */
+#define WM831X_CHG_INT_WIDTH 1 /* CHG_INT */
+#define WM831X_HC_INT 0x0002 /* HC_INT */
+#define WM831X_HC_INT_MASK 0x0002 /* HC_INT */
+#define WM831X_HC_INT_SHIFT 1 /* HC_INT */
+#define WM831X_HC_INT_WIDTH 1 /* HC_INT */
+#define WM831X_UV_INT 0x0001 /* UV_INT */
+#define WM831X_UV_INT_MASK 0x0001 /* UV_INT */
+#define WM831X_UV_INT_SHIFT 0 /* UV_INT */
+#define WM831X_UV_INT_WIDTH 1 /* UV_INT */
+
+/*
+ * R16401 (0x4011) - Interrupt Status 1
+ */
+#define WM831X_PPM_SYSLO_EINT 0x8000 /* PPM_SYSLO_EINT */
+#define WM831X_PPM_SYSLO_EINT_MASK 0x8000 /* PPM_SYSLO_EINT */
+#define WM831X_PPM_SYSLO_EINT_SHIFT 15 /* PPM_SYSLO_EINT */
+#define WM831X_PPM_SYSLO_EINT_WIDTH 1 /* PPM_SYSLO_EINT */
+#define WM831X_PPM_PWR_SRC_EINT 0x4000 /* PPM_PWR_SRC_EINT */
+#define WM831X_PPM_PWR_SRC_EINT_MASK 0x4000 /* PPM_PWR_SRC_EINT */
+#define WM831X_PPM_PWR_SRC_EINT_SHIFT 14 /* PPM_PWR_SRC_EINT */
+#define WM831X_PPM_PWR_SRC_EINT_WIDTH 1 /* PPM_PWR_SRC_EINT */
+#define WM831X_PPM_USB_CURR_EINT 0x2000 /* PPM_USB_CURR_EINT */
+#define WM831X_PPM_USB_CURR_EINT_MASK 0x2000 /* PPM_USB_CURR_EINT */
+#define WM831X_PPM_USB_CURR_EINT_SHIFT 13 /* PPM_USB_CURR_EINT */
+#define WM831X_PPM_USB_CURR_EINT_WIDTH 1 /* PPM_USB_CURR_EINT */
+#define WM831X_ON_PIN_EINT 0x1000 /* ON_PIN_EINT */
+#define WM831X_ON_PIN_EINT_MASK 0x1000 /* ON_PIN_EINT */
+#define WM831X_ON_PIN_EINT_SHIFT 12 /* ON_PIN_EINT */
+#define WM831X_ON_PIN_EINT_WIDTH 1 /* ON_PIN_EINT */
+#define WM831X_WDOG_TO_EINT 0x0800 /* WDOG_TO_EINT */
+#define WM831X_WDOG_TO_EINT_MASK 0x0800 /* WDOG_TO_EINT */
+#define WM831X_WDOG_TO_EINT_SHIFT 11 /* WDOG_TO_EINT */
+#define WM831X_WDOG_TO_EINT_WIDTH 1 /* WDOG_TO_EINT */
+#define WM831X_TCHDATA_EINT 0x0400 /* TCHDATA_EINT */
+#define WM831X_TCHDATA_EINT_MASK 0x0400 /* TCHDATA_EINT */
+#define WM831X_TCHDATA_EINT_SHIFT 10 /* TCHDATA_EINT */
+#define WM831X_TCHDATA_EINT_WIDTH 1 /* TCHDATA_EINT */
+#define WM831X_TCHPD_EINT 0x0200 /* TCHPD_EINT */
+#define WM831X_TCHPD_EINT_MASK 0x0200 /* TCHPD_EINT */
+#define WM831X_TCHPD_EINT_SHIFT 9 /* TCHPD_EINT */
+#define WM831X_TCHPD_EINT_WIDTH 1 /* TCHPD_EINT */
+#define WM831X_AUXADC_DATA_EINT 0x0100 /* AUXADC_DATA_EINT */
+#define WM831X_AUXADC_DATA_EINT_MASK 0x0100 /* AUXADC_DATA_EINT */
+#define WM831X_AUXADC_DATA_EINT_SHIFT 8 /* AUXADC_DATA_EINT */
+#define WM831X_AUXADC_DATA_EINT_WIDTH 1 /* AUXADC_DATA_EINT */
+#define WM831X_AUXADC_DCOMP4_EINT 0x0080 /* AUXADC_DCOMP4_EINT */
+#define WM831X_AUXADC_DCOMP4_EINT_MASK 0x0080 /* AUXADC_DCOMP4_EINT */
+#define WM831X_AUXADC_DCOMP4_EINT_SHIFT 7 /* AUXADC_DCOMP4_EINT */
+#define WM831X_AUXADC_DCOMP4_EINT_WIDTH 1 /* AUXADC_DCOMP4_EINT */
+#define WM831X_AUXADC_DCOMP3_EINT 0x0040 /* AUXADC_DCOMP3_EINT */
+#define WM831X_AUXADC_DCOMP3_EINT_MASK 0x0040 /* AUXADC_DCOMP3_EINT */
+#define WM831X_AUXADC_DCOMP3_EINT_SHIFT 6 /* AUXADC_DCOMP3_EINT */
+#define WM831X_AUXADC_DCOMP3_EINT_WIDTH 1 /* AUXADC_DCOMP3_EINT */
+#define WM831X_AUXADC_DCOMP2_EINT 0x0020 /* AUXADC_DCOMP2_EINT */
+#define WM831X_AUXADC_DCOMP2_EINT_MASK 0x0020 /* AUXADC_DCOMP2_EINT */
+#define WM831X_AUXADC_DCOMP2_EINT_SHIFT 5 /* AUXADC_DCOMP2_EINT */
+#define WM831X_AUXADC_DCOMP2_EINT_WIDTH 1 /* AUXADC_DCOMP2_EINT */
+#define WM831X_AUXADC_DCOMP1_EINT 0x0010 /* AUXADC_DCOMP1_EINT */
+#define WM831X_AUXADC_DCOMP1_EINT_MASK 0x0010 /* AUXADC_DCOMP1_EINT */
+#define WM831X_AUXADC_DCOMP1_EINT_SHIFT 4 /* AUXADC_DCOMP1_EINT */
+#define WM831X_AUXADC_DCOMP1_EINT_WIDTH 1 /* AUXADC_DCOMP1_EINT */
+#define WM831X_RTC_PER_EINT 0x0008 /* RTC_PER_EINT */
+#define WM831X_RTC_PER_EINT_MASK 0x0008 /* RTC_PER_EINT */
+#define WM831X_RTC_PER_EINT_SHIFT 3 /* RTC_PER_EINT */
+#define WM831X_RTC_PER_EINT_WIDTH 1 /* RTC_PER_EINT */
+#define WM831X_RTC_ALM_EINT 0x0004 /* RTC_ALM_EINT */
+#define WM831X_RTC_ALM_EINT_MASK 0x0004 /* RTC_ALM_EINT */
+#define WM831X_RTC_ALM_EINT_SHIFT 2 /* RTC_ALM_EINT */
+#define WM831X_RTC_ALM_EINT_WIDTH 1 /* RTC_ALM_EINT */
+#define WM831X_TEMP_THW_EINT 0x0002 /* TEMP_THW_EINT */
+#define WM831X_TEMP_THW_EINT_MASK 0x0002 /* TEMP_THW_EINT */
+#define WM831X_TEMP_THW_EINT_SHIFT 1 /* TEMP_THW_EINT */
+#define WM831X_TEMP_THW_EINT_WIDTH 1 /* TEMP_THW_EINT */
+
+/*
+ * R16402 (0x4012) - Interrupt Status 2
+ */
+#define WM831X_CHG_BATT_HOT_EINT 0x8000 /* CHG_BATT_HOT_EINT */
+#define WM831X_CHG_BATT_HOT_EINT_MASK 0x8000 /* CHG_BATT_HOT_EINT */
+#define WM831X_CHG_BATT_HOT_EINT_SHIFT 15 /* CHG_BATT_HOT_EINT */
+#define WM831X_CHG_BATT_HOT_EINT_WIDTH 1 /* CHG_BATT_HOT_EINT */
+#define WM831X_CHG_BATT_COLD_EINT 0x4000 /* CHG_BATT_COLD_EINT */
+#define WM831X_CHG_BATT_COLD_EINT_MASK 0x4000 /* CHG_BATT_COLD_EINT */
+#define WM831X_CHG_BATT_COLD_EINT_SHIFT 14 /* CHG_BATT_COLD_EINT */
+#define WM831X_CHG_BATT_COLD_EINT_WIDTH 1 /* CHG_BATT_COLD_EINT */
+#define WM831X_CHG_BATT_FAIL_EINT 0x2000 /* CHG_BATT_FAIL_EINT */
+#define WM831X_CHG_BATT_FAIL_EINT_MASK 0x2000 /* CHG_BATT_FAIL_EINT */
+#define WM831X_CHG_BATT_FAIL_EINT_SHIFT 13 /* CHG_BATT_FAIL_EINT */
+#define WM831X_CHG_BATT_FAIL_EINT_WIDTH 1 /* CHG_BATT_FAIL_EINT */
+#define WM831X_CHG_OV_EINT 0x1000 /* CHG_OV_EINT */
+#define WM831X_CHG_OV_EINT_MASK 0x1000 /* CHG_OV_EINT */
+#define WM831X_CHG_OV_EINT_SHIFT 12 /* CHG_OV_EINT */
+#define WM831X_CHG_OV_EINT_WIDTH 1 /* CHG_OV_EINT */
+#define WM831X_CHG_END_EINT 0x0800 /* CHG_END_EINT */
+#define WM831X_CHG_END_EINT_MASK 0x0800 /* CHG_END_EINT */
+#define WM831X_CHG_END_EINT_SHIFT 11 /* CHG_END_EINT */
+#define WM831X_CHG_END_EINT_WIDTH 1 /* CHG_END_EINT */
+#define WM831X_CHG_TO_EINT 0x0400 /* CHG_TO_EINT */
+#define WM831X_CHG_TO_EINT_MASK 0x0400 /* CHG_TO_EINT */
+#define WM831X_CHG_TO_EINT_SHIFT 10 /* CHG_TO_EINT */
+#define WM831X_CHG_TO_EINT_WIDTH 1 /* CHG_TO_EINT */
+#define WM831X_CHG_MODE_EINT 0x0200 /* CHG_MODE_EINT */
+#define WM831X_CHG_MODE_EINT_MASK 0x0200 /* CHG_MODE_EINT */
+#define WM831X_CHG_MODE_EINT_SHIFT 9 /* CHG_MODE_EINT */
+#define WM831X_CHG_MODE_EINT_WIDTH 1 /* CHG_MODE_EINT */
+#define WM831X_CHG_START_EINT 0x0100 /* CHG_START_EINT */
+#define WM831X_CHG_START_EINT_MASK 0x0100 /* CHG_START_EINT */
+#define WM831X_CHG_START_EINT_SHIFT 8 /* CHG_START_EINT */
+#define WM831X_CHG_START_EINT_WIDTH 1 /* CHG_START_EINT */
+#define WM831X_CS2_EINT 0x0080 /* CS2_EINT */
+#define WM831X_CS2_EINT_MASK 0x0080 /* CS2_EINT */
+#define WM831X_CS2_EINT_SHIFT 7 /* CS2_EINT */
+#define WM831X_CS2_EINT_WIDTH 1 /* CS2_EINT */
+#define WM831X_CS1_EINT 0x0040 /* CS1_EINT */
+#define WM831X_CS1_EINT_MASK 0x0040 /* CS1_EINT */
+#define WM831X_CS1_EINT_SHIFT 6 /* CS1_EINT */
+#define WM831X_CS1_EINT_WIDTH 1 /* CS1_EINT */
+#define WM831X_OTP_CMD_END_EINT 0x0020 /* OTP_CMD_END_EINT */
+#define WM831X_OTP_CMD_END_EINT_MASK 0x0020 /* OTP_CMD_END_EINT */
+#define WM831X_OTP_CMD_END_EINT_SHIFT 5 /* OTP_CMD_END_EINT */
+#define WM831X_OTP_CMD_END_EINT_WIDTH 1 /* OTP_CMD_END_EINT */
+#define WM831X_OTP_ERR_EINT 0x0010 /* OTP_ERR_EINT */
+#define WM831X_OTP_ERR_EINT_MASK 0x0010 /* OTP_ERR_EINT */
+#define WM831X_OTP_ERR_EINT_SHIFT 4 /* OTP_ERR_EINT */
+#define WM831X_OTP_ERR_EINT_WIDTH 1 /* OTP_ERR_EINT */
+#define WM831X_PS_POR_EINT 0x0004 /* PS_POR_EINT */
+#define WM831X_PS_POR_EINT_MASK 0x0004 /* PS_POR_EINT */
+#define WM831X_PS_POR_EINT_SHIFT 2 /* PS_POR_EINT */
+#define WM831X_PS_POR_EINT_WIDTH 1 /* PS_POR_EINT */
+#define WM831X_PS_SLEEP_OFF_EINT 0x0002 /* PS_SLEEP_OFF_EINT */
+#define WM831X_PS_SLEEP_OFF_EINT_MASK 0x0002 /* PS_SLEEP_OFF_EINT */
+#define WM831X_PS_SLEEP_OFF_EINT_SHIFT 1 /* PS_SLEEP_OFF_EINT */
+#define WM831X_PS_SLEEP_OFF_EINT_WIDTH 1 /* PS_SLEEP_OFF_EINT */
+#define WM831X_PS_ON_WAKE_EINT 0x0001 /* PS_ON_WAKE_EINT */
+#define WM831X_PS_ON_WAKE_EINT_MASK 0x0001 /* PS_ON_WAKE_EINT */
+#define WM831X_PS_ON_WAKE_EINT_SHIFT 0 /* PS_ON_WAKE_EINT */
+#define WM831X_PS_ON_WAKE_EINT_WIDTH 1 /* PS_ON_WAKE_EINT */
+
+/*
+ * R16403 (0x4013) - Interrupt Status 3
+ */
+#define WM831X_UV_LDO10_EINT 0x0200 /* UV_LDO10_EINT */
+#define WM831X_UV_LDO10_EINT_MASK 0x0200 /* UV_LDO10_EINT */
+#define WM831X_UV_LDO10_EINT_SHIFT 9 /* UV_LDO10_EINT */
+#define WM831X_UV_LDO10_EINT_WIDTH 1 /* UV_LDO10_EINT */
+#define WM831X_UV_LDO9_EINT 0x0100 /* UV_LDO9_EINT */
+#define WM831X_UV_LDO9_EINT_MASK 0x0100 /* UV_LDO9_EINT */
+#define WM831X_UV_LDO9_EINT_SHIFT 8 /* UV_LDO9_EINT */
+#define WM831X_UV_LDO9_EINT_WIDTH 1 /* UV_LDO9_EINT */
+#define WM831X_UV_LDO8_EINT 0x0080 /* UV_LDO8_EINT */
+#define WM831X_UV_LDO8_EINT_MASK 0x0080 /* UV_LDO8_EINT */
+#define WM831X_UV_LDO8_EINT_SHIFT 7 /* UV_LDO8_EINT */
+#define WM831X_UV_LDO8_EINT_WIDTH 1 /* UV_LDO8_EINT */
+#define WM831X_UV_LDO7_EINT 0x0040 /* UV_LDO7_EINT */
+#define WM831X_UV_LDO7_EINT_MASK 0x0040 /* UV_LDO7_EINT */
+#define WM831X_UV_LDO7_EINT_SHIFT 6 /* UV_LDO7_EINT */
+#define WM831X_UV_LDO7_EINT_WIDTH 1 /* UV_LDO7_EINT */
+#define WM831X_UV_LDO6_EINT 0x0020 /* UV_LDO6_EINT */
+#define WM831X_UV_LDO6_EINT_MASK 0x0020 /* UV_LDO6_EINT */
+#define WM831X_UV_LDO6_EINT_SHIFT 5 /* UV_LDO6_EINT */
+#define WM831X_UV_LDO6_EINT_WIDTH 1 /* UV_LDO6_EINT */
+#define WM831X_UV_LDO5_EINT 0x0010 /* UV_LDO5_EINT */
+#define WM831X_UV_LDO5_EINT_MASK 0x0010 /* UV_LDO5_EINT */
+#define WM831X_UV_LDO5_EINT_SHIFT 4 /* UV_LDO5_EINT */
+#define WM831X_UV_LDO5_EINT_WIDTH 1 /* UV_LDO5_EINT */
+#define WM831X_UV_LDO4_EINT 0x0008 /* UV_LDO4_EINT */
+#define WM831X_UV_LDO4_EINT_MASK 0x0008 /* UV_LDO4_EINT */
+#define WM831X_UV_LDO4_EINT_SHIFT 3 /* UV_LDO4_EINT */
+#define WM831X_UV_LDO4_EINT_WIDTH 1 /* UV_LDO4_EINT */
+#define WM831X_UV_LDO3_EINT 0x0004 /* UV_LDO3_EINT */
+#define WM831X_UV_LDO3_EINT_MASK 0x0004 /* UV_LDO3_EINT */
+#define WM831X_UV_LDO3_EINT_SHIFT 2 /* UV_LDO3_EINT */
+#define WM831X_UV_LDO3_EINT_WIDTH 1 /* UV_LDO3_EINT */
+#define WM831X_UV_LDO2_EINT 0x0002 /* UV_LDO2_EINT */
+#define WM831X_UV_LDO2_EINT_MASK 0x0002 /* UV_LDO2_EINT */
+#define WM831X_UV_LDO2_EINT_SHIFT 1 /* UV_LDO2_EINT */
+#define WM831X_UV_LDO2_EINT_WIDTH 1 /* UV_LDO2_EINT */
+#define WM831X_UV_LDO1_EINT 0x0001 /* UV_LDO1_EINT */
+#define WM831X_UV_LDO1_EINT_MASK 0x0001 /* UV_LDO1_EINT */
+#define WM831X_UV_LDO1_EINT_SHIFT 0 /* UV_LDO1_EINT */
+#define WM831X_UV_LDO1_EINT_WIDTH 1 /* UV_LDO1_EINT */
+
+/*
+ * R16404 (0x4014) - Interrupt Status 4
+ */
+#define WM831X_HC_DC2_EINT 0x0200 /* HC_DC2_EINT */
+#define WM831X_HC_DC2_EINT_MASK 0x0200 /* HC_DC2_EINT */
+#define WM831X_HC_DC2_EINT_SHIFT 9 /* HC_DC2_EINT */
+#define WM831X_HC_DC2_EINT_WIDTH 1 /* HC_DC2_EINT */
+#define WM831X_HC_DC1_EINT 0x0100 /* HC_DC1_EINT */
+#define WM831X_HC_DC1_EINT_MASK 0x0100 /* HC_DC1_EINT */
+#define WM831X_HC_DC1_EINT_SHIFT 8 /* HC_DC1_EINT */
+#define WM831X_HC_DC1_EINT_WIDTH 1 /* HC_DC1_EINT */
+#define WM831X_UV_DC4_EINT 0x0008 /* UV_DC4_EINT */
+#define WM831X_UV_DC4_EINT_MASK 0x0008 /* UV_DC4_EINT */
+#define WM831X_UV_DC4_EINT_SHIFT 3 /* UV_DC4_EINT */
+#define WM831X_UV_DC4_EINT_WIDTH 1 /* UV_DC4_EINT */
+#define WM831X_UV_DC3_EINT 0x0004 /* UV_DC3_EINT */
+#define WM831X_UV_DC3_EINT_MASK 0x0004 /* UV_DC3_EINT */
+#define WM831X_UV_DC3_EINT_SHIFT 2 /* UV_DC3_EINT */
+#define WM831X_UV_DC3_EINT_WIDTH 1 /* UV_DC3_EINT */
+#define WM831X_UV_DC2_EINT 0x0002 /* UV_DC2_EINT */
+#define WM831X_UV_DC2_EINT_MASK 0x0002 /* UV_DC2_EINT */
+#define WM831X_UV_DC2_EINT_SHIFT 1 /* UV_DC2_EINT */
+#define WM831X_UV_DC2_EINT_WIDTH 1 /* UV_DC2_EINT */
+#define WM831X_UV_DC1_EINT 0x0001 /* UV_DC1_EINT */
+#define WM831X_UV_DC1_EINT_MASK 0x0001 /* UV_DC1_EINT */
+#define WM831X_UV_DC1_EINT_SHIFT 0 /* UV_DC1_EINT */
+#define WM831X_UV_DC1_EINT_WIDTH 1 /* UV_DC1_EINT */
+
+/*
+ * R16405 (0x4015) - Interrupt Status 5
+ */
+#define WM831X_GP16_EINT 0x8000 /* GP16_EINT */
+#define WM831X_GP16_EINT_MASK 0x8000 /* GP16_EINT */
+#define WM831X_GP16_EINT_SHIFT 15 /* GP16_EINT */
+#define WM831X_GP16_EINT_WIDTH 1 /* GP16_EINT */
+#define WM831X_GP15_EINT 0x4000 /* GP15_EINT */
+#define WM831X_GP15_EINT_MASK 0x4000 /* GP15_EINT */
+#define WM831X_GP15_EINT_SHIFT 14 /* GP15_EINT */
+#define WM831X_GP15_EINT_WIDTH 1 /* GP15_EINT */
+#define WM831X_GP14_EINT 0x2000 /* GP14_EINT */
+#define WM831X_GP14_EINT_MASK 0x2000 /* GP14_EINT */
+#define WM831X_GP14_EINT_SHIFT 13 /* GP14_EINT */
+#define WM831X_GP14_EINT_WIDTH 1 /* GP14_EINT */
+#define WM831X_GP13_EINT 0x1000 /* GP13_EINT */
+#define WM831X_GP13_EINT_MASK 0x1000 /* GP13_EINT */
+#define WM831X_GP13_EINT_SHIFT 12 /* GP13_EINT */
+#define WM831X_GP13_EINT_WIDTH 1 /* GP13_EINT */
+#define WM831X_GP12_EINT 0x0800 /* GP12_EINT */
+#define WM831X_GP12_EINT_MASK 0x0800 /* GP12_EINT */
+#define WM831X_GP12_EINT_SHIFT 11 /* GP12_EINT */
+#define WM831X_GP12_EINT_WIDTH 1 /* GP12_EINT */
+#define WM831X_GP11_EINT 0x0400 /* GP11_EINT */
+#define WM831X_GP11_EINT_MASK 0x0400 /* GP11_EINT */
+#define WM831X_GP11_EINT_SHIFT 10 /* GP11_EINT */
+#define WM831X_GP11_EINT_WIDTH 1 /* GP11_EINT */
+#define WM831X_GP10_EINT 0x0200 /* GP10_EINT */
+#define WM831X_GP10_EINT_MASK 0x0200 /* GP10_EINT */
+#define WM831X_GP10_EINT_SHIFT 9 /* GP10_EINT */
+#define WM831X_GP10_EINT_WIDTH 1 /* GP10_EINT */
+#define WM831X_GP9_EINT 0x0100 /* GP9_EINT */
+#define WM831X_GP9_EINT_MASK 0x0100 /* GP9_EINT */
+#define WM831X_GP9_EINT_SHIFT 8 /* GP9_EINT */
+#define WM831X_GP9_EINT_WIDTH 1 /* GP9_EINT */
+#define WM831X_GP8_EINT 0x0080 /* GP8_EINT */
+#define WM831X_GP8_EINT_MASK 0x0080 /* GP8_EINT */
+#define WM831X_GP8_EINT_SHIFT 7 /* GP8_EINT */
+#define WM831X_GP8_EINT_WIDTH 1 /* GP8_EINT */
+#define WM831X_GP7_EINT 0x0040 /* GP7_EINT */
+#define WM831X_GP7_EINT_MASK 0x0040 /* GP7_EINT */
+#define WM831X_GP7_EINT_SHIFT 6 /* GP7_EINT */
+#define WM831X_GP7_EINT_WIDTH 1 /* GP7_EINT */
+#define WM831X_GP6_EINT 0x0020 /* GP6_EINT */
+#define WM831X_GP6_EINT_MASK 0x0020 /* GP6_EINT */
+#define WM831X_GP6_EINT_SHIFT 5 /* GP6_EINT */
+#define WM831X_GP6_EINT_WIDTH 1 /* GP6_EINT */
+#define WM831X_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM831X_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM831X_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM831X_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM831X_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM831X_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM831X_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM831X_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM831X_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM831X_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM831X_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM831X_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM831X_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM831X_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM831X_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM831X_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM831X_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM831X_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM831X_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM831X_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R16407 (0x4017) - IRQ Config
+ */
+#define WM831X_IRQ_OD 0x0002 /* IRQ_OD */
+#define WM831X_IRQ_OD_MASK 0x0002 /* IRQ_OD */
+#define WM831X_IRQ_OD_SHIFT 1 /* IRQ_OD */
+#define WM831X_IRQ_OD_WIDTH 1 /* IRQ_OD */
+#define WM831X_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM831X_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM831X_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM831X_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R16408 (0x4018) - System Interrupts Mask
+ */
+#define WM831X_IM_PS_INT 0x8000 /* IM_PS_INT */
+#define WM831X_IM_PS_INT_MASK 0x8000 /* IM_PS_INT */
+#define WM831X_IM_PS_INT_SHIFT 15 /* IM_PS_INT */
+#define WM831X_IM_PS_INT_WIDTH 1 /* IM_PS_INT */
+#define WM831X_IM_TEMP_INT 0x4000 /* IM_TEMP_INT */
+#define WM831X_IM_TEMP_INT_MASK 0x4000 /* IM_TEMP_INT */
+#define WM831X_IM_TEMP_INT_SHIFT 14 /* IM_TEMP_INT */
+#define WM831X_IM_TEMP_INT_WIDTH 1 /* IM_TEMP_INT */
+#define WM831X_IM_GP_INT 0x2000 /* IM_GP_INT */
+#define WM831X_IM_GP_INT_MASK 0x2000 /* IM_GP_INT */
+#define WM831X_IM_GP_INT_SHIFT 13 /* IM_GP_INT */
+#define WM831X_IM_GP_INT_WIDTH 1 /* IM_GP_INT */
+#define WM831X_IM_ON_PIN_INT 0x1000 /* IM_ON_PIN_INT */
+#define WM831X_IM_ON_PIN_INT_MASK 0x1000 /* IM_ON_PIN_INT */
+#define WM831X_IM_ON_PIN_INT_SHIFT 12 /* IM_ON_PIN_INT */
+#define WM831X_IM_ON_PIN_INT_WIDTH 1 /* IM_ON_PIN_INT */
+#define WM831X_IM_WDOG_INT 0x0800 /* IM_WDOG_INT */
+#define WM831X_IM_WDOG_INT_MASK 0x0800 /* IM_WDOG_INT */
+#define WM831X_IM_WDOG_INT_SHIFT 11 /* IM_WDOG_INT */
+#define WM831X_IM_WDOG_INT_WIDTH 1 /* IM_WDOG_INT */
+#define WM831X_IM_TCHDATA_INT 0x0400 /* IM_TCHDATA_INT */
+#define WM831X_IM_TCHDATA_INT_MASK 0x0400 /* IM_TCHDATA_INT */
+#define WM831X_IM_TCHDATA_INT_SHIFT 10 /* IM_TCHDATA_INT */
+#define WM831X_IM_TCHDATA_INT_WIDTH 1 /* IM_TCHDATA_INT */
+#define WM831X_IM_TCHPD_INT 0x0200 /* IM_TCHPD_INT */
+#define WM831X_IM_TCHPD_INT_MASK 0x0200 /* IM_TCHPD_INT */
+#define WM831X_IM_TCHPD_INT_SHIFT 9 /* IM_TCHPD_INT */
+#define WM831X_IM_TCHPD_INT_WIDTH 1 /* IM_TCHPD_INT */
+#define WM831X_IM_AUXADC_INT 0x0100 /* IM_AUXADC_INT */
+#define WM831X_IM_AUXADC_INT_MASK 0x0100 /* IM_AUXADC_INT */
+#define WM831X_IM_AUXADC_INT_SHIFT 8 /* IM_AUXADC_INT */
+#define WM831X_IM_AUXADC_INT_WIDTH 1 /* IM_AUXADC_INT */
+#define WM831X_IM_PPM_INT 0x0080 /* IM_PPM_INT */
+#define WM831X_IM_PPM_INT_MASK 0x0080 /* IM_PPM_INT */
+#define WM831X_IM_PPM_INT_SHIFT 7 /* IM_PPM_INT */
+#define WM831X_IM_PPM_INT_WIDTH 1 /* IM_PPM_INT */
+#define WM831X_IM_CS_INT 0x0040 /* IM_CS_INT */
+#define WM831X_IM_CS_INT_MASK 0x0040 /* IM_CS_INT */
+#define WM831X_IM_CS_INT_SHIFT 6 /* IM_CS_INT */
+#define WM831X_IM_CS_INT_WIDTH 1 /* IM_CS_INT */
+#define WM831X_IM_RTC_INT 0x0020 /* IM_RTC_INT */
+#define WM831X_IM_RTC_INT_MASK 0x0020 /* IM_RTC_INT */
+#define WM831X_IM_RTC_INT_SHIFT 5 /* IM_RTC_INT */
+#define WM831X_IM_RTC_INT_WIDTH 1 /* IM_RTC_INT */
+#define WM831X_IM_OTP_INT 0x0010 /* IM_OTP_INT */
+#define WM831X_IM_OTP_INT_MASK 0x0010 /* IM_OTP_INT */
+#define WM831X_IM_OTP_INT_SHIFT 4 /* IM_OTP_INT */
+#define WM831X_IM_OTP_INT_WIDTH 1 /* IM_OTP_INT */
+#define WM831X_IM_CHILD_INT 0x0008 /* IM_CHILD_INT */
+#define WM831X_IM_CHILD_INT_MASK 0x0008 /* IM_CHILD_INT */
+#define WM831X_IM_CHILD_INT_SHIFT 3 /* IM_CHILD_INT */
+#define WM831X_IM_CHILD_INT_WIDTH 1 /* IM_CHILD_INT */
+#define WM831X_IM_CHG_INT 0x0004 /* IM_CHG_INT */
+#define WM831X_IM_CHG_INT_MASK 0x0004 /* IM_CHG_INT */
+#define WM831X_IM_CHG_INT_SHIFT 2 /* IM_CHG_INT */
+#define WM831X_IM_CHG_INT_WIDTH 1 /* IM_CHG_INT */
+#define WM831X_IM_HC_INT 0x0002 /* IM_HC_INT */
+#define WM831X_IM_HC_INT_MASK 0x0002 /* IM_HC_INT */
+#define WM831X_IM_HC_INT_SHIFT 1 /* IM_HC_INT */
+#define WM831X_IM_HC_INT_WIDTH 1 /* IM_HC_INT */
+#define WM831X_IM_UV_INT 0x0001 /* IM_UV_INT */
+#define WM831X_IM_UV_INT_MASK 0x0001 /* IM_UV_INT */
+#define WM831X_IM_UV_INT_SHIFT 0 /* IM_UV_INT */
+#define WM831X_IM_UV_INT_WIDTH 1 /* IM_UV_INT */
+
+/*
+ * R16409 (0x4019) - Interrupt Status 1 Mask
+ */
+#define WM831X_IM_PPM_SYSLO_EINT 0x8000 /* IM_PPM_SYSLO_EINT */
+#define WM831X_IM_PPM_SYSLO_EINT_MASK 0x8000 /* IM_PPM_SYSLO_EINT */
+#define WM831X_IM_PPM_SYSLO_EINT_SHIFT 15 /* IM_PPM_SYSLO_EINT */
+#define WM831X_IM_PPM_SYSLO_EINT_WIDTH 1 /* IM_PPM_SYSLO_EINT */
+#define WM831X_IM_PPM_PWR_SRC_EINT 0x4000 /* IM_PPM_PWR_SRC_EINT */
+#define WM831X_IM_PPM_PWR_SRC_EINT_MASK 0x4000 /* IM_PPM_PWR_SRC_EINT */
+#define WM831X_IM_PPM_PWR_SRC_EINT_SHIFT 14 /* IM_PPM_PWR_SRC_EINT */
+#define WM831X_IM_PPM_PWR_SRC_EINT_WIDTH 1 /* IM_PPM_PWR_SRC_EINT */
+#define WM831X_IM_PPM_USB_CURR_EINT 0x2000 /* IM_PPM_USB_CURR_EINT */
+#define WM831X_IM_PPM_USB_CURR_EINT_MASK 0x2000 /* IM_PPM_USB_CURR_EINT */
+#define WM831X_IM_PPM_USB_CURR_EINT_SHIFT 13 /* IM_PPM_USB_CURR_EINT */
+#define WM831X_IM_PPM_USB_CURR_EINT_WIDTH 1 /* IM_PPM_USB_CURR_EINT */
+#define WM831X_IM_ON_PIN_EINT 0x1000 /* IM_ON_PIN_EINT */
+#define WM831X_IM_ON_PIN_EINT_MASK 0x1000 /* IM_ON_PIN_EINT */
+#define WM831X_IM_ON_PIN_EINT_SHIFT 12 /* IM_ON_PIN_EINT */
+#define WM831X_IM_ON_PIN_EINT_WIDTH 1 /* IM_ON_PIN_EINT */
+#define WM831X_IM_WDOG_TO_EINT 0x0800 /* IM_WDOG_TO_EINT */
+#define WM831X_IM_WDOG_TO_EINT_MASK 0x0800 /* IM_WDOG_TO_EINT */
+#define WM831X_IM_WDOG_TO_EINT_SHIFT 11 /* IM_WDOG_TO_EINT */
+#define WM831X_IM_WDOG_TO_EINT_WIDTH 1 /* IM_WDOG_TO_EINT */
+#define WM831X_IM_TCHDATA_EINT 0x0400 /* IM_TCHDATA_EINT */
+#define WM831X_IM_TCHDATA_EINT_MASK 0x0400 /* IM_TCHDATA_EINT */
+#define WM831X_IM_TCHDATA_EINT_SHIFT 10 /* IM_TCHDATA_EINT */
+#define WM831X_IM_TCHDATA_EINT_WIDTH 1 /* IM_TCHDATA_EINT */
+#define WM831X_IM_TCHPD_EINT 0x0200 /* IM_TCHPD_EINT */
+#define WM831X_IM_TCHPD_EINT_MASK 0x0200 /* IM_TCHPD_EINT */
+#define WM831X_IM_TCHPD_EINT_SHIFT 9 /* IM_TCHPD_EINT */
+#define WM831X_IM_TCHPD_EINT_WIDTH 1 /* IM_TCHPD_EINT */
+#define WM831X_IM_AUXADC_DATA_EINT 0x0100 /* IM_AUXADC_DATA_EINT */
+#define WM831X_IM_AUXADC_DATA_EINT_MASK 0x0100 /* IM_AUXADC_DATA_EINT */
+#define WM831X_IM_AUXADC_DATA_EINT_SHIFT 8 /* IM_AUXADC_DATA_EINT */
+#define WM831X_IM_AUXADC_DATA_EINT_WIDTH 1 /* IM_AUXADC_DATA_EINT */
+#define WM831X_IM_AUXADC_DCOMP4_EINT 0x0080 /* IM_AUXADC_DCOMP4_EINT */
+#define WM831X_IM_AUXADC_DCOMP4_EINT_MASK 0x0080 /* IM_AUXADC_DCOMP4_EINT */
+#define WM831X_IM_AUXADC_DCOMP4_EINT_SHIFT 7 /* IM_AUXADC_DCOMP4_EINT */
+#define WM831X_IM_AUXADC_DCOMP4_EINT_WIDTH 1 /* IM_AUXADC_DCOMP4_EINT */
+#define WM831X_IM_AUXADC_DCOMP3_EINT 0x0040 /* IM_AUXADC_DCOMP3_EINT */
+#define WM831X_IM_AUXADC_DCOMP3_EINT_MASK 0x0040 /* IM_AUXADC_DCOMP3_EINT */
+#define WM831X_IM_AUXADC_DCOMP3_EINT_SHIFT 6 /* IM_AUXADC_DCOMP3_EINT */
+#define WM831X_IM_AUXADC_DCOMP3_EINT_WIDTH 1 /* IM_AUXADC_DCOMP3_EINT */
+#define WM831X_IM_AUXADC_DCOMP2_EINT 0x0020 /* IM_AUXADC_DCOMP2_EINT */
+#define WM831X_IM_AUXADC_DCOMP2_EINT_MASK 0x0020 /* IM_AUXADC_DCOMP2_EINT */
+#define WM831X_IM_AUXADC_DCOMP2_EINT_SHIFT 5 /* IM_AUXADC_DCOMP2_EINT */
+#define WM831X_IM_AUXADC_DCOMP2_EINT_WIDTH 1 /* IM_AUXADC_DCOMP2_EINT */
+#define WM831X_IM_AUXADC_DCOMP1_EINT 0x0010 /* IM_AUXADC_DCOMP1_EINT */
+#define WM831X_IM_AUXADC_DCOMP1_EINT_MASK 0x0010 /* IM_AUXADC_DCOMP1_EINT */
+#define WM831X_IM_AUXADC_DCOMP1_EINT_SHIFT 4 /* IM_AUXADC_DCOMP1_EINT */
+#define WM831X_IM_AUXADC_DCOMP1_EINT_WIDTH 1 /* IM_AUXADC_DCOMP1_EINT */
+#define WM831X_IM_RTC_PER_EINT 0x0008 /* IM_RTC_PER_EINT */
+#define WM831X_IM_RTC_PER_EINT_MASK 0x0008 /* IM_RTC_PER_EINT */
+#define WM831X_IM_RTC_PER_EINT_SHIFT 3 /* IM_RTC_PER_EINT */
+#define WM831X_IM_RTC_PER_EINT_WIDTH 1 /* IM_RTC_PER_EINT */
+#define WM831X_IM_RTC_ALM_EINT 0x0004 /* IM_RTC_ALM_EINT */
+#define WM831X_IM_RTC_ALM_EINT_MASK 0x0004 /* IM_RTC_ALM_EINT */
+#define WM831X_IM_RTC_ALM_EINT_SHIFT 2 /* IM_RTC_ALM_EINT */
+#define WM831X_IM_RTC_ALM_EINT_WIDTH 1 /* IM_RTC_ALM_EINT */
+#define WM831X_IM_TEMP_THW_EINT 0x0002 /* IM_TEMP_THW_EINT */
+#define WM831X_IM_TEMP_THW_EINT_MASK 0x0002 /* IM_TEMP_THW_EINT */
+#define WM831X_IM_TEMP_THW_EINT_SHIFT 1 /* IM_TEMP_THW_EINT */
+#define WM831X_IM_TEMP_THW_EINT_WIDTH 1 /* IM_TEMP_THW_EINT */
+
+/*
+ * R16410 (0x401A) - Interrupt Status 2 Mask
+ */
+#define WM831X_IM_CHG_BATT_HOT_EINT 0x8000 /* IM_CHG_BATT_HOT_EINT */
+#define WM831X_IM_CHG_BATT_HOT_EINT_MASK 0x8000 /* IM_CHG_BATT_HOT_EINT */
+#define WM831X_IM_CHG_BATT_HOT_EINT_SHIFT 15 /* IM_CHG_BATT_HOT_EINT */
+#define WM831X_IM_CHG_BATT_HOT_EINT_WIDTH 1 /* IM_CHG_BATT_HOT_EINT */
+#define WM831X_IM_CHG_BATT_COLD_EINT 0x4000 /* IM_CHG_BATT_COLD_EINT */
+#define WM831X_IM_CHG_BATT_COLD_EINT_MASK 0x4000 /* IM_CHG_BATT_COLD_EINT */
+#define WM831X_IM_CHG_BATT_COLD_EINT_SHIFT 14 /* IM_CHG_BATT_COLD_EINT */
+#define WM831X_IM_CHG_BATT_COLD_EINT_WIDTH 1 /* IM_CHG_BATT_COLD_EINT */
+#define WM831X_IM_CHG_BATT_FAIL_EINT 0x2000 /* IM_CHG_BATT_FAIL_EINT */
+#define WM831X_IM_CHG_BATT_FAIL_EINT_MASK 0x2000 /* IM_CHG_BATT_FAIL_EINT */
+#define WM831X_IM_CHG_BATT_FAIL_EINT_SHIFT 13 /* IM_CHG_BATT_FAIL_EINT */
+#define WM831X_IM_CHG_BATT_FAIL_EINT_WIDTH 1 /* IM_CHG_BATT_FAIL_EINT */
+#define WM831X_IM_CHG_OV_EINT 0x1000 /* IM_CHG_OV_EINT */
+#define WM831X_IM_CHG_OV_EINT_MASK 0x1000 /* IM_CHG_OV_EINT */
+#define WM831X_IM_CHG_OV_EINT_SHIFT 12 /* IM_CHG_OV_EINT */
+#define WM831X_IM_CHG_OV_EINT_WIDTH 1 /* IM_CHG_OV_EINT */
+#define WM831X_IM_CHG_END_EINT 0x0800 /* IM_CHG_END_EINT */
+#define WM831X_IM_CHG_END_EINT_MASK 0x0800 /* IM_CHG_END_EINT */
+#define WM831X_IM_CHG_END_EINT_SHIFT 11 /* IM_CHG_END_EINT */
+#define WM831X_IM_CHG_END_EINT_WIDTH 1 /* IM_CHG_END_EINT */
+#define WM831X_IM_CHG_TO_EINT 0x0400 /* IM_CHG_TO_EINT */
+#define WM831X_IM_CHG_TO_EINT_MASK 0x0400 /* IM_CHG_TO_EINT */
+#define WM831X_IM_CHG_TO_EINT_SHIFT 10 /* IM_CHG_TO_EINT */
+#define WM831X_IM_CHG_TO_EINT_WIDTH 1 /* IM_CHG_TO_EINT */
+#define WM831X_IM_CHG_MODE_EINT 0x0200 /* IM_CHG_MODE_EINT */
+#define WM831X_IM_CHG_MODE_EINT_MASK 0x0200 /* IM_CHG_MODE_EINT */
+#define WM831X_IM_CHG_MODE_EINT_SHIFT 9 /* IM_CHG_MODE_EINT */
+#define WM831X_IM_CHG_MODE_EINT_WIDTH 1 /* IM_CHG_MODE_EINT */
+#define WM831X_IM_CHG_START_EINT 0x0100 /* IM_CHG_START_EINT */
+#define WM831X_IM_CHG_START_EINT_MASK 0x0100 /* IM_CHG_START_EINT */
+#define WM831X_IM_CHG_START_EINT_SHIFT 8 /* IM_CHG_START_EINT */
+#define WM831X_IM_CHG_START_EINT_WIDTH 1 /* IM_CHG_START_EINT */
+#define WM831X_IM_CS2_EINT 0x0080 /* IM_CS2_EINT */
+#define WM831X_IM_CS2_EINT_MASK 0x0080 /* IM_CS2_EINT */
+#define WM831X_IM_CS2_EINT_SHIFT 7 /* IM_CS2_EINT */
+#define WM831X_IM_CS2_EINT_WIDTH 1 /* IM_CS2_EINT */
+#define WM831X_IM_CS1_EINT 0x0040 /* IM_CS1_EINT */
+#define WM831X_IM_CS1_EINT_MASK 0x0040 /* IM_CS1_EINT */
+#define WM831X_IM_CS1_EINT_SHIFT 6 /* IM_CS1_EINT */
+#define WM831X_IM_CS1_EINT_WIDTH 1 /* IM_CS1_EINT */
+#define WM831X_IM_OTP_CMD_END_EINT 0x0020 /* IM_OTP_CMD_END_EINT */
+#define WM831X_IM_OTP_CMD_END_EINT_MASK 0x0020 /* IM_OTP_CMD_END_EINT */
+#define WM831X_IM_OTP_CMD_END_EINT_SHIFT 5 /* IM_OTP_CMD_END_EINT */
+#define WM831X_IM_OTP_CMD_END_EINT_WIDTH 1 /* IM_OTP_CMD_END_EINT */
+#define WM831X_IM_OTP_ERR_EINT 0x0010 /* IM_OTP_ERR_EINT */
+#define WM831X_IM_OTP_ERR_EINT_MASK 0x0010 /* IM_OTP_ERR_EINT */
+#define WM831X_IM_OTP_ERR_EINT_SHIFT 4 /* IM_OTP_ERR_EINT */
+#define WM831X_IM_OTP_ERR_EINT_WIDTH 1 /* IM_OTP_ERR_EINT */
+#define WM831X_IM_PS_POR_EINT 0x0004 /* IM_PS_POR_EINT */
+#define WM831X_IM_PS_POR_EINT_MASK 0x0004 /* IM_PS_POR_EINT */
+#define WM831X_IM_PS_POR_EINT_SHIFT 2 /* IM_PS_POR_EINT */
+#define WM831X_IM_PS_POR_EINT_WIDTH 1 /* IM_PS_POR_EINT */
+#define WM831X_IM_PS_SLEEP_OFF_EINT 0x0002 /* IM_PS_SLEEP_OFF_EINT */
+#define WM831X_IM_PS_SLEEP_OFF_EINT_MASK 0x0002 /* IM_PS_SLEEP_OFF_EINT */
+#define WM831X_IM_PS_SLEEP_OFF_EINT_SHIFT 1 /* IM_PS_SLEEP_OFF_EINT */
+#define WM831X_IM_PS_SLEEP_OFF_EINT_WIDTH 1 /* IM_PS_SLEEP_OFF_EINT */
+#define WM831X_IM_PS_ON_WAKE_EINT 0x0001 /* IM_PS_ON_WAKE_EINT */
+#define WM831X_IM_PS_ON_WAKE_EINT_MASK 0x0001 /* IM_PS_ON_WAKE_EINT */
+#define WM831X_IM_PS_ON_WAKE_EINT_SHIFT 0 /* IM_PS_ON_WAKE_EINT */
+#define WM831X_IM_PS_ON_WAKE_EINT_WIDTH 1 /* IM_PS_ON_WAKE_EINT */
+
+/*
+ * R16411 (0x401B) - Interrupt Status 3 Mask
+ */
+#define WM831X_IM_UV_LDO10_EINT 0x0200 /* IM_UV_LDO10_EINT */
+#define WM831X_IM_UV_LDO10_EINT_MASK 0x0200 /* IM_UV_LDO10_EINT */
+#define WM831X_IM_UV_LDO10_EINT_SHIFT 9 /* IM_UV_LDO10_EINT */
+#define WM831X_IM_UV_LDO10_EINT_WIDTH 1 /* IM_UV_LDO10_EINT */
+#define WM831X_IM_UV_LDO9_EINT 0x0100 /* IM_UV_LDO9_EINT */
+#define WM831X_IM_UV_LDO9_EINT_MASK 0x0100 /* IM_UV_LDO9_EINT */
+#define WM831X_IM_UV_LDO9_EINT_SHIFT 8 /* IM_UV_LDO9_EINT */
+#define WM831X_IM_UV_LDO9_EINT_WIDTH 1 /* IM_UV_LDO9_EINT */
+#define WM831X_IM_UV_LDO8_EINT 0x0080 /* IM_UV_LDO8_EINT */
+#define WM831X_IM_UV_LDO8_EINT_MASK 0x0080 /* IM_UV_LDO8_EINT */
+#define WM831X_IM_UV_LDO8_EINT_SHIFT 7 /* IM_UV_LDO8_EINT */
+#define WM831X_IM_UV_LDO8_EINT_WIDTH 1 /* IM_UV_LDO8_EINT */
+#define WM831X_IM_UV_LDO7_EINT 0x0040 /* IM_UV_LDO7_EINT */
+#define WM831X_IM_UV_LDO7_EINT_MASK 0x0040 /* IM_UV_LDO7_EINT */
+#define WM831X_IM_UV_LDO7_EINT_SHIFT 6 /* IM_UV_LDO7_EINT */
+#define WM831X_IM_UV_LDO7_EINT_WIDTH 1 /* IM_UV_LDO7_EINT */
+#define WM831X_IM_UV_LDO6_EINT 0x0020 /* IM_UV_LDO6_EINT */
+#define WM831X_IM_UV_LDO6_EINT_MASK 0x0020 /* IM_UV_LDO6_EINT */
+#define WM831X_IM_UV_LDO6_EINT_SHIFT 5 /* IM_UV_LDO6_EINT */
+#define WM831X_IM_UV_LDO6_EINT_WIDTH 1 /* IM_UV_LDO6_EINT */
+#define WM831X_IM_UV_LDO5_EINT 0x0010 /* IM_UV_LDO5_EINT */
+#define WM831X_IM_UV_LDO5_EINT_MASK 0x0010 /* IM_UV_LDO5_EINT */
+#define WM831X_IM_UV_LDO5_EINT_SHIFT 4 /* IM_UV_LDO5_EINT */
+#define WM831X_IM_UV_LDO5_EINT_WIDTH 1 /* IM_UV_LDO5_EINT */
+#define WM831X_IM_UV_LDO4_EINT 0x0008 /* IM_UV_LDO4_EINT */
+#define WM831X_IM_UV_LDO4_EINT_MASK 0x0008 /* IM_UV_LDO4_EINT */
+#define WM831X_IM_UV_LDO4_EINT_SHIFT 3 /* IM_UV_LDO4_EINT */
+#define WM831X_IM_UV_LDO4_EINT_WIDTH 1 /* IM_UV_LDO4_EINT */
+#define WM831X_IM_UV_LDO3_EINT 0x0004 /* IM_UV_LDO3_EINT */
+#define WM831X_IM_UV_LDO3_EINT_MASK 0x0004 /* IM_UV_LDO3_EINT */
+#define WM831X_IM_UV_LDO3_EINT_SHIFT 2 /* IM_UV_LDO3_EINT */
+#define WM831X_IM_UV_LDO3_EINT_WIDTH 1 /* IM_UV_LDO3_EINT */
+#define WM831X_IM_UV_LDO2_EINT 0x0002 /* IM_UV_LDO2_EINT */
+#define WM831X_IM_UV_LDO2_EINT_MASK 0x0002 /* IM_UV_LDO2_EINT */
+#define WM831X_IM_UV_LDO2_EINT_SHIFT 1 /* IM_UV_LDO2_EINT */
+#define WM831X_IM_UV_LDO2_EINT_WIDTH 1 /* IM_UV_LDO2_EINT */
+#define WM831X_IM_UV_LDO1_EINT 0x0001 /* IM_UV_LDO1_EINT */
+#define WM831X_IM_UV_LDO1_EINT_MASK 0x0001 /* IM_UV_LDO1_EINT */
+#define WM831X_IM_UV_LDO1_EINT_SHIFT 0 /* IM_UV_LDO1_EINT */
+#define WM831X_IM_UV_LDO1_EINT_WIDTH 1 /* IM_UV_LDO1_EINT */
+
+/*
+ * R16412 (0x401C) - Interrupt Status 4 Mask
+ */
+#define WM831X_IM_HC_DC2_EINT 0x0200 /* IM_HC_DC2_EINT */
+#define WM831X_IM_HC_DC2_EINT_MASK 0x0200 /* IM_HC_DC2_EINT */
+#define WM831X_IM_HC_DC2_EINT_SHIFT 9 /* IM_HC_DC2_EINT */
+#define WM831X_IM_HC_DC2_EINT_WIDTH 1 /* IM_HC_DC2_EINT */
+#define WM831X_IM_HC_DC1_EINT 0x0100 /* IM_HC_DC1_EINT */
+#define WM831X_IM_HC_DC1_EINT_MASK 0x0100 /* IM_HC_DC1_EINT */
+#define WM831X_IM_HC_DC1_EINT_SHIFT 8 /* IM_HC_DC1_EINT */
+#define WM831X_IM_HC_DC1_EINT_WIDTH 1 /* IM_HC_DC1_EINT */
+#define WM831X_IM_UV_DC4_EINT 0x0008 /* IM_UV_DC4_EINT */
+#define WM831X_IM_UV_DC4_EINT_MASK 0x0008 /* IM_UV_DC4_EINT */
+#define WM831X_IM_UV_DC4_EINT_SHIFT 3 /* IM_UV_DC4_EINT */
+#define WM831X_IM_UV_DC4_EINT_WIDTH 1 /* IM_UV_DC4_EINT */
+#define WM831X_IM_UV_DC3_EINT 0x0004 /* IM_UV_DC3_EINT */
+#define WM831X_IM_UV_DC3_EINT_MASK 0x0004 /* IM_UV_DC3_EINT */
+#define WM831X_IM_UV_DC3_EINT_SHIFT 2 /* IM_UV_DC3_EINT */
+#define WM831X_IM_UV_DC3_EINT_WIDTH 1 /* IM_UV_DC3_EINT */
+#define WM831X_IM_UV_DC2_EINT 0x0002 /* IM_UV_DC2_EINT */
+#define WM831X_IM_UV_DC2_EINT_MASK 0x0002 /* IM_UV_DC2_EINT */
+#define WM831X_IM_UV_DC2_EINT_SHIFT 1 /* IM_UV_DC2_EINT */
+#define WM831X_IM_UV_DC2_EINT_WIDTH 1 /* IM_UV_DC2_EINT */
+#define WM831X_IM_UV_DC1_EINT 0x0001 /* IM_UV_DC1_EINT */
+#define WM831X_IM_UV_DC1_EINT_MASK 0x0001 /* IM_UV_DC1_EINT */
+#define WM831X_IM_UV_DC1_EINT_SHIFT 0 /* IM_UV_DC1_EINT */
+#define WM831X_IM_UV_DC1_EINT_WIDTH 1 /* IM_UV_DC1_EINT */
+
+/*
+ * R16413 (0x401D) - Interrupt Status 5 Mask
+ */
+#define WM831X_IM_GP16_EINT 0x8000 /* IM_GP16_EINT */
+#define WM831X_IM_GP16_EINT_MASK 0x8000 /* IM_GP16_EINT */
+#define WM831X_IM_GP16_EINT_SHIFT 15 /* IM_GP16_EINT */
+#define WM831X_IM_GP16_EINT_WIDTH 1 /* IM_GP16_EINT */
+#define WM831X_IM_GP15_EINT 0x4000 /* IM_GP15_EINT */
+#define WM831X_IM_GP15_EINT_MASK 0x4000 /* IM_GP15_EINT */
+#define WM831X_IM_GP15_EINT_SHIFT 14 /* IM_GP15_EINT */
+#define WM831X_IM_GP15_EINT_WIDTH 1 /* IM_GP15_EINT */
+#define WM831X_IM_GP14_EINT 0x2000 /* IM_GP14_EINT */
+#define WM831X_IM_GP14_EINT_MASK 0x2000 /* IM_GP14_EINT */
+#define WM831X_IM_GP14_EINT_SHIFT 13 /* IM_GP14_EINT */
+#define WM831X_IM_GP14_EINT_WIDTH 1 /* IM_GP14_EINT */
+#define WM831X_IM_GP13_EINT 0x1000 /* IM_GP13_EINT */
+#define WM831X_IM_GP13_EINT_MASK 0x1000 /* IM_GP13_EINT */
+#define WM831X_IM_GP13_EINT_SHIFT 12 /* IM_GP13_EINT */
+#define WM831X_IM_GP13_EINT_WIDTH 1 /* IM_GP13_EINT */
+#define WM831X_IM_GP12_EINT 0x0800 /* IM_GP12_EINT */
+#define WM831X_IM_GP12_EINT_MASK 0x0800 /* IM_GP12_EINT */
+#define WM831X_IM_GP12_EINT_SHIFT 11 /* IM_GP12_EINT */
+#define WM831X_IM_GP12_EINT_WIDTH 1 /* IM_GP12_EINT */
+#define WM831X_IM_GP11_EINT 0x0400 /* IM_GP11_EINT */
+#define WM831X_IM_GP11_EINT_MASK 0x0400 /* IM_GP11_EINT */
+#define WM831X_IM_GP11_EINT_SHIFT 10 /* IM_GP11_EINT */
+#define WM831X_IM_GP11_EINT_WIDTH 1 /* IM_GP11_EINT */
+#define WM831X_IM_GP10_EINT 0x0200 /* IM_GP10_EINT */
+#define WM831X_IM_GP10_EINT_MASK 0x0200 /* IM_GP10_EINT */
+#define WM831X_IM_GP10_EINT_SHIFT 9 /* IM_GP10_EINT */
+#define WM831X_IM_GP10_EINT_WIDTH 1 /* IM_GP10_EINT */
+#define WM831X_IM_GP9_EINT 0x0100 /* IM_GP9_EINT */
+#define WM831X_IM_GP9_EINT_MASK 0x0100 /* IM_GP9_EINT */
+#define WM831X_IM_GP9_EINT_SHIFT 8 /* IM_GP9_EINT */
+#define WM831X_IM_GP9_EINT_WIDTH 1 /* IM_GP9_EINT */
+#define WM831X_IM_GP8_EINT 0x0080 /* IM_GP8_EINT */
+#define WM831X_IM_GP8_EINT_MASK 0x0080 /* IM_GP8_EINT */
+#define WM831X_IM_GP8_EINT_SHIFT 7 /* IM_GP8_EINT */
+#define WM831X_IM_GP8_EINT_WIDTH 1 /* IM_GP8_EINT */
+#define WM831X_IM_GP7_EINT 0x0040 /* IM_GP7_EINT */
+#define WM831X_IM_GP7_EINT_MASK 0x0040 /* IM_GP7_EINT */
+#define WM831X_IM_GP7_EINT_SHIFT 6 /* IM_GP7_EINT */
+#define WM831X_IM_GP7_EINT_WIDTH 1 /* IM_GP7_EINT */
+#define WM831X_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */
+#define WM831X_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */
+#define WM831X_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */
+#define WM831X_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */
+#define WM831X_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM831X_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM831X_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM831X_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM831X_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM831X_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM831X_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM831X_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM831X_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM831X_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM831X_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM831X_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM831X_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM831X_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM831X_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM831X_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM831X_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM831X_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM831X_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM831X_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+
+#endif
diff --git a/include/linux/mfd/wm831x/otp.h b/include/linux/mfd/wm831x/otp.h
new file mode 100644
index 0000000..ce1f81a
--- /dev/null
+++ b/include/linux/mfd/wm831x/otp.h
@@ -0,0 +1,162 @@
+/*
+ * include/linux/mfd/wm831x/otp.h -- OTP interface for WM831x
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM831X_OTP_H__
+#define __MFD_WM831X_OTP_H__
+
+int wm831x_otp_init(struct wm831x *wm831x);
+void wm831x_otp_exit(struct wm831x *wm831x);
+
+/*
+ * R30720 (0x7800) - Unique ID 1
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30721 (0x7801) - Unique ID 2
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30722 (0x7802) - Unique ID 3
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30723 (0x7803) - Unique ID 4
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30724 (0x7804) - Unique ID 5
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30725 (0x7805) - Unique ID 6
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30726 (0x7806) - Unique ID 7
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30727 (0x7807) - Unique ID 8
+ */
+#define WM831X_UNIQUE_ID_MASK 0xFFFF /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_SHIFT 0 /* UNIQUE_ID - [15:0] */
+#define WM831X_UNIQUE_ID_WIDTH 16 /* UNIQUE_ID - [15:0] */
+
+/*
+ * R30728 (0x7808) - Factory OTP ID
+ */
+#define WM831X_OTP_FACT_ID_MASK 0xFFFE /* OTP_FACT_ID - [15:1] */
+#define WM831X_OTP_FACT_ID_SHIFT 1 /* OTP_FACT_ID - [15:1] */
+#define WM831X_OTP_FACT_ID_WIDTH 15 /* OTP_FACT_ID - [15:1] */
+#define WM831X_OTP_FACT_FINAL 0x0001 /* OTP_FACT_FINAL */
+#define WM831X_OTP_FACT_FINAL_MASK 0x0001 /* OTP_FACT_FINAL */
+#define WM831X_OTP_FACT_FINAL_SHIFT 0 /* OTP_FACT_FINAL */
+#define WM831X_OTP_FACT_FINAL_WIDTH 1 /* OTP_FACT_FINAL */
+
+/*
+ * R30729 (0x7809) - Factory OTP 1
+ */
+#define WM831X_DC3_TRIM_MASK 0xF000 /* DC3_TRIM - [15:12] */
+#define WM831X_DC3_TRIM_SHIFT 12 /* DC3_TRIM - [15:12] */
+#define WM831X_DC3_TRIM_WIDTH 4 /* DC3_TRIM - [15:12] */
+#define WM831X_DC2_TRIM_MASK 0x0FC0 /* DC2_TRIM - [11:6] */
+#define WM831X_DC2_TRIM_SHIFT 6 /* DC2_TRIM - [11:6] */
+#define WM831X_DC2_TRIM_WIDTH 6 /* DC2_TRIM - [11:6] */
+#define WM831X_DC1_TRIM_MASK 0x003F /* DC1_TRIM - [5:0] */
+#define WM831X_DC1_TRIM_SHIFT 0 /* DC1_TRIM - [5:0] */
+#define WM831X_DC1_TRIM_WIDTH 6 /* DC1_TRIM - [5:0] */
+
+/*
+ * R30730 (0x780A) - Factory OTP 2
+ */
+#define WM831X_CHIP_ID_MASK 0xFFFF /* CHIP_ID - [15:0] */
+#define WM831X_CHIP_ID_SHIFT 0 /* CHIP_ID - [15:0] */
+#define WM831X_CHIP_ID_WIDTH 16 /* CHIP_ID - [15:0] */
+
+/*
+ * R30731 (0x780B) - Factory OTP 3
+ */
+#define WM831X_OSC_TRIM_MASK 0x0780 /* OSC_TRIM - [10:7] */
+#define WM831X_OSC_TRIM_SHIFT 7 /* OSC_TRIM - [10:7] */
+#define WM831X_OSC_TRIM_WIDTH 4 /* OSC_TRIM - [10:7] */
+#define WM831X_BG_TRIM_MASK 0x0078 /* BG_TRIM - [6:3] */
+#define WM831X_BG_TRIM_SHIFT 3 /* BG_TRIM - [6:3] */
+#define WM831X_BG_TRIM_WIDTH 4 /* BG_TRIM - [6:3] */
+#define WM831X_LPBG_TRIM_MASK 0x0007 /* LPBG_TRIM - [2:0] */
+#define WM831X_LPBG_TRIM_SHIFT 0 /* LPBG_TRIM - [2:0] */
+#define WM831X_LPBG_TRIM_WIDTH 3 /* LPBG_TRIM - [2:0] */
+
+/*
+ * R30732 (0x780C) - Factory OTP 4
+ */
+#define WM831X_CHILD_I2C_ADDR_MASK 0x00FE /* CHILD_I2C_ADDR - [7:1] */
+#define WM831X_CHILD_I2C_ADDR_SHIFT 1 /* CHILD_I2C_ADDR - [7:1] */
+#define WM831X_CHILD_I2C_ADDR_WIDTH 7 /* CHILD_I2C_ADDR - [7:1] */
+#define WM831X_CH_AW 0x0001 /* CH_AW */
+#define WM831X_CH_AW_MASK 0x0001 /* CH_AW */
+#define WM831X_CH_AW_SHIFT 0 /* CH_AW */
+#define WM831X_CH_AW_WIDTH 1 /* CH_AW */
+
+/*
+ * R30733 (0x780D) - Factory OTP 5
+ */
+#define WM831X_CHARGE_TRIM_MASK 0x003F /* CHARGE_TRIM - [5:0] */
+#define WM831X_CHARGE_TRIM_SHIFT 0 /* CHARGE_TRIM - [5:0] */
+#define WM831X_CHARGE_TRIM_WIDTH 6 /* CHARGE_TRIM - [5:0] */
+
+/*
+ * R30736 (0x7810) - Customer OTP ID
+ */
+#define WM831X_OTP_AUTO_PROG 0x8000 /* OTP_AUTO_PROG */
+#define WM831X_OTP_AUTO_PROG_MASK 0x8000 /* OTP_AUTO_PROG */
+#define WM831X_OTP_AUTO_PROG_SHIFT 15 /* OTP_AUTO_PROG */
+#define WM831X_OTP_AUTO_PROG_WIDTH 1 /* OTP_AUTO_PROG */
+#define WM831X_OTP_CUST_ID_MASK 0x7FFE /* OTP_CUST_ID - [14:1] */
+#define WM831X_OTP_CUST_ID_SHIFT 1 /* OTP_CUST_ID - [14:1] */
+#define WM831X_OTP_CUST_ID_WIDTH 14 /* OTP_CUST_ID - [14:1] */
+#define WM831X_OTP_CUST_FINAL 0x0001 /* OTP_CUST_FINAL */
+#define WM831X_OTP_CUST_FINAL_MASK 0x0001 /* OTP_CUST_FINAL */
+#define WM831X_OTP_CUST_FINAL_SHIFT 0 /* OTP_CUST_FINAL */
+#define WM831X_OTP_CUST_FINAL_WIDTH 1 /* OTP_CUST_FINAL */
+
+/*
+ * R30759 (0x7827) - DBE CHECK DATA
+ */
+#define WM831X_DBE_VALID_DATA_MASK 0xFFFF /* DBE_VALID_DATA - [15:0] */
+#define WM831X_DBE_VALID_DATA_SHIFT 0 /* DBE_VALID_DATA - [15:0] */
+#define WM831X_DBE_VALID_DATA_WIDTH 16 /* DBE_VALID_DATA - [15:0] */
+
+
+#endif
diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h
new file mode 100644
index 0000000..90d8202
--- /dev/null
+++ b/include/linux/mfd/wm831x/pdata.h
@@ -0,0 +1,113 @@
+/*
+ * include/linux/mfd/wm831x/pdata.h -- Platform data for WM831x
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM831X_PDATA_H__
+#define __MFD_WM831X_PDATA_H__
+
+struct wm831x;
+struct regulator_init_data;
+
+struct wm831x_backlight_pdata {
+ int isink; /** ISINK to use, 1 or 2 */
+ int max_uA; /** Maximum current to allow */
+};
+
+struct wm831x_backup_pdata {
+ int charger_enable;
+ int no_constant_voltage; /** Disable constant voltage charging */
+ int vlim; /** Voltage limit in milivolts */
+ int ilim; /** Current limit in microamps */
+};
+
+struct wm831x_battery_pdata {
+ int enable; /** Enable charging */
+ int fast_enable; /** Enable fast charging */
+ int off_mask; /** Mask OFF while charging */
+ int trickle_ilim; /** Trickle charge current limit, in mA */
+ int vsel; /** Target voltage, in mV */
+ int eoc_iterm; /** End of trickle charge current, in mA */
+ int fast_ilim; /** Fast charge current limit, in mA */
+ int timeout; /** Charge cycle timeout, in minutes */
+};
+
+/* Sources for status LED configuration. Values are register values
+ * plus 1 to allow for a zero default for preserve.
+ */
+enum wm831x_status_src {
+ WM831X_STATUS_PRESERVE = 0, /* Keep the current hardware setting */
+ WM831X_STATUS_OTP = 1,
+ WM831X_STATUS_POWER = 2,
+ WM831X_STATUS_CHARGER = 3,
+ WM831X_STATUS_MANUAL = 4,
+};
+
+struct wm831x_status_pdata {
+ enum wm831x_status_src default_src;
+ const char *name;
+ const char *default_trigger;
+};
+
+struct wm831x_touch_pdata {
+ int fivewire; /** 1 for five wire mode, 0 for 4 wire */
+ int isel; /** Current for pen down (uA) */
+ int rpu; /** Pen down sensitivity resistor divider */
+ int pressure; /** Report pressure (boolean) */
+ int data_irq; /** Touch data ready IRQ */
+};
+
+enum wm831x_watchdog_action {
+ WM831X_WDOG_NONE = 0,
+ WM831X_WDOG_INTERRUPT = 1,
+ WM831X_WDOG_RESET = 2,
+ WM831X_WDOG_WAKE = 3,
+};
+
+struct wm831x_watchdog_pdata {
+ enum wm831x_watchdog_action primary, secondary;
+ int update_gpio;
+ unsigned int software:1;
+};
+
+#define WM831X_MAX_STATUS 2
+#define WM831X_MAX_DCDC 4
+#define WM831X_MAX_EPE 2
+#define WM831X_MAX_LDO 11
+#define WM831X_MAX_ISINK 2
+
+struct wm831x_pdata {
+ /** Called before subdevices are set up */
+ int (*pre_init)(struct wm831x *wm831x);
+ /** Called after subdevices are set up */
+ int (*post_init)(struct wm831x *wm831x);
+
+ int gpio_base;
+ struct wm831x_backlight_pdata *backlight;
+ struct wm831x_backup_pdata *backup;
+ struct wm831x_battery_pdata *battery;
+ struct wm831x_touch_pdata *touch;
+ struct wm831x_watchdog_pdata *watchdog;
+
+ /** LED1 = 0 and so on */
+ struct wm831x_status_pdata *status[WM831X_MAX_STATUS];
+ /** DCDC1 = 0 and so on */
+ struct regulator_init_data *dcdc[WM831X_MAX_DCDC];
+ /** EPE1 = 0 and so on */
+ struct regulator_init_data *epe[WM831X_MAX_EPE];
+ /** LDO1 = 0 and so on */
+ struct regulator_init_data *ldo[WM831X_MAX_LDO];
+ /** ISINK1 = 0 and so on*/
+ struct regulator_init_data *isink[WM831X_MAX_ISINK];
+};
+
+#endif
diff --git a/include/linux/mfd/wm831x/regulator.h b/include/linux/mfd/wm831x/regulator.h
new file mode 100644
index 0000000..f954663
--- /dev/null
+++ b/include/linux/mfd/wm831x/regulator.h
@@ -0,0 +1,1218 @@
+/*
+ * linux/mfd/wm831x/regulator.h -- Regulator definitons for wm831x
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM831X_REGULATOR_H__
+#define __MFD_WM831X_REGULATOR_H__
+
+/*
+ * R16462 (0x404E) - Current Sink 1
+ */
+#define WM831X_CS1_ENA 0x8000 /* CS1_ENA */
+#define WM831X_CS1_ENA_MASK 0x8000 /* CS1_ENA */
+#define WM831X_CS1_ENA_SHIFT 15 /* CS1_ENA */
+#define WM831X_CS1_ENA_WIDTH 1 /* CS1_ENA */
+#define WM831X_CS1_DRIVE 0x4000 /* CS1_DRIVE */
+#define WM831X_CS1_DRIVE_MASK 0x4000 /* CS1_DRIVE */
+#define WM831X_CS1_DRIVE_SHIFT 14 /* CS1_DRIVE */
+#define WM831X_CS1_DRIVE_WIDTH 1 /* CS1_DRIVE */
+#define WM831X_CS1_SLPENA 0x1000 /* CS1_SLPENA */
+#define WM831X_CS1_SLPENA_MASK 0x1000 /* CS1_SLPENA */
+#define WM831X_CS1_SLPENA_SHIFT 12 /* CS1_SLPENA */
+#define WM831X_CS1_SLPENA_WIDTH 1 /* CS1_SLPENA */
+#define WM831X_CS1_OFF_RAMP_MASK 0x0C00 /* CS1_OFF_RAMP - [11:10] */
+#define WM831X_CS1_OFF_RAMP_SHIFT 10 /* CS1_OFF_RAMP - [11:10] */
+#define WM831X_CS1_OFF_RAMP_WIDTH 2 /* CS1_OFF_RAMP - [11:10] */
+#define WM831X_CS1_ON_RAMP_MASK 0x0300 /* CS1_ON_RAMP - [9:8] */
+#define WM831X_CS1_ON_RAMP_SHIFT 8 /* CS1_ON_RAMP - [9:8] */
+#define WM831X_CS1_ON_RAMP_WIDTH 2 /* CS1_ON_RAMP - [9:8] */
+#define WM831X_CS1_ISEL_MASK 0x003F /* CS1_ISEL - [5:0] */
+#define WM831X_CS1_ISEL_SHIFT 0 /* CS1_ISEL - [5:0] */
+#define WM831X_CS1_ISEL_WIDTH 6 /* CS1_ISEL - [5:0] */
+
+/*
+ * R16463 (0x404F) - Current Sink 2
+ */
+#define WM831X_CS2_ENA 0x8000 /* CS2_ENA */
+#define WM831X_CS2_ENA_MASK 0x8000 /* CS2_ENA */
+#define WM831X_CS2_ENA_SHIFT 15 /* CS2_ENA */
+#define WM831X_CS2_ENA_WIDTH 1 /* CS2_ENA */
+#define WM831X_CS2_DRIVE 0x4000 /* CS2_DRIVE */
+#define WM831X_CS2_DRIVE_MASK 0x4000 /* CS2_DRIVE */
+#define WM831X_CS2_DRIVE_SHIFT 14 /* CS2_DRIVE */
+#define WM831X_CS2_DRIVE_WIDTH 1 /* CS2_DRIVE */
+#define WM831X_CS2_SLPENA 0x1000 /* CS2_SLPENA */
+#define WM831X_CS2_SLPENA_MASK 0x1000 /* CS2_SLPENA */
+#define WM831X_CS2_SLPENA_SHIFT 12 /* CS2_SLPENA */
+#define WM831X_CS2_SLPENA_WIDTH 1 /* CS2_SLPENA */
+#define WM831X_CS2_OFF_RAMP_MASK 0x0C00 /* CS2_OFF_RAMP - [11:10] */
+#define WM831X_CS2_OFF_RAMP_SHIFT 10 /* CS2_OFF_RAMP - [11:10] */
+#define WM831X_CS2_OFF_RAMP_WIDTH 2 /* CS2_OFF_RAMP - [11:10] */
+#define WM831X_CS2_ON_RAMP_MASK 0x0300 /* CS2_ON_RAMP - [9:8] */
+#define WM831X_CS2_ON_RAMP_SHIFT 8 /* CS2_ON_RAMP - [9:8] */
+#define WM831X_CS2_ON_RAMP_WIDTH 2 /* CS2_ON_RAMP - [9:8] */
+#define WM831X_CS2_ISEL_MASK 0x003F /* CS2_ISEL - [5:0] */
+#define WM831X_CS2_ISEL_SHIFT 0 /* CS2_ISEL - [5:0] */
+#define WM831X_CS2_ISEL_WIDTH 6 /* CS2_ISEL - [5:0] */
+
+/*
+ * R16464 (0x4050) - DCDC Enable
+ */
+#define WM831X_EPE2_ENA 0x0080 /* EPE2_ENA */
+#define WM831X_EPE2_ENA_MASK 0x0080 /* EPE2_ENA */
+#define WM831X_EPE2_ENA_SHIFT 7 /* EPE2_ENA */
+#define WM831X_EPE2_ENA_WIDTH 1 /* EPE2_ENA */
+#define WM831X_EPE1_ENA 0x0040 /* EPE1_ENA */
+#define WM831X_EPE1_ENA_MASK 0x0040 /* EPE1_ENA */
+#define WM831X_EPE1_ENA_SHIFT 6 /* EPE1_ENA */
+#define WM831X_EPE1_ENA_WIDTH 1 /* EPE1_ENA */
+#define WM831X_DC4_ENA 0x0008 /* DC4_ENA */
+#define WM831X_DC4_ENA_MASK 0x0008 /* DC4_ENA */
+#define WM831X_DC4_ENA_SHIFT 3 /* DC4_ENA */
+#define WM831X_DC4_ENA_WIDTH 1 /* DC4_ENA */
+#define WM831X_DC3_ENA 0x0004 /* DC3_ENA */
+#define WM831X_DC3_ENA_MASK 0x0004 /* DC3_ENA */
+#define WM831X_DC3_ENA_SHIFT 2 /* DC3_ENA */
+#define WM831X_DC3_ENA_WIDTH 1 /* DC3_ENA */
+#define WM831X_DC2_ENA 0x0002 /* DC2_ENA */
+#define WM831X_DC2_ENA_MASK 0x0002 /* DC2_ENA */
+#define WM831X_DC2_ENA_SHIFT 1 /* DC2_ENA */
+#define WM831X_DC2_ENA_WIDTH 1 /* DC2_ENA */
+#define WM831X_DC1_ENA 0x0001 /* DC1_ENA */
+#define WM831X_DC1_ENA_MASK 0x0001 /* DC1_ENA */
+#define WM831X_DC1_ENA_SHIFT 0 /* DC1_ENA */
+#define WM831X_DC1_ENA_WIDTH 1 /* DC1_ENA */
+
+/*
+ * R16465 (0x4051) - LDO Enable
+ */
+#define WM831X_LDO11_ENA 0x0400 /* LDO11_ENA */
+#define WM831X_LDO11_ENA_MASK 0x0400 /* LDO11_ENA */
+#define WM831X_LDO11_ENA_SHIFT 10 /* LDO11_ENA */
+#define WM831X_LDO11_ENA_WIDTH 1 /* LDO11_ENA */
+#define WM831X_LDO10_ENA 0x0200 /* LDO10_ENA */
+#define WM831X_LDO10_ENA_MASK 0x0200 /* LDO10_ENA */
+#define WM831X_LDO10_ENA_SHIFT 9 /* LDO10_ENA */
+#define WM831X_LDO10_ENA_WIDTH 1 /* LDO10_ENA */
+#define WM831X_LDO9_ENA 0x0100 /* LDO9_ENA */
+#define WM831X_LDO9_ENA_MASK 0x0100 /* LDO9_ENA */
+#define WM831X_LDO9_ENA_SHIFT 8 /* LDO9_ENA */
+#define WM831X_LDO9_ENA_WIDTH 1 /* LDO9_ENA */
+#define WM831X_LDO8_ENA 0x0080 /* LDO8_ENA */
+#define WM831X_LDO8_ENA_MASK 0x0080 /* LDO8_ENA */
+#define WM831X_LDO8_ENA_SHIFT 7 /* LDO8_ENA */
+#define WM831X_LDO8_ENA_WIDTH 1 /* LDO8_ENA */
+#define WM831X_LDO7_ENA 0x0040 /* LDO7_ENA */
+#define WM831X_LDO7_ENA_MASK 0x0040 /* LDO7_ENA */
+#define WM831X_LDO7_ENA_SHIFT 6 /* LDO7_ENA */
+#define WM831X_LDO7_ENA_WIDTH 1 /* LDO7_ENA */
+#define WM831X_LDO6_ENA 0x0020 /* LDO6_ENA */
+#define WM831X_LDO6_ENA_MASK 0x0020 /* LDO6_ENA */
+#define WM831X_LDO6_ENA_SHIFT 5 /* LDO6_ENA */
+#define WM831X_LDO6_ENA_WIDTH 1 /* LDO6_ENA */
+#define WM831X_LDO5_ENA 0x0010 /* LDO5_ENA */
+#define WM831X_LDO5_ENA_MASK 0x0010 /* LDO5_ENA */
+#define WM831X_LDO5_ENA_SHIFT 4 /* LDO5_ENA */
+#define WM831X_LDO5_ENA_WIDTH 1 /* LDO5_ENA */
+#define WM831X_LDO4_ENA 0x0008 /* LDO4_ENA */
+#define WM831X_LDO4_ENA_MASK 0x0008 /* LDO4_ENA */
+#define WM831X_LDO4_ENA_SHIFT 3 /* LDO4_ENA */
+#define WM831X_LDO4_ENA_WIDTH 1 /* LDO4_ENA */
+#define WM831X_LDO3_ENA 0x0004 /* LDO3_ENA */
+#define WM831X_LDO3_ENA_MASK 0x0004 /* LDO3_ENA */
+#define WM831X_LDO3_ENA_SHIFT 2 /* LDO3_ENA */
+#define WM831X_LDO3_ENA_WIDTH 1 /* LDO3_ENA */
+#define WM831X_LDO2_ENA 0x0002 /* LDO2_ENA */
+#define WM831X_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
+#define WM831X_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
+#define WM831X_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
+#define WM831X_LDO1_ENA 0x0001 /* LDO1_ENA */
+#define WM831X_LDO1_ENA_MASK 0x0001 /* LDO1_ENA */
+#define WM831X_LDO1_ENA_SHIFT 0 /* LDO1_ENA */
+#define WM831X_LDO1_ENA_WIDTH 1 /* LDO1_ENA */
+
+/*
+ * R16466 (0x4052) - DCDC Status
+ */
+#define WM831X_EPE2_STS 0x0080 /* EPE2_STS */
+#define WM831X_EPE2_STS_MASK 0x0080 /* EPE2_STS */
+#define WM831X_EPE2_STS_SHIFT 7 /* EPE2_STS */
+#define WM831X_EPE2_STS_WIDTH 1 /* EPE2_STS */
+#define WM831X_EPE1_STS 0x0040 /* EPE1_STS */
+#define WM831X_EPE1_STS_MASK 0x0040 /* EPE1_STS */
+#define WM831X_EPE1_STS_SHIFT 6 /* EPE1_STS */
+#define WM831X_EPE1_STS_WIDTH 1 /* EPE1_STS */
+#define WM831X_DC4_STS 0x0008 /* DC4_STS */
+#define WM831X_DC4_STS_MASK 0x0008 /* DC4_STS */
+#define WM831X_DC4_STS_SHIFT 3 /* DC4_STS */
+#define WM831X_DC4_STS_WIDTH 1 /* DC4_STS */
+#define WM831X_DC3_STS 0x0004 /* DC3_STS */
+#define WM831X_DC3_STS_MASK 0x0004 /* DC3_STS */
+#define WM831X_DC3_STS_SHIFT 2 /* DC3_STS */
+#define WM831X_DC3_STS_WIDTH 1 /* DC3_STS */
+#define WM831X_DC2_STS 0x0002 /* DC2_STS */
+#define WM831X_DC2_STS_MASK 0x0002 /* DC2_STS */
+#define WM831X_DC2_STS_SHIFT 1 /* DC2_STS */
+#define WM831X_DC2_STS_WIDTH 1 /* DC2_STS */
+#define WM831X_DC1_STS 0x0001 /* DC1_STS */
+#define WM831X_DC1_STS_MASK 0x0001 /* DC1_STS */
+#define WM831X_DC1_STS_SHIFT 0 /* DC1_STS */
+#define WM831X_DC1_STS_WIDTH 1 /* DC1_STS */
+
+/*
+ * R16467 (0x4053) - LDO Status
+ */
+#define WM831X_LDO11_STS 0x0400 /* LDO11_STS */
+#define WM831X_LDO11_STS_MASK 0x0400 /* LDO11_STS */
+#define WM831X_LDO11_STS_SHIFT 10 /* LDO11_STS */
+#define WM831X_LDO11_STS_WIDTH 1 /* LDO11_STS */
+#define WM831X_LDO10_STS 0x0200 /* LDO10_STS */
+#define WM831X_LDO10_STS_MASK 0x0200 /* LDO10_STS */
+#define WM831X_LDO10_STS_SHIFT 9 /* LDO10_STS */
+#define WM831X_LDO10_STS_WIDTH 1 /* LDO10_STS */
+#define WM831X_LDO9_STS 0x0100 /* LDO9_STS */
+#define WM831X_LDO9_STS_MASK 0x0100 /* LDO9_STS */
+#define WM831X_LDO9_STS_SHIFT 8 /* LDO9_STS */
+#define WM831X_LDO9_STS_WIDTH 1 /* LDO9_STS */
+#define WM831X_LDO8_STS 0x0080 /* LDO8_STS */
+#define WM831X_LDO8_STS_MASK 0x0080 /* LDO8_STS */
+#define WM831X_LDO8_STS_SHIFT 7 /* LDO8_STS */
+#define WM831X_LDO8_STS_WIDTH 1 /* LDO8_STS */
+#define WM831X_LDO7_STS 0x0040 /* LDO7_STS */
+#define WM831X_LDO7_STS_MASK 0x0040 /* LDO7_STS */
+#define WM831X_LDO7_STS_SHIFT 6 /* LDO7_STS */
+#define WM831X_LDO7_STS_WIDTH 1 /* LDO7_STS */
+#define WM831X_LDO6_STS 0x0020 /* LDO6_STS */
+#define WM831X_LDO6_STS_MASK 0x0020 /* LDO6_STS */
+#define WM831X_LDO6_STS_SHIFT 5 /* LDO6_STS */
+#define WM831X_LDO6_STS_WIDTH 1 /* LDO6_STS */
+#define WM831X_LDO5_STS 0x0010 /* LDO5_STS */
+#define WM831X_LDO5_STS_MASK 0x0010 /* LDO5_STS */
+#define WM831X_LDO5_STS_SHIFT 4 /* LDO5_STS */
+#define WM831X_LDO5_STS_WIDTH 1 /* LDO5_STS */
+#define WM831X_LDO4_STS 0x0008 /* LDO4_STS */
+#define WM831X_LDO4_STS_MASK 0x0008 /* LDO4_STS */
+#define WM831X_LDO4_STS_SHIFT 3 /* LDO4_STS */
+#define WM831X_LDO4_STS_WIDTH 1 /* LDO4_STS */
+#define WM831X_LDO3_STS 0x0004 /* LDO3_STS */
+#define WM831X_LDO3_STS_MASK 0x0004 /* LDO3_STS */
+#define WM831X_LDO3_STS_SHIFT 2 /* LDO3_STS */
+#define WM831X_LDO3_STS_WIDTH 1 /* LDO3_STS */
+#define WM831X_LDO2_STS 0x0002 /* LDO2_STS */
+#define WM831X_LDO2_STS_MASK 0x0002 /* LDO2_STS */
+#define WM831X_LDO2_STS_SHIFT 1 /* LDO2_STS */
+#define WM831X_LDO2_STS_WIDTH 1 /* LDO2_STS */
+#define WM831X_LDO1_STS 0x0001 /* LDO1_STS */
+#define WM831X_LDO1_STS_MASK 0x0001 /* LDO1_STS */
+#define WM831X_LDO1_STS_SHIFT 0 /* LDO1_STS */
+#define WM831X_LDO1_STS_WIDTH 1 /* LDO1_STS */
+
+/*
+ * R16468 (0x4054) - DCDC UV Status
+ */
+#define WM831X_DC2_OV_STS 0x2000 /* DC2_OV_STS */
+#define WM831X_DC2_OV_STS_MASK 0x2000 /* DC2_OV_STS */
+#define WM831X_DC2_OV_STS_SHIFT 13 /* DC2_OV_STS */
+#define WM831X_DC2_OV_STS_WIDTH 1 /* DC2_OV_STS */
+#define WM831X_DC1_OV_STS 0x1000 /* DC1_OV_STS */
+#define WM831X_DC1_OV_STS_MASK 0x1000 /* DC1_OV_STS */
+#define WM831X_DC1_OV_STS_SHIFT 12 /* DC1_OV_STS */
+#define WM831X_DC1_OV_STS_WIDTH 1 /* DC1_OV_STS */
+#define WM831X_DC2_HC_STS 0x0200 /* DC2_HC_STS */
+#define WM831X_DC2_HC_STS_MASK 0x0200 /* DC2_HC_STS */
+#define WM831X_DC2_HC_STS_SHIFT 9 /* DC2_HC_STS */
+#define WM831X_DC2_HC_STS_WIDTH 1 /* DC2_HC_STS */
+#define WM831X_DC1_HC_STS 0x0100 /* DC1_HC_STS */
+#define WM831X_DC1_HC_STS_MASK 0x0100 /* DC1_HC_STS */
+#define WM831X_DC1_HC_STS_SHIFT 8 /* DC1_HC_STS */
+#define WM831X_DC1_HC_STS_WIDTH 1 /* DC1_HC_STS */
+#define WM831X_DC4_UV_STS 0x0008 /* DC4_UV_STS */
+#define WM831X_DC4_UV_STS_MASK 0x0008 /* DC4_UV_STS */
+#define WM831X_DC4_UV_STS_SHIFT 3 /* DC4_UV_STS */
+#define WM831X_DC4_UV_STS_WIDTH 1 /* DC4_UV_STS */
+#define WM831X_DC3_UV_STS 0x0004 /* DC3_UV_STS */
+#define WM831X_DC3_UV_STS_MASK 0x0004 /* DC3_UV_STS */
+#define WM831X_DC3_UV_STS_SHIFT 2 /* DC3_UV_STS */
+#define WM831X_DC3_UV_STS_WIDTH 1 /* DC3_UV_STS */
+#define WM831X_DC2_UV_STS 0x0002 /* DC2_UV_STS */
+#define WM831X_DC2_UV_STS_MASK 0x0002 /* DC2_UV_STS */
+#define WM831X_DC2_UV_STS_SHIFT 1 /* DC2_UV_STS */
+#define WM831X_DC2_UV_STS_WIDTH 1 /* DC2_UV_STS */
+#define WM831X_DC1_UV_STS 0x0001 /* DC1_UV_STS */
+#define WM831X_DC1_UV_STS_MASK 0x0001 /* DC1_UV_STS */
+#define WM831X_DC1_UV_STS_SHIFT 0 /* DC1_UV_STS */
+#define WM831X_DC1_UV_STS_WIDTH 1 /* DC1_UV_STS */
+
+/*
+ * R16469 (0x4055) - LDO UV Status
+ */
+#define WM831X_INTLDO_UV_STS 0x8000 /* INTLDO_UV_STS */
+#define WM831X_INTLDO_UV_STS_MASK 0x8000 /* INTLDO_UV_STS */
+#define WM831X_INTLDO_UV_STS_SHIFT 15 /* INTLDO_UV_STS */
+#define WM831X_INTLDO_UV_STS_WIDTH 1 /* INTLDO_UV_STS */
+#define WM831X_LDO10_UV_STS 0x0200 /* LDO10_UV_STS */
+#define WM831X_LDO10_UV_STS_MASK 0x0200 /* LDO10_UV_STS */
+#define WM831X_LDO10_UV_STS_SHIFT 9 /* LDO10_UV_STS */
+#define WM831X_LDO10_UV_STS_WIDTH 1 /* LDO10_UV_STS */
+#define WM831X_LDO9_UV_STS 0x0100 /* LDO9_UV_STS */
+#define WM831X_LDO9_UV_STS_MASK 0x0100 /* LDO9_UV_STS */
+#define WM831X_LDO9_UV_STS_SHIFT 8 /* LDO9_UV_STS */
+#define WM831X_LDO9_UV_STS_WIDTH 1 /* LDO9_UV_STS */
+#define WM831X_LDO8_UV_STS 0x0080 /* LDO8_UV_STS */
+#define WM831X_LDO8_UV_STS_MASK 0x0080 /* LDO8_UV_STS */
+#define WM831X_LDO8_UV_STS_SHIFT 7 /* LDO8_UV_STS */
+#define WM831X_LDO8_UV_STS_WIDTH 1 /* LDO8_UV_STS */
+#define WM831X_LDO7_UV_STS 0x0040 /* LDO7_UV_STS */
+#define WM831X_LDO7_UV_STS_MASK 0x0040 /* LDO7_UV_STS */
+#define WM831X_LDO7_UV_STS_SHIFT 6 /* LDO7_UV_STS */
+#define WM831X_LDO7_UV_STS_WIDTH 1 /* LDO7_UV_STS */
+#define WM831X_LDO6_UV_STS 0x0020 /* LDO6_UV_STS */
+#define WM831X_LDO6_UV_STS_MASK 0x0020 /* LDO6_UV_STS */
+#define WM831X_LDO6_UV_STS_SHIFT 5 /* LDO6_UV_STS */
+#define WM831X_LDO6_UV_STS_WIDTH 1 /* LDO6_UV_STS */
+#define WM831X_LDO5_UV_STS 0x0010 /* LDO5_UV_STS */
+#define WM831X_LDO5_UV_STS_MASK 0x0010 /* LDO5_UV_STS */
+#define WM831X_LDO5_UV_STS_SHIFT 4 /* LDO5_UV_STS */
+#define WM831X_LDO5_UV_STS_WIDTH 1 /* LDO5_UV_STS */
+#define WM831X_LDO4_UV_STS 0x0008 /* LDO4_UV_STS */
+#define WM831X_LDO4_UV_STS_MASK 0x0008 /* LDO4_UV_STS */
+#define WM831X_LDO4_UV_STS_SHIFT 3 /* LDO4_UV_STS */
+#define WM831X_LDO4_UV_STS_WIDTH 1 /* LDO4_UV_STS */
+#define WM831X_LDO3_UV_STS 0x0004 /* LDO3_UV_STS */
+#define WM831X_LDO3_UV_STS_MASK 0x0004 /* LDO3_UV_STS */
+#define WM831X_LDO3_UV_STS_SHIFT 2 /* LDO3_UV_STS */
+#define WM831X_LDO3_UV_STS_WIDTH 1 /* LDO3_UV_STS */
+#define WM831X_LDO2_UV_STS 0x0002 /* LDO2_UV_STS */
+#define WM831X_LDO2_UV_STS_MASK 0x0002 /* LDO2_UV_STS */
+#define WM831X_LDO2_UV_STS_SHIFT 1 /* LDO2_UV_STS */
+#define WM831X_LDO2_UV_STS_WIDTH 1 /* LDO2_UV_STS */
+#define WM831X_LDO1_UV_STS 0x0001 /* LDO1_UV_STS */
+#define WM831X_LDO1_UV_STS_MASK 0x0001 /* LDO1_UV_STS */
+#define WM831X_LDO1_UV_STS_SHIFT 0 /* LDO1_UV_STS */
+#define WM831X_LDO1_UV_STS_WIDTH 1 /* LDO1_UV_STS */
+
+/*
+ * R16470 (0x4056) - DC1 Control 1
+ */
+#define WM831X_DC1_RATE_MASK 0xC000 /* DC1_RATE - [15:14] */
+#define WM831X_DC1_RATE_SHIFT 14 /* DC1_RATE - [15:14] */
+#define WM831X_DC1_RATE_WIDTH 2 /* DC1_RATE - [15:14] */
+#define WM831X_DC1_PHASE 0x1000 /* DC1_PHASE */
+#define WM831X_DC1_PHASE_MASK 0x1000 /* DC1_PHASE */
+#define WM831X_DC1_PHASE_SHIFT 12 /* DC1_PHASE */
+#define WM831X_DC1_PHASE_WIDTH 1 /* DC1_PHASE */
+#define WM831X_DC1_FREQ_MASK 0x0300 /* DC1_FREQ - [9:8] */
+#define WM831X_DC1_FREQ_SHIFT 8 /* DC1_FREQ - [9:8] */
+#define WM831X_DC1_FREQ_WIDTH 2 /* DC1_FREQ - [9:8] */
+#define WM831X_DC1_FLT 0x0080 /* DC1_FLT */
+#define WM831X_DC1_FLT_MASK 0x0080 /* DC1_FLT */
+#define WM831X_DC1_FLT_SHIFT 7 /* DC1_FLT */
+#define WM831X_DC1_FLT_WIDTH 1 /* DC1_FLT */
+#define WM831X_DC1_SOFT_START_MASK 0x0030 /* DC1_SOFT_START - [5:4] */
+#define WM831X_DC1_SOFT_START_SHIFT 4 /* DC1_SOFT_START - [5:4] */
+#define WM831X_DC1_SOFT_START_WIDTH 2 /* DC1_SOFT_START - [5:4] */
+#define WM831X_DC1_CAP_MASK 0x0003 /* DC1_CAP - [1:0] */
+#define WM831X_DC1_CAP_SHIFT 0 /* DC1_CAP - [1:0] */
+#define WM831X_DC1_CAP_WIDTH 2 /* DC1_CAP - [1:0] */
+
+/*
+ * R16471 (0x4057) - DC1 Control 2
+ */
+#define WM831X_DC1_ERR_ACT_MASK 0xC000 /* DC1_ERR_ACT - [15:14] */
+#define WM831X_DC1_ERR_ACT_SHIFT 14 /* DC1_ERR_ACT - [15:14] */
+#define WM831X_DC1_ERR_ACT_WIDTH 2 /* DC1_ERR_ACT - [15:14] */
+#define WM831X_DC1_HWC_SRC_MASK 0x1800 /* DC1_HWC_SRC - [12:11] */
+#define WM831X_DC1_HWC_SRC_SHIFT 11 /* DC1_HWC_SRC - [12:11] */
+#define WM831X_DC1_HWC_SRC_WIDTH 2 /* DC1_HWC_SRC - [12:11] */
+#define WM831X_DC1_HWC_VSEL 0x0400 /* DC1_HWC_VSEL */
+#define WM831X_DC1_HWC_VSEL_MASK 0x0400 /* DC1_HWC_VSEL */
+#define WM831X_DC1_HWC_VSEL_SHIFT 10 /* DC1_HWC_VSEL */
+#define WM831X_DC1_HWC_VSEL_WIDTH 1 /* DC1_HWC_VSEL */
+#define WM831X_DC1_HWC_MODE_MASK 0x0300 /* DC1_HWC_MODE - [9:8] */
+#define WM831X_DC1_HWC_MODE_SHIFT 8 /* DC1_HWC_MODE - [9:8] */
+#define WM831X_DC1_HWC_MODE_WIDTH 2 /* DC1_HWC_MODE - [9:8] */
+#define WM831X_DC1_HC_THR_MASK 0x0070 /* DC1_HC_THR - [6:4] */
+#define WM831X_DC1_HC_THR_SHIFT 4 /* DC1_HC_THR - [6:4] */
+#define WM831X_DC1_HC_THR_WIDTH 3 /* DC1_HC_THR - [6:4] */
+#define WM831X_DC1_HC_IND_ENA 0x0001 /* DC1_HC_IND_ENA */
+#define WM831X_DC1_HC_IND_ENA_MASK 0x0001 /* DC1_HC_IND_ENA */
+#define WM831X_DC1_HC_IND_ENA_SHIFT 0 /* DC1_HC_IND_ENA */
+#define WM831X_DC1_HC_IND_ENA_WIDTH 1 /* DC1_HC_IND_ENA */
+
+/*
+ * R16472 (0x4058) - DC1 ON Config
+ */
+#define WM831X_DC1_ON_SLOT_MASK 0xE000 /* DC1_ON_SLOT - [15:13] */
+#define WM831X_DC1_ON_SLOT_SHIFT 13 /* DC1_ON_SLOT - [15:13] */
+#define WM831X_DC1_ON_SLOT_WIDTH 3 /* DC1_ON_SLOT - [15:13] */
+#define WM831X_DC1_ON_MODE_MASK 0x0300 /* DC1_ON_MODE - [9:8] */
+#define WM831X_DC1_ON_MODE_SHIFT 8 /* DC1_ON_MODE - [9:8] */
+#define WM831X_DC1_ON_MODE_WIDTH 2 /* DC1_ON_MODE - [9:8] */
+#define WM831X_DC1_ON_VSEL_MASK 0x007F /* DC1_ON_VSEL - [6:0] */
+#define WM831X_DC1_ON_VSEL_SHIFT 0 /* DC1_ON_VSEL - [6:0] */
+#define WM831X_DC1_ON_VSEL_WIDTH 7 /* DC1_ON_VSEL - [6:0] */
+
+/*
+ * R16473 (0x4059) - DC1 SLEEP Control
+ */
+#define WM831X_DC1_SLP_SLOT_MASK 0xE000 /* DC1_SLP_SLOT - [15:13] */
+#define WM831X_DC1_SLP_SLOT_SHIFT 13 /* DC1_SLP_SLOT - [15:13] */
+#define WM831X_DC1_SLP_SLOT_WIDTH 3 /* DC1_SLP_SLOT - [15:13] */
+#define WM831X_DC1_SLP_MODE_MASK 0x0300 /* DC1_SLP_MODE - [9:8] */
+#define WM831X_DC1_SLP_MODE_SHIFT 8 /* DC1_SLP_MODE - [9:8] */
+#define WM831X_DC1_SLP_MODE_WIDTH 2 /* DC1_SLP_MODE - [9:8] */
+#define WM831X_DC1_SLP_VSEL_MASK 0x007F /* DC1_SLP_VSEL - [6:0] */
+#define WM831X_DC1_SLP_VSEL_SHIFT 0 /* DC1_SLP_VSEL - [6:0] */
+#define WM831X_DC1_SLP_VSEL_WIDTH 7 /* DC1_SLP_VSEL - [6:0] */
+
+/*
+ * R16474 (0x405A) - DC1 DVS Control
+ */
+#define WM831X_DC1_DVS_SRC_MASK 0x1800 /* DC1_DVS_SRC - [12:11] */
+#define WM831X_DC1_DVS_SRC_SHIFT 11 /* DC1_DVS_SRC - [12:11] */
+#define WM831X_DC1_DVS_SRC_WIDTH 2 /* DC1_DVS_SRC - [12:11] */
+#define WM831X_DC1_DVS_VSEL_MASK 0x007F /* DC1_DVS_VSEL - [6:0] */
+#define WM831X_DC1_DVS_VSEL_SHIFT 0 /* DC1_DVS_VSEL - [6:0] */
+#define WM831X_DC1_DVS_VSEL_WIDTH 7 /* DC1_DVS_VSEL - [6:0] */
+
+/*
+ * R16475 (0x405B) - DC2 Control 1
+ */
+#define WM831X_DC2_RATE_MASK 0xC000 /* DC2_RATE - [15:14] */
+#define WM831X_DC2_RATE_SHIFT 14 /* DC2_RATE - [15:14] */
+#define WM831X_DC2_RATE_WIDTH 2 /* DC2_RATE - [15:14] */
+#define WM831X_DC2_PHASE 0x1000 /* DC2_PHASE */
+#define WM831X_DC2_PHASE_MASK 0x1000 /* DC2_PHASE */
+#define WM831X_DC2_PHASE_SHIFT 12 /* DC2_PHASE */
+#define WM831X_DC2_PHASE_WIDTH 1 /* DC2_PHASE */
+#define WM831X_DC2_FREQ_MASK 0x0300 /* DC2_FREQ - [9:8] */
+#define WM831X_DC2_FREQ_SHIFT 8 /* DC2_FREQ - [9:8] */
+#define WM831X_DC2_FREQ_WIDTH 2 /* DC2_FREQ - [9:8] */
+#define WM831X_DC2_FLT 0x0080 /* DC2_FLT */
+#define WM831X_DC2_FLT_MASK 0x0080 /* DC2_FLT */
+#define WM831X_DC2_FLT_SHIFT 7 /* DC2_FLT */
+#define WM831X_DC2_FLT_WIDTH 1 /* DC2_FLT */
+#define WM831X_DC2_SOFT_START_MASK 0x0030 /* DC2_SOFT_START - [5:4] */
+#define WM831X_DC2_SOFT_START_SHIFT 4 /* DC2_SOFT_START - [5:4] */
+#define WM831X_DC2_SOFT_START_WIDTH 2 /* DC2_SOFT_START - [5:4] */
+#define WM831X_DC2_CAP_MASK 0x0003 /* DC2_CAP - [1:0] */
+#define WM831X_DC2_CAP_SHIFT 0 /* DC2_CAP - [1:0] */
+#define WM831X_DC2_CAP_WIDTH 2 /* DC2_CAP - [1:0] */
+
+/*
+ * R16476 (0x405C) - DC2 Control 2
+ */
+#define WM831X_DC2_ERR_ACT_MASK 0xC000 /* DC2_ERR_ACT - [15:14] */
+#define WM831X_DC2_ERR_ACT_SHIFT 14 /* DC2_ERR_ACT - [15:14] */
+#define WM831X_DC2_ERR_ACT_WIDTH 2 /* DC2_ERR_ACT - [15:14] */
+#define WM831X_DC2_HWC_SRC_MASK 0x1800 /* DC2_HWC_SRC - [12:11] */
+#define WM831X_DC2_HWC_SRC_SHIFT 11 /* DC2_HWC_SRC - [12:11] */
+#define WM831X_DC2_HWC_SRC_WIDTH 2 /* DC2_HWC_SRC - [12:11] */
+#define WM831X_DC2_HWC_VSEL 0x0400 /* DC2_HWC_VSEL */
+#define WM831X_DC2_HWC_VSEL_MASK 0x0400 /* DC2_HWC_VSEL */
+#define WM831X_DC2_HWC_VSEL_SHIFT 10 /* DC2_HWC_VSEL */
+#define WM831X_DC2_HWC_VSEL_WIDTH 1 /* DC2_HWC_VSEL */
+#define WM831X_DC2_HWC_MODE_MASK 0x0300 /* DC2_HWC_MODE - [9:8] */
+#define WM831X_DC2_HWC_MODE_SHIFT 8 /* DC2_HWC_MODE - [9:8] */
+#define WM831X_DC2_HWC_MODE_WIDTH 2 /* DC2_HWC_MODE - [9:8] */
+#define WM831X_DC2_HC_THR_MASK 0x0070 /* DC2_HC_THR - [6:4] */
+#define WM831X_DC2_HC_THR_SHIFT 4 /* DC2_HC_THR - [6:4] */
+#define WM831X_DC2_HC_THR_WIDTH 3 /* DC2_HC_THR - [6:4] */
+#define WM831X_DC2_HC_IND_ENA 0x0001 /* DC2_HC_IND_ENA */
+#define WM831X_DC2_HC_IND_ENA_MASK 0x0001 /* DC2_HC_IND_ENA */
+#define WM831X_DC2_HC_IND_ENA_SHIFT 0 /* DC2_HC_IND_ENA */
+#define WM831X_DC2_HC_IND_ENA_WIDTH 1 /* DC2_HC_IND_ENA */
+
+/*
+ * R16477 (0x405D) - DC2 ON Config
+ */
+#define WM831X_DC2_ON_SLOT_MASK 0xE000 /* DC2_ON_SLOT - [15:13] */
+#define WM831X_DC2_ON_SLOT_SHIFT 13 /* DC2_ON_SLOT - [15:13] */
+#define WM831X_DC2_ON_SLOT_WIDTH 3 /* DC2_ON_SLOT - [15:13] */
+#define WM831X_DC2_ON_MODE_MASK 0x0300 /* DC2_ON_MODE - [9:8] */
+#define WM831X_DC2_ON_MODE_SHIFT 8 /* DC2_ON_MODE - [9:8] */
+#define WM831X_DC2_ON_MODE_WIDTH 2 /* DC2_ON_MODE - [9:8] */
+#define WM831X_DC2_ON_VSEL_MASK 0x007F /* DC2_ON_VSEL - [6:0] */
+#define WM831X_DC2_ON_VSEL_SHIFT 0 /* DC2_ON_VSEL - [6:0] */
+#define WM831X_DC2_ON_VSEL_WIDTH 7 /* DC2_ON_VSEL - [6:0] */
+
+/*
+ * R16478 (0x405E) - DC2 SLEEP Control
+ */
+#define WM831X_DC2_SLP_SLOT_MASK 0xE000 /* DC2_SLP_SLOT - [15:13] */
+#define WM831X_DC2_SLP_SLOT_SHIFT 13 /* DC2_SLP_SLOT - [15:13] */
+#define WM831X_DC2_SLP_SLOT_WIDTH 3 /* DC2_SLP_SLOT - [15:13] */
+#define WM831X_DC2_SLP_MODE_MASK 0x0300 /* DC2_SLP_MODE - [9:8] */
+#define WM831X_DC2_SLP_MODE_SHIFT 8 /* DC2_SLP_MODE - [9:8] */
+#define WM831X_DC2_SLP_MODE_WIDTH 2 /* DC2_SLP_MODE - [9:8] */
+#define WM831X_DC2_SLP_VSEL_MASK 0x007F /* DC2_SLP_VSEL - [6:0] */
+#define WM831X_DC2_SLP_VSEL_SHIFT 0 /* DC2_SLP_VSEL - [6:0] */
+#define WM831X_DC2_SLP_VSEL_WIDTH 7 /* DC2_SLP_VSEL - [6:0] */
+
+/*
+ * R16479 (0x405F) - DC2 DVS Control
+ */
+#define WM831X_DC2_DVS_SRC_MASK 0x1800 /* DC2_DVS_SRC - [12:11] */
+#define WM831X_DC2_DVS_SRC_SHIFT 11 /* DC2_DVS_SRC - [12:11] */
+#define WM831X_DC2_DVS_SRC_WIDTH 2 /* DC2_DVS_SRC - [12:11] */
+#define WM831X_DC2_DVS_VSEL_MASK 0x007F /* DC2_DVS_VSEL - [6:0] */
+#define WM831X_DC2_DVS_VSEL_SHIFT 0 /* DC2_DVS_VSEL - [6:0] */
+#define WM831X_DC2_DVS_VSEL_WIDTH 7 /* DC2_DVS_VSEL - [6:0] */
+
+/*
+ * R16480 (0x4060) - DC3 Control 1
+ */
+#define WM831X_DC3_PHASE 0x1000 /* DC3_PHASE */
+#define WM831X_DC3_PHASE_MASK 0x1000 /* DC3_PHASE */
+#define WM831X_DC3_PHASE_SHIFT 12 /* DC3_PHASE */
+#define WM831X_DC3_PHASE_WIDTH 1 /* DC3_PHASE */
+#define WM831X_DC3_FLT 0x0080 /* DC3_FLT */
+#define WM831X_DC3_FLT_MASK 0x0080 /* DC3_FLT */
+#define WM831X_DC3_FLT_SHIFT 7 /* DC3_FLT */
+#define WM831X_DC3_FLT_WIDTH 1 /* DC3_FLT */
+#define WM831X_DC3_SOFT_START_MASK 0x0030 /* DC3_SOFT_START - [5:4] */
+#define WM831X_DC3_SOFT_START_SHIFT 4 /* DC3_SOFT_START - [5:4] */
+#define WM831X_DC3_SOFT_START_WIDTH 2 /* DC3_SOFT_START - [5:4] */
+#define WM831X_DC3_STNBY_LIM_MASK 0x000C /* DC3_STNBY_LIM - [3:2] */
+#define WM831X_DC3_STNBY_LIM_SHIFT 2 /* DC3_STNBY_LIM - [3:2] */
+#define WM831X_DC3_STNBY_LIM_WIDTH 2 /* DC3_STNBY_LIM - [3:2] */
+#define WM831X_DC3_CAP_MASK 0x0003 /* DC3_CAP - [1:0] */
+#define WM831X_DC3_CAP_SHIFT 0 /* DC3_CAP - [1:0] */
+#define WM831X_DC3_CAP_WIDTH 2 /* DC3_CAP - [1:0] */
+
+/*
+ * R16481 (0x4061) - DC3 Control 2
+ */
+#define WM831X_DC3_ERR_ACT_MASK 0xC000 /* DC3_ERR_ACT - [15:14] */
+#define WM831X_DC3_ERR_ACT_SHIFT 14 /* DC3_ERR_ACT - [15:14] */
+#define WM831X_DC3_ERR_ACT_WIDTH 2 /* DC3_ERR_ACT - [15:14] */
+#define WM831X_DC3_HWC_SRC_MASK 0x1800 /* DC3_HWC_SRC - [12:11] */
+#define WM831X_DC3_HWC_SRC_SHIFT 11 /* DC3_HWC_SRC - [12:11] */
+#define WM831X_DC3_HWC_SRC_WIDTH 2 /* DC3_HWC_SRC - [12:11] */
+#define WM831X_DC3_HWC_VSEL 0x0400 /* DC3_HWC_VSEL */
+#define WM831X_DC3_HWC_VSEL_MASK 0x0400 /* DC3_HWC_VSEL */
+#define WM831X_DC3_HWC_VSEL_SHIFT 10 /* DC3_HWC_VSEL */
+#define WM831X_DC3_HWC_VSEL_WIDTH 1 /* DC3_HWC_VSEL */
+#define WM831X_DC3_HWC_MODE_MASK 0x0300 /* DC3_HWC_MODE - [9:8] */
+#define WM831X_DC3_HWC_MODE_SHIFT 8 /* DC3_HWC_MODE - [9:8] */
+#define WM831X_DC3_HWC_MODE_WIDTH 2 /* DC3_HWC_MODE - [9:8] */
+#define WM831X_DC3_OVP 0x0080 /* DC3_OVP */
+#define WM831X_DC3_OVP_MASK 0x0080 /* DC3_OVP */
+#define WM831X_DC3_OVP_SHIFT 7 /* DC3_OVP */
+#define WM831X_DC3_OVP_WIDTH 1 /* DC3_OVP */
+
+/*
+ * R16482 (0x4062) - DC3 ON Config
+ */
+#define WM831X_DC3_ON_SLOT_MASK 0xE000 /* DC3_ON_SLOT - [15:13] */
+#define WM831X_DC3_ON_SLOT_SHIFT 13 /* DC3_ON_SLOT - [15:13] */
+#define WM831X_DC3_ON_SLOT_WIDTH 3 /* DC3_ON_SLOT - [15:13] */
+#define WM831X_DC3_ON_MODE_MASK 0x0300 /* DC3_ON_MODE - [9:8] */
+#define WM831X_DC3_ON_MODE_SHIFT 8 /* DC3_ON_MODE - [9:8] */
+#define WM831X_DC3_ON_MODE_WIDTH 2 /* DC3_ON_MODE - [9:8] */
+#define WM831X_DC3_ON_VSEL_MASK 0x007F /* DC3_ON_VSEL - [6:0] */
+#define WM831X_DC3_ON_VSEL_SHIFT 0 /* DC3_ON_VSEL - [6:0] */
+#define WM831X_DC3_ON_VSEL_WIDTH 7 /* DC3_ON_VSEL - [6:0] */
+
+/*
+ * R16483 (0x4063) - DC3 SLEEP Control
+ */
+#define WM831X_DC3_SLP_SLOT_MASK 0xE000 /* DC3_SLP_SLOT - [15:13] */
+#define WM831X_DC3_SLP_SLOT_SHIFT 13 /* DC3_SLP_SLOT - [15:13] */
+#define WM831X_DC3_SLP_SLOT_WIDTH 3 /* DC3_SLP_SLOT - [15:13] */
+#define WM831X_DC3_SLP_MODE_MASK 0x0300 /* DC3_SLP_MODE - [9:8] */
+#define WM831X_DC3_SLP_MODE_SHIFT 8 /* DC3_SLP_MODE - [9:8] */
+#define WM831X_DC3_SLP_MODE_WIDTH 2 /* DC3_SLP_MODE - [9:8] */
+#define WM831X_DC3_SLP_VSEL_MASK 0x007F /* DC3_SLP_VSEL - [6:0] */
+#define WM831X_DC3_SLP_VSEL_SHIFT 0 /* DC3_SLP_VSEL - [6:0] */
+#define WM831X_DC3_SLP_VSEL_WIDTH 7 /* DC3_SLP_VSEL - [6:0] */
+
+/*
+ * R16484 (0x4064) - DC4 Control
+ */
+#define WM831X_DC4_ERR_ACT_MASK 0xC000 /* DC4_ERR_ACT - [15:14] */
+#define WM831X_DC4_ERR_ACT_SHIFT 14 /* DC4_ERR_ACT - [15:14] */
+#define WM831X_DC4_ERR_ACT_WIDTH 2 /* DC4_ERR_ACT - [15:14] */
+#define WM831X_DC4_HWC_SRC_MASK 0x1800 /* DC4_HWC_SRC - [12:11] */
+#define WM831X_DC4_HWC_SRC_SHIFT 11 /* DC4_HWC_SRC - [12:11] */
+#define WM831X_DC4_HWC_SRC_WIDTH 2 /* DC4_HWC_SRC - [12:11] */
+#define WM831X_DC4_HWC_MODE 0x0100 /* DC4_HWC_MODE */
+#define WM831X_DC4_HWC_MODE_MASK 0x0100 /* DC4_HWC_MODE */
+#define WM831X_DC4_HWC_MODE_SHIFT 8 /* DC4_HWC_MODE */
+#define WM831X_DC4_HWC_MODE_WIDTH 1 /* DC4_HWC_MODE */
+#define WM831X_DC4_RANGE_MASK 0x000C /* DC4_RANGE - [3:2] */
+#define WM831X_DC4_RANGE_SHIFT 2 /* DC4_RANGE - [3:2] */
+#define WM831X_DC4_RANGE_WIDTH 2 /* DC4_RANGE - [3:2] */
+#define WM831X_DC4_FBSRC 0x0001 /* DC4_FBSRC */
+#define WM831X_DC4_FBSRC_MASK 0x0001 /* DC4_FBSRC */
+#define WM831X_DC4_FBSRC_SHIFT 0 /* DC4_FBSRC */
+#define WM831X_DC4_FBSRC_WIDTH 1 /* DC4_FBSRC */
+
+/*
+ * R16485 (0x4065) - DC4 SLEEP Control
+ */
+#define WM831X_DC4_SLPENA 0x0100 /* DC4_SLPENA */
+#define WM831X_DC4_SLPENA_MASK 0x0100 /* DC4_SLPENA */
+#define WM831X_DC4_SLPENA_SHIFT 8 /* DC4_SLPENA */
+#define WM831X_DC4_SLPENA_WIDTH 1 /* DC4_SLPENA */
+
+/*
+ * R16488 (0x4068) - LDO1 Control
+ */
+#define WM831X_LDO1_ERR_ACT_MASK 0xC000 /* LDO1_ERR_ACT - [15:14] */
+#define WM831X_LDO1_ERR_ACT_SHIFT 14 /* LDO1_ERR_ACT - [15:14] */
+#define WM831X_LDO1_ERR_ACT_WIDTH 2 /* LDO1_ERR_ACT - [15:14] */
+#define WM831X_LDO1_HWC_SRC_MASK 0x1800 /* LDO1_HWC_SRC - [12:11] */
+#define WM831X_LDO1_HWC_SRC_SHIFT 11 /* LDO1_HWC_SRC - [12:11] */
+#define WM831X_LDO1_HWC_SRC_WIDTH 2 /* LDO1_HWC_SRC - [12:11] */
+#define WM831X_LDO1_HWC_VSEL 0x0400 /* LDO1_HWC_VSEL */
+#define WM831X_LDO1_HWC_VSEL_MASK 0x0400 /* LDO1_HWC_VSEL */
+#define WM831X_LDO1_HWC_VSEL_SHIFT 10 /* LDO1_HWC_VSEL */
+#define WM831X_LDO1_HWC_VSEL_WIDTH 1 /* LDO1_HWC_VSEL */
+#define WM831X_LDO1_HWC_MODE_MASK 0x0300 /* LDO1_HWC_MODE - [9:8] */
+#define WM831X_LDO1_HWC_MODE_SHIFT 8 /* LDO1_HWC_MODE - [9:8] */
+#define WM831X_LDO1_HWC_MODE_WIDTH 2 /* LDO1_HWC_MODE - [9:8] */
+#define WM831X_LDO1_FLT 0x0080 /* LDO1_FLT */
+#define WM831X_LDO1_FLT_MASK 0x0080 /* LDO1_FLT */
+#define WM831X_LDO1_FLT_SHIFT 7 /* LDO1_FLT */
+#define WM831X_LDO1_FLT_WIDTH 1 /* LDO1_FLT */
+#define WM831X_LDO1_SWI 0x0040 /* LDO1_SWI */
+#define WM831X_LDO1_SWI_MASK 0x0040 /* LDO1_SWI */
+#define WM831X_LDO1_SWI_SHIFT 6 /* LDO1_SWI */
+#define WM831X_LDO1_SWI_WIDTH 1 /* LDO1_SWI */
+#define WM831X_LDO1_LP_MODE 0x0001 /* LDO1_LP_MODE */
+#define WM831X_LDO1_LP_MODE_MASK 0x0001 /* LDO1_LP_MODE */
+#define WM831X_LDO1_LP_MODE_SHIFT 0 /* LDO1_LP_MODE */
+#define WM831X_LDO1_LP_MODE_WIDTH 1 /* LDO1_LP_MODE */
+
+/*
+ * R16489 (0x4069) - LDO1 ON Control
+ */
+#define WM831X_LDO1_ON_SLOT_MASK 0xE000 /* LDO1_ON_SLOT - [15:13] */
+#define WM831X_LDO1_ON_SLOT_SHIFT 13 /* LDO1_ON_SLOT - [15:13] */
+#define WM831X_LDO1_ON_SLOT_WIDTH 3 /* LDO1_ON_SLOT - [15:13] */
+#define WM831X_LDO1_ON_MODE 0x0100 /* LDO1_ON_MODE */
+#define WM831X_LDO1_ON_MODE_MASK 0x0100 /* LDO1_ON_MODE */
+#define WM831X_LDO1_ON_MODE_SHIFT 8 /* LDO1_ON_MODE */
+#define WM831X_LDO1_ON_MODE_WIDTH 1 /* LDO1_ON_MODE */
+#define WM831X_LDO1_ON_VSEL_MASK 0x001F /* LDO1_ON_VSEL - [4:0] */
+#define WM831X_LDO1_ON_VSEL_SHIFT 0 /* LDO1_ON_VSEL - [4:0] */
+#define WM831X_LDO1_ON_VSEL_WIDTH 5 /* LDO1_ON_VSEL - [4:0] */
+
+/*
+ * R16490 (0x406A) - LDO1 SLEEP Control
+ */
+#define WM831X_LDO1_SLP_SLOT_MASK 0xE000 /* LDO1_SLP_SLOT - [15:13] */
+#define WM831X_LDO1_SLP_SLOT_SHIFT 13 /* LDO1_SLP_SLOT - [15:13] */
+#define WM831X_LDO1_SLP_SLOT_WIDTH 3 /* LDO1_SLP_SLOT - [15:13] */
+#define WM831X_LDO1_SLP_MODE 0x0100 /* LDO1_SLP_MODE */
+#define WM831X_LDO1_SLP_MODE_MASK 0x0100 /* LDO1_SLP_MODE */
+#define WM831X_LDO1_SLP_MODE_SHIFT 8 /* LDO1_SLP_MODE */
+#define WM831X_LDO1_SLP_MODE_WIDTH 1 /* LDO1_SLP_MODE */
+#define WM831X_LDO1_SLP_VSEL_MASK 0x001F /* LDO1_SLP_VSEL - [4:0] */
+#define WM831X_LDO1_SLP_VSEL_SHIFT 0 /* LDO1_SLP_VSEL - [4:0] */
+#define WM831X_LDO1_SLP_VSEL_WIDTH 5 /* LDO1_SLP_VSEL - [4:0] */
+
+/*
+ * R16491 (0x406B) - LDO2 Control
+ */
+#define WM831X_LDO2_ERR_ACT_MASK 0xC000 /* LDO2_ERR_ACT - [15:14] */
+#define WM831X_LDO2_ERR_ACT_SHIFT 14 /* LDO2_ERR_ACT - [15:14] */
+#define WM831X_LDO2_ERR_ACT_WIDTH 2 /* LDO2_ERR_ACT - [15:14] */
+#define WM831X_LDO2_HWC_SRC_MASK 0x1800 /* LDO2_HWC_SRC - [12:11] */
+#define WM831X_LDO2_HWC_SRC_SHIFT 11 /* LDO2_HWC_SRC - [12:11] */
+#define WM831X_LDO2_HWC_SRC_WIDTH 2 /* LDO2_HWC_SRC - [12:11] */
+#define WM831X_LDO2_HWC_VSEL 0x0400 /* LDO2_HWC_VSEL */
+#define WM831X_LDO2_HWC_VSEL_MASK 0x0400 /* LDO2_HWC_VSEL */
+#define WM831X_LDO2_HWC_VSEL_SHIFT 10 /* LDO2_HWC_VSEL */
+#define WM831X_LDO2_HWC_VSEL_WIDTH 1 /* LDO2_HWC_VSEL */
+#define WM831X_LDO2_HWC_MODE_MASK 0x0300 /* LDO2_HWC_MODE - [9:8] */
+#define WM831X_LDO2_HWC_MODE_SHIFT 8 /* LDO2_HWC_MODE - [9:8] */
+#define WM831X_LDO2_HWC_MODE_WIDTH 2 /* LDO2_HWC_MODE - [9:8] */
+#define WM831X_LDO2_FLT 0x0080 /* LDO2_FLT */
+#define WM831X_LDO2_FLT_MASK 0x0080 /* LDO2_FLT */
+#define WM831X_LDO2_FLT_SHIFT 7 /* LDO2_FLT */
+#define WM831X_LDO2_FLT_WIDTH 1 /* LDO2_FLT */
+#define WM831X_LDO2_SWI 0x0040 /* LDO2_SWI */
+#define WM831X_LDO2_SWI_MASK 0x0040 /* LDO2_SWI */
+#define WM831X_LDO2_SWI_SHIFT 6 /* LDO2_SWI */
+#define WM831X_LDO2_SWI_WIDTH 1 /* LDO2_SWI */
+#define WM831X_LDO2_LP_MODE 0x0001 /* LDO2_LP_MODE */
+#define WM831X_LDO2_LP_MODE_MASK 0x0001 /* LDO2_LP_MODE */
+#define WM831X_LDO2_LP_MODE_SHIFT 0 /* LDO2_LP_MODE */
+#define WM831X_LDO2_LP_MODE_WIDTH 1 /* LDO2_LP_MODE */
+
+/*
+ * R16492 (0x406C) - LDO2 ON Control
+ */
+#define WM831X_LDO2_ON_SLOT_MASK 0xE000 /* LDO2_ON_SLOT - [15:13] */
+#define WM831X_LDO2_ON_SLOT_SHIFT 13 /* LDO2_ON_SLOT - [15:13] */
+#define WM831X_LDO2_ON_SLOT_WIDTH 3 /* LDO2_ON_SLOT - [15:13] */
+#define WM831X_LDO2_ON_MODE 0x0100 /* LDO2_ON_MODE */
+#define WM831X_LDO2_ON_MODE_MASK 0x0100 /* LDO2_ON_MODE */
+#define WM831X_LDO2_ON_MODE_SHIFT 8 /* LDO2_ON_MODE */
+#define WM831X_LDO2_ON_MODE_WIDTH 1 /* LDO2_ON_MODE */
+#define WM831X_LDO2_ON_VSEL_MASK 0x001F /* LDO2_ON_VSEL - [4:0] */
+#define WM831X_LDO2_ON_VSEL_SHIFT 0 /* LDO2_ON_VSEL - [4:0] */
+#define WM831X_LDO2_ON_VSEL_WIDTH 5 /* LDO2_ON_VSEL - [4:0] */
+
+/*
+ * R16493 (0x406D) - LDO2 SLEEP Control
+ */
+#define WM831X_LDO2_SLP_SLOT_MASK 0xE000 /* LDO2_SLP_SLOT - [15:13] */
+#define WM831X_LDO2_SLP_SLOT_SHIFT 13 /* LDO2_SLP_SLOT - [15:13] */
+#define WM831X_LDO2_SLP_SLOT_WIDTH 3 /* LDO2_SLP_SLOT - [15:13] */
+#define WM831X_LDO2_SLP_MODE 0x0100 /* LDO2_SLP_MODE */
+#define WM831X_LDO2_SLP_MODE_MASK 0x0100 /* LDO2_SLP_MODE */
+#define WM831X_LDO2_SLP_MODE_SHIFT 8 /* LDO2_SLP_MODE */
+#define WM831X_LDO2_SLP_MODE_WIDTH 1 /* LDO2_SLP_MODE */
+#define WM831X_LDO2_SLP_VSEL_MASK 0x001F /* LDO2_SLP_VSEL - [4:0] */
+#define WM831X_LDO2_SLP_VSEL_SHIFT 0 /* LDO2_SLP_VSEL - [4:0] */
+#define WM831X_LDO2_SLP_VSEL_WIDTH 5 /* LDO2_SLP_VSEL - [4:0] */
+
+/*
+ * R16494 (0x406E) - LDO3 Control
+ */
+#define WM831X_LDO3_ERR_ACT_MASK 0xC000 /* LDO3_ERR_ACT - [15:14] */
+#define WM831X_LDO3_ERR_ACT_SHIFT 14 /* LDO3_ERR_ACT - [15:14] */
+#define WM831X_LDO3_ERR_ACT_WIDTH 2 /* LDO3_ERR_ACT - [15:14] */
+#define WM831X_LDO3_HWC_SRC_MASK 0x1800 /* LDO3_HWC_SRC - [12:11] */
+#define WM831X_LDO3_HWC_SRC_SHIFT 11 /* LDO3_HWC_SRC - [12:11] */
+#define WM831X_LDO3_HWC_SRC_WIDTH 2 /* LDO3_HWC_SRC - [12:11] */
+#define WM831X_LDO3_HWC_VSEL 0x0400 /* LDO3_HWC_VSEL */
+#define WM831X_LDO3_HWC_VSEL_MASK 0x0400 /* LDO3_HWC_VSEL */
+#define WM831X_LDO3_HWC_VSEL_SHIFT 10 /* LDO3_HWC_VSEL */
+#define WM831X_LDO3_HWC_VSEL_WIDTH 1 /* LDO3_HWC_VSEL */
+#define WM831X_LDO3_HWC_MODE_MASK 0x0300 /* LDO3_HWC_MODE - [9:8] */
+#define WM831X_LDO3_HWC_MODE_SHIFT 8 /* LDO3_HWC_MODE - [9:8] */
+#define WM831X_LDO3_HWC_MODE_WIDTH 2 /* LDO3_HWC_MODE - [9:8] */
+#define WM831X_LDO3_FLT 0x0080 /* LDO3_FLT */
+#define WM831X_LDO3_FLT_MASK 0x0080 /* LDO3_FLT */
+#define WM831X_LDO3_FLT_SHIFT 7 /* LDO3_FLT */
+#define WM831X_LDO3_FLT_WIDTH 1 /* LDO3_FLT */
+#define WM831X_LDO3_SWI 0x0040 /* LDO3_SWI */
+#define WM831X_LDO3_SWI_MASK 0x0040 /* LDO3_SWI */
+#define WM831X_LDO3_SWI_SHIFT 6 /* LDO3_SWI */
+#define WM831X_LDO3_SWI_WIDTH 1 /* LDO3_SWI */
+#define WM831X_LDO3_LP_MODE 0x0001 /* LDO3_LP_MODE */
+#define WM831X_LDO3_LP_MODE_MASK 0x0001 /* LDO3_LP_MODE */
+#define WM831X_LDO3_LP_MODE_SHIFT 0 /* LDO3_LP_MODE */
+#define WM831X_LDO3_LP_MODE_WIDTH 1 /* LDO3_LP_MODE */
+
+/*
+ * R16495 (0x406F) - LDO3 ON Control
+ */
+#define WM831X_LDO3_ON_SLOT_MASK 0xE000 /* LDO3_ON_SLOT - [15:13] */
+#define WM831X_LDO3_ON_SLOT_SHIFT 13 /* LDO3_ON_SLOT - [15:13] */
+#define WM831X_LDO3_ON_SLOT_WIDTH 3 /* LDO3_ON_SLOT - [15:13] */
+#define WM831X_LDO3_ON_MODE 0x0100 /* LDO3_ON_MODE */
+#define WM831X_LDO3_ON_MODE_MASK 0x0100 /* LDO3_ON_MODE */
+#define WM831X_LDO3_ON_MODE_SHIFT 8 /* LDO3_ON_MODE */
+#define WM831X_LDO3_ON_MODE_WIDTH 1 /* LDO3_ON_MODE */
+#define WM831X_LDO3_ON_VSEL_MASK 0x001F /* LDO3_ON_VSEL - [4:0] */
+#define WM831X_LDO3_ON_VSEL_SHIFT 0 /* LDO3_ON_VSEL - [4:0] */
+#define WM831X_LDO3_ON_VSEL_WIDTH 5 /* LDO3_ON_VSEL - [4:0] */
+
+/*
+ * R16496 (0x4070) - LDO3 SLEEP Control
+ */
+#define WM831X_LDO3_SLP_SLOT_MASK 0xE000 /* LDO3_SLP_SLOT - [15:13] */
+#define WM831X_LDO3_SLP_SLOT_SHIFT 13 /* LDO3_SLP_SLOT - [15:13] */
+#define WM831X_LDO3_SLP_SLOT_WIDTH 3 /* LDO3_SLP_SLOT - [15:13] */
+#define WM831X_LDO3_SLP_MODE 0x0100 /* LDO3_SLP_MODE */
+#define WM831X_LDO3_SLP_MODE_MASK 0x0100 /* LDO3_SLP_MODE */
+#define WM831X_LDO3_SLP_MODE_SHIFT 8 /* LDO3_SLP_MODE */
+#define WM831X_LDO3_SLP_MODE_WIDTH 1 /* LDO3_SLP_MODE */
+#define WM831X_LDO3_SLP_VSEL_MASK 0x001F /* LDO3_SLP_VSEL - [4:0] */
+#define WM831X_LDO3_SLP_VSEL_SHIFT 0 /* LDO3_SLP_VSEL - [4:0] */
+#define WM831X_LDO3_SLP_VSEL_WIDTH 5 /* LDO3_SLP_VSEL - [4:0] */
+
+/*
+ * R16497 (0x4071) - LDO4 Control
+ */
+#define WM831X_LDO4_ERR_ACT_MASK 0xC000 /* LDO4_ERR_ACT - [15:14] */
+#define WM831X_LDO4_ERR_ACT_SHIFT 14 /* LDO4_ERR_ACT - [15:14] */
+#define WM831X_LDO4_ERR_ACT_WIDTH 2 /* LDO4_ERR_ACT - [15:14] */
+#define WM831X_LDO4_HWC_SRC_MASK 0x1800 /* LDO4_HWC_SRC - [12:11] */
+#define WM831X_LDO4_HWC_SRC_SHIFT 11 /* LDO4_HWC_SRC - [12:11] */
+#define WM831X_LDO4_HWC_SRC_WIDTH 2 /* LDO4_HWC_SRC - [12:11] */
+#define WM831X_LDO4_HWC_VSEL 0x0400 /* LDO4_HWC_VSEL */
+#define WM831X_LDO4_HWC_VSEL_MASK 0x0400 /* LDO4_HWC_VSEL */
+#define WM831X_LDO4_HWC_VSEL_SHIFT 10 /* LDO4_HWC_VSEL */
+#define WM831X_LDO4_HWC_VSEL_WIDTH 1 /* LDO4_HWC_VSEL */
+#define WM831X_LDO4_HWC_MODE_MASK 0x0300 /* LDO4_HWC_MODE - [9:8] */
+#define WM831X_LDO4_HWC_MODE_SHIFT 8 /* LDO4_HWC_MODE - [9:8] */
+#define WM831X_LDO4_HWC_MODE_WIDTH 2 /* LDO4_HWC_MODE - [9:8] */
+#define WM831X_LDO4_FLT 0x0080 /* LDO4_FLT */
+#define WM831X_LDO4_FLT_MASK 0x0080 /* LDO4_FLT */
+#define WM831X_LDO4_FLT_SHIFT 7 /* LDO4_FLT */
+#define WM831X_LDO4_FLT_WIDTH 1 /* LDO4_FLT */
+#define WM831X_LDO4_SWI 0x0040 /* LDO4_SWI */
+#define WM831X_LDO4_SWI_MASK 0x0040 /* LDO4_SWI */
+#define WM831X_LDO4_SWI_SHIFT 6 /* LDO4_SWI */
+#define WM831X_LDO4_SWI_WIDTH 1 /* LDO4_SWI */
+#define WM831X_LDO4_LP_MODE 0x0001 /* LDO4_LP_MODE */
+#define WM831X_LDO4_LP_MODE_MASK 0x0001 /* LDO4_LP_MODE */
+#define WM831X_LDO4_LP_MODE_SHIFT 0 /* LDO4_LP_MODE */
+#define WM831X_LDO4_LP_MODE_WIDTH 1 /* LDO4_LP_MODE */
+
+/*
+ * R16498 (0x4072) - LDO4 ON Control
+ */
+#define WM831X_LDO4_ON_SLOT_MASK 0xE000 /* LDO4_ON_SLOT - [15:13] */
+#define WM831X_LDO4_ON_SLOT_SHIFT 13 /* LDO4_ON_SLOT - [15:13] */
+#define WM831X_LDO4_ON_SLOT_WIDTH 3 /* LDO4_ON_SLOT - [15:13] */
+#define WM831X_LDO4_ON_MODE 0x0100 /* LDO4_ON_MODE */
+#define WM831X_LDO4_ON_MODE_MASK 0x0100 /* LDO4_ON_MODE */
+#define WM831X_LDO4_ON_MODE_SHIFT 8 /* LDO4_ON_MODE */
+#define WM831X_LDO4_ON_MODE_WIDTH 1 /* LDO4_ON_MODE */
+#define WM831X_LDO4_ON_VSEL_MASK 0x001F /* LDO4_ON_VSEL - [4:0] */
+#define WM831X_LDO4_ON_VSEL_SHIFT 0 /* LDO4_ON_VSEL - [4:0] */
+#define WM831X_LDO4_ON_VSEL_WIDTH 5 /* LDO4_ON_VSEL - [4:0] */
+
+/*
+ * R16499 (0x4073) - LDO4 SLEEP Control
+ */
+#define WM831X_LDO4_SLP_SLOT_MASK 0xE000 /* LDO4_SLP_SLOT - [15:13] */
+#define WM831X_LDO4_SLP_SLOT_SHIFT 13 /* LDO4_SLP_SLOT - [15:13] */
+#define WM831X_LDO4_SLP_SLOT_WIDTH 3 /* LDO4_SLP_SLOT - [15:13] */
+#define WM831X_LDO4_SLP_MODE 0x0100 /* LDO4_SLP_MODE */
+#define WM831X_LDO4_SLP_MODE_MASK 0x0100 /* LDO4_SLP_MODE */
+#define WM831X_LDO4_SLP_MODE_SHIFT 8 /* LDO4_SLP_MODE */
+#define WM831X_LDO4_SLP_MODE_WIDTH 1 /* LDO4_SLP_MODE */
+#define WM831X_LDO4_SLP_VSEL_MASK 0x001F /* LDO4_SLP_VSEL - [4:0] */
+#define WM831X_LDO4_SLP_VSEL_SHIFT 0 /* LDO4_SLP_VSEL - [4:0] */
+#define WM831X_LDO4_SLP_VSEL_WIDTH 5 /* LDO4_SLP_VSEL - [4:0] */
+
+/*
+ * R16500 (0x4074) - LDO5 Control
+ */
+#define WM831X_LDO5_ERR_ACT_MASK 0xC000 /* LDO5_ERR_ACT - [15:14] */
+#define WM831X_LDO5_ERR_ACT_SHIFT 14 /* LDO5_ERR_ACT - [15:14] */
+#define WM831X_LDO5_ERR_ACT_WIDTH 2 /* LDO5_ERR_ACT - [15:14] */
+#define WM831X_LDO5_HWC_SRC_MASK 0x1800 /* LDO5_HWC_SRC - [12:11] */
+#define WM831X_LDO5_HWC_SRC_SHIFT 11 /* LDO5_HWC_SRC - [12:11] */
+#define WM831X_LDO5_HWC_SRC_WIDTH 2 /* LDO5_HWC_SRC - [12:11] */
+#define WM831X_LDO5_HWC_VSEL 0x0400 /* LDO5_HWC_VSEL */
+#define WM831X_LDO5_HWC_VSEL_MASK 0x0400 /* LDO5_HWC_VSEL */
+#define WM831X_LDO5_HWC_VSEL_SHIFT 10 /* LDO5_HWC_VSEL */
+#define WM831X_LDO5_HWC_VSEL_WIDTH 1 /* LDO5_HWC_VSEL */
+#define WM831X_LDO5_HWC_MODE_MASK 0x0300 /* LDO5_HWC_MODE - [9:8] */
+#define WM831X_LDO5_HWC_MODE_SHIFT 8 /* LDO5_HWC_MODE - [9:8] */
+#define WM831X_LDO5_HWC_MODE_WIDTH 2 /* LDO5_HWC_MODE - [9:8] */
+#define WM831X_LDO5_FLT 0x0080 /* LDO5_FLT */
+#define WM831X_LDO5_FLT_MASK 0x0080 /* LDO5_FLT */
+#define WM831X_LDO5_FLT_SHIFT 7 /* LDO5_FLT */
+#define WM831X_LDO5_FLT_WIDTH 1 /* LDO5_FLT */
+#define WM831X_LDO5_SWI 0x0040 /* LDO5_SWI */
+#define WM831X_LDO5_SWI_MASK 0x0040 /* LDO5_SWI */
+#define WM831X_LDO5_SWI_SHIFT 6 /* LDO5_SWI */
+#define WM831X_LDO5_SWI_WIDTH 1 /* LDO5_SWI */
+#define WM831X_LDO5_LP_MODE 0x0001 /* LDO5_LP_MODE */
+#define WM831X_LDO5_LP_MODE_MASK 0x0001 /* LDO5_LP_MODE */
+#define WM831X_LDO5_LP_MODE_SHIFT 0 /* LDO5_LP_MODE */
+#define WM831X_LDO5_LP_MODE_WIDTH 1 /* LDO5_LP_MODE */
+
+/*
+ * R16501 (0x4075) - LDO5 ON Control
+ */
+#define WM831X_LDO5_ON_SLOT_MASK 0xE000 /* LDO5_ON_SLOT - [15:13] */
+#define WM831X_LDO5_ON_SLOT_SHIFT 13 /* LDO5_ON_SLOT - [15:13] */
+#define WM831X_LDO5_ON_SLOT_WIDTH 3 /* LDO5_ON_SLOT - [15:13] */
+#define WM831X_LDO5_ON_MODE 0x0100 /* LDO5_ON_MODE */
+#define WM831X_LDO5_ON_MODE_MASK 0x0100 /* LDO5_ON_MODE */
+#define WM831X_LDO5_ON_MODE_SHIFT 8 /* LDO5_ON_MODE */
+#define WM831X_LDO5_ON_MODE_WIDTH 1 /* LDO5_ON_MODE */
+#define WM831X_LDO5_ON_VSEL_MASK 0x001F /* LDO5_ON_VSEL - [4:0] */
+#define WM831X_LDO5_ON_VSEL_SHIFT 0 /* LDO5_ON_VSEL - [4:0] */
+#define WM831X_LDO5_ON_VSEL_WIDTH 5 /* LDO5_ON_VSEL - [4:0] */
+
+/*
+ * R16502 (0x4076) - LDO5 SLEEP Control
+ */
+#define WM831X_LDO5_SLP_SLOT_MASK 0xE000 /* LDO5_SLP_SLOT - [15:13] */
+#define WM831X_LDO5_SLP_SLOT_SHIFT 13 /* LDO5_SLP_SLOT - [15:13] */
+#define WM831X_LDO5_SLP_SLOT_WIDTH 3 /* LDO5_SLP_SLOT - [15:13] */
+#define WM831X_LDO5_SLP_MODE 0x0100 /* LDO5_SLP_MODE */
+#define WM831X_LDO5_SLP_MODE_MASK 0x0100 /* LDO5_SLP_MODE */
+#define WM831X_LDO5_SLP_MODE_SHIFT 8 /* LDO5_SLP_MODE */
+#define WM831X_LDO5_SLP_MODE_WIDTH 1 /* LDO5_SLP_MODE */
+#define WM831X_LDO5_SLP_VSEL_MASK 0x001F /* LDO5_SLP_VSEL - [4:0] */
+#define WM831X_LDO5_SLP_VSEL_SHIFT 0 /* LDO5_SLP_VSEL - [4:0] */
+#define WM831X_LDO5_SLP_VSEL_WIDTH 5 /* LDO5_SLP_VSEL - [4:0] */
+
+/*
+ * R16503 (0x4077) - LDO6 Control
+ */
+#define WM831X_LDO6_ERR_ACT_MASK 0xC000 /* LDO6_ERR_ACT - [15:14] */
+#define WM831X_LDO6_ERR_ACT_SHIFT 14 /* LDO6_ERR_ACT - [15:14] */
+#define WM831X_LDO6_ERR_ACT_WIDTH 2 /* LDO6_ERR_ACT - [15:14] */
+#define WM831X_LDO6_HWC_SRC_MASK 0x1800 /* LDO6_HWC_SRC - [12:11] */
+#define WM831X_LDO6_HWC_SRC_SHIFT 11 /* LDO6_HWC_SRC - [12:11] */
+#define WM831X_LDO6_HWC_SRC_WIDTH 2 /* LDO6_HWC_SRC - [12:11] */
+#define WM831X_LDO6_HWC_VSEL 0x0400 /* LDO6_HWC_VSEL */
+#define WM831X_LDO6_HWC_VSEL_MASK 0x0400 /* LDO6_HWC_VSEL */
+#define WM831X_LDO6_HWC_VSEL_SHIFT 10 /* LDO6_HWC_VSEL */
+#define WM831X_LDO6_HWC_VSEL_WIDTH 1 /* LDO6_HWC_VSEL */
+#define WM831X_LDO6_HWC_MODE_MASK 0x0300 /* LDO6_HWC_MODE - [9:8] */
+#define WM831X_LDO6_HWC_MODE_SHIFT 8 /* LDO6_HWC_MODE - [9:8] */
+#define WM831X_LDO6_HWC_MODE_WIDTH 2 /* LDO6_HWC_MODE - [9:8] */
+#define WM831X_LDO6_FLT 0x0080 /* LDO6_FLT */
+#define WM831X_LDO6_FLT_MASK 0x0080 /* LDO6_FLT */
+#define WM831X_LDO6_FLT_SHIFT 7 /* LDO6_FLT */
+#define WM831X_LDO6_FLT_WIDTH 1 /* LDO6_FLT */
+#define WM831X_LDO6_SWI 0x0040 /* LDO6_SWI */
+#define WM831X_LDO6_SWI_MASK 0x0040 /* LDO6_SWI */
+#define WM831X_LDO6_SWI_SHIFT 6 /* LDO6_SWI */
+#define WM831X_LDO6_SWI_WIDTH 1 /* LDO6_SWI */
+#define WM831X_LDO6_LP_MODE 0x0001 /* LDO6_LP_MODE */
+#define WM831X_LDO6_LP_MODE_MASK 0x0001 /* LDO6_LP_MODE */
+#define WM831X_LDO6_LP_MODE_SHIFT 0 /* LDO6_LP_MODE */
+#define WM831X_LDO6_LP_MODE_WIDTH 1 /* LDO6_LP_MODE */
+
+/*
+ * R16504 (0x4078) - LDO6 ON Control
+ */
+#define WM831X_LDO6_ON_SLOT_MASK 0xE000 /* LDO6_ON_SLOT - [15:13] */
+#define WM831X_LDO6_ON_SLOT_SHIFT 13 /* LDO6_ON_SLOT - [15:13] */
+#define WM831X_LDO6_ON_SLOT_WIDTH 3 /* LDO6_ON_SLOT - [15:13] */
+#define WM831X_LDO6_ON_MODE 0x0100 /* LDO6_ON_MODE */
+#define WM831X_LDO6_ON_MODE_MASK 0x0100 /* LDO6_ON_MODE */
+#define WM831X_LDO6_ON_MODE_SHIFT 8 /* LDO6_ON_MODE */
+#define WM831X_LDO6_ON_MODE_WIDTH 1 /* LDO6_ON_MODE */
+#define WM831X_LDO6_ON_VSEL_MASK 0x001F /* LDO6_ON_VSEL - [4:0] */
+#define WM831X_LDO6_ON_VSEL_SHIFT 0 /* LDO6_ON_VSEL - [4:0] */
+#define WM831X_LDO6_ON_VSEL_WIDTH 5 /* LDO6_ON_VSEL - [4:0] */
+
+/*
+ * R16505 (0x4079) - LDO6 SLEEP Control
+ */
+#define WM831X_LDO6_SLP_SLOT_MASK 0xE000 /* LDO6_SLP_SLOT - [15:13] */
+#define WM831X_LDO6_SLP_SLOT_SHIFT 13 /* LDO6_SLP_SLOT - [15:13] */
+#define WM831X_LDO6_SLP_SLOT_WIDTH 3 /* LDO6_SLP_SLOT - [15:13] */
+#define WM831X_LDO6_SLP_MODE 0x0100 /* LDO6_SLP_MODE */
+#define WM831X_LDO6_SLP_MODE_MASK 0x0100 /* LDO6_SLP_MODE */
+#define WM831X_LDO6_SLP_MODE_SHIFT 8 /* LDO6_SLP_MODE */
+#define WM831X_LDO6_SLP_MODE_WIDTH 1 /* LDO6_SLP_MODE */
+#define WM831X_LDO6_SLP_VSEL_MASK 0x001F /* LDO6_SLP_VSEL - [4:0] */
+#define WM831X_LDO6_SLP_VSEL_SHIFT 0 /* LDO6_SLP_VSEL - [4:0] */
+#define WM831X_LDO6_SLP_VSEL_WIDTH 5 /* LDO6_SLP_VSEL - [4:0] */
+
+/*
+ * R16506 (0x407A) - LDO7 Control
+ */
+#define WM831X_LDO7_ERR_ACT_MASK 0xC000 /* LDO7_ERR_ACT - [15:14] */
+#define WM831X_LDO7_ERR_ACT_SHIFT 14 /* LDO7_ERR_ACT - [15:14] */
+#define WM831X_LDO7_ERR_ACT_WIDTH 2 /* LDO7_ERR_ACT - [15:14] */
+#define WM831X_LDO7_HWC_SRC_MASK 0x1800 /* LDO7_HWC_SRC - [12:11] */
+#define WM831X_LDO7_HWC_SRC_SHIFT 11 /* LDO7_HWC_SRC - [12:11] */
+#define WM831X_LDO7_HWC_SRC_WIDTH 2 /* LDO7_HWC_SRC - [12:11] */
+#define WM831X_LDO7_HWC_VSEL 0x0400 /* LDO7_HWC_VSEL */
+#define WM831X_LDO7_HWC_VSEL_MASK 0x0400 /* LDO7_HWC_VSEL */
+#define WM831X_LDO7_HWC_VSEL_SHIFT 10 /* LDO7_HWC_VSEL */
+#define WM831X_LDO7_HWC_VSEL_WIDTH 1 /* LDO7_HWC_VSEL */
+#define WM831X_LDO7_HWC_MODE_MASK 0x0300 /* LDO7_HWC_MODE - [9:8] */
+#define WM831X_LDO7_HWC_MODE_SHIFT 8 /* LDO7_HWC_MODE - [9:8] */
+#define WM831X_LDO7_HWC_MODE_WIDTH 2 /* LDO7_HWC_MODE - [9:8] */
+#define WM831X_LDO7_FLT 0x0080 /* LDO7_FLT */
+#define WM831X_LDO7_FLT_MASK 0x0080 /* LDO7_FLT */
+#define WM831X_LDO7_FLT_SHIFT 7 /* LDO7_FLT */
+#define WM831X_LDO7_FLT_WIDTH 1 /* LDO7_FLT */
+#define WM831X_LDO7_SWI 0x0040 /* LDO7_SWI */
+#define WM831X_LDO7_SWI_MASK 0x0040 /* LDO7_SWI */
+#define WM831X_LDO7_SWI_SHIFT 6 /* LDO7_SWI */
+#define WM831X_LDO7_SWI_WIDTH 1 /* LDO7_SWI */
+
+/*
+ * R16507 (0x407B) - LDO7 ON Control
+ */
+#define WM831X_LDO7_ON_SLOT_MASK 0xE000 /* LDO7_ON_SLOT - [15:13] */
+#define WM831X_LDO7_ON_SLOT_SHIFT 13 /* LDO7_ON_SLOT - [15:13] */
+#define WM831X_LDO7_ON_SLOT_WIDTH 3 /* LDO7_ON_SLOT - [15:13] */
+#define WM831X_LDO7_ON_MODE 0x0100 /* LDO7_ON_MODE */
+#define WM831X_LDO7_ON_MODE_MASK 0x0100 /* LDO7_ON_MODE */
+#define WM831X_LDO7_ON_MODE_SHIFT 8 /* LDO7_ON_MODE */
+#define WM831X_LDO7_ON_MODE_WIDTH 1 /* LDO7_ON_MODE */
+#define WM831X_LDO7_ON_VSEL_MASK 0x001F /* LDO7_ON_VSEL - [4:0] */
+#define WM831X_LDO7_ON_VSEL_SHIFT 0 /* LDO7_ON_VSEL - [4:0] */
+#define WM831X_LDO7_ON_VSEL_WIDTH 5 /* LDO7_ON_VSEL - [4:0] */
+
+/*
+ * R16508 (0x407C) - LDO7 SLEEP Control
+ */
+#define WM831X_LDO7_SLP_SLOT_MASK 0xE000 /* LDO7_SLP_SLOT - [15:13] */
+#define WM831X_LDO7_SLP_SLOT_SHIFT 13 /* LDO7_SLP_SLOT - [15:13] */
+#define WM831X_LDO7_SLP_SLOT_WIDTH 3 /* LDO7_SLP_SLOT - [15:13] */
+#define WM831X_LDO7_SLP_MODE 0x0100 /* LDO7_SLP_MODE */
+#define WM831X_LDO7_SLP_MODE_MASK 0x0100 /* LDO7_SLP_MODE */
+#define WM831X_LDO7_SLP_MODE_SHIFT 8 /* LDO7_SLP_MODE */
+#define WM831X_LDO7_SLP_MODE_WIDTH 1 /* LDO7_SLP_MODE */
+#define WM831X_LDO7_SLP_VSEL_MASK 0x001F /* LDO7_SLP_VSEL - [4:0] */
+#define WM831X_LDO7_SLP_VSEL_SHIFT 0 /* LDO7_SLP_VSEL - [4:0] */
+#define WM831X_LDO7_SLP_VSEL_WIDTH 5 /* LDO7_SLP_VSEL - [4:0] */
+
+/*
+ * R16509 (0x407D) - LDO8 Control
+ */
+#define WM831X_LDO8_ERR_ACT_MASK 0xC000 /* LDO8_ERR_ACT - [15:14] */
+#define WM831X_LDO8_ERR_ACT_SHIFT 14 /* LDO8_ERR_ACT - [15:14] */
+#define WM831X_LDO8_ERR_ACT_WIDTH 2 /* LDO8_ERR_ACT - [15:14] */
+#define WM831X_LDO8_HWC_SRC_MASK 0x1800 /* LDO8_HWC_SRC - [12:11] */
+#define WM831X_LDO8_HWC_SRC_SHIFT 11 /* LDO8_HWC_SRC - [12:11] */
+#define WM831X_LDO8_HWC_SRC_WIDTH 2 /* LDO8_HWC_SRC - [12:11] */
+#define WM831X_LDO8_HWC_VSEL 0x0400 /* LDO8_HWC_VSEL */
+#define WM831X_LDO8_HWC_VSEL_MASK 0x0400 /* LDO8_HWC_VSEL */
+#define WM831X_LDO8_HWC_VSEL_SHIFT 10 /* LDO8_HWC_VSEL */
+#define WM831X_LDO8_HWC_VSEL_WIDTH 1 /* LDO8_HWC_VSEL */
+#define WM831X_LDO8_HWC_MODE_MASK 0x0300 /* LDO8_HWC_MODE - [9:8] */
+#define WM831X_LDO8_HWC_MODE_SHIFT 8 /* LDO8_HWC_MODE - [9:8] */
+#define WM831X_LDO8_HWC_MODE_WIDTH 2 /* LDO8_HWC_MODE - [9:8] */
+#define WM831X_LDO8_FLT 0x0080 /* LDO8_FLT */
+#define WM831X_LDO8_FLT_MASK 0x0080 /* LDO8_FLT */
+#define WM831X_LDO8_FLT_SHIFT 7 /* LDO8_FLT */
+#define WM831X_LDO8_FLT_WIDTH 1 /* LDO8_FLT */
+#define WM831X_LDO8_SWI 0x0040 /* LDO8_SWI */
+#define WM831X_LDO8_SWI_MASK 0x0040 /* LDO8_SWI */
+#define WM831X_LDO8_SWI_SHIFT 6 /* LDO8_SWI */
+#define WM831X_LDO8_SWI_WIDTH 1 /* LDO8_SWI */
+
+/*
+ * R16510 (0x407E) - LDO8 ON Control
+ */
+#define WM831X_LDO8_ON_SLOT_MASK 0xE000 /* LDO8_ON_SLOT - [15:13] */
+#define WM831X_LDO8_ON_SLOT_SHIFT 13 /* LDO8_ON_SLOT - [15:13] */
+#define WM831X_LDO8_ON_SLOT_WIDTH 3 /* LDO8_ON_SLOT - [15:13] */
+#define WM831X_LDO8_ON_MODE 0x0100 /* LDO8_ON_MODE */
+#define WM831X_LDO8_ON_MODE_MASK 0x0100 /* LDO8_ON_MODE */
+#define WM831X_LDO8_ON_MODE_SHIFT 8 /* LDO8_ON_MODE */
+#define WM831X_LDO8_ON_MODE_WIDTH 1 /* LDO8_ON_MODE */
+#define WM831X_LDO8_ON_VSEL_MASK 0x001F /* LDO8_ON_VSEL - [4:0] */
+#define WM831X_LDO8_ON_VSEL_SHIFT 0 /* LDO8_ON_VSEL - [4:0] */
+#define WM831X_LDO8_ON_VSEL_WIDTH 5 /* LDO8_ON_VSEL - [4:0] */
+
+/*
+ * R16511 (0x407F) - LDO8 SLEEP Control
+ */
+#define WM831X_LDO8_SLP_SLOT_MASK 0xE000 /* LDO8_SLP_SLOT - [15:13] */
+#define WM831X_LDO8_SLP_SLOT_SHIFT 13 /* LDO8_SLP_SLOT - [15:13] */
+#define WM831X_LDO8_SLP_SLOT_WIDTH 3 /* LDO8_SLP_SLOT - [15:13] */
+#define WM831X_LDO8_SLP_MODE 0x0100 /* LDO8_SLP_MODE */
+#define WM831X_LDO8_SLP_MODE_MASK 0x0100 /* LDO8_SLP_MODE */
+#define WM831X_LDO8_SLP_MODE_SHIFT 8 /* LDO8_SLP_MODE */
+#define WM831X_LDO8_SLP_MODE_WIDTH 1 /* LDO8_SLP_MODE */
+#define WM831X_LDO8_SLP_VSEL_MASK 0x001F /* LDO8_SLP_VSEL - [4:0] */
+#define WM831X_LDO8_SLP_VSEL_SHIFT 0 /* LDO8_SLP_VSEL - [4:0] */
+#define WM831X_LDO8_SLP_VSEL_WIDTH 5 /* LDO8_SLP_VSEL - [4:0] */
+
+/*
+ * R16512 (0x4080) - LDO9 Control
+ */
+#define WM831X_LDO9_ERR_ACT_MASK 0xC000 /* LDO9_ERR_ACT - [15:14] */
+#define WM831X_LDO9_ERR_ACT_SHIFT 14 /* LDO9_ERR_ACT - [15:14] */
+#define WM831X_LDO9_ERR_ACT_WIDTH 2 /* LDO9_ERR_ACT - [15:14] */
+#define WM831X_LDO9_HWC_SRC_MASK 0x1800 /* LDO9_HWC_SRC - [12:11] */
+#define WM831X_LDO9_HWC_SRC_SHIFT 11 /* LDO9_HWC_SRC - [12:11] */
+#define WM831X_LDO9_HWC_SRC_WIDTH 2 /* LDO9_HWC_SRC - [12:11] */
+#define WM831X_LDO9_HWC_VSEL 0x0400 /* LDO9_HWC_VSEL */
+#define WM831X_LDO9_HWC_VSEL_MASK 0x0400 /* LDO9_HWC_VSEL */
+#define WM831X_LDO9_HWC_VSEL_SHIFT 10 /* LDO9_HWC_VSEL */
+#define WM831X_LDO9_HWC_VSEL_WIDTH 1 /* LDO9_HWC_VSEL */
+#define WM831X_LDO9_HWC_MODE_MASK 0x0300 /* LDO9_HWC_MODE - [9:8] */
+#define WM831X_LDO9_HWC_MODE_SHIFT 8 /* LDO9_HWC_MODE - [9:8] */
+#define WM831X_LDO9_HWC_MODE_WIDTH 2 /* LDO9_HWC_MODE - [9:8] */
+#define WM831X_LDO9_FLT 0x0080 /* LDO9_FLT */
+#define WM831X_LDO9_FLT_MASK 0x0080 /* LDO9_FLT */
+#define WM831X_LDO9_FLT_SHIFT 7 /* LDO9_FLT */
+#define WM831X_LDO9_FLT_WIDTH 1 /* LDO9_FLT */
+#define WM831X_LDO9_SWI 0x0040 /* LDO9_SWI */
+#define WM831X_LDO9_SWI_MASK 0x0040 /* LDO9_SWI */
+#define WM831X_LDO9_SWI_SHIFT 6 /* LDO9_SWI */
+#define WM831X_LDO9_SWI_WIDTH 1 /* LDO9_SWI */
+
+/*
+ * R16513 (0x4081) - LDO9 ON Control
+ */
+#define WM831X_LDO9_ON_SLOT_MASK 0xE000 /* LDO9_ON_SLOT - [15:13] */
+#define WM831X_LDO9_ON_SLOT_SHIFT 13 /* LDO9_ON_SLOT - [15:13] */
+#define WM831X_LDO9_ON_SLOT_WIDTH 3 /* LDO9_ON_SLOT - [15:13] */
+#define WM831X_LDO9_ON_MODE 0x0100 /* LDO9_ON_MODE */
+#define WM831X_LDO9_ON_MODE_MASK 0x0100 /* LDO9_ON_MODE */
+#define WM831X_LDO9_ON_MODE_SHIFT 8 /* LDO9_ON_MODE */
+#define WM831X_LDO9_ON_MODE_WIDTH 1 /* LDO9_ON_MODE */
+#define WM831X_LDO9_ON_VSEL_MASK 0x001F /* LDO9_ON_VSEL - [4:0] */
+#define WM831X_LDO9_ON_VSEL_SHIFT 0 /* LDO9_ON_VSEL - [4:0] */
+#define WM831X_LDO9_ON_VSEL_WIDTH 5 /* LDO9_ON_VSEL - [4:0] */
+
+/*
+ * R16514 (0x4082) - LDO9 SLEEP Control
+ */
+#define WM831X_LDO9_SLP_SLOT_MASK 0xE000 /* LDO9_SLP_SLOT - [15:13] */
+#define WM831X_LDO9_SLP_SLOT_SHIFT 13 /* LDO9_SLP_SLOT - [15:13] */
+#define WM831X_LDO9_SLP_SLOT_WIDTH 3 /* LDO9_SLP_SLOT - [15:13] */
+#define WM831X_LDO9_SLP_MODE 0x0100 /* LDO9_SLP_MODE */
+#define WM831X_LDO9_SLP_MODE_MASK 0x0100 /* LDO9_SLP_MODE */
+#define WM831X_LDO9_SLP_MODE_SHIFT 8 /* LDO9_SLP_MODE */
+#define WM831X_LDO9_SLP_MODE_WIDTH 1 /* LDO9_SLP_MODE */
+#define WM831X_LDO9_SLP_VSEL_MASK 0x001F /* LDO9_SLP_VSEL - [4:0] */
+#define WM831X_LDO9_SLP_VSEL_SHIFT 0 /* LDO9_SLP_VSEL - [4:0] */
+#define WM831X_LDO9_SLP_VSEL_WIDTH 5 /* LDO9_SLP_VSEL - [4:0] */
+
+/*
+ * R16515 (0x4083) - LDO10 Control
+ */
+#define WM831X_LDO10_ERR_ACT_MASK 0xC000 /* LDO10_ERR_ACT - [15:14] */
+#define WM831X_LDO10_ERR_ACT_SHIFT 14 /* LDO10_ERR_ACT - [15:14] */
+#define WM831X_LDO10_ERR_ACT_WIDTH 2 /* LDO10_ERR_ACT - [15:14] */
+#define WM831X_LDO10_HWC_SRC_MASK 0x1800 /* LDO10_HWC_SRC - [12:11] */
+#define WM831X_LDO10_HWC_SRC_SHIFT 11 /* LDO10_HWC_SRC - [12:11] */
+#define WM831X_LDO10_HWC_SRC_WIDTH 2 /* LDO10_HWC_SRC - [12:11] */
+#define WM831X_LDO10_HWC_VSEL 0x0400 /* LDO10_HWC_VSEL */
+#define WM831X_LDO10_HWC_VSEL_MASK 0x0400 /* LDO10_HWC_VSEL */
+#define WM831X_LDO10_HWC_VSEL_SHIFT 10 /* LDO10_HWC_VSEL */
+#define WM831X_LDO10_HWC_VSEL_WIDTH 1 /* LDO10_HWC_VSEL */
+#define WM831X_LDO10_HWC_MODE_MASK 0x0300 /* LDO10_HWC_MODE - [9:8] */
+#define WM831X_LDO10_HWC_MODE_SHIFT 8 /* LDO10_HWC_MODE - [9:8] */
+#define WM831X_LDO10_HWC_MODE_WIDTH 2 /* LDO10_HWC_MODE - [9:8] */
+#define WM831X_LDO10_FLT 0x0080 /* LDO10_FLT */
+#define WM831X_LDO10_FLT_MASK 0x0080 /* LDO10_FLT */
+#define WM831X_LDO10_FLT_SHIFT 7 /* LDO10_FLT */
+#define WM831X_LDO10_FLT_WIDTH 1 /* LDO10_FLT */
+#define WM831X_LDO10_SWI 0x0040 /* LDO10_SWI */
+#define WM831X_LDO10_SWI_MASK 0x0040 /* LDO10_SWI */
+#define WM831X_LDO10_SWI_SHIFT 6 /* LDO10_SWI */
+#define WM831X_LDO10_SWI_WIDTH 1 /* LDO10_SWI */
+
+/*
+ * R16516 (0x4084) - LDO10 ON Control
+ */
+#define WM831X_LDO10_ON_SLOT_MASK 0xE000 /* LDO10_ON_SLOT - [15:13] */
+#define WM831X_LDO10_ON_SLOT_SHIFT 13 /* LDO10_ON_SLOT - [15:13] */
+#define WM831X_LDO10_ON_SLOT_WIDTH 3 /* LDO10_ON_SLOT - [15:13] */
+#define WM831X_LDO10_ON_MODE 0x0100 /* LDO10_ON_MODE */
+#define WM831X_LDO10_ON_MODE_MASK 0x0100 /* LDO10_ON_MODE */
+#define WM831X_LDO10_ON_MODE_SHIFT 8 /* LDO10_ON_MODE */
+#define WM831X_LDO10_ON_MODE_WIDTH 1 /* LDO10_ON_MODE */
+#define WM831X_LDO10_ON_VSEL_MASK 0x001F /* LDO10_ON_VSEL - [4:0] */
+#define WM831X_LDO10_ON_VSEL_SHIFT 0 /* LDO10_ON_VSEL - [4:0] */
+#define WM831X_LDO10_ON_VSEL_WIDTH 5 /* LDO10_ON_VSEL - [4:0] */
+
+/*
+ * R16517 (0x4085) - LDO10 SLEEP Control
+ */
+#define WM831X_LDO10_SLP_SLOT_MASK 0xE000 /* LDO10_SLP_SLOT - [15:13] */
+#define WM831X_LDO10_SLP_SLOT_SHIFT 13 /* LDO10_SLP_SLOT - [15:13] */
+#define WM831X_LDO10_SLP_SLOT_WIDTH 3 /* LDO10_SLP_SLOT - [15:13] */
+#define WM831X_LDO10_SLP_MODE 0x0100 /* LDO10_SLP_MODE */
+#define WM831X_LDO10_SLP_MODE_MASK 0x0100 /* LDO10_SLP_MODE */
+#define WM831X_LDO10_SLP_MODE_SHIFT 8 /* LDO10_SLP_MODE */
+#define WM831X_LDO10_SLP_MODE_WIDTH 1 /* LDO10_SLP_MODE */
+#define WM831X_LDO10_SLP_VSEL_MASK 0x001F /* LDO10_SLP_VSEL - [4:0] */
+#define WM831X_LDO10_SLP_VSEL_SHIFT 0 /* LDO10_SLP_VSEL - [4:0] */
+#define WM831X_LDO10_SLP_VSEL_WIDTH 5 /* LDO10_SLP_VSEL - [4:0] */
+
+/*
+ * R16519 (0x4087) - LDO11 ON Control
+ */
+#define WM831X_LDO11_ON_SLOT_MASK 0xE000 /* LDO11_ON_SLOT - [15:13] */
+#define WM831X_LDO11_ON_SLOT_SHIFT 13 /* LDO11_ON_SLOT - [15:13] */
+#define WM831X_LDO11_ON_SLOT_WIDTH 3 /* LDO11_ON_SLOT - [15:13] */
+#define WM831X_LDO11_OFFENA 0x1000 /* LDO11_OFFENA */
+#define WM831X_LDO11_OFFENA_MASK 0x1000 /* LDO11_OFFENA */
+#define WM831X_LDO11_OFFENA_SHIFT 12 /* LDO11_OFFENA */
+#define WM831X_LDO11_OFFENA_WIDTH 1 /* LDO11_OFFENA */
+#define WM831X_LDO11_VSEL_SRC 0x0080 /* LDO11_VSEL_SRC */
+#define WM831X_LDO11_VSEL_SRC_MASK 0x0080 /* LDO11_VSEL_SRC */
+#define WM831X_LDO11_VSEL_SRC_SHIFT 7 /* LDO11_VSEL_SRC */
+#define WM831X_LDO11_VSEL_SRC_WIDTH 1 /* LDO11_VSEL_SRC */
+#define WM831X_LDO11_ON_VSEL_MASK 0x000F /* LDO11_ON_VSEL - [3:0] */
+#define WM831X_LDO11_ON_VSEL_SHIFT 0 /* LDO11_ON_VSEL - [3:0] */
+#define WM831X_LDO11_ON_VSEL_WIDTH 4 /* LDO11_ON_VSEL - [3:0] */
+
+/*
+ * R16520 (0x4088) - LDO11 SLEEP Control
+ */
+#define WM831X_LDO11_SLP_SLOT_MASK 0xE000 /* LDO11_SLP_SLOT - [15:13] */
+#define WM831X_LDO11_SLP_SLOT_SHIFT 13 /* LDO11_SLP_SLOT - [15:13] */
+#define WM831X_LDO11_SLP_SLOT_WIDTH 3 /* LDO11_SLP_SLOT - [15:13] */
+#define WM831X_LDO11_SLP_VSEL_MASK 0x000F /* LDO11_SLP_VSEL - [3:0] */
+#define WM831X_LDO11_SLP_VSEL_SHIFT 0 /* LDO11_SLP_VSEL - [3:0] */
+#define WM831X_LDO11_SLP_VSEL_WIDTH 4 /* LDO11_SLP_VSEL - [3:0] */
+
+/*
+ * R16526 (0x408E) - Power Good Source 1
+ */
+#define WM831X_DC4_OK 0x0008 /* DC4_OK */
+#define WM831X_DC4_OK_MASK 0x0008 /* DC4_OK */
+#define WM831X_DC4_OK_SHIFT 3 /* DC4_OK */
+#define WM831X_DC4_OK_WIDTH 1 /* DC4_OK */
+#define WM831X_DC3_OK 0x0004 /* DC3_OK */
+#define WM831X_DC3_OK_MASK 0x0004 /* DC3_OK */
+#define WM831X_DC3_OK_SHIFT 2 /* DC3_OK */
+#define WM831X_DC3_OK_WIDTH 1 /* DC3_OK */
+#define WM831X_DC2_OK 0x0002 /* DC2_OK */
+#define WM831X_DC2_OK_MASK 0x0002 /* DC2_OK */
+#define WM831X_DC2_OK_SHIFT 1 /* DC2_OK */
+#define WM831X_DC2_OK_WIDTH 1 /* DC2_OK */
+#define WM831X_DC1_OK 0x0001 /* DC1_OK */
+#define WM831X_DC1_OK_MASK 0x0001 /* DC1_OK */
+#define WM831X_DC1_OK_SHIFT 0 /* DC1_OK */
+#define WM831X_DC1_OK_WIDTH 1 /* DC1_OK */
+
+/*
+ * R16527 (0x408F) - Power Good Source 2
+ */
+#define WM831X_LDO10_OK 0x0200 /* LDO10_OK */
+#define WM831X_LDO10_OK_MASK 0x0200 /* LDO10_OK */
+#define WM831X_LDO10_OK_SHIFT 9 /* LDO10_OK */
+#define WM831X_LDO10_OK_WIDTH 1 /* LDO10_OK */
+#define WM831X_LDO9_OK 0x0100 /* LDO9_OK */
+#define WM831X_LDO9_OK_MASK 0x0100 /* LDO9_OK */
+#define WM831X_LDO9_OK_SHIFT 8 /* LDO9_OK */
+#define WM831X_LDO9_OK_WIDTH 1 /* LDO9_OK */
+#define WM831X_LDO8_OK 0x0080 /* LDO8_OK */
+#define WM831X_LDO8_OK_MASK 0x0080 /* LDO8_OK */
+#define WM831X_LDO8_OK_SHIFT 7 /* LDO8_OK */
+#define WM831X_LDO8_OK_WIDTH 1 /* LDO8_OK */
+#define WM831X_LDO7_OK 0x0040 /* LDO7_OK */
+#define WM831X_LDO7_OK_MASK 0x0040 /* LDO7_OK */
+#define WM831X_LDO7_OK_SHIFT 6 /* LDO7_OK */
+#define WM831X_LDO7_OK_WIDTH 1 /* LDO7_OK */
+#define WM831X_LDO6_OK 0x0020 /* LDO6_OK */
+#define WM831X_LDO6_OK_MASK 0x0020 /* LDO6_OK */
+#define WM831X_LDO6_OK_SHIFT 5 /* LDO6_OK */
+#define WM831X_LDO6_OK_WIDTH 1 /* LDO6_OK */
+#define WM831X_LDO5_OK 0x0010 /* LDO5_OK */
+#define WM831X_LDO5_OK_MASK 0x0010 /* LDO5_OK */
+#define WM831X_LDO5_OK_SHIFT 4 /* LDO5_OK */
+#define WM831X_LDO5_OK_WIDTH 1 /* LDO5_OK */
+#define WM831X_LDO4_OK 0x0008 /* LDO4_OK */
+#define WM831X_LDO4_OK_MASK 0x0008 /* LDO4_OK */
+#define WM831X_LDO4_OK_SHIFT 3 /* LDO4_OK */
+#define WM831X_LDO4_OK_WIDTH 1 /* LDO4_OK */
+#define WM831X_LDO3_OK 0x0004 /* LDO3_OK */
+#define WM831X_LDO3_OK_MASK 0x0004 /* LDO3_OK */
+#define WM831X_LDO3_OK_SHIFT 2 /* LDO3_OK */
+#define WM831X_LDO3_OK_WIDTH 1 /* LDO3_OK */
+#define WM831X_LDO2_OK 0x0002 /* LDO2_OK */
+#define WM831X_LDO2_OK_MASK 0x0002 /* LDO2_OK */
+#define WM831X_LDO2_OK_SHIFT 1 /* LDO2_OK */
+#define WM831X_LDO2_OK_WIDTH 1 /* LDO2_OK */
+#define WM831X_LDO1_OK 0x0001 /* LDO1_OK */
+#define WM831X_LDO1_OK_MASK 0x0001 /* LDO1_OK */
+#define WM831X_LDO1_OK_SHIFT 0 /* LDO1_OK */
+#define WM831X_LDO1_OK_WIDTH 1 /* LDO1_OK */
+
+#define WM831X_ISINK_MAX_ISEL 56
+extern int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL];
+
+#endif
diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h
index 42cca67..1d595de 100644
--- a/include/linux/mfd/wm8350/core.h
+++ b/include/linux/mfd/wm8350/core.h
@@ -605,6 +605,11 @@ struct wm8350_irq {
void *data;
};
+struct wm8350_hwmon {
+ struct platform_device *pdev;
+ struct device *classdev;
+};
+
struct wm8350 {
struct device *dev;
@@ -621,7 +626,6 @@ struct wm8350 {
struct mutex auxadc_mutex;
/* Interrupt handling */
- struct work_struct irq_work;
struct mutex irq_mutex; /* IRQ table mutex */
struct wm8350_irq irq[WM8350_NUM_IRQ];
int chip_irq;
@@ -629,6 +633,7 @@ struct wm8350 {
/* Client devices */
struct wm8350_codec codec;
struct wm8350_gpio gpio;
+ struct wm8350_hwmon hwmon;
struct wm8350_pmic pmic;
struct wm8350_power power;
struct wm8350_rtc rtc;
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 225f733..ce1be70 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -193,6 +193,8 @@ void *rdev_get_drvdata(struct regulator_dev *rdev);
struct device *rdev_get_dev(struct regulator_dev *rdev);
int rdev_get_id(struct regulator_dev *rdev);
+int regulator_mode_to_status(unsigned int);
+
void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
#endif