mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 19:26:25 +00:00
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:
commit
6f130478e2
65 changed files with 12775 additions and 124 deletions
37
Documentation/hwmon/wm831x
Normal file
37
Documentation/hwmon/wm831x
Normal 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.
|
26
Documentation/hwmon/wm8350
Normal file
26
Documentation/hwmon/wm8350
Normal 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.
|
20
MAINTAINERS
20
MAINTAINERS
|
@ -5683,6 +5683,26 @@ S: Supported
|
|||
F: drivers/input/touchscreen/*wm97*
|
||||
F: include/linux/wm97xx.h
|
||||
|
||||
WOLFSON MICROELECTRONICS PMIC DRIVERS
|
||||
P: Mark Brown
|
||||
M: broonie@opensource.wolfsonmicro.com
|
||||
L: linux-kernel@vger.kernel.org
|
||||
T: git git://opensource.wolfsonmicro.com/linux-2.6-audioplus
|
||||
W: http://opensource.wolfsonmicro.com/node/8
|
||||
S: Supported
|
||||
F: drivers/leds/leds-wm83*.c
|
||||
F: drivers/mfd/wm8*.c
|
||||
F: drivers/power/wm83*.c
|
||||
F: drivers/rtc/rtc-wm83*.c
|
||||
F: drivers/regulator/wm8*.c
|
||||
F: drivers/video/backlight/wm83*_bl.c
|
||||
F: drivers/watchdog/wm83*_wdt.c
|
||||
F: include/linux/mfd/wm831x/
|
||||
F: include/linux/mfd/wm8350/
|
||||
F: include/linux/mfd/wm8400/
|
||||
F: sound/soc/codecs/wm8350.c
|
||||
F: sound/soc/codecs/wm8400.c
|
||||
|
||||
X.25 NETWORK LAYER
|
||||
M: Henner Eisen <eis@baty.hanse.de>
|
||||
L: linux-x25@vger.kernel.org
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
252
drivers/gpio/wm831x-gpio.c
Normal 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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
226
drivers/hwmon/wm831x-hwmon.c
Normal file
226
drivers/hwmon/wm831x-hwmon.c
Normal 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");
|
151
drivers/hwmon/wm8350-hwmon.c
Normal file
151
drivers/hwmon/wm8350-hwmon.c
Normal 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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
144
drivers/input/misc/pcap_keys.c
Normal file
144
drivers/input/misc/pcap_keys.c
Normal 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");
|
163
drivers/input/misc/wm831x-on.c
Normal file
163
drivers/input/misc/wm831x-on.c
Normal 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>");
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
271
drivers/input/touchscreen/pcap_ts.c
Normal file
271
drivers/input/touchscreen/pcap_ts.c
Normal 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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ®value);
|
||||
ab3100_get_register_interruptible(ab3100, reg, ®value);
|
||||
|
||||
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, ®value);
|
||||
ab3100_set_register_interruptible(ab3100, reg, value);
|
||||
ab3100_get_register_interruptible(ab3100, reg, ®value);
|
||||
|
||||
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
268
drivers/mfd/ab3100-otp.c
Normal 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");
|
|
@ -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)
|
||||
|
|
|
@ -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
427
drivers/mfd/mc13783-core.c
Normal 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");
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
472
drivers/mfd/twl4030-power.c
Normal 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
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
559
drivers/mfd/wm831x-irq.c
Normal 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
83
drivers/mfd/wm831x-otp.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
700
drivers/regulator/ab3100.c
Normal 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,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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(®->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,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_err(®->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,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_err(®->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, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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(®->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, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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 = ®ulator_ops_fixed,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_C",
|
||||
.id = AB3100_LDO_C,
|
||||
.ops = ®ulator_ops_fixed,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_D",
|
||||
.id = AB3100_LDO_D,
|
||||
.ops = ®ulator_ops_fixed,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_E",
|
||||
.id = AB3100_LDO_E,
|
||||
.ops = ®ulator_ops_variable_sleepable,
|
||||
.n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages),
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_F",
|
||||
.id = AB3100_LDO_F,
|
||||
.ops = ®ulator_ops_variable,
|
||||
.n_voltages = ARRAY_SIZE(ldo_f_typ_voltages),
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_G",
|
||||
.id = AB3100_LDO_G,
|
||||
.ops = ®ulator_ops_variable,
|
||||
.n_voltages = ARRAY_SIZE(ldo_g_typ_voltages),
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_H",
|
||||
.id = AB3100_LDO_H,
|
||||
.ops = ®ulator_ops_variable,
|
||||
.n_voltages = ARRAY_SIZE(ldo_h_typ_voltages),
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_K",
|
||||
.id = AB3100_LDO_K,
|
||||
.ops = ®ulator_ops_variable,
|
||||
.n_voltages = ARRAY_SIZE(ldo_k_typ_voltages),
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "LDO_EXT",
|
||||
.id = AB3100_LDO_EXT,
|
||||
.ops = ®ulator_ops_external,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
},
|
||||
{
|
||||
.name = "BUCK",
|
||||
.id = AB3100_BUCK,
|
||||
.ops = ®ulator_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");
|
|
@ -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
410
drivers/regulator/mc13783.c
Normal 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");
|
318
drivers/regulator/pcap-regulator.c
Normal file
318
drivers/regulator/pcap-regulator.c
Normal 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");
|
862
drivers/regulator/wm831x-dcdc.c
Normal file
862
drivers/regulator/wm831x-dcdc.c
Normal 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");
|
260
drivers/regulator/wm831x-isink.c
Normal file
260
drivers/regulator/wm831x-isink.c
Normal 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");
|
852
drivers/regulator/wm831x-ldo.c
Normal file
852
drivers/regulator/wm831x-ldo.c
Normal 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");
|
|
@ -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
|
||||
|
|
|
@ -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
281
drivers/rtc/rtc-ab3100.c
Normal 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, ®val);
|
||||
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
523
drivers/rtc/rtc-wm831x.c
Normal 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");
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
struct mfd_cell {
|
||||
const char *name;
|
||||
int id;
|
||||
|
||||
int (*enable)(struct platform_device *dev);
|
||||
int (*disable)(struct platform_device *dev);
|
||||
|
|
|
@ -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
|
||||
|
|
396
include/linux/mfd/mc13783-private.h
Normal file
396
include/linux/mfd/mc13783-private.h
Normal 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 */
|
||||
|
84
include/linux/mfd/mc13783.h
Normal file
84
include/linux/mfd/mc13783.h
Normal 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 */
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
216
include/linux/mfd/wm831x/auxadc.h
Normal file
216
include/linux/mfd/wm831x/auxadc.h
Normal 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
|
289
include/linux/mfd/wm831x/core.h
Normal file
289
include/linux/mfd/wm831x/core.h
Normal 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
|
55
include/linux/mfd/wm831x/gpio.h
Normal file
55
include/linux/mfd/wm831x/gpio.h
Normal 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
|
764
include/linux/mfd/wm831x/irq.h
Normal file
764
include/linux/mfd/wm831x/irq.h
Normal 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
|
162
include/linux/mfd/wm831x/otp.h
Normal file
162
include/linux/mfd/wm831x/otp.h
Normal 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
|
113
include/linux/mfd/wm831x/pdata.h
Normal file
113
include/linux/mfd/wm831x/pdata.h
Normal 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
|
1218
include/linux/mfd/wm831x/regulator.h
Normal file
1218
include/linux/mfd/wm831x/regulator.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue