Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (55 commits)
  regulator: Voltage count for AB3100
  mfd: Convert WM8350 to use request_threaded_irq()
  mfd: Update MAINTAINERS patterns for WM831x
  mfd: Fix twl4030-power warnings
  regulator: AB3100 support
  rtc: AB3100 RTC support
  mfd: Fix ab3100-otp build failure
  mfd: OMAP: Board-specifc twl4030 DPS scripts for RX51 board
  mfd: Print warning for twl4030 out-of-order script loading
  mfd: Add support for TWL4030/5030 dynamic power switching
  mfd: AB3100 OTP readout
  regulator: Add Freescale MC13783 driver
  mfd: Add Freescale MC13783 driver
  mfd: AB3100 disable irq nosync
  mfd: AB3100 alter default setting
  mfd: AB3100 propagate error
  mfd: AB3100 accessor function cleanups
  rtc: Add support for RTCs on Wolfson WM831x devices
  regulator: get pcap data from the parent device
  input: PCAP2 misc input driver
  ...
This commit is contained in:
Linus Torvalds 2009-09-18 09:22:36 -07:00
commit 6f130478e2
65 changed files with 12775 additions and 124 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

252
drivers/gpio/wm831x-gpio.c Normal file
View file

@ -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");

View file

@ -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

View file

@ -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

View file

@ -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");

View file

@ -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");

View file

@ -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

View file

@ -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

View file

@ -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");

View file

@ -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>");

View file

@ -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

View file

@ -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

View file

@ -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");

View file

@ -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

View file

@ -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

View file

@ -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);
}

268
drivers/mfd/ab3100-otp.c Normal file
View file

@ -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");

View file

@ -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)

View file

@ -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);
/* IRQ */
static inline unsigned int irq2pcap(struct pcap_chip *pcap, int irq)
int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val)
{
return 1 << (irq - pcap->irq_base);
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 */
int irq_to_pcap(struct pcap_chip *pcap, int irq)
{
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);
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 (desc->status & IRQ_DISABLED)
note_interrupt(irq, desc, IRQ_NONE);
else
desc->handle_irq(irq, desc);
/* 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_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
ezx_pcap_write(pcap, PCAP_REG_ISR, isr);
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 (desc->status & IRQ_DISABLED)
note_interrupt(irq, desc, IRQ_NONE);
else
desc->handle_irq(irq, desc);
}
}
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");

427
drivers/mfd/mc13783-core.c Normal file
View file

@ -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");

View file

@ -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;

View file

@ -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)

View file

@ -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);

View file

@ -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

View file

@ -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)

472
drivers/mfd/twl4030-power.c Normal file
View file

@ -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;
}

1549
drivers/mfd/wm831x-core.c Normal file

File diff suppressed because it is too large Load diff

559
drivers/mfd/wm831x-irq.c Normal file
View file

@ -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);
}

83
drivers/mfd/wm831x-otp.c Normal file
View file

@ -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);
}

View file

@ -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);

View file

@ -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

View file

@ -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

700
drivers/regulator/ab3100.c Normal file
View file

@ -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");

View file

@ -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.

410
drivers/regulator/mc13783.c Normal file
View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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

View file

@ -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

281
drivers/rtc/rtc-ab3100.c Normal file
View file

@ -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");

523
drivers/rtc/rtc-wm831x.c Normal file
View file

@ -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");

View file

@ -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);

View file

@ -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,

View file

@ -23,6 +23,7 @@
*/
struct mfd_cell {
const char *name;
int id;
int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev);

View file

@ -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

View file

@ -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 */

View file

@ -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 */

View file

@ -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

View file

@ -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];

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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