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: (58 commits)
  mfd: Add twl6030 regulator subdevices
  regulator: Add support for twl6030 regulators
  rtc: Add twl6030 RTC support
  mfd: Add support for twl6030 irq framework
  mfd: Rename twl4030_ routines in twl-regulator.c
  mfd: Rename twl4030_ routines in rtc-twl.c
  mfd: Rename all twl4030_i2c*
  mfd: Rename twl4030* driver files to enable re-use
  mfd: Clarify twl4030 return value for read and write
  mfd: Add all twl4030 regulators to the twl4030 mfd driver
  mfd: Don't set mc13783 ADREFMODE for touch conversions
  mfd: Remove ezx-pcap defines for custom led gpio encoding
  mfd: Near complete mc13783 rewrite
  mfd: Remove build time warning for WM835x register default tables
  mfd: Force I2C to be built in when building WM831x
  mfd: Don't allow wm831x to be built as a module
  mfd: Fix incorrect error check for wm8350-core
  mfd: Fix twl4030 warning
  gpiolib: Implement gpio_to_irq() for wm831x
  mfd: Remove default selection of AB4500
  ...
This commit is contained in:
Linus Torvalds 2009-12-14 10:02:35 -08:00
commit 76b8f82cde
69 changed files with 5237 additions and 2135 deletions

View File

@ -19,7 +19,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>

View File

@ -20,7 +20,7 @@
#include <linux/input/matrix_keypad.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/machine.h>
#include <linux/io.h>
#include <linux/gpio.h>

View File

@ -24,7 +24,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <linux/regulator/machine.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <linux/io.h>
#include <linux/smsc911x.h>

View File

@ -29,7 +29,7 @@
#include <linux/mtd/nand.h>
#include <linux/regulator/machine.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>

View File

@ -24,7 +24,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <linux/regulator/machine.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <linux/leds.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>

View File

@ -26,7 +26,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/machine.h>
#include <linux/mtd/mtd.h>

View File

@ -402,15 +402,9 @@ static struct twl4030_usb_data rx51_usb_data = {
static struct twl4030_ins sleep_on_seq[] __initdata = {
/*
* Turn off VDD1 and VDD2.
* Turn off everything
*/
{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},
{MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, 1, 0, RES_STATE_SLEEP), 2},
};
static struct twl4030_script sleep_on_script __initdata = {
@ -421,14 +415,9 @@ static struct twl4030_script sleep_on_script __initdata = {
static struct twl4030_ins wakeup_seq[] __initdata = {
/*
* Reenable the OMAP3 PLLs.
* Wakeup VDD1 and VDD2.
* Reenable sysclk output.
* Reenable everything
*/
{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},
{MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, 1, 0, RES_STATE_ACTIVE), 2},
};
static struct twl4030_script wakeup_script __initdata = {
@ -439,10 +428,9 @@ static struct twl4030_script wakeup_script __initdata = {
static struct twl4030_ins wakeup_p3_seq[] __initdata = {
/*
* Wakeup VDD1 (dummy to be able to insert a delay)
* Enable CLKEN
* Reenable everything
*/
{MSG_SINGULAR(DEV_GRP_P1, 0x17, RES_STATE_ACTIVE), 3},
{MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, 1, 0, RES_STATE_ACTIVE), 2},
};
static struct twl4030_script wakeup_p3_script __initdata = {
@ -463,12 +451,11 @@ static struct twl4030_ins wrst_seq[] __initdata = {
{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_P3, RES_HFCLKOUT, RES_STATE_ACTIVE), 2},
{MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_ACTIVE), 2},
};
@ -490,22 +477,81 @@ static struct twl4030_script *twl4030_scripts[] __initdata = {
};
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 },
{ .resource = RES_VDD1, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = RES_STATE_OFF,
.remap_sleep = RES_STATE_OFF
},
{ .resource = RES_VDD2, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = RES_STATE_OFF,
.remap_sleep = RES_STATE_OFF
},
{ .resource = RES_VPLL1, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = RES_STATE_OFF,
.remap_sleep = RES_STATE_OFF
},
{ .resource = RES_VPLL2, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VAUX1, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VAUX2, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VAUX3, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VAUX4, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VMMC1, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VMMC2, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VDAC, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VSIM, .devgroup = -1,
.type = -1, .type2 = 3, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VINTANA1, .devgroup = DEV_GRP_P1 | DEV_GRP_P3,
.type = -1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VINTANA2, .devgroup = DEV_GRP_P1 | DEV_GRP_P3,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VINTDIG, .devgroup = DEV_GRP_P1 | DEV_GRP_P3,
.type = -1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_VIO, .devgroup = DEV_GRP_P3,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_CLKEN, .devgroup = DEV_GRP_P1 | DEV_GRP_P3,
.type = 1, .type2 = -1 , .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_REGEN, .devgroup = DEV_GRP_P1 | DEV_GRP_P3,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_NRES_PWRON, .devgroup = DEV_GRP_P1 | DEV_GRP_P3,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_SYSEN, .devgroup = DEV_GRP_P1 | DEV_GRP_P3,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_HFCLKOUT, .devgroup = DEV_GRP_P3,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_32KCLKOUT, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_RESET, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ .resource = RES_Main_Ref, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ 0, 0},
};

View File

@ -472,8 +472,22 @@
#endif
#define TWL4030_GPIO_IRQ_END (TWL4030_GPIO_IRQ_BASE + TWL4030_GPIO_NR_IRQS)
#define TWL6030_IRQ_BASE (OMAP_FPGA_IRQ_END)
#ifdef CONFIG_TWL4030_CORE
#define TWL6030_BASE_NR_IRQS 20
#else
#define TWL6030_BASE_NR_IRQS 0
#endif
#define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS)
/* Total number of interrupts depends on the enabled blocks above */
#define NR_IRQS TWL4030_GPIO_IRQ_END
#if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END)
#define TWL_IRQ_END TWL4030_GPIO_IRQ_END
#else
#define TWL_IRQ_END TWL6030_IRQ_END
#endif
#define NR_IRQS TWL_IRQ_END
#define OMAP_IRQ_BIT(irq) (1 << ((irq) % 32))

View File

@ -34,9 +34,9 @@ static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
*/
if (test_bit(off, &dev->output))
adp5520_read(dev->master, GPIO_OUT, &reg_val);
adp5520_read(dev->master, ADP5520_GPIO_OUT, &reg_val);
else
adp5520_read(dev->master, GPIO_IN, &reg_val);
adp5520_read(dev->master, ADP5520_GPIO_IN, &reg_val);
return !!(reg_val & dev->lut[off]);
}
@ -48,9 +48,9 @@ static void adp5520_gpio_set_value(struct gpio_chip *chip,
dev = container_of(chip, struct adp5520_gpio, gpio_chip);
if (val)
adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
else
adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
}
static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
@ -60,7 +60,8 @@ static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
clear_bit(off, &dev->output);
return adp5520_clr_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2,
dev->lut[off]);
}
static int adp5520_gpio_direction_output(struct gpio_chip *chip,
@ -73,18 +74,21 @@ static int adp5520_gpio_direction_output(struct gpio_chip *chip,
set_bit(off, &dev->output);
if (val)
ret |= adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
dev->lut[off]);
else
ret |= adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT,
dev->lut[off]);
ret |= adp5520_set_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2,
dev->lut[off]);
return ret;
}
static int __devinit adp5520_gpio_probe(struct platform_device *pdev)
{
struct adp5520_gpio_platfrom_data *pdata = pdev->dev.platform_data;
struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data;
struct adp5520_gpio *dev;
struct gpio_chip *gc;
int ret, i, gpios;
@ -129,20 +133,20 @@ static int __devinit adp5520_gpio_probe(struct platform_device *pdev)
gc->label = pdev->name;
gc->owner = THIS_MODULE;
ret = adp5520_clr_bits(dev->master, GPIO_CFG_1,
ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1,
pdata->gpio_en_mask);
if (pdata->gpio_en_mask & GPIO_C3)
ctl_mask |= C3_MODE;
if (pdata->gpio_en_mask & ADP5520_GPIO_C3)
ctl_mask |= ADP5520_C3_MODE;
if (pdata->gpio_en_mask & GPIO_R3)
ctl_mask |= R3_MODE;
if (pdata->gpio_en_mask & ADP5520_GPIO_R3)
ctl_mask |= ADP5520_R3_MODE;
if (ctl_mask)
ret = adp5520_set_bits(dev->master, LED_CONTROL,
ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
ctl_mask);
ret |= adp5520_set_bits(dev->master, GPIO_PULLUP,
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
pdata->gpio_pullup_mask);
if (ret) {

View File

@ -34,7 +34,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
/*
@ -80,7 +80,7 @@ static unsigned int gpio_usage_count;
*/
static inline int gpio_twl4030_write(u8 address, u8 data)
{
return twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
return twl_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
}
/*----------------------------------------------------------------------*/
@ -117,7 +117,7 @@ static inline int gpio_twl4030_read(u8 address)
u8 data;
int ret = 0;
ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address);
ret = twl_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address);
return (ret < 0) ? ret : data;
}
@ -142,7 +142,7 @@ static void twl4030_led_set_value(int led, int value)
cached_leden &= ~mask;
else
cached_leden |= mask;
status = twl4030_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
TWL4030_LED_LEDEN);
mutex_unlock(&gpio_lock);
}
@ -223,23 +223,23 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)
}
/* initialize PWM to always-drive */
status = twl4030_i2c_write_u8(module, 0x7f,
status = twl_i2c_write_u8(module, 0x7f,
TWL4030_PWMx_PWMxOFF);
if (status < 0)
goto done;
status = twl4030_i2c_write_u8(module, 0x7f,
status = twl_i2c_write_u8(module, 0x7f,
TWL4030_PWMx_PWMxON);
if (status < 0)
goto done;
/* init LED to not-driven (high) */
module = TWL4030_MODULE_LED;
status = twl4030_i2c_read_u8(module, &cached_leden,
status = twl_i2c_read_u8(module, &cached_leden,
TWL4030_LED_LEDEN);
if (status < 0)
goto done;
cached_leden &= ~ledclr_mask;
status = twl4030_i2c_write_u8(module, cached_leden,
status = twl_i2c_write_u8(module, cached_leden,
TWL4030_LED_LEDEN);
if (status < 0)
goto done;
@ -370,7 +370,7 @@ static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs)
message[i] = bit_mask;
}
return twl4030_i2c_write(TWL4030_MODULE_GPIO, message,
return twl_i2c_write(TWL4030_MODULE_GPIO, message,
REG_GPIOPUPDCTR1, 5);
}
@ -387,7 +387,7 @@ static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
debounce >>= 8;
message[3] = (debounce & 0x03);
return twl4030_i2c_write(TWL4030_MODULE_GPIO, message,
return twl_i2c_write(TWL4030_MODULE_GPIO, message,
REG_GPIO_DEBEN1, 3);
}

View File

@ -22,8 +22,7 @@
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/gpio.h>
#define WM831X_GPIO_MAX 16
#include <linux/mfd/wm831x/irq.h>
struct wm831x_gpio {
struct wm831x *wm831x;
@ -80,6 +79,17 @@ static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
value << offset);
}
static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
if (!wm831x->irq_base)
return -EINVAL;
return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
}
#ifdef CONFIG_DEBUG_FS
static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
@ -175,6 +185,7 @@ static struct gpio_chip template_chip = {
.get = wm831x_gpio_get,
.direction_output = wm831x_gpio_direction_out,
.set = wm831x_gpio_set,
.to_irq = wm831x_gpio_to_irq,
.dbg_show = wm831x_gpio_dbg_show,
.can_sleep = 1,
};
@ -192,7 +203,7 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
wm831x_gpio->wm831x = wm831x;
wm831x_gpio->gpio_chip = template_chip;
wm831x_gpio->gpio_chip.ngpio = WM831X_GPIO_MAX;
wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio;
wm831x_gpio->gpio_chip.dev = &pdev->dev;
if (pdata && pdata->gpio_base)
wm831x_gpio->gpio_chip.base = pdata->gpio_base;

View File

@ -24,6 +24,16 @@ config KEYBOARD_AAED2000
To compile this driver as a module, choose M here: the
module will be called aaed2000_kbd.
config KEYBOARD_ADP5520
tristate "Keypad Support for ADP5520 PMIC"
depends on PMIC_ADP5520
help
This option enables support for the keypad scan matrix
on Analog Devices ADP5520 PMICs.
To compile this driver as a module, choose M here: the module will
be called adp5520-keys.
config KEYBOARD_ADP5588
tristate "ADP5588 I2C QWERTY Keypad and IO Expander"
depends on I2C

View File

@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o

View File

@ -0,0 +1,220 @@
/*
* Keypad driver for Analog Devices ADP5520 MFD PMICs
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/mfd/adp5520.h>
struct adp5520_keys {
struct input_dev *input;
struct notifier_block notifier;
struct device *master;
unsigned short keycode[ADP5520_KEYMAPSIZE];
};
static void adp5520_keys_report_event(struct adp5520_keys *dev,
unsigned short keymask, int value)
{
int i;
for (i = 0; i < ADP5520_MAXKEYS; i++)
if (keymask & (1 << i))
input_report_key(dev->input, dev->keycode[i], value);
input_sync(dev->input);
}
static int adp5520_keys_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct adp5520_keys *dev;
uint8_t reg_val_lo, reg_val_hi;
unsigned short keymask;
dev = container_of(nb, struct adp5520_keys, notifier);
if (event & ADP5520_KP_INT) {
adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
keymask = (reg_val_hi << 8) | reg_val_lo;
/* Read twice to clear */
adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
keymask |= (reg_val_hi << 8) | reg_val_lo;
adp5520_keys_report_event(dev, keymask, 1);
}
if (event & ADP5520_KR_INT) {
adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
keymask = (reg_val_hi << 8) | reg_val_lo;
/* Read twice to clear */
adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
keymask |= (reg_val_hi << 8) | reg_val_lo;
adp5520_keys_report_event(dev, keymask, 0);
}
return 0;
}
static int __devinit adp5520_keys_probe(struct platform_device *pdev)
{
struct adp5520_keys_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input;
struct adp5520_keys *dev;
int ret, i;
unsigned char en_mask, ctl_mask = 0;
if (pdev->id != ID_ADP5520) {
dev_err(&pdev->dev, "only ADP5520 supports Keypad\n");
return -EINVAL;
}
if (pdata == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
return -EINVAL;
}
if (!(pdata->rows_en_mask && pdata->cols_en_mask))
return -EINVAL;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&pdev->dev, "failed to alloc memory\n");
return -ENOMEM;
}
input = input_allocate_device();
if (!input) {
ret = -ENOMEM;
goto err;
}
dev->master = pdev->dev.parent;
dev->input = input;
input->name = pdev->name;
input->phys = "adp5520-keys/input0";
input->dev.parent = &pdev->dev;
input_set_drvdata(input, dev);
input->id.bustype = BUS_I2C;
input->id.vendor = 0x0001;
input->id.product = 0x5520;
input->id.version = 0x0001;
input->keycodesize = sizeof(dev->keycode[0]);
input->keycodemax = pdata->keymapsize;
input->keycode = dev->keycode;
memcpy(dev->keycode, pdata->keymap,
pdata->keymapsize * input->keycodesize);
/* setup input device */
__set_bit(EV_KEY, input->evbit);
if (pdata->repeat)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < input->keycodemax; i++)
__set_bit(dev->keycode[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
ret = input_register_device(input);
if (ret) {
dev_err(&pdev->dev, "unable to register input device\n");
goto err;
}
en_mask = pdata->rows_en_mask | pdata->cols_en_mask;
ret = adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_1, en_mask);
if (en_mask & ADP5520_COL_C3)
ctl_mask |= ADP5520_C3_MODE;
if (en_mask & ADP5520_ROW_R3)
ctl_mask |= ADP5520_R3_MODE;
if (ctl_mask)
ret |= adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
ctl_mask);
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
pdata->rows_en_mask);
if (ret) {
dev_err(&pdev->dev, "failed to write\n");
ret = -EIO;
goto err1;
}
dev->notifier.notifier_call = adp5520_keys_notifier;
ret = adp5520_register_notifier(dev->master, &dev->notifier,
ADP5520_KP_IEN | ADP5520_KR_IEN);
if (ret) {
dev_err(&pdev->dev, "failed to register notifier\n");
goto err1;
}
platform_set_drvdata(pdev, dev);
return 0;
err1:
input_unregister_device(input);
input = NULL;
err:
input_free_device(input);
kfree(dev);
return ret;
}
static int __devexit adp5520_keys_remove(struct platform_device *pdev)
{
struct adp5520_keys *dev = platform_get_drvdata(pdev);
adp5520_unregister_notifier(dev->master, &dev->notifier,
ADP5520_KP_IEN | ADP5520_KR_IEN);
input_unregister_device(dev->input);
kfree(dev);
return 0;
}
static struct platform_driver adp5520_keys_driver = {
.driver = {
.name = "adp5520-keys",
.owner = THIS_MODULE,
},
.probe = adp5520_keys_probe,
.remove = __devexit_p(adp5520_keys_remove),
};
static int __init adp5520_keys_init(void)
{
return platform_driver_register(&adp5520_keys_driver);
}
module_init(adp5520_keys_init);
static void __exit adp5520_keys_exit(void)
{
platform_driver_unregister(&adp5520_keys_driver);
}
module_exit(adp5520_keys_exit);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Keys ADP5520 Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:adp5520-keys");

View File

@ -31,7 +31,7 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
/*
@ -133,7 +133,7 @@ struct twl4030_keypad {
static int twl4030_kpread(struct twl4030_keypad *kp,
u8 *data, u32 reg, u8 num_bytes)
{
int ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
int ret = twl_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
if (ret < 0)
dev_warn(kp->dbg_dev,
@ -145,7 +145,7 @@ static int twl4030_kpread(struct twl4030_keypad *kp,
static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
{
int ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
int ret = twl_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
if (ret < 0)
dev_warn(kp->dbg_dev,

View File

@ -55,7 +55,6 @@ pcf50633_input_irq(int irq, void *data)
static int __devinit pcf50633_input_probe(struct platform_device *pdev)
{
struct pcf50633_input *input;
struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
struct input_dev *input_dev;
int ret;
@ -71,7 +70,7 @@ static int __devinit pcf50633_input_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, input);
input->pcf = pdata->pcf;
input->pcf = dev_to_pcf50633(pdev->dev.parent);
input->input_dev = input_dev;
input_dev->name = "PCF50633 PMU events";
@ -85,9 +84,9 @@ static int __devinit pcf50633_input_probe(struct platform_device *pdev)
kfree(input);
return ret;
}
pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYR,
pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR,
pcf50633_input_irq, input);
pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYF,
pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF,
pcf50633_input_irq, input);
return 0;

View File

@ -27,7 +27,7 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#define PWR_PWRON_IRQ (1 << 0)
@ -49,7 +49,7 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr)
local_irq_enable();
#endif
err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
STS_HW_CONDITIONS);
if (!err) {
input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);

302
drivers/mfd/88pm8607.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Base driver for Marvell 88PM8607
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.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/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/88pm8607.h>
#define PM8607_REG_RESOURCE(_start, _end) \
{ \
.start = PM8607_##_start, \
.end = PM8607_##_end, \
.flags = IORESOURCE_IO, \
}
static struct resource pm8607_regulator_resources[] = {
PM8607_REG_RESOURCE(BUCK1, BUCK1),
PM8607_REG_RESOURCE(BUCK2, BUCK2),
PM8607_REG_RESOURCE(BUCK3, BUCK3),
PM8607_REG_RESOURCE(LDO1, LDO1),
PM8607_REG_RESOURCE(LDO2, LDO2),
PM8607_REG_RESOURCE(LDO3, LDO3),
PM8607_REG_RESOURCE(LDO4, LDO4),
PM8607_REG_RESOURCE(LDO5, LDO5),
PM8607_REG_RESOURCE(LDO6, LDO6),
PM8607_REG_RESOURCE(LDO7, LDO7),
PM8607_REG_RESOURCE(LDO8, LDO8),
PM8607_REG_RESOURCE(LDO9, LDO9),
PM8607_REG_RESOURCE(LDO10, LDO10),
PM8607_REG_RESOURCE(LDO12, LDO12),
PM8607_REG_RESOURCE(LDO14, LDO14),
};
#define PM8607_REG_DEVS(_name, _id) \
{ \
.name = "88pm8607-" #_name, \
.num_resources = 1, \
.resources = &pm8607_regulator_resources[PM8607_ID_##_id], \
}
static struct mfd_cell pm8607_devs[] = {
PM8607_REG_DEVS(buck1, BUCK1),
PM8607_REG_DEVS(buck2, BUCK2),
PM8607_REG_DEVS(buck3, BUCK3),
PM8607_REG_DEVS(ldo1, LDO1),
PM8607_REG_DEVS(ldo2, LDO2),
PM8607_REG_DEVS(ldo3, LDO3),
PM8607_REG_DEVS(ldo4, LDO4),
PM8607_REG_DEVS(ldo5, LDO5),
PM8607_REG_DEVS(ldo6, LDO6),
PM8607_REG_DEVS(ldo7, LDO7),
PM8607_REG_DEVS(ldo8, LDO8),
PM8607_REG_DEVS(ldo9, LDO9),
PM8607_REG_DEVS(ldo10, LDO10),
PM8607_REG_DEVS(ldo12, LDO12),
PM8607_REG_DEVS(ldo14, LDO14),
};
static inline int pm8607_read_device(struct pm8607_chip *chip,
int reg, int bytes, void *dest)
{
struct i2c_client *i2c = chip->client;
unsigned char data;
int ret;
data = (unsigned char)reg;
ret = i2c_master_send(i2c, &data, 1);
if (ret < 0)
return ret;
ret = i2c_master_recv(i2c, dest, bytes);
if (ret < 0)
return ret;
return 0;
}
static inline int pm8607_write_device(struct pm8607_chip *chip,
int reg, int bytes, void *src)
{
struct i2c_client *i2c = chip->client;
unsigned char buf[bytes + 1];
int ret;
buf[0] = (unsigned char)reg;
memcpy(&buf[1], src, bytes);
ret = i2c_master_send(i2c, buf, bytes + 1);
if (ret < 0)
return ret;
return 0;
}
int pm8607_reg_read(struct pm8607_chip *chip, int reg)
{
unsigned char data;
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, 1, &data);
mutex_unlock(&chip->io_lock);
if (ret < 0)
return ret;
else
return (int)data;
}
EXPORT_SYMBOL(pm8607_reg_read);
int pm8607_reg_write(struct pm8607_chip *chip, int reg,
unsigned char data)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->write(chip, reg, 1, &data);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_reg_write);
int pm8607_bulk_read(struct pm8607_chip *chip, int reg,
int count, unsigned char *buf)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_bulk_read);
int pm8607_bulk_write(struct pm8607_chip *chip, int reg,
int count, unsigned char *buf)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->write(chip, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_bulk_write);
int pm8607_set_bits(struct pm8607_chip *chip, int reg,
unsigned char mask, unsigned char data)
{
unsigned char value;
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, 1, &value);
if (ret < 0)
goto out;
value &= ~mask;
value |= data;
ret = chip->write(chip, reg, 1, &value);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_set_bits);
static const struct i2c_device_id pm8607_id_table[] = {
{ "88PM8607", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, pm8607_id_table);
static int __devinit pm8607_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pm8607_platform_data *pdata = client->dev.platform_data;
struct pm8607_chip *chip;
int i, count;
int ret;
chip = kzalloc(sizeof(struct pm8607_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->client = client;
chip->dev = &client->dev;
chip->read = pm8607_read_device;
chip->write = pm8607_write_device;
i2c_set_clientdata(client, chip);
mutex_init(&chip->io_lock);
dev_set_drvdata(chip->dev, chip);
ret = pm8607_reg_read(chip, PM8607_CHIP_ID);
if (ret < 0) {
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
goto out;
}
if ((ret & CHIP_ID_MASK) == CHIP_ID)
dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
ret);
else {
dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
"Chip ID: %02x\n", ret);
goto out;
}
chip->chip_id = ret;
ret = pm8607_reg_read(chip, PM8607_BUCK3);
if (ret < 0) {
dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
goto out;
}
if (ret & PM8607_BUCK3_DOUBLE)
chip->buck3_double = 1;
ret = pm8607_reg_read(chip, PM8607_MISC1);
if (ret < 0) {
dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
goto out;
}
if (pdata->i2c_port == PI2C_PORT)
ret |= PM8607_MISC1_PI2C;
else
ret &= ~PM8607_MISC1_PI2C;
ret = pm8607_reg_write(chip, PM8607_MISC1, ret);
if (ret < 0) {
dev_err(chip->dev, "Failed to write MISC1 register: %d\n", ret);
goto out;
}
count = ARRAY_SIZE(pm8607_devs);
for (i = 0; i < count; i++) {
ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i],
1, NULL, 0);
if (ret != 0) {
dev_err(chip->dev, "Failed to add subdevs\n");
goto out;
}
}
return 0;
out:
i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
static int __devexit pm8607_remove(struct i2c_client *client)
{
struct pm8607_chip *chip = i2c_get_clientdata(client);
mfd_remove_devices(chip->dev);
kfree(chip);
return 0;
}
static struct i2c_driver pm8607_driver = {
.driver = {
.name = "88PM8607",
.owner = THIS_MODULE,
},
.probe = pm8607_probe,
.remove = __devexit_p(pm8607_remove),
.id_table = pm8607_id_table,
};
static int __init pm8607_init(void)
{
int ret;
ret = i2c_add_driver(&pm8607_driver);
if (ret != 0)
pr_err("Failed to register 88PM8607 I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(pm8607_init);
static void __exit pm8607_exit(void)
{
i2c_del_driver(&pm8607_driver);
}
module_exit(pm8607_exit);
MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM8607");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

View File

@ -103,10 +103,10 @@ config MENELAUS
cell phones and PDAs.
config TWL4030_CORE
bool "Texas Instruments TWL4030/TPS659x0 Support"
bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
depends on I2C=y && GENERIC_HARDIRQS
help
Say yes here if you have TWL4030 family chip on your board.
Say yes here if you have TWL4030 / TWL6030 family chip on your board.
This core driver provides register access and IRQ handling
facilities, and registers devices for the various functions
so that function-specific drivers can bind to them.
@ -174,6 +174,16 @@ config PMIC_DA903X
individual components like LCD backlight, voltage regulators,
LEDs and battery-charger under the corresponding menus.
config PMIC_ADP5520
bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
depends on I2C=y
help
Say yes here to add support for Analog Devices AD5520 and ADP5501,
Multifunction Power Management IC. This includes
the I2C driver and the core APIs _only_, you have to select
individual components like LCD backlight, LEDs, GPIOs and Kepad
under the corresponding menus.
config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
@ -185,12 +195,12 @@ config MFD_WM8400
the functionality of the device.
config MFD_WM831X
tristate "Support Wolfson Microelectronics WM831x PMICs"
bool "Support Wolfson Microelectronics WM831x/2x PMICs"
select MFD_CORE
depends on I2C
depends on I2C=y
help
Support for the Wolfson Microelecronics WM831x PMICs. This
driver provides common support for accessing the device,
Support for the Wolfson Microelecronics WM831x and WM832x PMICs.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
@ -319,6 +329,25 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc..
config MFD_88PM8607
bool "Support Marvell 88PM8607"
depends on I2C=y
select MFD_CORE
help
This supports for Marvell 88PM8607 Power Management IC. This includes
the I2C driver and the core APIs _only_, you have to select
individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
config AB4500_CORE
tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip"
depends on SPI
help
Select this option to enable access to AB4500 power management
chip. This connects to U8500 on the SSP/SPI bus and exports
read/write functions for the devices to get access to this chip.
This chip embeds various other multimedia funtionalities as well.
endmenu
menu "Multimedia Capabilities Port drivers"

View File

@ -19,13 +19,14 @@ 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
wm8350-objs += wm8350-irq.o
obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
@ -52,3 +53,6 @@ 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
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o

View File

@ -900,9 +900,6 @@ static int __init ab3100_probe(struct i2c_client *client,
goto exit_no_testreg_client;
}
strlcpy(ab3100->testreg_client->name, id->name,
sizeof(ab3100->testreg_client->name));
err = ab3100_setup(ab3100);
if (err)
goto exit_no_setup;

208
drivers/mfd/ab4500-core.c Normal file
View File

@ -0,0 +1,208 @@
/*
* Copyright (C) 2009 ST-Ericsson
*
* Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.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.
*
* AB4500 is a companion power management chip used with U8500.
* On this platform, this is interfaced with SSP0 controller
* which is a ARM primecell pl022.
*
* At the moment the module just exports read/write features.
* Interrupt management to be added - TODO.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/mfd/ab4500.h>
/* just required if probe fails, we need to
* unregister the device
*/
static struct spi_driver ab4500_driver;
/*
* This funtion writes to any AB4500 registers using
* SPI protocol & before it writes it packs the data
* in the below 24 bit frame format
*
* *|------------------------------------|
* *| 23|22...18|17.......10|9|8|7......0|
* *| r/w bank adr data |
* * ------------------------------------
*
* This function shouldn't be called from interrupt
* context
*/
int ab4500_write(struct ab4500 *ab4500, unsigned char block,
unsigned long addr, unsigned char data)
{
struct spi_transfer xfer;
struct spi_message msg;
int err;
unsigned long spi_data =
block << 18 | addr << 10 | data;
mutex_lock(&ab4500->lock);
ab4500->tx_buf[0] = spi_data;
ab4500->rx_buf[0] = 0;
xfer.tx_buf = ab4500->tx_buf;
xfer.rx_buf = NULL;
xfer.len = sizeof(unsigned long);
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
err = spi_sync(ab4500->spi, &msg);
mutex_unlock(&ab4500->lock);
return err;
}
EXPORT_SYMBOL(ab4500_write);
int ab4500_read(struct ab4500 *ab4500, unsigned char block,
unsigned long addr)
{
struct spi_transfer xfer;
struct spi_message msg;
unsigned long spi_data =
1 << 23 | block << 18 | addr << 10;
mutex_lock(&ab4500->lock);
ab4500->tx_buf[0] = spi_data;
ab4500->rx_buf[0] = 0;
xfer.tx_buf = ab4500->tx_buf;
xfer.rx_buf = ab4500->rx_buf;
xfer.len = sizeof(unsigned long);
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
spi_sync(ab4500->spi, &msg);
mutex_unlock(&ab4500->lock);
return ab4500->rx_buf[0];
}
EXPORT_SYMBOL(ab4500_read);
/* ref: ab3100 core */
#define AB4500_DEVICE(devname, devid) \
static struct platform_device ab4500_##devname##_device = { \
.name = devid, \
.id = -1, \
}
/* list of childern devices of ab4500 - all are
* not populated here - TODO
*/
AB4500_DEVICE(charger, "ab4500-charger");
AB4500_DEVICE(audio, "ab4500-audio");
AB4500_DEVICE(usb, "ab4500-usb");
AB4500_DEVICE(tvout, "ab4500-tvout");
AB4500_DEVICE(sim, "ab4500-sim");
AB4500_DEVICE(gpadc, "ab4500-gpadc");
AB4500_DEVICE(clkmgt, "ab4500-clkmgt");
AB4500_DEVICE(misc, "ab4500-misc");
static struct platform_device *ab4500_platform_devs[] = {
&ab4500_charger_device,
&ab4500_audio_device,
&ab4500_usb_device,
&ab4500_tvout_device,
&ab4500_sim_device,
&ab4500_gpadc_device,
&ab4500_clkmgt_device,
&ab4500_misc_device,
};
static int __init ab4500_probe(struct spi_device *spi)
{
struct ab4500 *ab4500;
unsigned char revision;
int err = 0;
int i;
ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL);
if (!ab4500) {
dev_err(&spi->dev, "could not allocate AB4500\n");
err = -ENOMEM;
goto not_detect;
}
ab4500->spi = spi;
spi_set_drvdata(spi, ab4500);
mutex_init(&ab4500->lock);
/* read the revision register */
revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG);
/* revision id 0x0 is for early drop, 0x10 is for cut1.0 */
if (revision == 0x0 || revision == 0x10)
dev_info(&spi->dev, "Detected chip: %s, revision = %x\n",
ab4500_driver.driver.name, revision);
else {
dev_err(&spi->dev, "unknown chip: 0x%x\n", revision);
goto not_detect;
}
for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) {
ab4500_platform_devs[i]->dev.parent =
&spi->dev;
platform_set_drvdata(ab4500_platform_devs[i], ab4500);
}
/* register the ab4500 platform devices */
platform_add_devices(ab4500_platform_devs,
ARRAY_SIZE(ab4500_platform_devs));
return err;
not_detect:
spi_unregister_driver(&ab4500_driver);
kfree(ab4500);
return err;
}
static int __devexit ab4500_remove(struct spi_device *spi)
{
struct ab4500 *ab4500 =
spi_get_drvdata(spi);
kfree(ab4500);
return 0;
}
static struct spi_driver ab4500_driver = {
.driver = {
.name = "ab4500",
.owner = THIS_MODULE,
},
.probe = ab4500_probe,
.remove = __devexit_p(ab4500_remove)
};
static int __devinit ab4500_init(void)
{
return spi_register_driver(&ab4500_driver);
}
static void __exit ab4500_exit(void)
{
spi_unregister_driver(&ab4500_driver);
}
subsys_initcall(ab4500_init);
module_exit(ab4500_exit);
MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
MODULE_DESCRIPTION("AB4500 core driver");
MODULE_LICENSE("GPL");

379
drivers/mfd/adp5520.c Normal file
View File

@ -0,0 +1,379 @@
/*
* Base driver for Analog Devices ADP5520/ADP5501 MFD PMICs
* LCD Backlight: drivers/video/backlight/adp5520_bl
* LEDs : drivers/led/leds-adp5520
* GPIO : drivers/gpio/adp5520-gpio (ADP5520 only)
* Keys : drivers/input/keyboard/adp5520-keys (ADP5520 only)
*
* Copyright 2009 Analog Devices Inc.
*
* Derived from da903x:
* Copyright (C) 2008 Compulab, Ltd.
* Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
* Eric Miao <eric.miao@marvell.com>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mfd/adp5520.h>
struct adp5520_chip {
struct i2c_client *client;
struct device *dev;
struct mutex lock;
struct blocking_notifier_head notifier_list;
int irq;
unsigned long id;
};
static int __adp5520_read(struct i2c_client *client,
int reg, uint8_t *val)
{
int ret;
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0) {
dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
return ret;
}
*val = (uint8_t)ret;
return 0;
}
static int __adp5520_write(struct i2c_client *client,
int reg, uint8_t val)
{
int ret;
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret < 0) {
dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
val, reg);
return ret;
}
return 0;
}
static int __adp5520_ack_bits(struct i2c_client *client, int reg,
uint8_t bit_mask)
{
struct adp5520_chip *chip = i2c_get_clientdata(client);
uint8_t reg_val;
int ret;
mutex_lock(&chip->lock);
ret = __adp5520_read(client, reg, &reg_val);
if (!ret) {
reg_val |= bit_mask;
ret = __adp5520_write(client, reg, reg_val);
}
mutex_unlock(&chip->lock);
return ret;
}
int adp5520_write(struct device *dev, int reg, uint8_t val)
{
return __adp5520_write(to_i2c_client(dev), reg, val);
}
EXPORT_SYMBOL_GPL(adp5520_write);
int adp5520_read(struct device *dev, int reg, uint8_t *val)
{
return __adp5520_read(to_i2c_client(dev), reg, val);
}
EXPORT_SYMBOL_GPL(adp5520_read);
int adp5520_set_bits(struct device *dev, int reg, uint8_t bit_mask)
{
struct adp5520_chip *chip = dev_get_drvdata(dev);
uint8_t reg_val;
int ret;
mutex_lock(&chip->lock);
ret = __adp5520_read(chip->client, reg, &reg_val);
if (!ret && ((reg_val & bit_mask) == 0)) {
reg_val |= bit_mask;
ret = __adp5520_write(chip->client, reg, reg_val);
}
mutex_unlock(&chip->lock);
return ret;
}
EXPORT_SYMBOL_GPL(adp5520_set_bits);
int adp5520_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
{
struct adp5520_chip *chip = dev_get_drvdata(dev);
uint8_t reg_val;
int ret;
mutex_lock(&chip->lock);
ret = __adp5520_read(chip->client, reg, &reg_val);
if (!ret && (reg_val & bit_mask)) {
reg_val &= ~bit_mask;
ret = __adp5520_write(chip->client, reg, reg_val);
}
mutex_unlock(&chip->lock);
return ret;
}
EXPORT_SYMBOL_GPL(adp5520_clr_bits);
int adp5520_register_notifier(struct device *dev, struct notifier_block *nb,
unsigned int events)
{
struct adp5520_chip *chip = dev_get_drvdata(dev);
if (chip->irq) {
adp5520_set_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
return blocking_notifier_chain_register(&chip->notifier_list,
nb);
}
return -ENODEV;
}
EXPORT_SYMBOL_GPL(adp5520_register_notifier);
int adp5520_unregister_notifier(struct device *dev, struct notifier_block *nb,
unsigned int events)
{
struct adp5520_chip *chip = dev_get_drvdata(dev);
adp5520_clr_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
}
EXPORT_SYMBOL_GPL(adp5520_unregister_notifier);
static irqreturn_t adp5520_irq_thread(int irq, void *data)
{
struct adp5520_chip *chip = data;
unsigned int events;
uint8_t reg_val;
int ret;
ret = __adp5520_read(chip->client, ADP5520_MODE_STATUS, &reg_val);
if (ret)
goto out;
events = reg_val & (ADP5520_OVP_INT | ADP5520_CMPR_INT |
ADP5520_GPI_INT | ADP5520_KR_INT | ADP5520_KP_INT);
blocking_notifier_call_chain(&chip->notifier_list, events, NULL);
/* ACK, Sticky bits are W1C */
__adp5520_ack_bits(chip->client, ADP5520_MODE_STATUS, events);
out:
return IRQ_HANDLED;
}
static int __remove_subdev(struct device *dev, void *unused)
{
platform_device_unregister(to_platform_device(dev));
return 0;
}
static int adp5520_remove_subdevs(struct adp5520_chip *chip)
{
return device_for_each_child(chip->dev, NULL, __remove_subdev);
}
static int __devinit adp5520_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adp5520_platform_data *pdata = client->dev.platform_data;
struct platform_device *pdev;
struct adp5520_chip *chip;
int ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "SMBUS Word Data not Supported\n");
return -EIO;
}
if (pdata == NULL) {
dev_err(&client->dev, "missing platform data\n");
return -ENODEV;
}
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
i2c_set_clientdata(client, chip);
chip->client = client;
chip->dev = &client->dev;
chip->irq = client->irq;
chip->id = id->driver_data;
mutex_init(&chip->lock);
if (chip->irq) {
BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
ret = request_threaded_irq(chip->irq, NULL, adp5520_irq_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"adp5520", chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
chip->irq);
goto out_free_chip;
}
}
ret = adp5520_write(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
if (ret) {
dev_err(&client->dev, "failed to write\n");
goto out_free_irq;
}
if (pdata->keys) {
pdev = platform_device_register_data(chip->dev, "adp5520-keys",
chip->id, pdata->keys, sizeof(*pdata->keys));
if (IS_ERR(pdev)) {
ret = PTR_ERR(pdev);
goto out_remove_subdevs;
}
}
if (pdata->gpio) {
pdev = platform_device_register_data(chip->dev, "adp5520-gpio",
chip->id, pdata->gpio, sizeof(*pdata->gpio));
if (IS_ERR(pdev)) {
ret = PTR_ERR(pdev);
goto out_remove_subdevs;
}
}
if (pdata->leds) {
pdev = platform_device_register_data(chip->dev, "adp5520-led",
chip->id, pdata->leds, sizeof(*pdata->leds));
if (IS_ERR(pdev)) {
ret = PTR_ERR(pdev);
goto out_remove_subdevs;
}
}
if (pdata->backlight) {
pdev = platform_device_register_data(chip->dev,
"adp5520-backlight",
chip->id,
pdata->backlight,
sizeof(*pdata->backlight));
if (IS_ERR(pdev)) {
ret = PTR_ERR(pdev);
goto out_remove_subdevs;
}
}
return 0;
out_remove_subdevs:
adp5520_remove_subdevs(chip);
out_free_irq:
if (chip->irq)
free_irq(chip->irq, chip);
out_free_chip:
i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
static int __devexit adp5520_remove(struct i2c_client *client)
{
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
if (chip->irq)
free_irq(chip->irq, chip);
adp5520_remove_subdevs(chip);
adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
i2c_set_clientdata(client, NULL);
kfree(chip);
return 0;
}
#ifdef CONFIG_PM
static int adp5520_suspend(struct i2c_client *client,
pm_message_t state)
{
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
return 0;
}
static int adp5520_resume(struct i2c_client *client)
{
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
return 0;
}
#else
#define adp5520_suspend NULL
#define adp5520_resume NULL
#endif
static const struct i2c_device_id adp5520_id[] = {
{ "pmic-adp5520", ID_ADP5520 },
{ "pmic-adp5501", ID_ADP5501 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adp5520_id);
static struct i2c_driver adp5520_driver = {
.driver = {
.name = "adp5520",
.owner = THIS_MODULE,
},
.probe = adp5520_probe,
.remove = __devexit_p(adp5520_remove),
.suspend = adp5520_suspend,
.resume = adp5520_resume,
.id_table = adp5520_id,
};
static int __init adp5520_init(void)
{
return i2c_add_driver(&adp5520_driver);
}
module_init(adp5520_init);
static void __exit adp5520_exit(void)
{
i2c_del_driver(&adp5520_driver);
}
module_exit(adp5520_exit);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver");
MODULE_LICENSE("GPL");

View File

@ -908,7 +908,7 @@ static int __init asic3_probe(struct platform_device *pdev)
return ret;
}
static int asic3_remove(struct platform_device *pdev)
static int __devexit asic3_remove(struct platform_device *pdev)
{
int ret;
struct asic3 *asic = platform_get_drvdata(pdev);

View File

@ -387,7 +387,6 @@ static int __devinit pcap_add_subdev(struct pcap_chip *pcap,
pdev = platform_device_alloc(subdev->name, subdev->id);
pdev->dev.parent = &pcap->spi->dev;
pdev->dev.platform_data = subdev->platform_data;
platform_set_drvdata(pdev, pcap);
return platform_device_add(pdev);
}

View File

@ -1,286 +1,549 @@
/*
* Copyright 2009 Pengutronix
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
*
* loosely based on an earlier driver that has
* 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.
* 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/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>
#include <linux/spi/spi.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mc13783-private.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
#define MC13783_IRQSTAT0 0
#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
#define MC13783_IRQSTAT0_ADCBISDONEI (1 << 1)
#define MC13783_IRQSTAT0_TSI (1 << 2)
#define MC13783_IRQSTAT0_WHIGHI (1 << 3)
#define MC13783_IRQSTAT0_WLOWI (1 << 4)
#define MC13783_IRQSTAT0_CHGDETI (1 << 6)
#define MC13783_IRQSTAT0_CHGOVI (1 << 7)
#define MC13783_IRQSTAT0_CHGREVI (1 << 8)
#define MC13783_IRQSTAT0_CHGSHORTI (1 << 9)
#define MC13783_IRQSTAT0_CCCVI (1 << 10)
#define MC13783_IRQSTAT0_CHGCURRI (1 << 11)
#define MC13783_IRQSTAT0_BPONI (1 << 12)
#define MC13783_IRQSTAT0_LOBATLI (1 << 13)
#define MC13783_IRQSTAT0_LOBATHI (1 << 14)
#define MC13783_IRQSTAT0_UDPI (1 << 15)
#define MC13783_IRQSTAT0_USBI (1 << 16)
#define MC13783_IRQSTAT0_IDI (1 << 19)
#define MC13783_IRQSTAT0_SE1I (1 << 21)
#define MC13783_IRQSTAT0_CKDETI (1 << 22)
#define MC13783_IRQSTAT0_UDMI (1 << 23)
static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
#define MC13783_IRQMASK0 1
#define MC13783_IRQMASK0_ADCDONEM MC13783_IRQSTAT0_ADCDONEI
#define MC13783_IRQMASK0_ADCBISDONEM MC13783_IRQSTAT0_ADCBISDONEI
#define MC13783_IRQMASK0_TSM MC13783_IRQSTAT0_TSI
#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI
#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI
#define MC13783_IRQMASK0_CHGDETM MC13783_IRQSTAT0_CHGDETI
#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI
#define MC13783_IRQMASK0_CHGREVM MC13783_IRQSTAT0_CHGREVI
#define MC13783_IRQMASK0_CHGSHORTM MC13783_IRQSTAT0_CHGSHORTI
#define MC13783_IRQMASK0_CCCVM MC13783_IRQSTAT0_CCCVI
#define MC13783_IRQMASK0_CHGCURRM MC13783_IRQSTAT0_CHGCURRI
#define MC13783_IRQMASK0_BPONM MC13783_IRQSTAT0_BPONI
#define MC13783_IRQMASK0_LOBATLM MC13783_IRQSTAT0_LOBATLI
#define MC13783_IRQMASK0_LOBATHM MC13783_IRQSTAT0_LOBATHI
#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI
#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI
#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI
#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I
#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI
#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI
#define MC13783_IRQSTAT1 3
#define MC13783_IRQSTAT1_1HZI (1 << 0)
#define MC13783_IRQSTAT1_TODAI (1 << 1)
#define MC13783_IRQSTAT1_ONOFD1I (1 << 3)
#define MC13783_IRQSTAT1_ONOFD2I (1 << 4)
#define MC13783_IRQSTAT1_ONOFD3I (1 << 5)
#define MC13783_IRQSTAT1_SYSRSTI (1 << 6)
#define MC13783_IRQSTAT1_RTCRSTI (1 << 7)
#define MC13783_IRQSTAT1_PCI (1 << 8)
#define MC13783_IRQSTAT1_WARMI (1 << 9)
#define MC13783_IRQSTAT1_MEMHLDI (1 << 10)
#define MC13783_IRQSTAT1_PWRRDYI (1 << 11)
#define MC13783_IRQSTAT1_THWARNLI (1 << 12)
#define MC13783_IRQSTAT1_THWARNHI (1 << 13)
#define MC13783_IRQSTAT1_CLKI (1 << 14)
#define MC13783_IRQSTAT1_SEMAFI (1 << 15)
#define MC13783_IRQSTAT1_MC2BI (1 << 17)
#define MC13783_IRQSTAT1_HSDETI (1 << 18)
#define MC13783_IRQSTAT1_HSLI (1 << 19)
#define MC13783_IRQSTAT1_ALSPTHI (1 << 20)
#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21)
#define MC13783_IRQMASK1 4
#define MC13783_IRQMASK1_1HZM MC13783_IRQSTAT1_1HZI
#define MC13783_IRQMASK1_TODAM MC13783_IRQSTAT1_TODAI
#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I
#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I
#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I
#define MC13783_IRQMASK1_SYSRSTM MC13783_IRQSTAT1_SYSRSTI
#define MC13783_IRQMASK1_RTCRSTM MC13783_IRQSTAT1_RTCRSTI
#define MC13783_IRQMASK1_PCM MC13783_IRQSTAT1_PCI
#define MC13783_IRQMASK1_WARMM MC13783_IRQSTAT1_WARMI
#define MC13783_IRQMASK1_MEMHLDM MC13783_IRQSTAT1_MEMHLDI
#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI
#define MC13783_IRQMASK1_THWARNLM MC13783_IRQSTAT1_THWARNLI
#define MC13783_IRQMASK1_THWARNHM MC13783_IRQSTAT1_THWARNHI
#define MC13783_IRQMASK1_CLKM MC13783_IRQSTAT1_CLKI
#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI
#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI
#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI
#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI
#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI
#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI
#define MC13783_ADC1 44
#define MC13783_ADC1_ADEN (1 << 0)
#define MC13783_ADC1_RAND (1 << 1)
#define MC13783_ADC1_ADSEL (1 << 3)
#define MC13783_ADC1_ASC (1 << 20)
#define MC13783_ADC1_ADTRIGIGN (1 << 21)
#define MC13783_NUMREGS 0x3f
void mc13783_lock(struct mc13783 *mc13783)
{
struct spi_transfer t = {
.tx_buf = (const void *)buf,
.rx_buf = buf,
.len = len,
.cs_change = 0,
.delay_usecs = 0,
};
if (!mutex_trylock(&mc13783->lock)) {
dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
__func__, __builtin_return_address(0));
mutex_lock(&mc13783->lock);
}
dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
__func__, __builtin_return_address(0));
}
EXPORT_SYMBOL(mc13783_lock);
void mc13783_unlock(struct mc13783 *mc13783)
{
dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
__func__, __builtin_return_address(0));
mutex_unlock(&mc13783->lock);
}
EXPORT_SYMBOL(mc13783_unlock);
#define MC13783_REGOFFSET_SHIFT 25
int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
{
struct spi_transfer t;
struct spi_message m;
int ret;
BUG_ON(!mutex_is_locked(&mc13783->lock));
if (offset > MC13783_NUMREGS)
return -EINVAL;
*val = offset << MC13783_REGOFFSET_SHIFT;
memset(&t, 0, sizeof(t));
t.tx_buf = val;
t.rx_buf = val;
t.len = sizeof(u32);
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;
ret = spi_sync(mc13783->spidev, &m);
/* error in message.status implies error return from spi_sync */
BUG_ON(!ret && m.status);
if (ret)
return ret;
*val &= 0xffffff;
dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
return 0;
}
EXPORT_SYMBOL(mc13783_reg_read);
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 mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
{
u32 buf;
struct spi_transfer t;
struct spi_message m;
int ret;
mutex_lock(&mc13783->io_lock);
ret = mc13783_read(mc13783, reg_num, reg_val);
mutex_unlock(&mc13783->io_lock);
BUG_ON(!mutex_is_locked(&mc13783->lock));
return ret;
}
EXPORT_SYMBOL_GPL(mc13783_reg_read);
dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
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)
if (offset > MC13783_NUMREGS || val > 0xffffff)
return -EINVAL;
if (WARN_ON(mc13783->irq_handler[irq].handler))
buf = 1 << 31 | offset << MC13783_REGOFFSET_SHIFT | val;
memset(&t, 0, sizeof(t));
t.tx_buf = &buf;
t.rx_buf = &buf;
t.len = sizeof(u32);
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(mc13783->spidev, &m);
BUG_ON(!ret && m.status);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL(mc13783_reg_write);
int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
u32 mask, u32 val)
{
int ret;
u32 valread;
BUG_ON(val & ~mask);
ret = mc13783_reg_read(mc13783, offset, &valread);
if (ret)
return ret;
valread = (valread & ~mask) | val;
return mc13783_reg_write(mc13783, offset, valread);
}
EXPORT_SYMBOL(mc13783_reg_rmw);
int mc13783_mask(struct mc13783 *mc13783, int irq)
{
int ret;
unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
u32 mask;
if (irq < 0 || irq >= MC13783_NUM_IRQ)
return -EINVAL;
ret = mc13783_reg_read(mc13783, offmask, &mask);
if (ret)
return ret;
if (mask & irqbit)
/* already masked */
return 0;
return mc13783_reg_write(mc13783, offmask, mask | irqbit);
}
EXPORT_SYMBOL(mc13783_mask);
int mc13783_unmask(struct mc13783 *mc13783, int irq)
{
int ret;
unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
u32 mask;
if (irq < 0 || irq >= MC13783_NUM_IRQ)
return -EINVAL;
ret = mc13783_reg_read(mc13783, offmask, &mask);
if (ret)
return ret;
if (!(mask & irqbit))
/* already unmasked */
return 0;
return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
}
EXPORT_SYMBOL(mc13783_unmask);
int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
irq_handler_t handler, const char *name, void *dev)
{
BUG_ON(!mutex_is_locked(&mc13783->lock));
BUG_ON(!handler);
if (irq < 0 || irq >= MC13783_NUM_IRQ)
return -EINVAL;
if (mc13783->irqhandler[irq])
return -EBUSY;
mutex_lock(&mc13783->io_lock);
mc13783->irq_handler[irq].handler = handler;
mc13783->irq_handler[irq].data = data;
mutex_unlock(&mc13783->io_lock);
mc13783->irqhandler[irq] = handler;
mc13783->irqdata[irq] = dev;
return 0;
}
EXPORT_SYMBOL_GPL(mc13783_register_irq);
EXPORT_SYMBOL(mc13783_irq_request_nounmask);
int mc13783_free_irq(struct mc13783 *mc13783, int irq)
int mc13783_irq_request(struct mc13783 *mc13783, int irq,
irq_handler_t handler, const char *name, void *dev)
{
if (irq < 0 || irq > MC13783_NUM_IRQ)
int ret;
ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
if (ret)
return ret;
ret = mc13783_unmask(mc13783, irq);
if (ret) {
mc13783->irqhandler[irq] = NULL;
mc13783->irqdata[irq] = NULL;
return ret;
}
return 0;
}
EXPORT_SYMBOL(mc13783_irq_request);
int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
{
int ret;
BUG_ON(!mutex_is_locked(&mc13783->lock));
if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
mc13783->irqdata[irq] != dev)
return -EINVAL;
mutex_lock(&mc13783->io_lock);
mc13783->irq_handler[irq].handler = NULL;
mutex_unlock(&mc13783->io_lock);
ret = mc13783_mask(mc13783, irq);
if (ret)
return ret;
mc13783->irqhandler[irq] = NULL;
mc13783->irqdata[irq] = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(mc13783_free_irq);
EXPORT_SYMBOL(mc13783_irq_free);
static void mc13783_irq_work(struct work_struct *work)
static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
{
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);
return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
}
static irqreturn_t mc13783_interrupt(int irq, void *dev_id)
int mc13783_ackirq(struct mc13783 *mc13783, int irq)
{
struct mc13783 *mc13783 = dev_id;
unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
disable_irq_nosync(irq);
BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
return mc13783_reg_write(mc13783, offstat, val);
}
EXPORT_SYMBOL(mc13783_ackirq);
/*
* returns: number of handled irqs or negative error
* locking: holds mc13783->lock
*/
static int mc13783_irq_handle(struct mc13783 *mc13783,
unsigned int offstat, unsigned int offmask, int baseirq)
{
u32 stat, mask;
int ret = mc13783_reg_read(mc13783, offstat, &stat);
int num_handled = 0;
if (ret)
return ret;
ret = mc13783_reg_read(mc13783, offmask, &mask);
if (ret)
return ret;
while (stat & ~mask) {
int irq = __ffs(stat & ~mask);
stat &= ~(1 << irq);
if (likely(mc13783->irqhandler[baseirq + irq])) {
irqreturn_t handled;
handled = mc13783_irqhandler(mc13783, baseirq + irq);
if (handled == IRQ_HANDLED)
num_handled++;
} else {
dev_err(&mc13783->spidev->dev,
"BUG: irq %u but no handler\n",
baseirq + irq);
mask |= 1 << irq;
ret = mc13783_reg_write(mc13783, offmask, mask);
}
}
return num_handled;
}
static irqreturn_t mc13783_irq_thread(int irq, void *data)
{
struct mc13783 *mc13783 = data;
irqreturn_t ret;
int handled = 0;
mc13783_lock(mc13783);
ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
if (ret > 0)
handled = 1;
ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
MC13783_IRQMASK1, MC13783_IRQ_1HZ);
if (ret > 0)
handled = 1;
mc13783_unlock(mc13783);
return IRQ_RETVAL(handled);
}
#define MC13783_ADC1_CHAN0_SHIFT 5
#define MC13783_ADC1_CHAN1_SHIFT 8
struct mc13783_adcdone_data {
struct mc13783 *mc13783;
struct completion done;
};
static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
{
struct mc13783_adcdone_data *adcdone_data = data;
mc13783_ackirq(adcdone_data->mc13783, irq);
complete_all(&adcdone_data->done);
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);
}
#define MC13783_ADC_WORKING (1 << 16)
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;
u32 adc0, adc1, old_adc0;
int i, ret;
struct mc13783_adcdone_data adcdone_data = {
.mc13783 = mc13783,
};
init_completion(&adcdone_data.done);
mutex_lock(&mc13783->adc_conv_lock);
dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
/* 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;
mc13783_lock(mc13783);
if (mc13783->flags & MC13783_ADC_WORKING) {
ret = -EBUSY;
goto out;
}
mc13783->flags |= MC13783_ADC_WORKING;
mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
/* setup channel number */
if (channel > 7)
reg_adc1 |= MC13783_ADC1_ADSEL;
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;
adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
MC13783_ADC0_TSMOD1;
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;
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
adc1 |= MC13783_ADC1_RAND;
break;
case MC13783_ADC_MODE_MULT_CHAN:
reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
break;
default:
mc13783_unlock(mc13783);
return -EINVAL;
}
mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
mc13783_handler_adcdone, __func__, &adcdone_data);
mc13783_ackirq(mc13783, MC13783_IRQ_ADCDONE);
wait_for_completion_interruptible(&mc13783->adc_done);
mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
for (i = 0; i < 4; i++)
mc13783_reg_read(mc13783, MC13783_REG_ADC_2, &sample[i]);
mc13783_unlock(mc13783);
if (mc13783->ts_active)
mc13783_adc_set_ts_irq_mode(mc13783);
ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
mutex_unlock(&mc13783->adc_conv_lock);
if (!ret)
ret = -ETIMEDOUT;
return 0;
mc13783_lock(mc13783);
mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
if (ret > 0)
for (i = 0; i < 4; ++i) {
ret = mc13783_reg_read(mc13783,
MC13783_REG_ADC_2, &sample[i]);
if (ret)
break;
}
if (mode == MC13783_ADC_MODE_TS)
/* restore TSMOD */
mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
mc13783->flags &= ~MC13783_ADC_WORKING;
out:
mc13783_unlock(mc13783);
return ret;
}
EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
const char *name, void *pdata, size_t pdata_size)
{
mc13783->ts_active = status;
struct mfd_cell cell = {
.name = name,
.platform_data = pdata,
.data_size = pdata_size,
};
return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
}
static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
{
return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
}
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);
mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
rev1 = (rev_id & 0x018) >> 3;
rev2 = (rev_id & 0x007);
@ -292,38 +555,24 @@ static int mc13783_check_revision(struct mc13783 *mc13783)
rev1 = 3;
if (rev1 == 0 || icid != 2) {
dev_err(mc13783->dev, "No MC13783 detected.\n");
dev_err(&mc13783->spidev->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);
dev_info(&mc13783->spidev->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)
static int mc13783_probe(struct spi_device *spi)
{
struct mc13783 *mc13783;
struct mc13783_platform_data *pdata = spi->dev.platform_data;
struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
int ret;
mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
if (!mc13783)
return -ENOMEM;
@ -332,96 +581,104 @@ static int __devinit mc13783_probe(struct spi_device *spi)
spi->bits_per_word = 32;
spi_setup(spi);
mc13783->spi_device = spi;
mc13783->dev = &spi->dev;
mc13783->irq = spi->irq;
mc13783->spidev = spi;
INIT_WORK(&mc13783->work, mc13783_irq_work);
mutex_init(&mc13783->io_lock);
mutex_init(&mc13783->adc_conv_lock);
init_completion(&mc13783->adc_done);
mutex_init(&mc13783->lock);
mc13783_lock(mc13783);
ret = mc13783_check_revision(mc13783);
if (ret)
goto err_revision;
/* mask all irqs */
ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
if (ret)
goto err_mask;
ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
if (ret)
goto err_mask;
ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
if (ret) {
err_mask:
err_revision:
mutex_unlock(&mc13783->lock);
dev_set_drvdata(&spi->dev, NULL);
kfree(mc13783);
return ret;
}
/* This should go away (BEGIN) */
if (pdata) {
mc13783->flags = pdata->flags;
mc13783->regulators = pdata->regulators;
mc13783->num_regulators = pdata->num_regulators;
}
/* This should go away (END) */
if (mc13783_check_revision(mc13783)) {
ret = -ENODEV;
goto err_out;
if (pdata->flags & MC13783_USE_ADC)
mc13783_add_subdevice(mc13783, "mc13783-adc");
if (pdata->flags & MC13783_USE_CODEC)
mc13783_add_subdevice(mc13783, "mc13783-codec");
if (pdata->flags & MC13783_USE_REGULATOR) {
struct mc13783_regulator_platform_data regulator_pdata = {
.num_regulators = pdata->num_regulators,
.regulators = pdata->regulators,
};
mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
&regulator_pdata, sizeof(regulator_pdata));
}
/* 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);
if (pdata->flags & MC13783_USE_RTC)
mc13783_add_subdevice(mc13783, "mc13783-rtc");
/* unmask adcdone interrupts */
mc13783_set_bits(mc13783, MC13783_REG_INTERRUPT_MASK_0,
MC13783_INT_MASK_ADCDONEM, 0);
if (pdata->flags & MC13783_USE_TOUCHSCREEN)
mc13783_add_subdevice(mc13783, "mc13783-ts");
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");
mc13783_unlock(mc13783);
return 0;
err_out:
kfree(mc13783);
return ret;
}
static int __devexit mc13783_remove(struct spi_device *spi)
{
struct mc13783 *mc13783;
struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
mc13783 = dev_get_drvdata(&spi->dev);
free_irq(mc13783->irq, mc13783);
free_irq(mc13783->spidev->irq, mc13783);
mfd_remove_devices(&spi->dev);
return 0;
}
static struct spi_driver pmic_driver = {
static struct spi_driver mc13783_driver = {
.driver = {
.name = "mc13783",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.name = "mc13783",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = mc13783_probe,
.remove = __devexit_p(mc13783_remove),
};
static int __init pmic_init(void)
static int __init mc13783_init(void)
{
return spi_register_driver(&pmic_driver);
return spi_register_driver(&mc13783_driver);
}
subsys_initcall(pmic_init);
subsys_initcall(mc13783_init);
static void __exit pmic_exit(void)
static void __exit mc13783_exit(void)
{
spi_unregister_driver(&pmic_driver);
spi_unregister_driver(&mc13783_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");
module_exit(mc13783_exit);
MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
MODULE_LICENSE("GPL v2");

View File

@ -209,17 +209,16 @@ static void pcf50633_adc_irq(int irq, void *data)
static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
{
struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
struct pcf50633_adc *adc;
adc = kzalloc(sizeof(*adc), GFP_KERNEL);
if (!adc)
return -ENOMEM;
adc->pcf = pdata->pcf;
adc->pcf = dev_to_pcf50633(pdev->dev.parent);
platform_set_drvdata(pdev, adc);
pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ADCRDY,
pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,
pcf50633_adc_irq, adc);
mutex_init(&adc->queue_mutex);

View File

@ -290,7 +290,7 @@ static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
{
dev_info(pcf->dev, "Masking IRQ %d\n", irq);
dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
return __pcf50633_irq_mask_set(pcf, irq, 1);
}
@ -298,7 +298,7 @@ EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
{
dev_info(pcf->dev, "Unmasking IRQ %d\n", irq);
dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
return __pcf50633_irq_mask_set(pcf, irq, 0);
}
@ -345,6 +345,9 @@ static void pcf50633_irq_worker(struct work_struct *work)
goto out;
}
/* defeat 8s death from lowsys on A5 */
pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04);
/* We immediately read the usb and adapter status. We thus make sure
* only of USBINS/USBREM IRQ handlers are called */
if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
@ -453,7 +456,6 @@ static void
pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
struct platform_device **pdev)
{
struct pcf50633_subdev_pdata *subdev_pdata;
int ret;
*pdev = platform_device_alloc(name, -1);
@ -462,15 +464,6 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
return;
}
subdev_pdata = kmalloc(sizeof(*subdev_pdata), GFP_KERNEL);
if (!subdev_pdata) {
dev_err(pcf->dev, "Error allocating subdev pdata\n");
platform_device_put(*pdev);
}
subdev_pdata->pcf = pcf;
platform_device_add_data(*pdev, subdev_pdata, sizeof(*subdev_pdata));
(*pdev)->dev.parent = pcf->dev;
ret = platform_device_add(*pdev);
@ -482,13 +475,13 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
}
#ifdef CONFIG_PM
static int pcf50633_suspend(struct device *dev, pm_message_t state)
static int pcf50633_suspend(struct i2c_client *client, pm_message_t state)
{
struct pcf50633 *pcf;
int ret = 0, i;
u8 res[5];
pcf = dev_get_drvdata(dev);
pcf = i2c_get_clientdata(client);
/* Make sure our interrupt handlers are not called
* henceforth */
@ -523,12 +516,12 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
return ret;
}
static int pcf50633_resume(struct device *dev)
static int pcf50633_resume(struct i2c_client *client)
{
struct pcf50633 *pcf;
int ret;
pcf = dev_get_drvdata(dev);
pcf = i2c_get_clientdata(client);
/* Write the saved mask registers */
ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
@ -560,9 +553,14 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
{
struct pcf50633 *pcf;
struct pcf50633_platform_data *pdata = client->dev.platform_data;
int i, ret = 0;
int i, ret;
int version, variant;
if (!client->irq) {
dev_err(&client->dev, "Missing IRQ\n");
return -ENOENT;
}
pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
if (!pcf)
return -ENOMEM;
@ -577,6 +575,12 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
pcf->irq = client->irq;
pcf->work_queue = create_singlethread_workqueue("pcf50633");
if (!pcf->work_queue) {
dev_err(&client->dev, "Failed to alloc workqueue\n");
ret = -ENOMEM;
goto err_free;
}
INIT_WORK(&pcf->irq_work, pcf50633_irq_worker);
version = pcf50633_reg_read(pcf, 0);
@ -584,7 +588,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
if (version < 0 || variant < 0) {
dev_err(pcf->dev, "Unable to probe pcf50633\n");
ret = -ENODEV;
goto err;
goto err_destroy_workqueue;
}
dev_info(pcf->dev, "Probed device version %d variant %d\n",
@ -598,6 +602,14 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
ret = request_irq(client->irq, pcf50633_irq,
IRQF_TRIGGER_LOW, "pcf50633", pcf);
if (ret) {
dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
goto err_destroy_workqueue;
}
/* Create sub devices */
pcf50633_client_dev_register(pcf, "pcf50633-input",
&pcf->input_pdev);
@ -613,31 +625,18 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
pdev = platform_device_alloc("pcf50633-regltr", i);
if (!pdev) {
dev_err(pcf->dev, "Cannot create regulator\n");
dev_err(pcf->dev, "Cannot create regulator %d\n", i);
continue;
}
pdev->dev.parent = pcf->dev;
pdev->dev.platform_data = &pdata->reg_init_data[i];
dev_set_drvdata(&pdev->dev, pcf);
platform_device_add_data(pdev, &pdata->reg_init_data[i],
sizeof(pdata->reg_init_data[i]));
pcf->regulator_pdev[i] = pdev;
platform_device_add(pdev);
}
if (client->irq) {
ret = request_irq(client->irq, pcf50633_irq,
IRQF_TRIGGER_LOW, "pcf50633", pcf);
if (ret) {
dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
goto err;
}
} else {
dev_err(pcf->dev, "No IRQ configured\n");
goto err;
}
if (enable_irq_wake(client->irq) < 0)
dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
"in this hardware revision", client->irq);
@ -651,9 +650,12 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
return 0;
err:
err_destroy_workqueue:
destroy_workqueue(pcf->work_queue);
err_free:
i2c_set_clientdata(client, NULL);
kfree(pcf);
return ret;
}
@ -686,12 +688,12 @@ static struct i2c_device_id pcf50633_id_table[] = {
static struct i2c_driver pcf50633_driver = {
.driver = {
.name = "pcf50633",
.suspend = pcf50633_suspend,
.resume = pcf50633_resume,
},
.id_table = pcf50633_id_table,
.probe = pcf50633_probe,
.remove = __devexit_p(pcf50633_remove),
.suspend = pcf50633_suspend,
.resume = pcf50633_resume,
};
static int __init pcf50633_init(void)

View File

@ -637,7 +637,7 @@ static int tps65010_probe(struct i2c_client *client,
tps, DEBUG_FOPS);
/* optionally register GPIOs */
if (board && board->base > 0) {
if (board && board->base != 0) {
tps->outmask = board->outmask;
tps->chip.label = client->name;
@ -964,6 +964,34 @@ int tps65010_config_vregs1(unsigned value)
}
EXPORT_SYMBOL(tps65010_config_vregs1);
int tps65010_config_vdcdc2(unsigned value)
{
struct i2c_client *c;
int status;
if (!the_tps)
return -ENODEV;
c = the_tps->client;
mutex_lock(&the_tps->lock);
pr_debug("%s: vdcdc2 0x%02x\n", DRIVER_NAME,
i2c_smbus_read_byte_data(c, TPS_VDCDC2));
status = i2c_smbus_write_byte_data(c, TPS_VDCDC2, value);
if (status != 0)
printk(KERN_ERR "%s: Failed to write vdcdc2 register\n",
DRIVER_NAME);
else
pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
i2c_smbus_read_byte_data(c, TPS_VDCDC2));
mutex_unlock(&the_tps->lock);
return status;
}
EXPORT_SYMBOL(tps65010_config_vdcdc2);
/*-------------------------------------------------------------------------*/
/* tps65013_set_low_pwr parameter:
* mode: ON or OFF

View File

@ -1,5 +1,6 @@
/*
* twl4030_core.c - driver for TWL4030/TPS659x0 PM and audio CODEC devices
* twl_core.c - driver for TWL4030/TWL5030/TWL60X0/TPS659x0 PM
* and audio CODEC devices
*
* Copyright (C) 2005-2006 Texas Instruments, Inc.
*
@ -36,7 +37,7 @@
#include <linux/regulator/machine.h>
#include <linux/i2c.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
#include <plat/cpu.h>
@ -55,7 +56,7 @@
* (and associated registers).
*/
#define DRIVER_NAME "twl4030"
#define DRIVER_NAME "twl"
#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
@ -125,7 +126,7 @@
/* Last - for index max*/
#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG
#define TWL4030_NUM_SLAVES 4
#define TWL_NUM_SLAVES 4
#if defined(CONFIG_INPUT_TWL4030_PWRBUTTON) \
|| defined(CONFIG_INPUT_TWL4030_PWBUTTON_MODULE)
@ -134,6 +135,13 @@
#define twl_has_pwrbutton() false
#endif
#define SUB_CHIP_ID0 0
#define SUB_CHIP_ID1 1
#define SUB_CHIP_ID2 2
#define SUB_CHIP_ID3 3
#define TWL_MODULE_LAST TWL4030_MODULE_LAST
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
@ -158,6 +166,10 @@
#define TWL4030_BASEADD_PWMB 0x00F1
#define TWL4030_BASEADD_KEYPAD 0x00D2
#define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */
#define TWL5031_BASEADD_INTERRUPTS 0x00B9 /* Different than TWL4030's
one */
/* subchip/slave 3 - POWER ID */
#define TWL4030_BASEADD_BACKUP 0x0014
#define TWL4030_BASEADD_INT 0x002E
@ -169,6 +181,30 @@
/* Triton Core internal information (END) */
/* subchip/slave 0 0x48 - POWER */
#define TWL6030_BASEADD_RTC 0x0000
#define TWL6030_BASEADD_MEM 0x0017
#define TWL6030_BASEADD_PM_MASTER 0x001F
#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
#define TWL6030_BASEADD_PM_MISC 0x00E2
#define TWL6030_BASEADD_PM_PUPD 0x00F0
/* subchip/slave 1 0x49 - FEATURE */
#define TWL6030_BASEADD_USB 0x0000
#define TWL6030_BASEADD_GPADC_CTRL 0x002E
#define TWL6030_BASEADD_AUX 0x0090
#define TWL6030_BASEADD_PWM 0x00BA
#define TWL6030_BASEADD_GASGAUGE 0x00C0
#define TWL6030_BASEADD_PIH 0x00D0
#define TWL6030_BASEADD_CHARGER 0x00E0
/* subchip/slave 2 0x4A - DFT */
#define TWL6030_BASEADD_DIEID 0x00C0
/* subchip/slave 3 0x4B - AUDIO */
#define TWL6030_BASEADD_AUDIO 0x0000
#define TWL6030_BASEADD_RSV 0x0000
/* Few power values */
#define R_CFG_BOOT 0x05
#define R_PROTECT_KEY 0x0E
@ -183,19 +219,29 @@
#define HFCLK_FREQ_26_MHZ (2 << 0)
#define HFCLK_FREQ_38p4_MHZ (3 << 0)
#define HIGH_PERF_SQ (1 << 3)
#define CK32K_LOWPWR_EN (1 << 7)
/* chip-specific feature flags, for i2c_device_id.driver_data */
#define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */
#define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */
#define TWL5031 BIT(2) /* twl5031 has different registers */
#define TWL6030_CLASS BIT(3) /* TWL6030 class */
/*----------------------------------------------------------------------*/
/* is driver active, bound to a chip? */
static bool inuse;
/* Structure for each TWL4030 Slave */
struct twl4030_client {
static unsigned int twl_id;
unsigned int twl_rev(void)
{
return twl_id;
}
EXPORT_SYMBOL(twl_rev);
/* Structure for each TWL4030/TWL6030 Slave */
struct twl_client {
struct i2c_client *client;
u8 address;
@ -206,19 +252,20 @@ struct twl4030_client {
struct mutex xfer_lock;
};
static struct twl4030_client twl4030_modules[TWL4030_NUM_SLAVES];
static struct twl_client twl_modules[TWL_NUM_SLAVES];
/* mapping the module id to slave id and base address */
struct twl4030mapping {
struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
struct twl_mapping *twl_map;
static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
/*
* NOTE: don't change this table without updating the
* <linux/i2c/twl4030.h> defines for TWL4030_MODULE_*
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
@ -240,6 +287,8 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
{ 2, TWL4030_BASEADD_PWM1 },
{ 2, TWL4030_BASEADD_PWMA },
{ 2, TWL4030_BASEADD_PWMB },
{ 2, TWL5031_BASEADD_ACCESSORY },
{ 2, TWL5031_BASEADD_INTERRUPTS },
{ 3, TWL4030_BASEADD_BACKUP },
{ 3, TWL4030_BASEADD_INT },
@ -249,12 +298,46 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
{ 3, TWL4030_BASEADD_SECURED_REG },
};
static struct twl_mapping twl6030_map[] = {
/*
* NOTE: don't change this table without updating the
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
{ SUB_CHIP_ID1, TWL6030_BASEADD_USB },
{ SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
{ SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
{ SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
{ SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
};
/*----------------------------------------------------------------------*/
/* Exported Functions */
/**
* twl4030_i2c_write - Writes a n bit register in TWL4030
* twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: an array of num_bytes+1 containing data to write
* @reg: register address (just offset will do)
@ -265,19 +348,19 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
*
* Returns the result of operation - 0 is success
*/
int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{
int ret;
int sid;
struct twl4030_client *twl;
struct twl_client *twl;
struct i2c_msg *msg;
if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
if (unlikely(mod_no > TWL_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
twl = &twl4030_modules[sid];
sid = twl_map[mod_no].sid;
twl = &twl_modules[sid];
if (unlikely(!inuse)) {
pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
@ -294,19 +377,26 @@ int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
msg->flags = 0;
msg->buf = value;
/* over write the first byte of buffer with the register address */
*value = twl4030_map[mod_no].base + reg;
*value = twl_map[mod_no].base + reg;
ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
mutex_unlock(&twl->xfer_lock);
/* i2cTransfer returns num messages.translate it pls.. */
if (ret >= 0)
ret = 0;
return ret;
/* i2c_transfer returns number of messages transferred */
if (ret != 1) {
pr_err("%s: i2c_write failed to transfer all messages\n",
DRIVER_NAME);
if (ret < 0)
return ret;
else
return -EIO;
} else {
return 0;
}
}
EXPORT_SYMBOL(twl4030_i2c_write);
EXPORT_SYMBOL(twl_i2c_write);
/**
* twl4030_i2c_read - Reads a n bit register in TWL4030
* twl_i2c_read - Reads a n bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: an array of num_bytes containing data to be read
* @reg: register address (just offset will do)
@ -314,20 +404,20 @@ EXPORT_SYMBOL(twl4030_i2c_write);
*
* Returns result of operation - num_bytes is success else failure.
*/
int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{
int ret;
u8 val;
int sid;
struct twl4030_client *twl;
struct twl_client *twl;
struct i2c_msg *msg;
if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
if (unlikely(mod_no > TWL_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
twl = &twl4030_modules[sid];
sid = twl_map[mod_no].sid;
twl = &twl_modules[sid];
if (unlikely(!inuse)) {
pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
@ -339,7 +429,7 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
msg->addr = twl->address;
msg->len = 1;
msg->flags = 0; /* Read the register value */
val = twl4030_map[mod_no].base + reg;
val = twl_map[mod_no].base + reg;
msg->buf = &val;
/* [MSG2] fill the data rx buffer */
msg = &twl->xfer_msg[1];
@ -350,45 +440,52 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 2);
mutex_unlock(&twl->xfer_lock);
/* i2cTransfer returns num messages.translate it pls.. */
if (ret >= 0)
ret = 0;
return ret;
/* i2c_transfer returns number of messages transferred */
if (ret != 2) {
pr_err("%s: i2c_read failed to transfer all messages\n",
DRIVER_NAME);
if (ret < 0)
return ret;
else
return -EIO;
} else {
return 0;
}
}
EXPORT_SYMBOL(twl4030_i2c_read);
EXPORT_SYMBOL(twl_i2c_read);
/**
* twl4030_i2c_write_u8 - Writes a 8 bit register in TWL4030
* twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: the value to be written 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
int twl4030_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
{
/* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
u8 temp_buffer[2] = { 0 };
/* offset 1 contains the data */
temp_buffer[1] = value;
return twl4030_i2c_write(mod_no, temp_buffer, reg, 1);
return twl_i2c_write(mod_no, temp_buffer, reg, 1);
}
EXPORT_SYMBOL(twl4030_i2c_write_u8);
EXPORT_SYMBOL(twl_i2c_write_u8);
/**
* twl4030_i2c_read_u8 - Reads a 8 bit register from TWL4030
* twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: the value read 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
int twl4030_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
{
return twl4030_i2c_read(mod_no, value, reg, 1);
return twl_i2c_read(mod_no, value, reg, 1);
}
EXPORT_SYMBOL(twl4030_i2c_read_u8);
EXPORT_SYMBOL(twl_i2c_read_u8);
/*----------------------------------------------------------------------*/
@ -398,7 +495,7 @@ add_numbered_child(unsigned chip, const char *name, int num,
bool can_wakeup, int irq0, int irq1)
{
struct platform_device *pdev;
struct twl4030_client *twl = &twl4030_modules[chip];
struct twl_client *twl = &twl_modules[chip];
int status;
pdev = platform_device_alloc(name, num);
@ -456,6 +553,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
struct regulator_consumer_supply *consumers,
unsigned num_consumers)
{
unsigned sub_chip_id;
/* regulator framework demands init_data ... */
if (!pdata)
return NULL;
@ -466,7 +564,8 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
}
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
return add_numbered_child(3, "twl4030_reg", num,
sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
return add_numbered_child(sub_chip_id, "twl_reg", num,
pdata, sizeof(*pdata), false, 0, 0);
}
@ -486,29 +585,32 @@ static int
add_children(struct twl4030_platform_data *pdata, unsigned long features)
{
struct device *child;
unsigned sub_chip_id;
if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) {
if (twl_has_bci() && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
child = add_child(3, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci),
false,
/* irq0 = CHG_PRES, irq1 = BCI */
pdata->irq_base + 8 + 1, pdata->irq_base + 2);
pdata->irq_base + BCI_PRES_INTR_OFFSET,
pdata->irq_base + BCI_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_gpio() && pdata->gpio) {
child = add_child(1, "twl4030_gpio",
child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
false, pdata->irq_base + 0, 0);
false, pdata->irq_base + GPIO_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_keypad() && pdata->keypad) {
child = add_child(2, "twl4030_keypad",
child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
pdata->keypad, sizeof(*pdata->keypad),
true, pdata->irq_base + 1, 0);
true, pdata->irq_base + KEYPAD_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@ -516,7 +618,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_madc() && pdata->madc) {
child = add_child(2, "twl4030_madc",
pdata->madc, sizeof(*pdata->madc),
true, pdata->irq_base + 3, 0);
true, pdata->irq_base + MADC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@ -529,14 +631,15 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
* Eventually, Linux might become more aware of such
* HW security concerns, and "least privilege".
*/
child = add_child(3, "twl4030_rtc",
sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
child = add_child(sub_chip_id, "twl_rtc",
NULL, 0,
true, pdata->irq_base + 8 + 3, 0);
true, pdata->irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_usb() && pdata->usb) {
if (twl_has_usb() && pdata->usb && twl_class_is_4030()) {
static struct regulator_consumer_supply usb1v5 = {
.supply = "usb1v5",
@ -581,7 +684,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
pdata->usb, sizeof(*pdata->usb),
true,
/* irq0 = USB_PRES, irq1 = USB */
pdata->irq_base + 8 + 2, pdata->irq_base + 4);
pdata->irq_base + USB_PRES_INTR_OFFSET,
pdata->irq_base + USB_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
@ -615,12 +719,23 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
if (twl_has_regulator()) {
/*
/* twl4030 regulators */
if (twl_has_regulator() && twl_class_is_4030()) {
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
if (IS_ERR(child))
return PTR_ERR(child);
*/
child = add_regulator(TWL4030_REG_VIO, pdata->vio);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1);
if (IS_ERR(child))
@ -636,10 +751,23 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
pdata->vaux2);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig);
if (IS_ERR(child))
return PTR_ERR(child);
}
/* maybe add LDOs that are omitted on cost-reduced parts */
if (twl_has_regulator() && !(features & TPS_SUBSET)) {
if (twl_has_regulator() && !(features & TPS_SUBSET)
&& twl_class_is_4030()) {
child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2);
if (IS_ERR(child))
return PTR_ERR(child);
@ -665,6 +793,49 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
/* twl6030 regulators */
if (twl_has_regulator() && twl_class_is_6030()) {
child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VPP, pdata->vpp);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VANA, pdata->vana);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VDAC, pdata->vdac);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VUSB, pdata->vusb);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3);
if (IS_ERR(child))
return PTR_ERR(child);
}
return 0;
}
@ -679,7 +850,7 @@ static inline int __init protect_pm_master(void)
{
int e = 0;
e = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_LOCK,
e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_LOCK,
R_PROTECT_KEY);
return e;
}
@ -688,14 +859,15 @@ static inline int __init unprotect_pm_master(void)
{
int e = 0;
e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK1,
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK1,
R_PROTECT_KEY);
e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK2,
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK2,
R_PROTECT_KEY);
return e;
}
static void clocks_init(struct device *dev)
static void clocks_init(struct device *dev,
struct twl4030_clock_init_data *clock)
{
int e = 0;
struct clk *osc;
@ -709,7 +881,7 @@ static void clocks_init(struct device *dev)
osc = clk_get(dev, "osc_sys_ck");
if (IS_ERR(osc)) {
printk(KERN_WARNING "Skipping twl4030 internal clock init and "
printk(KERN_WARNING "Skipping twl internal clock init and "
"using bootloader value (unknown osc rate)\n");
return;
}
@ -723,7 +895,7 @@ static void clocks_init(struct device *dev)
*/
osc = ERR_PTR(-EIO);
printk(KERN_WARNING "Skipping twl4030 internal clock init and "
printk(KERN_WARNING "Skipping twl internal clock init and "
"using bootloader value (unknown osc rate)\n");
return;
@ -742,9 +914,12 @@ static void clocks_init(struct device *dev)
}
ctrl |= HIGH_PERF_SQ;
if (clock && clock->ck32k_lowpwr_enable)
ctrl |= CK32K_LOWPWR_EN;
e |= unprotect_pm_master();
/* effect->MADC+USB ck en */
e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
e |= protect_pm_master();
if (e < 0)
@ -753,24 +928,31 @@ static void clocks_init(struct device *dev)
/*----------------------------------------------------------------------*/
int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl_exit_irq(void);
int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl4030_exit_irq(void);
int twl4030_init_chip_irq(const char *chip);
int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl6030_exit_irq(void);
static int twl4030_remove(struct i2c_client *client)
static int twl_remove(struct i2c_client *client)
{
unsigned i;
int status;
status = twl_exit_irq();
if (twl_class_is_4030())
status = twl4030_exit_irq();
else
status = twl6030_exit_irq();
if (status < 0)
return status;
for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
struct twl4030_client *twl = &twl4030_modules[i];
for (i = 0; i < TWL_NUM_SLAVES; i++) {
struct twl_client *twl = &twl_modules[i];
if (twl->client && twl->client != client)
i2c_unregister_device(twl->client);
twl4030_modules[i].client = NULL;
twl_modules[i].client = NULL;
}
inuse = false;
return 0;
@ -778,7 +960,7 @@ static int twl4030_remove(struct i2c_client *client)
/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
static int __init
twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int status;
unsigned i;
@ -799,8 +981,8 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -EBUSY;
}
for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
struct twl4030_client *twl = &twl4030_modules[i];
for (i = 0; i < TWL_NUM_SLAVES; i++) {
struct twl_client *twl = &twl_modules[i];
twl->address = client->addr + i;
if (i == 0)
@ -814,15 +996,20 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
status = -ENOMEM;
goto fail;
}
strlcpy(twl->client->name, id->name,
sizeof(twl->client->name));
}
mutex_init(&twl->xfer_lock);
}
inuse = true;
if ((id->driver_data) & TWL6030_CLASS) {
twl_id = TWL6030_CLASS_ID;
twl_map = &twl6030_map[0];
} else {
twl_id = TWL4030_CLASS_ID;
twl_map = &twl4030_map[0];
}
/* setup clock framework */
clocks_init(&client->dev);
clocks_init(&client->dev, pdata->clock);
/* load power event scripts */
if (twl_has_power() && pdata->power)
@ -832,7 +1019,15 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (client->irq
&& pdata->irq_base
&& pdata->irq_end > pdata->irq_base) {
status = twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end);
if (twl_class_is_4030()) {
twl4030_init_chip_irq(id->name);
status = twl4030_init_irq(client->irq, pdata->irq_base,
pdata->irq_end);
} else {
status = twl6030_init_irq(client->irq, pdata->irq_base,
pdata->irq_end);
}
if (status < 0)
goto fail;
}
@ -840,40 +1035,42 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
status = add_children(pdata, id->driver_data);
fail:
if (status < 0)
twl4030_remove(client);
twl_remove(client);
return status;
}
static const struct i2c_device_id twl4030_ids[] = {
static const struct i2c_device_id twl_ids[] = {
{ "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */
{ "twl5030", 0 }, /* T2 updated */
{ "twl5031", TWL5031 }, /* TWL5030 updated */
{ "tps65950", 0 }, /* catalog version of twl5030 */
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(i2c, twl4030_ids);
MODULE_DEVICE_TABLE(i2c, twl_ids);
/* One Client Driver , 4 Clients */
static struct i2c_driver twl4030_driver = {
static struct i2c_driver twl_driver = {
.driver.name = DRIVER_NAME,
.id_table = twl4030_ids,
.probe = twl4030_probe,
.remove = twl4030_remove,
.id_table = twl_ids,
.probe = twl_probe,
.remove = twl_remove,
};
static int __init twl4030_init(void)
static int __init twl_init(void)
{
return i2c_add_driver(&twl4030_driver);
return i2c_add_driver(&twl_driver);
}
subsys_initcall(twl4030_init);
subsys_initcall(twl_init);
static void __exit twl4030_exit(void)
static void __exit twl_exit(void)
{
i2c_del_driver(&twl4030_driver);
i2c_del_driver(&twl_driver);
}
module_exit(twl4030_exit);
module_exit(twl_exit);
MODULE_AUTHOR("Texas Instruments, Inc.");
MODULE_DESCRIPTION("I2C Core interface for TWL4030");
MODULE_DESCRIPTION("I2C Core interface for TWL");
MODULE_LICENSE("GPL");

View File

@ -32,7 +32,7 @@
#include <linux/irq.h>
#include <linux/kthread.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
/*
@ -74,6 +74,8 @@ struct sih {
u8 edr_offset;
u8 bytes_edr; /* bytelen of EDR */
u8 irq_lines; /* number of supported irq lines */
/* SIR ignored -- set interrupt, for testing only */
struct irq_data {
u8 isr_offset;
@ -82,6 +84,9 @@ struct sih {
/* + 2 bytes padding */
};
static const struct sih *sih_modules;
static int nr_sih_modules;
#define SIH_INITIALIZER(modname, nbits) \
.module = TWL4030_MODULE_ ## modname, \
.control_offset = TWL4030_ ## modname ## _SIH_CTRL, \
@ -89,6 +94,7 @@ struct sih {
.bytes_ixr = DIV_ROUND_UP(nbits, 8), \
.edr_offset = TWL4030_ ## modname ## _EDR, \
.bytes_edr = DIV_ROUND_UP((2*(nbits)), 8), \
.irq_lines = 2, \
.mask = { { \
.isr_offset = TWL4030_ ## modname ## _ISR1, \
.imr_offset = TWL4030_ ## modname ## _IMR1, \
@ -107,7 +113,8 @@ struct sih {
/* Order in this table matches order in PIH_ISR. That is,
* BIT(n) in PIH_ISR is sih_modules[n].
*/
static const struct sih sih_modules[6] = {
/* sih_modules_twl4030 is used both in twl4030 and twl5030 */
static const struct sih sih_modules_twl4030[6] = {
[0] = {
.name = "gpio",
.module = TWL4030_MODULE_GPIO,
@ -118,6 +125,7 @@ static const struct sih sih_modules[6] = {
/* Note: *all* of these IRQs default to no-trigger */
.edr_offset = REG_GPIO_EDR1,
.bytes_edr = 5,
.irq_lines = 2,
.mask = { {
.isr_offset = REG_GPIO_ISR1A,
.imr_offset = REG_GPIO_IMR1A,
@ -140,6 +148,7 @@ static const struct sih sih_modules[6] = {
.edr_offset = TWL4030_INTERRUPTS_BCIEDR1,
/* Note: most of these IRQs default to no-trigger */
.bytes_edr = 3,
.irq_lines = 2,
.mask = { {
.isr_offset = TWL4030_INTERRUPTS_BCIISR1A,
.imr_offset = TWL4030_INTERRUPTS_BCIIMR1A,
@ -164,6 +173,99 @@ static const struct sih sih_modules[6] = {
/* there are no SIH modules #6 or #7 ... */
};
static const struct sih sih_modules_twl5031[8] = {
[0] = {
.name = "gpio",
.module = TWL4030_MODULE_GPIO,
.control_offset = REG_GPIO_SIH_CTRL,
.set_cor = true,
.bits = TWL4030_GPIO_MAX,
.bytes_ixr = 3,
/* Note: *all* of these IRQs default to no-trigger */
.edr_offset = REG_GPIO_EDR1,
.bytes_edr = 5,
.irq_lines = 2,
.mask = { {
.isr_offset = REG_GPIO_ISR1A,
.imr_offset = REG_GPIO_IMR1A,
}, {
.isr_offset = REG_GPIO_ISR1B,
.imr_offset = REG_GPIO_IMR1B,
}, },
},
[1] = {
.name = "keypad",
.set_cor = true,
SIH_INITIALIZER(KEYPAD_KEYP, 4)
},
[2] = {
.name = "bci",
.module = TWL5031_MODULE_INTERRUPTS,
.control_offset = TWL5031_INTERRUPTS_BCISIHCTRL,
.bits = 7,
.bytes_ixr = 1,
.edr_offset = TWL5031_INTERRUPTS_BCIEDR1,
/* Note: most of these IRQs default to no-trigger */
.bytes_edr = 2,
.irq_lines = 2,
.mask = { {
.isr_offset = TWL5031_INTERRUPTS_BCIISR1,
.imr_offset = TWL5031_INTERRUPTS_BCIIMR1,
}, {
.isr_offset = TWL5031_INTERRUPTS_BCIISR2,
.imr_offset = TWL5031_INTERRUPTS_BCIIMR2,
}, },
},
[3] = {
.name = "madc",
SIH_INITIALIZER(MADC, 4)
},
[4] = {
/* USB doesn't use the same SIH organization */
.name = "usb",
},
[5] = {
.name = "power",
.set_cor = true,
SIH_INITIALIZER(INT_PWR, 8)
},
[6] = {
/*
* ACI doesn't use the same SIH organization.
* For example, it supports only one interrupt line
*/
.name = "aci",
.module = TWL5031_MODULE_ACCESSORY,
.bits = 9,
.bytes_ixr = 2,
.irq_lines = 1,
.mask = { {
.isr_offset = TWL5031_ACIIDR_LSB,
.imr_offset = TWL5031_ACIIMR_LSB,
}, },
},
[7] = {
/* Accessory */
.name = "acc",
.module = TWL5031_MODULE_ACCESSORY,
.control_offset = TWL5031_ACCSIHCTRL,
.bits = 2,
.bytes_ixr = 1,
.edr_offset = TWL5031_ACCEDR1,
/* Note: most of these IRQs default to no-trigger */
.bytes_edr = 1,
.irq_lines = 2,
.mask = { {
.isr_offset = TWL5031_ACCISR1,
.imr_offset = TWL5031_ACCIMR1,
}, {
.isr_offset = TWL5031_ACCISR2,
.imr_offset = TWL5031_ACCIMR2,
}, },
},
};
#undef TWL4030_MODULE_KEYPAD_KEYP
#undef TWL4030_MODULE_INT_PWR
#undef TWL4030_INT_PWR_EDR
@ -194,7 +296,7 @@ static int twl4030_irq_thread(void *data)
/* Wait for IRQ, then read PIH irq status (also blocking) */
wait_for_completion_interruptible(&irq_event);
ret = twl4030_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
REG_PIH_ISR_P1);
if (ret) {
pr_warning("twl4030: I2C error %d reading PIH ISR\n",
@ -284,13 +386,17 @@ static int twl4030_init_sih_modules(unsigned line)
/* disable all interrupts on our line */
memset(buf, 0xff, sizeof buf);
sih = sih_modules;
for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) {
for (i = 0; i < nr_sih_modules; i++, sih++) {
/* skip USB -- it's funky */
if (!sih->bytes_ixr)
continue;
status = twl4030_i2c_write(sih->module, buf,
/* Not all the SIH modules support multiple interrupt lines */
if (sih->irq_lines <= line)
continue;
status = twl_i2c_write(sih->module, buf,
sih->mask[line].imr_offset, sih->bytes_ixr);
if (status < 0)
pr_err("twl4030: err %d initializing %s %s\n",
@ -304,7 +410,7 @@ static int twl4030_init_sih_modules(unsigned line)
* And for PWR_INT it's not documented...
*/
if (sih->set_cor) {
status = twl4030_i2c_write_u8(sih->module,
status = twl_i2c_write_u8(sih->module,
TWL4030_SIH_CTRL_COR_MASK,
sih->control_offset);
if (status < 0)
@ -314,7 +420,7 @@ static int twl4030_init_sih_modules(unsigned line)
}
sih = sih_modules;
for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) {
for (i = 0; i < nr_sih_modules; i++, sih++) {
u8 rxbuf[4];
int j;
@ -322,20 +428,24 @@ static int twl4030_init_sih_modules(unsigned line)
if (!sih->bytes_ixr)
continue;
/* Not all the SIH modules support multiple interrupt lines */
if (sih->irq_lines <= line)
continue;
/* Clear pending interrupt status. Either the read was
* enough, or we need to write those bits. Repeat, in
* case an IRQ is pending (PENDDIS=0) ... that's not
* uncommon with PWR_INT.PWRON.
*/
for (j = 0; j < 2; j++) {
status = twl4030_i2c_read(sih->module, rxbuf,
status = twl_i2c_read(sih->module, rxbuf,
sih->mask[line].isr_offset, sih->bytes_ixr);
if (status < 0)
pr_err("twl4030: err %d initializing %s %s\n",
status, sih->name, "ISR");
if (!sih->set_cor)
status = twl4030_i2c_write(sih->module, buf,
status = twl_i2c_write(sih->module, buf,
sih->mask[line].isr_offset,
sih->bytes_ixr);
/* else COR=1 means read sufficed.
@ -404,7 +514,7 @@ static void twl4030_sih_do_mask(struct work_struct *work)
return;
/* write the whole mask ... simpler than subsetting it */
status = twl4030_i2c_write(sih->module, imr.bytes,
status = twl_i2c_write(sih->module, imr.bytes,
sih->mask[irq_line].imr_offset, sih->bytes_ixr);
if (status)
pr_err("twl4030: %s, %s --> %d\n", __func__,
@ -435,7 +545,7 @@ static void twl4030_sih_do_edge(struct work_struct *work)
* any processor on the other IRQ line, EDR registers are
* shared.
*/
status = twl4030_i2c_read(sih->module, bytes + 1,
status = twl_i2c_read(sih->module, bytes + 1,
sih->edr_offset, sih->bytes_edr);
if (status) {
pr_err("twl4030: %s, %s --> %d\n", __func__,
@ -469,7 +579,7 @@ static void twl4030_sih_do_edge(struct work_struct *work)
}
/* Write */
status = twl4030_i2c_write(sih->module, bytes,
status = twl_i2c_write(sih->module, bytes,
sih->edr_offset, sih->bytes_edr);
if (status)
pr_err("twl4030: %s, %s --> %d\n", __func__,
@ -554,7 +664,7 @@ static inline int sih_read_isr(const struct sih *sih)
/* FIXME need retry-on-error ... */
isr.word = 0;
status = twl4030_i2c_read(sih->module, isr.bytes,
status = twl_i2c_read(sih->module, isr.bytes,
sih->mask[irq_line].isr_offset, sih->bytes_ixr);
return (status < 0) ? status : le32_to_cpu(isr.word);
@ -611,7 +721,7 @@ int twl4030_sih_setup(int module)
/* only support modules with standard clear-on-read for now */
for (sih_mod = 0, sih = sih_modules;
sih_mod < ARRAY_SIZE(sih_modules);
sih_mod < nr_sih_modules;
sih_mod++, sih++) {
if (sih->module == module && sih->set_cor) {
if (!WARN((irq_base + sih->bits) > NR_IRQS,
@ -668,7 +778,7 @@ int twl4030_sih_setup(int module)
/* FIXME pass in which interrupt line we'll use ... */
#define twl_irq_line 0
int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
static struct irq_chip twl4030_irq_chip;
@ -728,7 +838,8 @@ int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
goto fail_rqirq;
}
task = kthread_run(twl4030_irq_thread, (void *)irq_num, "twl4030-irq");
task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
"twl4030-irq");
if (IS_ERR(task)) {
pr_err("twl4030: could not create irq %d thread!\n", irq_num);
status = PTR_ERR(task);
@ -747,7 +858,7 @@ int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
return status;
}
int twl_exit_irq(void)
int twl4030_exit_irq(void)
{
/* FIXME undo twl_init_irq() */
if (twl4030_irq_base) {
@ -756,3 +867,16 @@ int twl_exit_irq(void)
}
return 0;
}
int twl4030_init_chip_irq(const char *chip)
{
if (!strcmp(chip, "twl5031")) {
sih_modules = sih_modules_twl5031;
nr_sih_modules = ARRAY_SIZE(sih_modules_twl5031);
} else {
sih_modules = sih_modules_twl4030;
nr_sih_modules = ARRAY_SIZE(sih_modules_twl4030);
}
return 0;
}

View File

@ -26,7 +26,7 @@
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
@ -67,19 +67,35 @@ static u8 twl4030_start_script_address = 0x2b;
#define R_KEY_1 0xC0
#define R_KEY_2 0x0C
/* resource configuration registers */
#define DEVGROUP_OFFSET 0
/* resource configuration registers
<RESOURCE>_DEV_GRP at address 'n+0'
<RESOURCE>_TYPE at address 'n+1'
<RESOURCE>_REMAP at address 'n+2'
<RESOURCE>_DEDICATED at address 'n+3'
*/
#define DEV_GRP_OFFSET 0
#define TYPE_OFFSET 1
#define REMAP_OFFSET 2
#define DEDICATED_OFFSET 3
/* Bit positions */
#define DEVGROUP_SHIFT 5
#define DEVGROUP_MASK (7 << DEVGROUP_SHIFT)
/* Bit positions in the registers */
/* <RESOURCE>_DEV_GRP */
#define DEV_GRP_SHIFT 5
#define DEV_GRP_MASK (7 << DEV_GRP_SHIFT)
/* <RESOURCE>_TYPE */
#define TYPE_SHIFT 0
#define TYPE_MASK (7 << TYPE_SHIFT)
#define TYPE2_SHIFT 3
#define TYPE2_MASK (3 << TYPE2_SHIFT)
/* <RESOURCE>_REMAP */
#define SLEEP_STATE_SHIFT 0
#define SLEEP_STATE_MASK (0xf << SLEEP_STATE_SHIFT)
#define OFF_STATE_SHIFT 4
#define OFF_STATE_MASK (0xf << OFF_STATE_SHIFT)
static u8 res_config_addrs[] = {
[RES_VAUX1] = 0x17,
[RES_VAUX2] = 0x1b,
@ -115,11 +131,11 @@ static int __init twl4030_write_script_byte(u8 address, u8 byte)
{
int err;
err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
err = twl_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,
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
R_MEMORY_DATA);
out:
return err;
@ -176,18 +192,18 @@ static int __init twl4030_config_wakeup3_sequence(u8 address)
u8 data;
/* Set SLEEP to ACTIVE SEQ address for P3 */
err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
err = twl_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,
err = twl_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,
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
R_P3_SW_EVENTS);
out:
if (err)
@ -201,42 +217,42 @@ static int __init twl4030_config_wakeup12_sequence(u8 address)
u8 data;
/* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
err = twl_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,
err = twl_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,
err = twl_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,
err = twl_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,
err = twl_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,
err = twl_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 ,
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
R_CFG_P1_TRANSITION);
if (err)
goto out;
@ -254,7 +270,7 @@ 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,
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
R_SEQ_ADD_A2S);
if (err)
@ -269,41 +285,41 @@ static int __init twl4030_config_warmreset_sequence(u8 address)
u8 rd_data;
/* Set WARM RESET SEQ address for P1 */
err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
err = twl_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,
err = twl_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,
err = twl_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,
err = twl_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,
err = twl_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,
err = twl_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,
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
R_P3_SW_EVENTS);
out:
if (err)
@ -317,6 +333,7 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
int err;
u8 type;
u8 grp;
u8 remap;
if (rconfig->resource > TOTAL_RESOURCES) {
pr_err("TWL4030 Resource %d does not exist\n",
@ -327,19 +344,19 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
rconfig_addr = res_config_addrs[rconfig->resource];
/* Set resource group */
err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
rconfig_addr + DEVGROUP_OFFSET);
err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
rconfig_addr + DEV_GRP_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 (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) {
grp &= ~DEV_GRP_MASK;
grp |= rconfig->devgroup << DEV_GRP_SHIFT;
err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
grp, rconfig_addr + DEV_GRP_OFFSET);
if (err < 0) {
pr_err("TWL4030 failed to program devgroup\n");
return err;
@ -347,7 +364,7 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
}
/* Set resource types */
err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
err = twl_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",
@ -355,23 +372,50 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
return err;
}
if (rconfig->type >= 0) {
if (rconfig->type != TWL4030_RESCONFIG_UNDEF) {
type &= ~TYPE_MASK;
type |= rconfig->type << TYPE_SHIFT;
}
if (rconfig->type2 >= 0) {
if (rconfig->type2 != TWL4030_RESCONFIG_UNDEF) {
type &= ~TYPE2_MASK;
type |= rconfig->type2 << TYPE2_SHIFT;
}
err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
err = twl_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;
}
/* Set remap states */
err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap,
rconfig_addr + REMAP_OFFSET);
if (err < 0) {
pr_err("TWL4030 Resource %d remap could not be read\n",
rconfig->resource);
return err;
}
if (rconfig->remap_off != TWL4030_RESCONFIG_UNDEF) {
remap &= ~OFF_STATE_MASK;
remap |= rconfig->remap_off << OFF_STATE_SHIFT;
}
if (rconfig->remap_sleep != TWL4030_RESCONFIG_UNDEF) {
remap &= ~SLEEP_STATE_MASK;
remap |= rconfig->remap_off << SLEEP_STATE_SHIFT;
}
err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
remap,
rconfig_addr + REMAP_OFFSET);
if (err < 0) {
pr_err("TWL4030 failed to program remap\n");
return err;
}
return 0;
}
@ -424,12 +468,12 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
struct twl4030_resconfig *resconfig;
u8 address = twl4030_start_script_address;
err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
err = twl_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,
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
R_PROTECT_KEY);
if (err)
goto unlock;
@ -452,7 +496,7 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
}
}
err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
return;

299
drivers/mfd/twl6030-irq.c Normal file
View File

@ -0,0 +1,299 @@
/*
* twl6030-irq.c - TWL6030 irq support
*
* Copyright (C) 2005-2009 Texas Instruments, Inc.
*
* Modifications to defer interrupt handling to a kernel thread:
* Copyright (C) 2006 MontaVista Software, Inc.
*
* Based on tlv320aic23.c:
* Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
*
* Code cleanup and modifications to IRQ handler.
* by syed khasim <x0khasim@ti.com>
*
* TWL6030 specific code and IRQ handling changes by
* Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
* Balaji T K <balajitk@ti.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.
*
* 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/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kthread.h>
#include <linux/i2c/twl.h>
/*
* TWL6030 (unlike its predecessors, which had two level interrupt handling)
* three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
* It exposes status bits saying who has raised an interrupt. There are
* three mask registers that corresponds to these status registers, that
* enables/disables these interrupts.
*
* We set up IRQs starting at a platform-specified base. An interrupt map table,
* specifies mapping between interrupt number and the associated module.
*
*/
static int twl6030_interrupt_mapping[24] = {
PWR_INTR_OFFSET, /* Bit 0 PWRON */
PWR_INTR_OFFSET, /* Bit 1 RPWRON */
PWR_INTR_OFFSET, /* Bit 2 BAT_VLOW */
RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */
RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */
HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */
SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */
SMPSLDO_INTR_OFFSET, /* Bit 7 VMMC_SHORT */
SMPSLDO_INTR_OFFSET, /* Bit 8 VUSIM_SHORT */
BATDETECT_INTR_OFFSET, /* Bit 9 BAT */
SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */
MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */
RSV_INTR_OFFSET, /* Bit 12 Reserved */
MADC_INTR_OFFSET, /* Bit 13 GPADC_RT_EOC */
MADC_INTR_OFFSET, /* Bit 14 GPADC_SW_EOC */
GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */
USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */
USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */
USBOTG_INTR_OFFSET, /* Bit 18 ID */
USBOTG_INTR_OFFSET, /* Bit 19 VBUS */
CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */
CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */
RSV_INTR_OFFSET, /* Bit 23 Reserved */
};
/*----------------------------------------------------------------------*/
static unsigned twl6030_irq_base;
static struct completion irq_event;
/*
* This thread processes interrupts reported by the Primary Interrupt Handler.
*/
static int twl6030_irq_thread(void *data)
{
long irq = (long)data;
static unsigned i2c_errors;
static const unsigned max_i2c_errors = 100;
int ret;
current->flags |= PF_NOFREEZE;
while (!kthread_should_stop()) {
int i;
union {
u8 bytes[4];
u32 int_sts;
} sts;
/* Wait for IRQ, then read PIH irq status (also blocking) */
wait_for_completion_interruptible(&irq_event);
/* read INT_STS_A, B and C in one shot using a burst read */
ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
REG_INT_STS_A, 3);
if (ret) {
pr_warning("twl6030: I2C error %d reading PIH ISR\n",
ret);
if (++i2c_errors >= max_i2c_errors) {
printk(KERN_ERR "Maximum I2C error count"
" exceeded. Terminating %s.\n",
__func__);
break;
}
complete(&irq_event);
continue;
}
sts.bytes[3] = 0; /* Only 24 bits are valid*/
for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
local_irq_disable();
if (sts.int_sts & 0x1) {
int module_irq = twl6030_irq_base +
twl6030_interrupt_mapping[i];
struct irq_desc *d = irq_to_desc(module_irq);
if (!d) {
pr_err("twl6030: Invalid SIH IRQ: %d\n",
module_irq);
return -EINVAL;
}
/* These can't be masked ... always warn
* if we get any surprises.
*/
if (d->status & IRQ_DISABLED)
note_interrupt(module_irq, d,
IRQ_NONE);
else
d->handle_irq(module_irq, d);
}
local_irq_enable();
}
ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
REG_INT_STS_A, 3); /* clear INT_STS_A */
if (ret)
pr_warning("twl6030: I2C error in clearing PIH ISR\n");
enable_irq(irq);
}
return 0;
}
/*
* handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
* This is a chained interrupt, so there is no desc->action method for it.
* Now we need to query the interrupt controller in the twl6030 to determine
* which module is generating the interrupt request. However, we can't do i2c
* transactions in interrupt context, so we must defer that work to a kernel
* thread. All we do here is acknowledge and mask the interrupt and wakeup
* the kernel thread.
*/
static irqreturn_t handle_twl6030_pih(int irq, void *devid)
{
disable_irq_nosync(irq);
complete(devid);
return IRQ_HANDLED;
}
/*----------------------------------------------------------------------*/
static inline void activate_irq(int irq)
{
#ifdef CONFIG_ARM
/* ARM requires an extra step to clear IRQ_NOREQUEST, which it
* sets on behalf of every irq_chip. Also sets IRQ_NOPROBE.
*/
set_irq_flags(irq, IRQF_VALID);
#else
/* same effect on other architectures */
set_irq_noprobe(irq);
#endif
}
/*----------------------------------------------------------------------*/
static unsigned twl6030_irq_next;
/*----------------------------------------------------------------------*/
int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
{
int ret;
u8 unmask_value;
ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
REG_INT_STS_A + offset);
unmask_value &= (~(bit_mask));
ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
return ret;
}
EXPORT_SYMBOL(twl6030_interrupt_unmask);
int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
{
int ret;
u8 mask_value;
ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
REG_INT_STS_A + offset);
mask_value |= (bit_mask);
ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
return ret;
}
EXPORT_SYMBOL(twl6030_interrupt_mask);
int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
int status = 0;
int i;
struct task_struct *task;
int ret;
u8 mask[4];
static struct irq_chip twl6030_irq_chip;
mask[1] = 0xFF;
mask[2] = 0xFF;
mask[3] = 0xFF;
ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */
ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */
ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */
twl6030_irq_base = irq_base;
/* install an irq handler for each of the modules;
* clone dummy irq_chip since PIH can't *do* anything
*/
twl6030_irq_chip = dummy_irq_chip;
twl6030_irq_chip.name = "twl6030";
twl6030_irq_chip.set_type = NULL;
for (i = irq_base; i < irq_end; i++) {
set_irq_chip_and_handler(i, &twl6030_irq_chip,
handle_simple_irq);
activate_irq(i);
}
twl6030_irq_next = i;
pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
irq_num, irq_base, twl6030_irq_next - 1);
/* install an irq handler to demultiplex the TWL6030 interrupt */
init_completion(&irq_event);
task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
if (IS_ERR(task)) {
pr_err("twl6030: could not create irq %d thread!\n", irq_num);
status = PTR_ERR(task);
goto fail_kthread;
}
status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
"TWL6030-PIH", &irq_event);
if (status < 0) {
pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
goto fail_irq;
}
return status;
fail_irq:
free_irq(irq_num, &irq_event);
fail_kthread:
for (i = irq_base; i < irq_end; i++)
set_irq_chip_and_handler(i, NULL, NULL);
return status;
}
int twl6030_exit_irq(void)
{
if (twl6030_irq_base) {
pr_err("twl6030: can't yet clean up IRQs?\n");
return -ENOSYS;
}
return 0;
}

View File

@ -90,9 +90,10 @@ int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
enum wm831x_parent {
WM8310 = 0,
WM8311 = 1,
WM8312 = 2,
WM8310 = 0x8310,
WM8311 = 0x8311,
WM8312 = 0x8312,
WM8320 = 0x8320,
};
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
@ -478,6 +479,20 @@ static struct resource wm831x_dcdc4_resources[] = {
},
};
static struct resource wm8320_dcdc4_buck_resources[] = {
{
.start = WM831X_DC4_CONTROL,
.end = WM832X_DC4_SLEEP_CONTROL,
.flags = IORESOURCE_IO,
},
{
.name = "UV",
.start = WM831X_IRQ_UV_DC4,
.end = WM831X_IRQ_UV_DC4,
.flags = IORESOURCE_IRQ,
},
};
static struct resource wm831x_gpio_resources[] = {
{
.start = WM831X_IRQ_GPIO_1,
@ -1237,6 +1252,137 @@ static struct mfd_cell wm8312_devs[] = {
},
};
static struct mfd_cell wm8320_devs[] = {
{
.name = "wm831x-backup",
},
{
.name = "wm831x-buckv",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
.resources = wm831x_dcdc1_resources,
},
{
.name = "wm831x-buckv",
.id = 2,
.num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
.resources = wm831x_dcdc2_resources,
},
{
.name = "wm831x-buckp",
.id = 3,
.num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
.resources = wm831x_dcdc3_resources,
},
{
.name = "wm831x-buckp",
.id = 4,
.num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
.resources = wm8320_dcdc4_buck_resources,
},
{
.name = "wm831x-gpio",
.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
.resources = wm831x_gpio_resources,
},
{
.name = "wm831x-hwmon",
},
{
.name = "wm831x-ldo",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
.resources = wm831x_ldo1_resources,
},
{
.name = "wm831x-ldo",
.id = 2,
.num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
.resources = wm831x_ldo2_resources,
},
{
.name = "wm831x-ldo",
.id = 3,
.num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
.resources = wm831x_ldo3_resources,
},
{
.name = "wm831x-ldo",
.id = 4,
.num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
.resources = wm831x_ldo4_resources,
},
{
.name = "wm831x-ldo",
.id = 5,
.num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
.resources = wm831x_ldo5_resources,
},
{
.name = "wm831x-ldo",
.id = 6,
.num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
.resources = wm831x_ldo6_resources,
},
{
.name = "wm831x-aldo",
.id = 7,
.num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
.resources = wm831x_ldo7_resources,
},
{
.name = "wm831x-aldo",
.id = 8,
.num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
.resources = wm831x_ldo8_resources,
},
{
.name = "wm831x-aldo",
.id = 9,
.num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
.resources = wm831x_ldo9_resources,
},
{
.name = "wm831x-aldo",
.id = 10,
.num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
.resources = wm831x_ldo10_resources,
},
{
.name = "wm831x-alive-ldo",
.id = 11,
.num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
.resources = wm831x_ldo11_resources,
},
{
.name = "wm831x-on",
.num_resources = ARRAY_SIZE(wm831x_on_resources),
.resources = wm831x_on_resources,
},
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
.resources = wm831x_rtc_resources,
},
{
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
.resources = wm831x_status1_resources,
},
{
.name = "wm831x-status",
.id = 2,
.num_resources = ARRAY_SIZE(wm831x_status2_resources),
.resources = wm831x_status2_resources,
},
{
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
.resources = wm831x_wdt_resources,
},
};
static struct mfd_cell backlight_devs[] = {
{
.name = "wm831x-backlight",
@ -1282,50 +1428,37 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
goto err;
}
/* Some engineering samples do not have the ID set, rely on
* the device being registered correctly.
*/
if (ret == 0) {
dev_info(wm831x->dev, "Device is an engineering sample\n");
ret = id;
}
switch (ret) {
case 0x8310:
case WM8310:
parent = WM8310;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM8310 revision %c\n",
'A' + rev);
break;
}
wm831x->num_gpio = 16;
dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
break;
case 0x8311:
case WM8311:
parent = WM8311;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM8311 revision %c\n",
'A' + rev);
break;
}
wm831x->num_gpio = 16;
dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
break;
case 0x8312:
case WM8312:
parent = WM8312;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM8312 revision %c\n",
'A' + rev);
break;
}
wm831x->num_gpio = 16;
dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
break;
case 0:
/* Some engineering samples do not have the ID set,
* rely on the device being registered correctly.
* This will need revisiting for future devices with
* multiple dies.
*/
parent = id;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM831%d ES revision %c\n",
parent, 'A' + rev);
break;
}
case WM8320:
parent = WM8320;
wm831x->num_gpio = 12;
dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
break;
default:
@ -1338,7 +1471,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
* current parts.
*/
if (parent != id)
dev_warn(wm831x->dev, "Device was registered as a WM831%lu\n",
dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
id);
/* Bootstrap the user key */
@ -1371,18 +1504,24 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8310:
ret = mfd_add_devices(wm831x->dev, -1,
wm8310_devs, ARRAY_SIZE(wm8310_devs),
NULL, 0);
NULL, wm831x->irq_base);
break;
case WM8311:
ret = mfd_add_devices(wm831x->dev, -1,
wm8311_devs, ARRAY_SIZE(wm8311_devs),
NULL, 0);
NULL, wm831x->irq_base);
break;
case WM8312:
ret = mfd_add_devices(wm831x->dev, -1,
wm8312_devs, ARRAY_SIZE(wm8312_devs),
NULL, wm831x->irq_base);
break;
case WM8320:
ret = mfd_add_devices(wm831x->dev, -1,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, 0);
break;
@ -1399,7 +1538,8 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
if (pdata && pdata->backlight) {
/* Treat errors as non-critical */
ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
ARRAY_SIZE(backlight_devs), NULL, 0);
ARRAY_SIZE(backlight_devs), NULL,
wm831x->irq_base);
if (ret < 0)
dev_err(wm831x->dev, "Failed to add backlight: %d\n",
ret);
@ -1511,6 +1651,7 @@ static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8310", WM8310 },
{ "wm8311", WM8311 },
{ "wm8312", WM8312 },
{ "wm8320", WM8320 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);

View File

@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/interrupt.h>
@ -339,110 +340,71 @@ 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)
static inline struct wm831x_irq_data *irq_to_wm831x_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]);
return &wm831x_irqs[irq - wm831x->irq_base];
}
void wm831x_enable_irq(struct wm831x *wm831x, int irq)
static void wm831x_irq_lock(unsigned 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;
struct wm831x *wm831x = get_irq_chip_data(irq);
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;
static void wm831x_irq_sync_unlock(unsigned int irq)
{
struct wm831x *wm831x = get_irq_chip_data(irq);
int i;
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
wm831x_reg_write(wm831x,
WM831X_INTERRUPT_STATUS_1_MASK + i,
wm831x->irq_masks_cur[i]);
}
}
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)
static void wm831x_irq_unmask(unsigned int irq)
{
struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
struct wm831x *wm831x = get_irq_chip_data(irq);
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, 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);
}
wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
}
/* 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)
static void wm831x_irq_mask(unsigned int irq)
{
struct wm831x *wm831x = container_of(work, struct wm831x, irq_work);
struct wm831x *wm831x = get_irq_chip_data(irq);
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq);
wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
}
static struct irq_chip wm831x_irq_chip = {
.name = "wm831x",
.bus_lock = wm831x_irq_lock,
.bus_sync_unlock = wm831x_irq_sync_unlock,
.mask = wm831x_irq_mask,
.unmask = wm831x_irq_unmask,
};
/* The processing of the primary interrupt occurs in a thread so that
* we can interact with the device over I2C or SPI. */
static irqreturn_t wm831x_irq_thread(int irq, void *data)
{
struct wm831x *wm831x = data;
unsigned int i;
int primary;
int status_regs[5];
int read[5] = { 0 };
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS);
@ -452,8 +414,6 @@ static void wm831x_irq_worker(struct work_struct *work)
goto out;
}
mutex_lock(&wm831x->irq_lock);
for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
int offset = wm831x_irqs[i].reg - 1;
@ -471,41 +431,34 @@ static void wm831x_irq_worker(struct work_struct *work)
dev_err(wm831x->dev,
"Failed to read IRQ status: %d\n",
*status);
goto out_lock;
goto out;
}
/* 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);
/* Report it if it isn't masked, or forget the status. */
if ((*status & ~wm831x->irq_masks_cur[offset])
& wm831x_irqs[i].mask)
handle_nested_irq(wm831x->irq_base + i);
else
*status &= ~wm831x_irqs[i].mask;
}
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);
for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
if (status_regs[i])
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
status_regs[i]);
}
return IRQ_HANDLED;
}
int wm831x_irq_init(struct wm831x *wm831x, int irq)
{
int i, ret;
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
int i, cur_irq, ret;
mutex_init(&wm831x->irq_lock);
@ -515,41 +468,53 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
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;
if (!pdata || !pdata->irq_base) {
dev_err(wm831x->dev,
"No interrupt base specified, no interrupts\n");
return 0;
}
wm831x->irq = irq;
INIT_WORK(&wm831x->irq_work, wm831x_irq_worker);
wm831x->irq_base = pdata->irq_base;
/* Mask the individual interrupt sources */
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks); i++) {
wm831x->irq_masks[i] = 0xffff;
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
wm831x->irq_masks_cur[i] = 0xffff;
wm831x->irq_masks_cache[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);
/* Register them with genirq */
for (cur_irq = wm831x->irq_base;
cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
cur_irq++) {
set_irq_chip_data(cur_irq, wm831x);
set_irq_chip_and_handler(cur_irq, &wm831x_irq_chip,
handle_edge_irq);
set_irq_nested_thread(cur_irq, 1);
/* 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);
/* ARM needs us to explicitly flag the IRQ as valid
* and will set them noprobe when we do so. */
#ifdef CONFIG_ARM
set_irq_flags(cur_irq, IRQF_VALID);
#else
set_irq_noprobe(cur_irq);
#endif
}
ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"wm831x", wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
irq, ret);
return ret;
}
/* Enable top level interrupts, we mask at secondary level */
wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
return 0;
}

View File

@ -337,733 +337,6 @@ int wm8350_reg_unlock(struct wm8350 *wm8350)
}
EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
{
mutex_lock(&wm8350->irq_mutex);
if (wm8350->irq[irq].handler)
wm8350->irq[irq].handler(wm8350, irq, wm8350->irq[irq].data);
else {
dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
irq);
wm8350_mask_irq(wm8350, irq);
}
mutex_unlock(&wm8350->irq_mutex);
}
/*
* 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 irqreturn_t wm8350_irq(int irq, void *data)
{
struct wm8350 *wm8350 = data;
u16 level_one, status1, status2, comp;
/* TODO: Use block reads to improve performance? */
level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
& ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
status1 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_1)
& ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_1_MASK);
status2 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_2)
& ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_2_MASK);
comp = wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS)
& ~wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK);
/* over current */
if (level_one & WM8350_OC_INT) {
u16 oc;
oc = wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INT_STATUS);
oc &= ~wm8350_reg_read(wm8350,
WM8350_OVER_CURRENT_INT_STATUS_MASK);
if (oc & WM8350_OC_LS_EINT) /* limit switch */
wm8350_irq_call_handler(wm8350, WM8350_IRQ_OC_LS);
}
/* under voltage */
if (level_one & WM8350_UV_INT) {
u16 uv;
uv = wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS);
uv &= ~wm8350_reg_read(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK);
if (uv & WM8350_UV_DC1_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC1);
if (uv & WM8350_UV_DC2_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC2);
if (uv & WM8350_UV_DC3_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC3);
if (uv & WM8350_UV_DC4_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC4);
if (uv & WM8350_UV_DC5_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC5);
if (uv & WM8350_UV_DC6_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC6);
if (uv & WM8350_UV_LDO1_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO1);
if (uv & WM8350_UV_LDO2_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO2);
if (uv & WM8350_UV_LDO3_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO3);
if (uv & WM8350_UV_LDO4_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO4);
}
/* charger, RTC */
if (status1) {
if (status1 & WM8350_CHG_BAT_HOT_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CHG_BAT_HOT);
if (status1 & WM8350_CHG_BAT_COLD_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CHG_BAT_COLD);
if (status1 & WM8350_CHG_BAT_FAIL_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CHG_BAT_FAIL);
if (status1 & WM8350_CHG_TO_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_TO);
if (status1 & WM8350_CHG_END_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_END);
if (status1 & WM8350_CHG_START_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_START);
if (status1 & WM8350_CHG_FAST_RDY_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CHG_FAST_RDY);
if (status1 & WM8350_CHG_VBATT_LT_3P9_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CHG_VBATT_LT_3P9);
if (status1 & WM8350_CHG_VBATT_LT_3P1_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CHG_VBATT_LT_3P1);
if (status1 & WM8350_CHG_VBATT_LT_2P85_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CHG_VBATT_LT_2P85);
if (status1 & WM8350_RTC_ALM_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_ALM);
if (status1 & WM8350_RTC_SEC_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_SEC);
if (status1 & WM8350_RTC_PER_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_PER);
}
/* current sink, system, aux adc */
if (status2) {
if (status2 & WM8350_CS1_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS1);
if (status2 & WM8350_CS2_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS2);
if (status2 & WM8350_SYS_HYST_COMP_FAIL_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_SYS_HYST_COMP_FAIL);
if (status2 & WM8350_SYS_CHIP_GT115_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_SYS_CHIP_GT115);
if (status2 & WM8350_SYS_CHIP_GT140_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_SYS_CHIP_GT140);
if (status2 & WM8350_SYS_WDOG_TO_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_SYS_WDOG_TO);
if (status2 & WM8350_AUXADC_DATARDY_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_AUXADC_DATARDY);
if (status2 & WM8350_AUXADC_DCOMP4_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_AUXADC_DCOMP4);
if (status2 & WM8350_AUXADC_DCOMP3_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_AUXADC_DCOMP3);
if (status2 & WM8350_AUXADC_DCOMP2_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_AUXADC_DCOMP2);
if (status2 & WM8350_AUXADC_DCOMP1_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_AUXADC_DCOMP1);
if (status2 & WM8350_USB_LIMIT_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_USB_LIMIT);
}
/* wake, codec, ext */
if (comp) {
if (comp & WM8350_WKUP_OFF_STATE_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_WKUP_OFF_STATE);
if (comp & WM8350_WKUP_HIB_STATE_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_WKUP_HIB_STATE);
if (comp & WM8350_WKUP_CONV_FAULT_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_WKUP_CONV_FAULT);
if (comp & WM8350_WKUP_WDOG_RST_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_WKUP_WDOG_RST);
if (comp & WM8350_WKUP_GP_PWR_ON_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_WKUP_GP_PWR_ON);
if (comp & WM8350_WKUP_ONKEY_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_WKUP_ONKEY);
if (comp & WM8350_WKUP_GP_WAKEUP_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_WKUP_GP_WAKEUP);
if (comp & WM8350_CODEC_JCK_DET_L_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CODEC_JCK_DET_L);
if (comp & WM8350_CODEC_JCK_DET_R_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CODEC_JCK_DET_R);
if (comp & WM8350_CODEC_MICSCD_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_CODEC_MICSCD);
if (comp & WM8350_CODEC_MICD_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_CODEC_MICD);
if (comp & WM8350_EXT_USB_FB_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_USB_FB);
if (comp & WM8350_EXT_WALL_FB_EINT)
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_EXT_WALL_FB);
if (comp & WM8350_EXT_BAT_FB_EINT)
wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_BAT_FB);
}
if (level_one & WM8350_GP_INT) {
int i;
u16 gpio;
gpio = wm8350_reg_read(wm8350, WM8350_GPIO_INT_STATUS);
gpio &= ~wm8350_reg_read(wm8350,
WM8350_GPIO_INT_STATUS_MASK);
for (i = 0; i < 12; i++) {
if (gpio & (1 << i))
wm8350_irq_call_handler(wm8350,
WM8350_IRQ_GPIO(i));
}
}
return IRQ_HANDLED;
}
int wm8350_register_irq(struct wm8350 *wm8350, int irq,
void (*handler) (struct wm8350 *, int, void *),
void *data)
{
if (irq < 0 || irq > WM8350_NUM_IRQ || !handler)
return -EINVAL;
if (wm8350->irq[irq].handler)
return -EBUSY;
mutex_lock(&wm8350->irq_mutex);
wm8350->irq[irq].handler = handler;
wm8350->irq[irq].data = data;
mutex_unlock(&wm8350->irq_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_register_irq);
int wm8350_free_irq(struct wm8350 *wm8350, int irq)
{
if (irq < 0 || irq > WM8350_NUM_IRQ)
return -EINVAL;
mutex_lock(&wm8350->irq_mutex);
wm8350->irq[irq].handler = NULL;
mutex_unlock(&wm8350->irq_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_free_irq);
int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
{
switch (irq) {
case WM8350_IRQ_CHG_BAT_HOT:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_BAT_HOT_EINT);
case WM8350_IRQ_CHG_BAT_COLD:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_BAT_COLD_EINT);
case WM8350_IRQ_CHG_BAT_FAIL:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_BAT_FAIL_EINT);
case WM8350_IRQ_CHG_TO:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_TO_EINT);
case WM8350_IRQ_CHG_END:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_END_EINT);
case WM8350_IRQ_CHG_START:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_START_EINT);
case WM8350_IRQ_CHG_FAST_RDY:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_FAST_RDY_EINT);
case WM8350_IRQ_RTC_PER:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_RTC_PER_EINT);
case WM8350_IRQ_RTC_SEC:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_RTC_SEC_EINT);
case WM8350_IRQ_RTC_ALM:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_RTC_ALM_EINT);
case WM8350_IRQ_CHG_VBATT_LT_3P9:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_VBATT_LT_3P9_EINT);
case WM8350_IRQ_CHG_VBATT_LT_3P1:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_VBATT_LT_3P1_EINT);
case WM8350_IRQ_CHG_VBATT_LT_2P85:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_VBATT_LT_2P85_EINT);
case WM8350_IRQ_CS1:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_CS1_EINT);
case WM8350_IRQ_CS2:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_CS2_EINT);
case WM8350_IRQ_USB_LIMIT:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_USB_LIMIT_EINT);
case WM8350_IRQ_AUXADC_DATARDY:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DATARDY_EINT);
case WM8350_IRQ_AUXADC_DCOMP4:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP4_EINT);
case WM8350_IRQ_AUXADC_DCOMP3:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP3_EINT);
case WM8350_IRQ_AUXADC_DCOMP2:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP2_EINT);
case WM8350_IRQ_AUXADC_DCOMP1:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP1_EINT);
case WM8350_IRQ_SYS_HYST_COMP_FAIL:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
case WM8350_IRQ_SYS_CHIP_GT115:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_CHIP_GT115_EINT);
case WM8350_IRQ_SYS_CHIP_GT140:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_CHIP_GT140_EINT);
case WM8350_IRQ_SYS_WDOG_TO:
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_WDOG_TO_EINT);
case WM8350_IRQ_UV_LDO4:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO4_EINT);
case WM8350_IRQ_UV_LDO3:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO3_EINT);
case WM8350_IRQ_UV_LDO2:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO2_EINT);
case WM8350_IRQ_UV_LDO1:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO1_EINT);
case WM8350_IRQ_UV_DC6:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC6_EINT);
case WM8350_IRQ_UV_DC5:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC5_EINT);
case WM8350_IRQ_UV_DC4:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC4_EINT);
case WM8350_IRQ_UV_DC3:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC3_EINT);
case WM8350_IRQ_UV_DC2:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC2_EINT);
case WM8350_IRQ_UV_DC1:
return wm8350_set_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC1_EINT);
case WM8350_IRQ_OC_LS:
return wm8350_set_bits(wm8350,
WM8350_OVER_CURRENT_INT_STATUS_MASK,
WM8350_IM_OC_LS_EINT);
case WM8350_IRQ_EXT_USB_FB:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_EXT_USB_FB_EINT);
case WM8350_IRQ_EXT_WALL_FB:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_EXT_WALL_FB_EINT);
case WM8350_IRQ_EXT_BAT_FB:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_EXT_BAT_FB_EINT);
case WM8350_IRQ_CODEC_JCK_DET_L:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_JCK_DET_L_EINT);
case WM8350_IRQ_CODEC_JCK_DET_R:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_JCK_DET_R_EINT);
case WM8350_IRQ_CODEC_MICSCD:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_MICSCD_EINT);
case WM8350_IRQ_CODEC_MICD:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_MICD_EINT);
case WM8350_IRQ_WKUP_OFF_STATE:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_OFF_STATE_EINT);
case WM8350_IRQ_WKUP_HIB_STATE:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_HIB_STATE_EINT);
case WM8350_IRQ_WKUP_CONV_FAULT:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_CONV_FAULT_EINT);
case WM8350_IRQ_WKUP_WDOG_RST:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_OFF_STATE_EINT);
case WM8350_IRQ_WKUP_GP_PWR_ON:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_GP_PWR_ON_EINT);
case WM8350_IRQ_WKUP_ONKEY:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_ONKEY_EINT);
case WM8350_IRQ_WKUP_GP_WAKEUP:
return wm8350_set_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_GP_WAKEUP_EINT);
case WM8350_IRQ_GPIO(0):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP0_EINT);
case WM8350_IRQ_GPIO(1):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP1_EINT);
case WM8350_IRQ_GPIO(2):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP2_EINT);
case WM8350_IRQ_GPIO(3):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP3_EINT);
case WM8350_IRQ_GPIO(4):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP4_EINT);
case WM8350_IRQ_GPIO(5):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP5_EINT);
case WM8350_IRQ_GPIO(6):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP6_EINT);
case WM8350_IRQ_GPIO(7):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP7_EINT);
case WM8350_IRQ_GPIO(8):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP8_EINT);
case WM8350_IRQ_GPIO(9):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP9_EINT);
case WM8350_IRQ_GPIO(10):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP10_EINT);
case WM8350_IRQ_GPIO(11):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP11_EINT);
case WM8350_IRQ_GPIO(12):
return wm8350_set_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP12_EINT);
default:
dev_warn(wm8350->dev, "Attempting to mask unknown IRQ %d\n",
irq);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_mask_irq);
int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
{
switch (irq) {
case WM8350_IRQ_CHG_BAT_HOT:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_BAT_HOT_EINT);
case WM8350_IRQ_CHG_BAT_COLD:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_BAT_COLD_EINT);
case WM8350_IRQ_CHG_BAT_FAIL:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_BAT_FAIL_EINT);
case WM8350_IRQ_CHG_TO:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_TO_EINT);
case WM8350_IRQ_CHG_END:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_END_EINT);
case WM8350_IRQ_CHG_START:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_START_EINT);
case WM8350_IRQ_CHG_FAST_RDY:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_FAST_RDY_EINT);
case WM8350_IRQ_RTC_PER:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_RTC_PER_EINT);
case WM8350_IRQ_RTC_SEC:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_RTC_SEC_EINT);
case WM8350_IRQ_RTC_ALM:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_RTC_ALM_EINT);
case WM8350_IRQ_CHG_VBATT_LT_3P9:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_VBATT_LT_3P9_EINT);
case WM8350_IRQ_CHG_VBATT_LT_3P1:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_VBATT_LT_3P1_EINT);
case WM8350_IRQ_CHG_VBATT_LT_2P85:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
WM8350_IM_CHG_VBATT_LT_2P85_EINT);
case WM8350_IRQ_CS1:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_CS1_EINT);
case WM8350_IRQ_CS2:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_CS2_EINT);
case WM8350_IRQ_USB_LIMIT:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_USB_LIMIT_EINT);
case WM8350_IRQ_AUXADC_DATARDY:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DATARDY_EINT);
case WM8350_IRQ_AUXADC_DCOMP4:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP4_EINT);
case WM8350_IRQ_AUXADC_DCOMP3:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP3_EINT);
case WM8350_IRQ_AUXADC_DCOMP2:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP2_EINT);
case WM8350_IRQ_AUXADC_DCOMP1:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_AUXADC_DCOMP1_EINT);
case WM8350_IRQ_SYS_HYST_COMP_FAIL:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
case WM8350_IRQ_SYS_CHIP_GT115:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_CHIP_GT115_EINT);
case WM8350_IRQ_SYS_CHIP_GT140:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_CHIP_GT140_EINT);
case WM8350_IRQ_SYS_WDOG_TO:
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
WM8350_IM_SYS_WDOG_TO_EINT);
case WM8350_IRQ_UV_LDO4:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO4_EINT);
case WM8350_IRQ_UV_LDO3:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO3_EINT);
case WM8350_IRQ_UV_LDO2:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO2_EINT);
case WM8350_IRQ_UV_LDO1:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_LDO1_EINT);
case WM8350_IRQ_UV_DC6:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC6_EINT);
case WM8350_IRQ_UV_DC5:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC5_EINT);
case WM8350_IRQ_UV_DC4:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC4_EINT);
case WM8350_IRQ_UV_DC3:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC3_EINT);
case WM8350_IRQ_UV_DC2:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC2_EINT);
case WM8350_IRQ_UV_DC1:
return wm8350_clear_bits(wm8350,
WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
WM8350_IM_UV_DC1_EINT);
case WM8350_IRQ_OC_LS:
return wm8350_clear_bits(wm8350,
WM8350_OVER_CURRENT_INT_STATUS_MASK,
WM8350_IM_OC_LS_EINT);
case WM8350_IRQ_EXT_USB_FB:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_EXT_USB_FB_EINT);
case WM8350_IRQ_EXT_WALL_FB:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_EXT_WALL_FB_EINT);
case WM8350_IRQ_EXT_BAT_FB:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_EXT_BAT_FB_EINT);
case WM8350_IRQ_CODEC_JCK_DET_L:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_JCK_DET_L_EINT);
case WM8350_IRQ_CODEC_JCK_DET_R:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_JCK_DET_R_EINT);
case WM8350_IRQ_CODEC_MICSCD:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_MICSCD_EINT);
case WM8350_IRQ_CODEC_MICD:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_CODEC_MICD_EINT);
case WM8350_IRQ_WKUP_OFF_STATE:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_OFF_STATE_EINT);
case WM8350_IRQ_WKUP_HIB_STATE:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_HIB_STATE_EINT);
case WM8350_IRQ_WKUP_CONV_FAULT:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_CONV_FAULT_EINT);
case WM8350_IRQ_WKUP_WDOG_RST:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_OFF_STATE_EINT);
case WM8350_IRQ_WKUP_GP_PWR_ON:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_GP_PWR_ON_EINT);
case WM8350_IRQ_WKUP_ONKEY:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_ONKEY_EINT);
case WM8350_IRQ_WKUP_GP_WAKEUP:
return wm8350_clear_bits(wm8350,
WM8350_COMPARATOR_INT_STATUS_MASK,
WM8350_IM_WKUP_GP_WAKEUP_EINT);
case WM8350_IRQ_GPIO(0):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP0_EINT);
case WM8350_IRQ_GPIO(1):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP1_EINT);
case WM8350_IRQ_GPIO(2):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP2_EINT);
case WM8350_IRQ_GPIO(3):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP3_EINT);
case WM8350_IRQ_GPIO(4):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP4_EINT);
case WM8350_IRQ_GPIO(5):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP5_EINT);
case WM8350_IRQ_GPIO(6):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP6_EINT);
case WM8350_IRQ_GPIO(7):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP7_EINT);
case WM8350_IRQ_GPIO(8):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP8_EINT);
case WM8350_IRQ_GPIO(9):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP9_EINT);
case WM8350_IRQ_GPIO(10):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP10_EINT);
case WM8350_IRQ_GPIO(11):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP11_EINT);
case WM8350_IRQ_GPIO(12):
return wm8350_clear_bits(wm8350,
WM8350_GPIO_INT_STATUS_MASK,
WM8350_IM_GP12_EINT);
default:
dev_warn(wm8350->dev, "Attempting to unmask unknown IRQ %d\n",
irq);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
{
u16 reg, result = 0;
@ -1264,7 +537,7 @@ static void wm8350_client_dev_register(struct wm8350 *wm8350,
int ret;
*pdev = platform_device_alloc(name, -1);
if (pdev == NULL) {
if (*pdev == NULL) {
dev_err(wm8350->dev, "Failed to allocate %s\n", name);
return;
}
@ -1409,49 +682,18 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
return ret;
}
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF);
mutex_init(&wm8350->auxadc_mutex);
mutex_init(&wm8350->irq_mutex);
if (irq) {
int flags = IRQF_ONESHOT;
if (pdata && pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;
wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
WM8350_IRQ_POL);
} else {
flags |= IRQF_TRIGGER_LOW;
wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
WM8350_IRQ_POL);
}
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);
goto err;
}
} else {
dev_err(wm8350->dev, "No IRQ configured\n");
ret = wm8350_irq_init(wm8350, irq, pdata);
if (ret < 0)
goto err;
}
wm8350->chip_irq = irq;
if (pdata && pdata->init) {
ret = pdata->init(wm8350);
if (ret != 0) {
dev_err(wm8350->dev, "Platform init() failed: %d\n",
ret);
goto err;
goto err_irq;
}
}
@ -1470,6 +712,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
return 0;
err_irq:
wm8350_irq_exit(wm8350);
err:
kfree(wm8350->reg_cache);
return ret;
@ -1493,7 +737,8 @@ void wm8350_device_exit(struct wm8350 *wm8350)
platform_device_unregister(wm8350->gpio.pdev);
platform_device_unregister(wm8350->codec.pdev);
free_irq(wm8350->chip_irq, wm8350);
wm8350_irq_exit(wm8350);
kfree(wm8350->reg_cache);
}
EXPORT_SYMBOL_GPL(wm8350_device_exit);

529
drivers/mfd/wm8350-irq.c Normal file
View File

@ -0,0 +1,529 @@
/*
* wm8350-irq.c -- IRQ support for Wolfson WM8350
*
* Copyright 2007, 2008, 2009 Wolfson Microelectronics PLC.
*
* Author: Liam Girdwood, Mark Brown
*
* 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/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/mfd/wm8350/core.h>
#include <linux/mfd/wm8350/audio.h>
#include <linux/mfd/wm8350/comparator.h>
#include <linux/mfd/wm8350/gpio.h>
#include <linux/mfd/wm8350/pmic.h>
#include <linux/mfd/wm8350/rtc.h>
#include <linux/mfd/wm8350/supply.h>
#include <linux/mfd/wm8350/wdt.h>
#define WM8350_NUM_IRQ_REGS 7
#define WM8350_INT_OFFSET_1 0
#define WM8350_INT_OFFSET_2 1
#define WM8350_POWER_UP_INT_OFFSET 2
#define WM8350_UNDER_VOLTAGE_INT_OFFSET 3
#define WM8350_OVER_CURRENT_INT_OFFSET 4
#define WM8350_GPIO_INT_OFFSET 5
#define WM8350_COMPARATOR_INT_OFFSET 6
struct wm8350_irq_data {
int primary;
int reg;
int mask;
int primary_only;
};
static struct wm8350_irq_data wm8350_irqs[] = {
[WM8350_IRQ_OC_LS] = {
.primary = WM8350_OC_INT,
.reg = WM8350_OVER_CURRENT_INT_OFFSET,
.mask = WM8350_OC_LS_EINT,
.primary_only = 1,
},
[WM8350_IRQ_UV_DC1] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_DC1_EINT,
},
[WM8350_IRQ_UV_DC2] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_DC2_EINT,
},
[WM8350_IRQ_UV_DC3] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_DC3_EINT,
},
[WM8350_IRQ_UV_DC4] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_DC4_EINT,
},
[WM8350_IRQ_UV_DC5] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_DC5_EINT,
},
[WM8350_IRQ_UV_DC6] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_DC6_EINT,
},
[WM8350_IRQ_UV_LDO1] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_LDO1_EINT,
},
[WM8350_IRQ_UV_LDO2] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_LDO2_EINT,
},
[WM8350_IRQ_UV_LDO3] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_LDO3_EINT,
},
[WM8350_IRQ_UV_LDO4] = {
.primary = WM8350_UV_INT,
.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
.mask = WM8350_UV_LDO4_EINT,
},
[WM8350_IRQ_CHG_BAT_HOT] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_BAT_HOT_EINT,
},
[WM8350_IRQ_CHG_BAT_COLD] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_BAT_COLD_EINT,
},
[WM8350_IRQ_CHG_BAT_FAIL] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_BAT_FAIL_EINT,
},
[WM8350_IRQ_CHG_TO] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_TO_EINT,
},
[WM8350_IRQ_CHG_END] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_END_EINT,
},
[WM8350_IRQ_CHG_START] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_START_EINT,
},
[WM8350_IRQ_CHG_FAST_RDY] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_FAST_RDY_EINT,
},
[WM8350_IRQ_CHG_VBATT_LT_3P9] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_VBATT_LT_3P9_EINT,
},
[WM8350_IRQ_CHG_VBATT_LT_3P1] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_VBATT_LT_3P1_EINT,
},
[WM8350_IRQ_CHG_VBATT_LT_2P85] = {
.primary = WM8350_CHG_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_CHG_VBATT_LT_2P85_EINT,
},
[WM8350_IRQ_RTC_ALM] = {
.primary = WM8350_RTC_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_RTC_ALM_EINT,
},
[WM8350_IRQ_RTC_SEC] = {
.primary = WM8350_RTC_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_RTC_SEC_EINT,
},
[WM8350_IRQ_RTC_PER] = {
.primary = WM8350_RTC_INT,
.reg = WM8350_INT_OFFSET_1,
.mask = WM8350_RTC_PER_EINT,
},
[WM8350_IRQ_CS1] = {
.primary = WM8350_CS_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_CS1_EINT,
},
[WM8350_IRQ_CS2] = {
.primary = WM8350_CS_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_CS2_EINT,
},
[WM8350_IRQ_SYS_HYST_COMP_FAIL] = {
.primary = WM8350_SYS_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_SYS_HYST_COMP_FAIL_EINT,
},
[WM8350_IRQ_SYS_CHIP_GT115] = {
.primary = WM8350_SYS_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_SYS_CHIP_GT115_EINT,
},
[WM8350_IRQ_SYS_CHIP_GT140] = {
.primary = WM8350_SYS_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_SYS_CHIP_GT140_EINT,
},
[WM8350_IRQ_SYS_WDOG_TO] = {
.primary = WM8350_SYS_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_SYS_WDOG_TO_EINT,
},
[WM8350_IRQ_AUXADC_DATARDY] = {
.primary = WM8350_AUXADC_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_AUXADC_DATARDY_EINT,
},
[WM8350_IRQ_AUXADC_DCOMP4] = {
.primary = WM8350_AUXADC_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_AUXADC_DCOMP4_EINT,
},
[WM8350_IRQ_AUXADC_DCOMP3] = {
.primary = WM8350_AUXADC_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_AUXADC_DCOMP3_EINT,
},
[WM8350_IRQ_AUXADC_DCOMP2] = {
.primary = WM8350_AUXADC_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_AUXADC_DCOMP2_EINT,
},
[WM8350_IRQ_AUXADC_DCOMP1] = {
.primary = WM8350_AUXADC_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_AUXADC_DCOMP1_EINT,
},
[WM8350_IRQ_USB_LIMIT] = {
.primary = WM8350_USB_INT,
.reg = WM8350_INT_OFFSET_2,
.mask = WM8350_USB_LIMIT_EINT,
.primary_only = 1,
},
[WM8350_IRQ_WKUP_OFF_STATE] = {
.primary = WM8350_WKUP_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_WKUP_OFF_STATE_EINT,
},
[WM8350_IRQ_WKUP_HIB_STATE] = {
.primary = WM8350_WKUP_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_WKUP_HIB_STATE_EINT,
},
[WM8350_IRQ_WKUP_CONV_FAULT] = {
.primary = WM8350_WKUP_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_WKUP_CONV_FAULT_EINT,
},
[WM8350_IRQ_WKUP_WDOG_RST] = {
.primary = WM8350_WKUP_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_WKUP_WDOG_RST_EINT,
},
[WM8350_IRQ_WKUP_GP_PWR_ON] = {
.primary = WM8350_WKUP_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_WKUP_GP_PWR_ON_EINT,
},
[WM8350_IRQ_WKUP_ONKEY] = {
.primary = WM8350_WKUP_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_WKUP_ONKEY_EINT,
},
[WM8350_IRQ_WKUP_GP_WAKEUP] = {
.primary = WM8350_WKUP_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_WKUP_GP_WAKEUP_EINT,
},
[WM8350_IRQ_CODEC_JCK_DET_L] = {
.primary = WM8350_CODEC_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_CODEC_JCK_DET_L_EINT,
},
[WM8350_IRQ_CODEC_JCK_DET_R] = {
.primary = WM8350_CODEC_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_CODEC_JCK_DET_R_EINT,
},
[WM8350_IRQ_CODEC_MICSCD] = {
.primary = WM8350_CODEC_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_CODEC_MICSCD_EINT,
},
[WM8350_IRQ_CODEC_MICD] = {
.primary = WM8350_CODEC_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_CODEC_MICD_EINT,
},
[WM8350_IRQ_EXT_USB_FB] = {
.primary = WM8350_EXT_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_EXT_USB_FB_EINT,
},
[WM8350_IRQ_EXT_WALL_FB] = {
.primary = WM8350_EXT_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_EXT_WALL_FB_EINT,
},
[WM8350_IRQ_EXT_BAT_FB] = {
.primary = WM8350_EXT_INT,
.reg = WM8350_COMPARATOR_INT_OFFSET,
.mask = WM8350_EXT_BAT_FB_EINT,
},
[WM8350_IRQ_GPIO(0)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP0_EINT,
},
[WM8350_IRQ_GPIO(1)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP1_EINT,
},
[WM8350_IRQ_GPIO(2)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP2_EINT,
},
[WM8350_IRQ_GPIO(3)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP3_EINT,
},
[WM8350_IRQ_GPIO(4)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP4_EINT,
},
[WM8350_IRQ_GPIO(5)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP5_EINT,
},
[WM8350_IRQ_GPIO(6)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP6_EINT,
},
[WM8350_IRQ_GPIO(7)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP7_EINT,
},
[WM8350_IRQ_GPIO(8)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP8_EINT,
},
[WM8350_IRQ_GPIO(9)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP9_EINT,
},
[WM8350_IRQ_GPIO(10)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP10_EINT,
},
[WM8350_IRQ_GPIO(11)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP11_EINT,
},
[WM8350_IRQ_GPIO(12)] = {
.primary = WM8350_GP_INT,
.reg = WM8350_GPIO_INT_OFFSET,
.mask = WM8350_GP12_EINT,
},
};
static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
{
mutex_lock(&wm8350->irq_mutex);
if (wm8350->irq[irq].handler)
wm8350->irq[irq].handler(irq, wm8350->irq[irq].data);
else {
dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
irq);
wm8350_mask_irq(wm8350, irq);
}
mutex_unlock(&wm8350->irq_mutex);
}
/*
* 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 irqreturn_t wm8350_irq(int irq, void *irq_data)
{
struct wm8350 *wm8350 = irq_data;
u16 level_one;
u16 sub_reg[WM8350_NUM_IRQ_REGS];
int read_done[WM8350_NUM_IRQ_REGS];
struct wm8350_irq_data *data;
int i;
/* TODO: Use block reads to improve performance? */
level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
& ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
if (!level_one)
return IRQ_NONE;
memset(&read_done, 0, sizeof(read_done));
for (i = 0; i < ARRAY_SIZE(wm8350_irqs); i++) {
data = &wm8350_irqs[i];
if (!(level_one & data->primary))
continue;
if (!read_done[data->reg]) {
sub_reg[data->reg] =
wm8350_reg_read(wm8350, WM8350_INT_STATUS_1 +
data->reg);
sub_reg[data->reg] &=
~wm8350_reg_read(wm8350,
WM8350_INT_STATUS_1_MASK +
data->reg);
read_done[data->reg] = 1;
}
if (sub_reg[data->reg] & data->mask)
wm8350_irq_call_handler(wm8350, i);
}
return IRQ_HANDLED;
}
int wm8350_register_irq(struct wm8350 *wm8350, int irq,
irq_handler_t handler, unsigned long flags,
const char *name, void *data)
{
if (irq < 0 || irq > WM8350_NUM_IRQ || !handler)
return -EINVAL;
if (wm8350->irq[irq].handler)
return -EBUSY;
mutex_lock(&wm8350->irq_mutex);
wm8350->irq[irq].handler = handler;
wm8350->irq[irq].data = data;
mutex_unlock(&wm8350->irq_mutex);
wm8350_unmask_irq(wm8350, irq);
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_register_irq);
int wm8350_free_irq(struct wm8350 *wm8350, int irq)
{
if (irq < 0 || irq > WM8350_NUM_IRQ)
return -EINVAL;
wm8350_mask_irq(wm8350, irq);
mutex_lock(&wm8350->irq_mutex);
wm8350->irq[irq].handler = NULL;
mutex_unlock(&wm8350->irq_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_free_irq);
int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
{
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK +
wm8350_irqs[irq].reg,
wm8350_irqs[irq].mask);
}
EXPORT_SYMBOL_GPL(wm8350_mask_irq);
int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
{
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK +
wm8350_irqs[irq].reg,
wm8350_irqs[irq].mask);
}
EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
int wm8350_irq_init(struct wm8350 *wm8350, int irq,
struct wm8350_platform_data *pdata)
{
int ret;
int flags = IRQF_ONESHOT;
if (!irq) {
dev_err(wm8350->dev, "No IRQ configured\n");
return -EINVAL;
}
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF);
mutex_init(&wm8350->irq_mutex);
wm8350->chip_irq = irq;
if (pdata && pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;
wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
WM8350_IRQ_POL);
} else {
flags |= IRQF_TRIGGER_LOW;
wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
WM8350_IRQ_POL);
}
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);
return ret;
}
int wm8350_irq_exit(struct wm8350 *wm8350)
{
free_irq(wm8350->chip_irq, wm8350);
return 0;
}

View File

@ -3170,14 +3170,6 @@ const u16 wm8352_mode3_defaults[] = {
};
#endif
/* The register defaults for the config mode used must be compiled in but
* due to the impact on kernel size it is possible to disable
*/
#ifndef WM8350_HAVE_CONFIG_MODE
#warning No WM8350 config modes supported - select at least one of the
#warning MFD_WM8350_CONFIG_MODE_n options from the board driver.
#endif
/*
* Access masks.
*/

View File

@ -303,7 +303,6 @@ static const u8 mbc_irq_handlers[] = {
static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
{
struct pcf50633_mbc *mbc;
struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
int ret;
int i;
u8 mbcs1;
@ -313,7 +312,7 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, mbc);
mbc->pcf = pdata->pcf;
mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
/* Set up IRQ handlers */
for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)

View File

@ -184,8 +184,9 @@ static ssize_t charger_state_show(struct device *dev,
static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL);
static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data)
static irqreturn_t wm8350_charger_handler(int irq, void *data)
{
struct wm8350 *wm8350 = data;
struct wm8350_power *power = &wm8350->power;
struct wm8350_charger_policy *policy = power->policy;
@ -238,6 +239,8 @@ static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data)
default:
dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);
}
return IRQ_HANDLED;
}
/*********************************************************************
@ -387,73 +390,55 @@ static void wm8350_init_charger(struct wm8350 *wm8350)
{
/* register our interest in charger events */
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
wm8350_charger_handler, 0, "Battery hot", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
wm8350_charger_handler, 0, "Battery cold", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
wm8350_charger_handler, 0, "Battery fail", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_TO);
wm8350_charger_handler, 0,
"Charger timeout", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_END);
wm8350_charger_handler, 0,
"Charge end", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_START);
wm8350_charger_handler, 0,
"Charge start", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY);
wm8350_charger_handler, 0,
"Fast charge ready", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
wm8350_charger_handler, 0,
"Battery <3.9V", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
wm8350_charger_handler, 0,
"Battery <3.1V", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
wm8350_charger_handler, 0,
"Battery <2.85V", wm8350);
/* and supply change events */
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
wm8350_charger_handler, 0, "USB", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
wm8350_charger_handler, 0, "Wall", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,
wm8350_charger_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
wm8350_charger_handler, 0, "Battery", wm8350);
}
static void free_charger_irq(struct wm8350 *wm8350)
{
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_TO);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_END);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_START);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
}

View File

@ -70,7 +70,7 @@ config REGULATOR_MAX1586
for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TPS695x0 PMIC"
bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC"
depends on TWL4030_CORE
help
This driver supports the voltage regulators provided by

View File

@ -11,7 +11,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
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_TWL4030) += twl-regulator.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o

View File

@ -314,13 +314,15 @@ static int __devinit pcf50633_regulator_probe(struct platform_device *pdev)
struct pcf50633 *pcf;
/* Already set by core driver */
pcf = platform_get_drvdata(pdev);
pcf = dev_to_pcf50633(pdev->dev.parent);
rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
pdev->dev.platform_data, pcf);
if (IS_ERR(rdev))
return PTR_ERR(rdev);
platform_set_drvdata(pdev, rdev);
if (pcf->pdata->regulator_registered)
pcf->pdata->regulator_registered(pcf, pdev->id);
@ -331,6 +333,7 @@ static int __devexit pcf50633_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(rdev);
return 0;

View File

@ -1,5 +1,5 @@
/*
* twl4030-regulator.c -- support regulators in twl4030 family chips
* twl-regulator.c -- support regulators in twl4030/twl6030 family chips
*
* Copyright (C) 2008 David Brownell
*
@ -15,11 +15,11 @@
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
/*
* The TWL4030/TW5030/TPS659x0 family chips include power management, a
* The TWL4030/TW5030/TPS659x0/TWL6030 family chips include power management, a
* USB OTG transceiver, an RTC, ADC, PWM, and lots more. Some versions
* include an audio codec, battery charger, and more voltage regulators.
* These chips are often used in OMAP-based systems.
@ -33,7 +33,7 @@ struct twlreg_info {
/* start of regulator's PM_RECEIVER control register bank */
u8 base;
/* twl4030 resource ID, for resource control state machine */
/* twl resource ID, for resource control state machine */
u8 id;
/* voltage in mV = table[VSEL]; table_len must be a power-of-two */
@ -52,27 +52,38 @@ struct twlreg_info {
* The first three registers of all power resource banks help hardware to
* manage the various resource groups.
*/
/* Common offset in TWL4030/6030 */
#define VREG_GRP 0
/* TWL4030 register offsets */
#define VREG_TYPE 1
#define VREG_REMAP 2
#define VREG_DEDICATED 3 /* LDO control */
/* TWL6030 register offsets */
#define VREG_TRANS 1
#define VREG_STATE 2
#define VREG_VOLTAGE 3
/* TWL6030 Misc register offsets */
#define VREG_BC_ALL 1
#define VREG_BC_REF 2
#define VREG_BC_PROC 3
#define VREG_BC_CLK_RST 4
static inline int
twl4030reg_read(struct twlreg_info *info, unsigned offset)
twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset)
{
u8 value;
int status;
status = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER,
status = twl_i2c_read_u8(slave_subgp,
&value, info->base + offset);
return (status < 0) ? status : value;
}
static inline int
twl4030reg_write(struct twlreg_info *info, unsigned offset, u8 value)
twlreg_write(struct twlreg_info *info, unsigned slave_subgp, unsigned offset,
u8 value)
{
return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
return twl_i2c_write_u8(slave_subgp,
value, info->base + offset);
}
@ -80,59 +91,79 @@ twl4030reg_write(struct twlreg_info *info, unsigned offset, u8 value)
/* generic power resource operations, which work on all regulators */
static int twl4030reg_grp(struct regulator_dev *rdev)
static int twlreg_grp(struct regulator_dev *rdev)
{
return twl4030reg_read(rdev_get_drvdata(rdev), VREG_GRP);
return twlreg_read(rdev_get_drvdata(rdev), TWL_MODULE_PM_RECEIVER,
VREG_GRP);
}
/*
* Enable/disable regulators by joining/leaving the P1 (processor) group.
* We assume nobody else is updating the DEV_GRP registers.
*/
/* definition for 4030 family */
#define P3_GRP_4030 BIT(7) /* "peripherals" */
#define P2_GRP_4030 BIT(6) /* secondary processor, modem, etc */
#define P1_GRP_4030 BIT(5) /* CPU/Linux */
/* definition for 6030 family */
#define P3_GRP_6030 BIT(2) /* secondary processor, modem, etc */
#define P2_GRP_6030 BIT(1) /* "peripherals" */
#define P1_GRP_6030 BIT(0) /* CPU/Linux */
#define P3_GRP BIT(7) /* "peripherals" */
#define P2_GRP BIT(6) /* secondary processor, modem, etc */
#define P1_GRP BIT(5) /* CPU/Linux */
static int twl4030reg_is_enabled(struct regulator_dev *rdev)
static int twlreg_is_enabled(struct regulator_dev *rdev)
{
int state = twl4030reg_grp(rdev);
int state = twlreg_grp(rdev);
if (state < 0)
return state;
return (state & P1_GRP) != 0;
if (twl_class_is_4030())
state &= P1_GRP_4030;
else
state &= P1_GRP_6030;
return state;
}
static int twl4030reg_enable(struct regulator_dev *rdev)
static int twlreg_enable(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int grp;
grp = twl4030reg_read(info, VREG_GRP);
grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
if (grp < 0)
return grp;
grp |= P1_GRP;
return twl4030reg_write(info, VREG_GRP, grp);
if (twl_class_is_4030())
grp |= P1_GRP_4030;
else
grp |= P1_GRP_6030;
return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
}
static int twl4030reg_disable(struct regulator_dev *rdev)
static int twlreg_disable(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int grp;
grp = twl4030reg_read(info, VREG_GRP);
grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
if (grp < 0)
return grp;
grp &= ~P1_GRP;
return twl4030reg_write(info, VREG_GRP, grp);
if (twl_class_is_4030())
grp &= ~P1_GRP_4030;
else
grp &= ~P1_GRP_6030;
return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
}
static int twl4030reg_get_status(struct regulator_dev *rdev)
static int twlreg_get_status(struct regulator_dev *rdev)
{
int state = twl4030reg_grp(rdev);
int state = twlreg_grp(rdev);
if (twl_class_is_6030())
return 0; /* FIXME return for 6030 regulator */
if (state < 0)
return state;
@ -146,12 +177,15 @@ static int twl4030reg_get_status(struct regulator_dev *rdev)
: REGULATOR_STATUS_STANDBY;
}
static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
static int twlreg_set_mode(struct regulator_dev *rdev, unsigned mode)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
unsigned message;
int status;
if (twl_class_is_6030())
return 0; /* FIXME return for 6030 regulator */
/* We can only set the mode through state machine commands... */
switch (mode) {
case REGULATOR_MODE_NORMAL:
@ -165,18 +199,18 @@ static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
}
/* Ensure the resource is associated with some group */
status = twl4030reg_grp(rdev);
status = twlreg_grp(rdev);
if (status < 0)
return status;
if (!(status & (P3_GRP | P2_GRP | P1_GRP)))
if (!(status & (P3_GRP_4030 | P2_GRP_4030 | P1_GRP_4030)))
return -EACCES;
status = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
status = twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
message >> 8, 0x15 /* PB_WORD_MSB */ );
if (status >= 0)
return status;
return twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
return twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
message, 0x16 /* PB_WORD_LSB */ );
}
@ -260,9 +294,31 @@ static const u16 VSIM_VSEL_table[] = {
static const u16 VDAC_VSEL_table[] = {
1200, 1300, 1800, 1800,
};
static const u16 VAUX1_6030_VSEL_table[] = {
1000, 1300, 1800, 2500,
2800, 2900, 3000, 3000,
};
static const u16 VAUX2_6030_VSEL_table[] = {
1200, 1800, 2500, 2750,
2800, 2800, 2800, 2800,
};
static const u16 VAUX3_6030_VSEL_table[] = {
1000, 1200, 1300, 1800,
2500, 2800, 3000, 3000,
};
static const u16 VMMC_VSEL_table[] = {
1200, 1800, 2800, 2900,
3000, 3000, 3000, 3000,
};
static const u16 VPP_VSEL_table[] = {
1800, 1900, 2000, 2100,
2200, 2300, 2400, 2500,
};
static const u16 VUSIM_VSEL_table[] = {
1200, 1800, 2500, 2900,
};
static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index)
static int twlldo_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int mV = info->table[index];
@ -271,7 +327,7 @@ static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index)
}
static int
twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
twlldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int vsel;
@ -288,16 +344,18 @@ twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
/* use the first in-range value */
if (min_uV <= uV && uV <= max_uV)
return twl4030reg_write(info, VREG_DEDICATED, vsel);
return twlreg_write(info, TWL_MODULE_PM_RECEIVER,
VREG_VOLTAGE, vsel);
}
return -EDOM;
}
static int twl4030ldo_get_voltage(struct regulator_dev *rdev)
static int twlldo_get_voltage(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int vsel = twl4030reg_read(info, VREG_DEDICATED);
int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER,
VREG_VOLTAGE);
if (vsel < 0)
return vsel;
@ -306,19 +364,19 @@ static int twl4030ldo_get_voltage(struct regulator_dev *rdev)
return LDO_MV(info->table[vsel]) * 1000;
}
static struct regulator_ops twl4030ldo_ops = {
.list_voltage = twl4030ldo_list_voltage,
static struct regulator_ops twlldo_ops = {
.list_voltage = twlldo_list_voltage,
.set_voltage = twl4030ldo_set_voltage,
.get_voltage = twl4030ldo_get_voltage,
.set_voltage = twlldo_set_voltage,
.get_voltage = twlldo_get_voltage,
.enable = twl4030reg_enable,
.disable = twl4030reg_disable,
.is_enabled = twl4030reg_is_enabled,
.enable = twlreg_enable,
.disable = twlreg_disable,
.is_enabled = twlreg_is_enabled,
.set_mode = twl4030reg_set_mode,
.set_mode = twlreg_set_mode,
.get_status = twl4030reg_get_status,
.get_status = twlreg_get_status,
};
/*----------------------------------------------------------------------*/
@ -326,60 +384,69 @@ static struct regulator_ops twl4030ldo_ops = {
/*
* Fixed voltage LDOs don't have a VSEL field to update.
*/
static int twl4030fixed_list_voltage(struct regulator_dev *rdev, unsigned index)
static int twlfixed_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
return info->min_mV * 1000;
}
static int twl4030fixed_get_voltage(struct regulator_dev *rdev)
static int twlfixed_get_voltage(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
return info->min_mV * 1000;
}
static struct regulator_ops twl4030fixed_ops = {
.list_voltage = twl4030fixed_list_voltage,
static struct regulator_ops twlfixed_ops = {
.list_voltage = twlfixed_list_voltage,
.get_voltage = twl4030fixed_get_voltage,
.get_voltage = twlfixed_get_voltage,
.enable = twl4030reg_enable,
.disable = twl4030reg_disable,
.is_enabled = twl4030reg_is_enabled,
.enable = twlreg_enable,
.disable = twlreg_disable,
.is_enabled = twlreg_is_enabled,
.set_mode = twl4030reg_set_mode,
.set_mode = twlreg_set_mode,
.get_status = twl4030reg_get_status,
.get_status = twlreg_get_status,
};
/*----------------------------------------------------------------------*/
#define TWL_ADJUSTABLE_LDO(label, offset, num) { \
#define TWL4030_ADJUSTABLE_LDO(label, offset, num) \
TWL_ADJUSTABLE_LDO(label, offset, num, TWL4030)
#define TWL4030_FIXED_LDO(label, offset, mVolts, num) \
TWL_FIXED_LDO(label, offset, mVolts, num, TWL4030)
#define TWL6030_ADJUSTABLE_LDO(label, offset, num) \
TWL_ADJUSTABLE_LDO(label, offset, num, TWL6030)
#define TWL6030_FIXED_LDO(label, offset, mVolts, num) \
TWL_FIXED_LDO(label, offset, mVolts, num, TWL6030)
#define TWL_ADJUSTABLE_LDO(label, offset, num, family) { \
.base = offset, \
.id = num, \
.table_len = ARRAY_SIZE(label##_VSEL_table), \
.table = label##_VSEL_table, \
.desc = { \
.name = #label, \
.id = TWL4030_REG_##label, \
.id = family##_REG_##label, \
.n_voltages = ARRAY_SIZE(label##_VSEL_table), \
.ops = &twl4030ldo_ops, \
.ops = &twlldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
}
#define TWL_FIXED_LDO(label, offset, mVolts, num) { \
#define TWL_FIXED_LDO(label, offset, mVolts, num, family) { \
.base = offset, \
.id = num, \
.min_mV = mVolts, \
.desc = { \
.name = #label, \
.id = TWL4030_REG_##label, \
.id = family##_REG_##label, \
.n_voltages = 1, \
.ops = &twl4030fixed_ops, \
.ops = &twlfixed_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
@ -389,35 +456,47 @@ static struct regulator_ops twl4030fixed_ops = {
* We list regulators here if systems need some level of
* software control over them after boot.
*/
static struct twlreg_info twl4030_regs[] = {
TWL_ADJUSTABLE_LDO(VAUX1, 0x17, 1),
TWL_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2),
TWL_ADJUSTABLE_LDO(VAUX2, 0x1b, 2),
TWL_ADJUSTABLE_LDO(VAUX3, 0x1f, 3),
TWL_ADJUSTABLE_LDO(VAUX4, 0x23, 4),
TWL_ADJUSTABLE_LDO(VMMC1, 0x27, 5),
TWL_ADJUSTABLE_LDO(VMMC2, 0x2b, 6),
static struct twlreg_info twl_regs[] = {
TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1),
TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2),
TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2),
TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3),
TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4),
TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5),
TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6),
/*
TWL_ADJUSTABLE_LDO(VPLL1, 0x2f, 7),
TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7),
*/
TWL_ADJUSTABLE_LDO(VPLL2, 0x33, 8),
TWL_ADJUSTABLE_LDO(VSIM, 0x37, 9),
TWL_ADJUSTABLE_LDO(VDAC, 0x3b, 10),
TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8),
TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9),
TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10),
/*
TWL_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11),
TWL_ADJUSTABLE_LDO(VINTANA2, 0x43, 12),
TWL_ADJUSTABLE_LDO(VINTDIG, 0x47, 13),
TWL_SMPS(VIO, 0x4b, 14),
TWL_SMPS(VDD1, 0x55, 15),
TWL_SMPS(VDD2, 0x63, 16),
TWL4030_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11),
TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12),
TWL4030_ADJUSTABLE_LDO(VINTDIG, 0x47, 13),
TWL4030_SMPS(VIO, 0x4b, 14),
TWL4030_SMPS(VDD1, 0x55, 15),
TWL4030_SMPS(VDD2, 0x63, 16),
*/
TWL_FIXED_LDO(VUSB1V5, 0x71, 1500, 17),
TWL_FIXED_LDO(VUSB1V8, 0x74, 1800, 18),
TWL_FIXED_LDO(VUSB3V1, 0x77, 3100, 19),
TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17),
TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18),
TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19),
/* VUSBCP is managed *only* by the USB subchip */
/* 6030 REG with base as PMC Slave Misc : 0x0030 */
TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1),
TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2),
TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3),
TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4),
TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5),
TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7),
TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15),
TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16),
TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17),
TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18)
};
static int twl4030reg_probe(struct platform_device *pdev)
static int twlreg_probe(struct platform_device *pdev)
{
int i;
struct twlreg_info *info;
@ -425,10 +504,10 @@ static int twl4030reg_probe(struct platform_device *pdev)
struct regulation_constraints *c;
struct regulator_dev *rdev;
for (i = 0, info = NULL; i < ARRAY_SIZE(twl4030_regs); i++) {
if (twl4030_regs[i].desc.id != pdev->id)
for (i = 0, info = NULL; i < ARRAY_SIZE(twl_regs); i++) {
if (twl_regs[i].desc.id != pdev->id)
continue;
info = twl4030_regs + i;
info = twl_regs + i;
break;
}
if (!info)
@ -466,35 +545,35 @@ static int twl4030reg_probe(struct platform_device *pdev)
return 0;
}
static int __devexit twl4030reg_remove(struct platform_device *pdev)
static int __devexit twlreg_remove(struct platform_device *pdev)
{
regulator_unregister(platform_get_drvdata(pdev));
return 0;
}
MODULE_ALIAS("platform:twl4030_reg");
MODULE_ALIAS("platform:twl_reg");
static struct platform_driver twl4030reg_driver = {
.probe = twl4030reg_probe,
.remove = __devexit_p(twl4030reg_remove),
static struct platform_driver twlreg_driver = {
.probe = twlreg_probe,
.remove = __devexit_p(twlreg_remove),
/* NOTE: short name, to work around driver model truncation of
* "twl4030_regulator.12" (and friends) to "twl4030_regulator.1".
* "twl_regulator.12" (and friends) to "twl_regulator.1".
*/
.driver.name = "twl4030_reg",
.driver.name = "twl_reg",
.driver.owner = THIS_MODULE,
};
static int __init twl4030reg_init(void)
static int __init twlreg_init(void)
{
return platform_driver_register(&twl4030reg_driver);
return platform_driver_register(&twlreg_driver);
}
subsys_initcall(twl4030reg_init);
subsys_initcall(twlreg_init);
static void __exit twl4030reg_exit(void)
static void __exit twlreg_exit(void)
{
platform_driver_unregister(&twl4030reg_driver);
platform_driver_unregister(&twlreg_driver);
}
module_exit(twl4030reg_exit)
module_exit(twlreg_exit)
MODULE_DESCRIPTION("TWL4030 regulator driver");
MODULE_DESCRIPTION("TWL regulator driver");
MODULE_LICENSE("GPL");

View File

@ -1330,9 +1330,10 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = {
},
};
static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data)
static irqreturn_t pmic_uv_handler(int irq, void *data)
{
struct regulator_dev *rdev = (struct regulator_dev *)data;
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
mutex_lock(&rdev->mutex);
if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2)
@ -1344,6 +1345,8 @@ static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data)
REGULATOR_EVENT_UNDER_VOLTAGE,
wm8350);
mutex_unlock(&rdev->mutex);
return IRQ_HANDLED;
}
static int wm8350_regulator_probe(struct platform_device *pdev)
@ -1388,7 +1391,7 @@ static int wm8350_regulator_probe(struct platform_device *pdev)
/* register regulator IRQ */
ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq,
pmic_uv_handler, rdev);
pmic_uv_handler, 0, "UV", rdev);
if (ret < 0) {
regulator_unregister(rdev);
dev_err(&pdev->dev, "failed to register regulator %s IRQ\n",
@ -1396,8 +1399,6 @@ static int wm8350_regulator_probe(struct platform_device *pdev)
return ret;
}
wm8350_unmask_irq(wm8350, wm8350_reg[pdev->id].irq);
return 0;
}
@ -1406,7 +1407,6 @@ static int wm8350_regulator_remove(struct platform_device *pdev)
struct regulator_dev *rdev = platform_get_drvdata(pdev);
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
wm8350_mask_irq(wm8350, wm8350_reg[pdev->id].irq);
wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq);
regulator_unregister(rdev);

View File

@ -258,14 +258,14 @@ config RTC_DRV_TWL92330
the Menelaus driver; it's not separate module.
config RTC_DRV_TWL4030
tristate "TI TWL4030/TWL5030/TPS659x0"
tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0"
depends on RTC_CLASS && TWL4030_CORE
help
If you say yes here you get support for the RTC on the
TWL4030 family chips, used mostly with OMAP3 platforms.
TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms.
This driver can also be built as a module. If so, the module
will be called rtc-twl4030.
will be called rtc-twl.
config RTC_DRV_S35390A
tristate "Seiko Instruments S-35390A"

View File

@ -80,7 +80,7 @@ obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.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

View File

@ -277,16 +277,13 @@ static void pcf50633_rtc_irq(int irq, void *data)
static int __devinit pcf50633_rtc_probe(struct platform_device *pdev)
{
struct pcf50633_subdev_pdata *pdata;
struct pcf50633_rtc *rtc;
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
pdata = pdev->dev.platform_data;
rtc->pcf = pdata->pcf;
rtc->pcf = dev_to_pcf50633(pdev->dev.parent);
platform_set_drvdata(pdev, rtc);
rtc->rtc_dev = rtc_device_register("pcf50633-rtc", &pdev->dev,
&pcf50633_rtc_ops, THIS_MODULE);

View File

@ -1,5 +1,5 @@
/*
* rtc-twl4030.c -- TWL4030 Real Time Clock interface
* rtc-twl.c -- TWL Real Time Clock interface
*
* Copyright (C) 2007 MontaVista Software, Inc
* Author: Alexandre Rusev <source@mvista.com>
@ -28,33 +28,81 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
/*
* RTC block register offsets (use TWL_MODULE_RTC)
*/
#define REG_SECONDS_REG 0x00
#define REG_MINUTES_REG 0x01
#define REG_HOURS_REG 0x02
#define REG_DAYS_REG 0x03
#define REG_MONTHS_REG 0x04
#define REG_YEARS_REG 0x05
#define REG_WEEKS_REG 0x06
enum {
REG_SECONDS_REG = 0,
REG_MINUTES_REG,
REG_HOURS_REG,
REG_DAYS_REG,
REG_MONTHS_REG,
REG_YEARS_REG,
REG_WEEKS_REG,
#define REG_ALARM_SECONDS_REG 0x07
#define REG_ALARM_MINUTES_REG 0x08
#define REG_ALARM_HOURS_REG 0x09
#define REG_ALARM_DAYS_REG 0x0A
#define REG_ALARM_MONTHS_REG 0x0B
#define REG_ALARM_YEARS_REG 0x0C
REG_ALARM_SECONDS_REG,
REG_ALARM_MINUTES_REG,
REG_ALARM_HOURS_REG,
REG_ALARM_DAYS_REG,
REG_ALARM_MONTHS_REG,
REG_ALARM_YEARS_REG,
#define REG_RTC_CTRL_REG 0x0D
#define REG_RTC_STATUS_REG 0x0E
#define REG_RTC_INTERRUPTS_REG 0x0F
REG_RTC_CTRL_REG,
REG_RTC_STATUS_REG,
REG_RTC_INTERRUPTS_REG,
#define REG_RTC_COMP_LSB_REG 0x10
#define REG_RTC_COMP_MSB_REG 0x11
REG_RTC_COMP_LSB_REG,
REG_RTC_COMP_MSB_REG,
};
const static u8 twl4030_rtc_reg_map[] = {
[REG_SECONDS_REG] = 0x00,
[REG_MINUTES_REG] = 0x01,
[REG_HOURS_REG] = 0x02,
[REG_DAYS_REG] = 0x03,
[REG_MONTHS_REG] = 0x04,
[REG_YEARS_REG] = 0x05,
[REG_WEEKS_REG] = 0x06,
[REG_ALARM_SECONDS_REG] = 0x07,
[REG_ALARM_MINUTES_REG] = 0x08,
[REG_ALARM_HOURS_REG] = 0x09,
[REG_ALARM_DAYS_REG] = 0x0A,
[REG_ALARM_MONTHS_REG] = 0x0B,
[REG_ALARM_YEARS_REG] = 0x0C,
[REG_RTC_CTRL_REG] = 0x0D,
[REG_RTC_STATUS_REG] = 0x0E,
[REG_RTC_INTERRUPTS_REG] = 0x0F,
[REG_RTC_COMP_LSB_REG] = 0x10,
[REG_RTC_COMP_MSB_REG] = 0x11,
};
const static u8 twl6030_rtc_reg_map[] = {
[REG_SECONDS_REG] = 0x00,
[REG_MINUTES_REG] = 0x01,
[REG_HOURS_REG] = 0x02,
[REG_DAYS_REG] = 0x03,
[REG_MONTHS_REG] = 0x04,
[REG_YEARS_REG] = 0x05,
[REG_WEEKS_REG] = 0x06,
[REG_ALARM_SECONDS_REG] = 0x08,
[REG_ALARM_MINUTES_REG] = 0x09,
[REG_ALARM_HOURS_REG] = 0x0A,
[REG_ALARM_DAYS_REG] = 0x0B,
[REG_ALARM_MONTHS_REG] = 0x0C,
[REG_ALARM_YEARS_REG] = 0x0D,
[REG_RTC_CTRL_REG] = 0x10,
[REG_RTC_STATUS_REG] = 0x11,
[REG_RTC_INTERRUPTS_REG] = 0x12,
[REG_RTC_COMP_LSB_REG] = 0x13,
[REG_RTC_COMP_MSB_REG] = 0x14,
};
/* RTC_CTRL_REG bitfields */
#define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01
@ -84,31 +132,32 @@
#define ALL_TIME_REGS 6
/*----------------------------------------------------------------------*/
static u8 *rtc_reg_map;
/*
* Supports 1 byte read from TWL4030 RTC register.
* Supports 1 byte read from TWL RTC register.
*/
static int twl4030_rtc_read_u8(u8 *data, u8 reg)
static int twl_rtc_read_u8(u8 *data, u8 reg)
{
int ret;
ret = twl4030_i2c_read_u8(TWL4030_MODULE_RTC, data, reg);
ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
if (ret < 0)
pr_err("twl4030_rtc: Could not read TWL4030"
pr_err("twl_rtc: Could not read TWL"
"register %X - error %d\n", reg, ret);
return ret;
}
/*
* Supports 1 byte write to TWL4030 RTC registers.
* Supports 1 byte write to TWL RTC registers.
*/
static int twl4030_rtc_write_u8(u8 data, u8 reg)
static int twl_rtc_write_u8(u8 data, u8 reg)
{
int ret;
ret = twl4030_i2c_write_u8(TWL4030_MODULE_RTC, data, reg);
ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
if (ret < 0)
pr_err("twl4030_rtc: Could not write TWL4030"
pr_err("twl_rtc: Could not write TWL"
"register %X - error %d\n", reg, ret);
return ret;
}
@ -129,7 +178,7 @@ static int set_rtc_irq_bit(unsigned char bit)
val = rtc_irq_bits | bit;
val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M;
ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
rtc_irq_bits = val;
@ -145,14 +194,14 @@ static int mask_rtc_irq_bit(unsigned char bit)
int ret;
val = rtc_irq_bits & ~bit;
ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
rtc_irq_bits = val;
return ret;
}
static int twl4030_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
{
int ret;
@ -164,7 +213,7 @@ static int twl4030_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
return ret;
}
static int twl4030_rtc_update_irq_enable(struct device *dev, unsigned enabled)
static int twl_rtc_update_irq_enable(struct device *dev, unsigned enabled)
{
int ret;
@ -177,7 +226,7 @@ static int twl4030_rtc_update_irq_enable(struct device *dev, unsigned enabled)
}
/*
* Gets current TWL4030 RTC time and date parameters.
* Gets current TWL RTC time and date parameters.
*
* The RTC's time/alarm representation is not what gmtime(3) requires
* Linux to use:
@ -185,24 +234,24 @@ static int twl4030_rtc_update_irq_enable(struct device *dev, unsigned enabled)
* - Months are 1..12 vs Linux 0-11
* - Years are 0..99 vs Linux 1900..N (we assume 21st century)
*/
static int twl4030_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned char rtc_data[ALL_TIME_REGS + 1];
int ret;
u8 save_control;
ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
if (ret < 0)
return ret;
save_control |= BIT_RTC_CTRL_REG_GET_TIME_M;
ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
if (ret < 0)
return ret;
ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
REG_SECONDS_REG, ALL_TIME_REGS);
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
(rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_read_time error %d\n", ret);
@ -219,7 +268,7 @@ static int twl4030_rtc_read_time(struct device *dev, struct rtc_time *tm)
return ret;
}
static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned char save_control;
unsigned char rtc_data[ALL_TIME_REGS + 1];
@ -233,18 +282,18 @@ static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_data[6] = bin2bcd(tm->tm_year - 100);
/* Stop RTC while updating the TC registers */
ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
if (ret < 0)
goto out;
save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
if (ret < 0)
goto out;
/* update all the time registers in one shot */
ret = twl4030_i2c_write(TWL4030_MODULE_RTC, rtc_data,
REG_SECONDS_REG, ALL_TIME_REGS);
ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data,
(rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_set_time error %d\n", ret);
goto out;
@ -252,22 +301,22 @@ static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
/* Start back RTC */
save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
out:
return ret;
}
/*
* Gets current TWL4030 RTC alarm time.
* Gets current TWL RTC alarm time.
*/
static int twl4030_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
unsigned char rtc_data[ALL_TIME_REGS + 1];
int ret;
ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
(rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_read_alarm error %d\n", ret);
return ret;
@ -288,12 +337,12 @@ static int twl4030_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
return ret;
}
static int twl4030_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
unsigned char alarm_data[ALL_TIME_REGS + 1];
int ret;
ret = twl4030_rtc_alarm_irq_enable(dev, 0);
ret = twl_rtc_alarm_irq_enable(dev, 0);
if (ret)
goto out;
@ -305,20 +354,20 @@ static int twl4030_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
alarm_data[6] = bin2bcd(alm->time.tm_year - 100);
/* update all the alarm registers in one shot */
ret = twl4030_i2c_write(TWL4030_MODULE_RTC, alarm_data,
REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data,
(rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
if (ret) {
dev_err(dev, "rtc_set_alarm error %d\n", ret);
goto out;
}
if (alm->enabled)
ret = twl4030_rtc_alarm_irq_enable(dev, 1);
ret = twl_rtc_alarm_irq_enable(dev, 1);
out:
return ret;
}
static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
{
unsigned long events = 0;
int ret = IRQ_NONE;
@ -333,7 +382,7 @@ static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
local_irq_enable();
#endif
res = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (res)
goto out;
/*
@ -347,26 +396,28 @@ static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
else
events |= RTC_IRQF | RTC_UF;
res = twl4030_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
res = twl_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
REG_RTC_STATUS_REG);
if (res)
goto out;
/* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1
* needs 2 reads to clear the interrupt. One read is done in
* do_twl4030_pwrirq(). Doing the second read, to clear
* the bit.
*
* FIXME the reason PWR_ISR1 needs an extra read is that
* RTC_IF retriggered until we cleared REG_ALARM_M above.
* But re-reading like this is a bad hack; by doing so we
* risk wrongly clearing status for some other IRQ (losing
* the interrupt). Be smarter about handling RTC_UF ...
*/
res = twl4030_i2c_read_u8(TWL4030_MODULE_INT,
if (twl_class_is_4030()) {
/* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1
* needs 2 reads to clear the interrupt. One read is done in
* do_twl_pwrirq(). Doing the second read, to clear
* the bit.
*
* FIXME the reason PWR_ISR1 needs an extra read is that
* RTC_IF retriggered until we cleared REG_ALARM_M above.
* But re-reading like this is a bad hack; by doing so we
* risk wrongly clearing status for some other IRQ (losing
* the interrupt). Be smarter about handling RTC_UF ...
*/
res = twl_i2c_read_u8(TWL4030_MODULE_INT,
&rd_reg, TWL4030_INT_PWR_ISR1);
if (res)
goto out;
if (res)
goto out;
}
/* Notify RTC core on event */
rtc_update_irq(rtc, 1, events);
@ -376,18 +427,18 @@ static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
return ret;
}
static struct rtc_class_ops twl4030_rtc_ops = {
.read_time = twl4030_rtc_read_time,
.set_time = twl4030_rtc_set_time,
.read_alarm = twl4030_rtc_read_alarm,
.set_alarm = twl4030_rtc_set_alarm,
.alarm_irq_enable = twl4030_rtc_alarm_irq_enable,
.update_irq_enable = twl4030_rtc_update_irq_enable,
static struct rtc_class_ops twl_rtc_ops = {
.read_time = twl_rtc_read_time,
.set_time = twl_rtc_set_time,
.read_alarm = twl_rtc_read_alarm,
.set_alarm = twl_rtc_set_alarm,
.alarm_irq_enable = twl_rtc_alarm_irq_enable,
.update_irq_enable = twl_rtc_update_irq_enable,
};
/*----------------------------------------------------------------------*/
static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
static int __devinit twl_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
int ret = 0;
@ -398,7 +449,7 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
return -EINVAL;
rtc = rtc_device_register(pdev->name,
&pdev->dev, &twl4030_rtc_ops, THIS_MODULE);
&pdev->dev, &twl_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
@ -409,7 +460,7 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rtc);
ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
goto out1;
@ -420,11 +471,11 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
/* Clear RTC Power up reset and pending alarm interrupts */
ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
ret = twl_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
goto out1;
ret = request_irq(irq, twl4030_rtc_interrupt,
ret = request_irq(irq, twl_rtc_interrupt,
IRQF_TRIGGER_RISING,
dev_name(&rtc->dev), rtc);
if (ret < 0) {
@ -432,21 +483,28 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
goto out1;
}
if (twl_class_is_6030()) {
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
REG_INT_MSK_LINE_A);
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
REG_INT_MSK_STS_A);
}
/* Check RTC module status, Enable if it is off */
ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
if (ret < 0)
goto out2;
if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
dev_info(&pdev->dev, "Enabling TWL4030-RTC.\n");
dev_info(&pdev->dev, "Enabling TWL-RTC.\n");
rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
if (ret < 0)
goto out2;
}
/* init cached IRQ enable bits */
ret = twl4030_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
goto out2;
@ -461,10 +519,10 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
}
/*
* Disable all TWL4030 RTC module interrupts.
* Disable all TWL RTC module interrupts.
* Sets status flag to free.
*/
static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
static int __devexit twl_rtc_remove(struct platform_device *pdev)
{
/* leave rtc running, but disable irqs */
struct rtc_device *rtc = platform_get_drvdata(pdev);
@ -472,6 +530,13 @@ static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
if (twl_class_is_6030()) {
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
REG_INT_MSK_LINE_A);
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
REG_INT_MSK_STS_A);
}
free_irq(irq, rtc);
@ -480,7 +545,7 @@ static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
return 0;
}
static void twl4030_rtc_shutdown(struct platform_device *pdev)
static void twl_rtc_shutdown(struct platform_device *pdev)
{
/* mask timer interrupts, but leave alarm interrupts on to enable
power-on when alarm is triggered */
@ -491,7 +556,7 @@ static void twl4030_rtc_shutdown(struct platform_device *pdev)
static unsigned char irqstat;
static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state)
static int twl_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
irqstat = rtc_irq_bits;
@ -499,42 +564,47 @@ static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
static int twl4030_rtc_resume(struct platform_device *pdev)
static int twl_rtc_resume(struct platform_device *pdev)
{
set_rtc_irq_bit(irqstat);
return 0;
}
#else
#define twl4030_rtc_suspend NULL
#define twl4030_rtc_resume NULL
#define twl_rtc_suspend NULL
#define twl_rtc_resume NULL
#endif
MODULE_ALIAS("platform:twl4030_rtc");
MODULE_ALIAS("platform:twl_rtc");
static struct platform_driver twl4030rtc_driver = {
.probe = twl4030_rtc_probe,
.remove = __devexit_p(twl4030_rtc_remove),
.shutdown = twl4030_rtc_shutdown,
.suspend = twl4030_rtc_suspend,
.resume = twl4030_rtc_resume,
.probe = twl_rtc_probe,
.remove = __devexit_p(twl_rtc_remove),
.shutdown = twl_rtc_shutdown,
.suspend = twl_rtc_suspend,
.resume = twl_rtc_resume,
.driver = {
.owner = THIS_MODULE,
.name = "twl4030_rtc",
.name = "twl_rtc",
},
};
static int __init twl4030_rtc_init(void)
static int __init twl_rtc_init(void)
{
if (twl_class_is_4030())
rtc_reg_map = (u8 *) twl4030_rtc_reg_map;
else
rtc_reg_map = (u8 *) twl6030_rtc_reg_map;
return platform_driver_register(&twl4030rtc_driver);
}
module_init(twl4030_rtc_init);
module_init(twl_rtc_init);
static void __exit twl4030_rtc_exit(void)
static void __exit twl_rtc_exit(void)
{
platform_driver_unregister(&twl4030rtc_driver);
}
module_exit(twl4030_rtc_exit);
module_exit(twl_rtc_exit);
MODULE_AUTHOR("Texas Instruments, MontaVista Software");
MODULE_LICENSE("GPL");

View File

@ -315,9 +315,9 @@ static int wm8350_rtc_update_irq_enable(struct device *dev,
return 0;
}
static void wm8350_rtc_alarm_handler(struct wm8350 *wm8350, int irq,
void *data)
static irqreturn_t wm8350_rtc_alarm_handler(int irq, void *data)
{
struct wm8350 *wm8350 = data;
struct rtc_device *rtc = wm8350->rtc.rtc;
int ret;
@ -330,14 +330,18 @@ static void wm8350_rtc_alarm_handler(struct wm8350 *wm8350, int irq,
dev_err(&(wm8350->rtc.pdev->dev),
"Failed to disable alarm: %d\n", ret);
}
return IRQ_HANDLED;
}
static void wm8350_rtc_update_handler(struct wm8350 *wm8350, int irq,
void *data)
static irqreturn_t wm8350_rtc_update_handler(int irq, void *data)
{
struct wm8350 *wm8350 = data;
struct rtc_device *rtc = wm8350->rtc.rtc;
rtc_update_irq(rtc, 1, RTC_IRQF | RTC_UF);
return IRQ_HANDLED;
}
static const struct rtc_class_ops wm8350_rtc_ops = {
@ -455,15 +459,14 @@ static int wm8350_rtc_probe(struct platform_device *pdev)
return ret;
}
wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_PER);
wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
wm8350_rtc_update_handler, NULL);
wm8350_rtc_update_handler, 0,
"RTC Seconds", wm8350);
wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
wm8350_rtc_alarm_handler, NULL);
wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_ALM);
wm8350_rtc_alarm_handler, 0,
"RTC Alarm", wm8350);
return 0;
}
@ -473,8 +476,6 @@ static int __devexit wm8350_rtc_remove(struct platform_device *pdev)
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
struct wm8350_rtc *wm_rtc = &wm8350->rtc;
wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM);

View File

@ -33,7 +33,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
@ -276,16 +276,16 @@ static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
{
u8 check;
if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
(twl4030_i2c_read_u8(module, &check, address) >= 0) &&
if ((twl_i2c_write_u8(module, data, address) >= 0) &&
(twl_i2c_read_u8(module, &check, address) >= 0) &&
(check == data))
return 0;
dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
1, module, address, check, data);
/* Failed once: Try again */
if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
(twl4030_i2c_read_u8(module, &check, address) >= 0) &&
if ((twl_i2c_write_u8(module, data, address) >= 0) &&
(twl_i2c_read_u8(module, &check, address) >= 0) &&
(check == data))
return 0;
dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
@ -303,7 +303,7 @@ static inline int twl4030_usb_write(struct twl4030_usb *twl,
{
int ret = 0;
ret = twl4030_i2c_write_u8(TWL4030_MODULE_USB, data, address);
ret = twl_i2c_write_u8(TWL4030_MODULE_USB, data, address);
if (ret < 0)
dev_dbg(twl->dev,
"TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
@ -315,7 +315,7 @@ static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address)
u8 data;
int ret = 0;
ret = twl4030_i2c_read_u8(module, &data, address);
ret = twl_i2c_read_u8(module, &data, address);
if (ret >= 0)
ret = data;
else
@ -462,7 +462,7 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
* SLEEP. We work around this by clearing the bit after usv3v1
* is re-activated. This ensures that VUSB3V1 is really active.
*/
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
VUSB_DEDICATED2);
regulator_enable(twl->usb1v5);
pwr &= ~PHY_PWR_PHYPWD;
@ -505,44 +505,44 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
{
/* Enable writing to power configuration registers */
twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
/* put VUSB3V1 LDO in active state */
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
/* input to VUSB3V1 LDO is from VBAT, not VBUS */
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
/* Initialize 3.1V regulator */
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
twl->usb3v1 = regulator_get(twl->dev, "usb3v1");
if (IS_ERR(twl->usb3v1))
return -ENODEV;
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
/* Initialize 1.5V regulator */
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
twl->usb1v5 = regulator_get(twl->dev, "usb1v5");
if (IS_ERR(twl->usb1v5))
goto fail1;
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
/* Initialize 1.8V regulator */
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
twl->usb1v8 = regulator_get(twl->dev, "usb1v8");
if (IS_ERR(twl->usb1v8))
goto fail2;
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
/* disable access to power configuration registers */
twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
return 0;

View File

@ -15,7 +15,7 @@
struct adp5520_bl {
struct device *master;
struct adp5520_backlight_platfrom_data *pdata;
struct adp5520_backlight_platform_data *pdata;
struct mutex lock;
unsigned long cached_daylight_max;
int id;
@ -31,29 +31,30 @@ static int adp5520_bl_set(struct backlight_device *bl, int brightness)
if (data->pdata->en_ambl_sens) {
if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) {
/* Disable Ambient Light auto adjust */
ret |= adp5520_clr_bits(master, BL_CONTROL,
BL_AUTO_ADJ);
ret |= adp5520_write(master, DAYLIGHT_MAX, brightness);
ret |= adp5520_clr_bits(master, ADP5520_BL_CONTROL,
ADP5520_BL_AUTO_ADJ);
ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
brightness);
} else {
/*
* MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
* restore daylight l3 sysfs brightness
*/
ret |= adp5520_write(master, DAYLIGHT_MAX,
ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
data->cached_daylight_max);
ret |= adp5520_set_bits(master, BL_CONTROL,
BL_AUTO_ADJ);
ret |= adp5520_set_bits(master, ADP5520_BL_CONTROL,
ADP5520_BL_AUTO_ADJ);
}
} else {
ret |= adp5520_write(master, DAYLIGHT_MAX, brightness);
ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, brightness);
}
if (data->current_brightness && brightness == 0)
ret |= adp5520_set_bits(master,
MODE_STATUS, DIM_EN);
ADP5520_MODE_STATUS, ADP5520_DIM_EN);
else if (data->current_brightness == 0 && brightness)
ret |= adp5520_clr_bits(master,
MODE_STATUS, DIM_EN);
ADP5520_MODE_STATUS, ADP5520_DIM_EN);
if (!ret)
data->current_brightness = brightness;
@ -79,7 +80,7 @@ static int adp5520_bl_get_brightness(struct backlight_device *bl)
int error;
uint8_t reg_val;
error = adp5520_read(data->master, BL_VALUE, &reg_val);
error = adp5520_read(data->master, ADP5520_BL_VALUE, &reg_val);
return error ? data->current_brightness : reg_val;
}
@ -93,33 +94,46 @@ static int adp5520_bl_setup(struct backlight_device *bl)
{
struct adp5520_bl *data = bl_get_data(bl);
struct device *master = data->master;
struct adp5520_backlight_platfrom_data *pdata = data->pdata;
struct adp5520_backlight_platform_data *pdata = data->pdata;
int ret = 0;
ret |= adp5520_write(master, DAYLIGHT_MAX, pdata->l1_daylight_max);
ret |= adp5520_write(master, DAYLIGHT_DIM, pdata->l1_daylight_dim);
ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
pdata->l1_daylight_max);
ret |= adp5520_write(master, ADP5520_DAYLIGHT_DIM,
pdata->l1_daylight_dim);
if (pdata->en_ambl_sens) {
data->cached_daylight_max = pdata->l1_daylight_max;
ret |= adp5520_write(master, OFFICE_MAX, pdata->l2_office_max);
ret |= adp5520_write(master, OFFICE_DIM, pdata->l2_office_dim);
ret |= adp5520_write(master, DARK_MAX, pdata->l3_dark_max);
ret |= adp5520_write(master, DARK_DIM, pdata->l3_dark_dim);
ret |= adp5520_write(master, L2_TRIP, pdata->l2_trip);
ret |= adp5520_write(master, L2_HYS, pdata->l2_hyst);
ret |= adp5520_write(master, L3_TRIP, pdata->l3_trip);
ret |= adp5520_write(master, L3_HYS, pdata->l3_hyst);
ret |= adp5520_write(master, ALS_CMPR_CFG,
ALS_CMPR_CFG_VAL(pdata->abml_filt, L3_EN));
ret |= adp5520_write(master, ADP5520_OFFICE_MAX,
pdata->l2_office_max);
ret |= adp5520_write(master, ADP5520_OFFICE_DIM,
pdata->l2_office_dim);
ret |= adp5520_write(master, ADP5520_DARK_MAX,
pdata->l3_dark_max);
ret |= adp5520_write(master, ADP5520_DARK_DIM,
pdata->l3_dark_dim);
ret |= adp5520_write(master, ADP5520_L2_TRIP,
pdata->l2_trip);
ret |= adp5520_write(master, ADP5520_L2_HYS,
pdata->l2_hyst);
ret |= adp5520_write(master, ADP5520_L3_TRIP,
pdata->l3_trip);
ret |= adp5520_write(master, ADP5520_L3_HYS,
pdata->l3_hyst);
ret |= adp5520_write(master, ADP5520_ALS_CMPR_CFG,
ALS_CMPR_CFG_VAL(pdata->abml_filt,
ADP5520_L3_EN));
}
ret |= adp5520_write(master, BL_CONTROL,
BL_CTRL_VAL(pdata->fade_led_law, pdata->en_ambl_sens));
ret |= adp5520_write(master, ADP5520_BL_CONTROL,
BL_CTRL_VAL(pdata->fade_led_law,
pdata->en_ambl_sens));
ret |= adp5520_write(master, BL_FADE, FADE_VAL(pdata->fade_in,
ret |= adp5520_write(master, ADP5520_BL_FADE, FADE_VAL(pdata->fade_in,
pdata->fade_out));
ret |= adp5520_set_bits(master, MODE_STATUS, BL_EN | DIM_EN);
ret |= adp5520_set_bits(master, ADP5520_MODE_STATUS,
ADP5520_BL_EN | ADP5520_DIM_EN);
return ret;
}
@ -156,29 +170,31 @@ static ssize_t adp5520_store(struct device *dev, const char *buf,
}
static ssize_t adp5520_bl_dark_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
struct device_attribute *attr, char *buf)
{
return adp5520_show(dev, buf, DARK_MAX);
return adp5520_show(dev, buf, ADP5520_DARK_MAX);
}
static ssize_t adp5520_bl_dark_max_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
struct device_attribute *attr,
const char *buf, size_t count)
{
return adp5520_store(dev, buf, count, DARK_MAX);
return adp5520_store(dev, buf, count, ADP5520_DARK_MAX);
}
static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show,
adp5520_bl_dark_max_store);
static ssize_t adp5520_bl_office_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
struct device_attribute *attr, char *buf)
{
return adp5520_show(dev, buf, OFFICE_MAX);
return adp5520_show(dev, buf, ADP5520_OFFICE_MAX);
}
static ssize_t adp5520_bl_office_max_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
struct device_attribute *attr,
const char *buf, size_t count)
{
return adp5520_store(dev, buf, count, OFFICE_MAX);
return adp5520_store(dev, buf, count, ADP5520_OFFICE_MAX);
}
static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
adp5520_bl_office_max_store);
@ -186,16 +202,17 @@ static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
static ssize_t adp5520_bl_daylight_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return adp5520_show(dev, buf, DAYLIGHT_MAX);
return adp5520_show(dev, buf, ADP5520_DAYLIGHT_MAX);
}
static ssize_t adp5520_bl_daylight_max_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
struct device_attribute *attr,
const char *buf, size_t count)
{
struct adp5520_bl *data = dev_get_drvdata(dev);
strict_strtoul(buf, 10, &data->cached_daylight_max);
return adp5520_store(dev, buf, count, DAYLIGHT_MAX);
return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_MAX);
}
static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show,
adp5520_bl_daylight_max_store);
@ -203,14 +220,14 @@ static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show,
static ssize_t adp5520_bl_dark_dim_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return adp5520_show(dev, buf, DARK_DIM);
return adp5520_show(dev, buf, ADP5520_DARK_DIM);
}
static ssize_t adp5520_bl_dark_dim_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
struct device_attribute *attr,
const char *buf, size_t count)
{
return adp5520_store(dev, buf, count, DARK_DIM);
return adp5520_store(dev, buf, count, ADP5520_DARK_DIM);
}
static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show,
adp5520_bl_dark_dim_store);
@ -218,29 +235,29 @@ static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show,
static ssize_t adp5520_bl_office_dim_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return adp5520_show(dev, buf, OFFICE_DIM);
return adp5520_show(dev, buf, ADP5520_OFFICE_DIM);
}
static ssize_t adp5520_bl_office_dim_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
struct device_attribute *attr,
const char *buf, size_t count)
{
return adp5520_store(dev, buf, count, OFFICE_DIM);
return adp5520_store(dev, buf, count, ADP5520_OFFICE_DIM);
}
static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show,
adp5520_bl_office_dim_store);
static ssize_t adp5520_bl_daylight_dim_show(struct device *dev,
struct device_attribute *attr, char *buf)
struct device_attribute *attr, char *buf)
{
return adp5520_show(dev, buf, DAYLIGHT_DIM);
return adp5520_show(dev, buf, ADP5520_DAYLIGHT_DIM);
}
static ssize_t adp5520_bl_daylight_dim_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
struct device_attribute *attr,
const char *buf, size_t count)
{
return adp5520_store(dev, buf, count, DAYLIGHT_DIM);
return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_DIM);
}
static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show,
adp5520_bl_daylight_dim_store);
@ -316,7 +333,7 @@ static int __devexit adp5520_bl_remove(struct platform_device *pdev)
struct backlight_device *bl = platform_get_drvdata(pdev);
struct adp5520_bl *data = bl_get_data(bl);
adp5520_clr_bits(data->master, MODE_STATUS, BL_EN);
adp5520_clr_bits(data->master, ADP5520_MODE_STATUS, ADP5520_BL_EN);
if (data->pdata->en_ambl_sens)
sysfs_remove_group(&bl->dev.kobj,

View File

@ -25,7 +25,7 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <plat/mux.h>
#include <asm/mach-types.h>
@ -52,7 +52,7 @@ static unsigned enable_gpio;
#define TWL4030_VPLL2_DEV_GRP 0x33
#define TWL4030_VPLL2_DEDICATED 0x36
#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v)
#define t2_out(c, r, v) twl_i2c_write_u8(c, r, v)
static int sdp2430_panel_init(struct lcd_panel *panel,

View File

@ -26,7 +26,7 @@
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3
@ -48,7 +48,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
static int twl4030_wdt_write(unsigned char val)
{
return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
return twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
TWL4030_WATCHDOG_CFG_REG_OFFS);
}

View File

@ -72,6 +72,21 @@
#define TPS_VDCDC1 0x0c
# define TPS_ENABLE_LP (1 << 3)
#define TPS_VDCDC2 0x0d
# define TPS_LP_COREOFF (1 << 7)
# define TPS_VCORE_1_8V (7<<4)
# define TPS_VCORE_1_5V (6 << 4)
# define TPS_VCORE_1_4V (5 << 4)
# define TPS_VCORE_1_3V (4 << 4)
# define TPS_VCORE_1_2V (3 << 4)
# define TPS_VCORE_1_1V (2 << 4)
# define TPS_VCORE_1_0V (1 << 4)
# define TPS_VCORE_0_85V (0 << 4)
# define TPS_VCORE_LP_1_2V (3 << 2)
# define TPS_VCORE_LP_1_1V (2 << 2)
# define TPS_VCORE_LP_1_0V (1 << 2)
# define TPS_VCORE_LP_0_85V (0 << 2)
# define TPS_VIB (1 << 1)
# define TPS_VCORE_DISCH (1 << 0)
#define TPS_VREGS1 0x0e
# define TPS_LDO2_ENABLE (1 << 7)
# define TPS_LDO2_OFF (1 << 6)
@ -152,6 +167,10 @@ extern int tps65010_config_vregs1(unsigned value);
*/
extern int tps65013_set_low_pwr(unsigned mode);
/* tps65010_set_vdcdc2
* value to be written to VDCDC2
*/
extern int tps65010_config_vdcdc2(unsigned value);
struct i2c_client;

View File

@ -22,8 +22,8 @@
*
*/
#ifndef __TWL4030_H_
#define __TWL4030_H_
#ifndef __TWL_H_
#define __TWL_H_
#include <linux/types.h>
#include <linux/input/matrix_keypad.h>
@ -61,28 +61,112 @@
#define TWL4030_MODULE_PWMA 0x0E
#define TWL4030_MODULE_PWMB 0x0F
#define TWL5031_MODULE_ACCESSORY 0x10
#define TWL5031_MODULE_INTERRUPTS 0x11
/* Slave 3 (i2c address 0x4b) */
#define TWL4030_MODULE_BACKUP 0x10
#define TWL4030_MODULE_INT 0x11
#define TWL4030_MODULE_PM_MASTER 0x12
#define TWL4030_MODULE_PM_RECEIVER 0x13
#define TWL4030_MODULE_RTC 0x14
#define TWL4030_MODULE_SECURED_REG 0x15
#define TWL4030_MODULE_BACKUP 0x12
#define TWL4030_MODULE_INT 0x13
#define TWL4030_MODULE_PM_MASTER 0x14
#define TWL4030_MODULE_PM_RECEIVER 0x15
#define TWL4030_MODULE_RTC 0x16
#define TWL4030_MODULE_SECURED_REG 0x17
#define TWL_MODULE_USB TWL4030_MODULE_USB
#define TWL_MODULE_AUDIO_VOICE TWL4030_MODULE_AUDIO_VOICE
#define TWL_MODULE_PIH TWL4030_MODULE_PIH
#define TWL_MODULE_MADC TWL4030_MODULE_MADC
#define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE
#define TWL_MODULE_PM_MASTER TWL4030_MODULE_PM_MASTER
#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
#define TWL_MODULE_RTC TWL4030_MODULE_RTC
#define GPIO_INTR_OFFSET 0
#define KEYPAD_INTR_OFFSET 1
#define BCI_INTR_OFFSET 2
#define MADC_INTR_OFFSET 3
#define USB_INTR_OFFSET 4
#define BCI_PRES_INTR_OFFSET 9
#define USB_PRES_INTR_OFFSET 10
#define RTC_INTR_OFFSET 11
/*
* Offset from TWL6030_IRQ_BASE / pdata->irq_base
*/
#define PWR_INTR_OFFSET 0
#define HOTDIE_INTR_OFFSET 12
#define SMPSLDO_INTR_OFFSET 13
#define BATDETECT_INTR_OFFSET 14
#define SIMDETECT_INTR_OFFSET 15
#define MMCDETECT_INTR_OFFSET 16
#define GASGAUGE_INTR_OFFSET 17
#define USBOTG_INTR_OFFSET 4
#define CHARGER_INTR_OFFSET 2
#define RSV_INTR_OFFSET 0
/* INT register offsets */
#define REG_INT_STS_A 0x00
#define REG_INT_STS_B 0x01
#define REG_INT_STS_C 0x02
#define REG_INT_MSK_LINE_A 0x03
#define REG_INT_MSK_LINE_B 0x04
#define REG_INT_MSK_LINE_C 0x05
#define REG_INT_MSK_STS_A 0x06
#define REG_INT_MSK_STS_B 0x07
#define REG_INT_MSK_STS_C 0x08
/* MASK INT REG GROUP A */
#define TWL6030_PWR_INT_MASK 0x07
#define TWL6030_RTC_INT_MASK 0x18
#define TWL6030_HOTDIE_INT_MASK 0x20
#define TWL6030_SMPSLDOA_INT_MASK 0xC0
/* MASK INT REG GROUP B */
#define TWL6030_SMPSLDOB_INT_MASK 0x01
#define TWL6030_BATDETECT_INT_MASK 0x02
#define TWL6030_SIMDETECT_INT_MASK 0x04
#define TWL6030_MMCDETECT_INT_MASK 0x08
#define TWL6030_GPADC_INT_MASK 0x60
#define TWL6030_GASGAUGE_INT_MASK 0x80
/* MASK INT REG GROUP C */
#define TWL6030_USBOTG_INT_MASK 0x0F
#define TWL6030_CHARGER_CTRL_INT_MASK 0x10
#define TWL6030_CHARGER_FAULT_INT_MASK 0x60
#define TWL4030_CLASS_ID 0x4030
#define TWL6030_CLASS_ID 0x6030
unsigned int twl_rev(void);
#define GET_TWL_REV (twl_rev())
#define TWL_CLASS_IS(class, id) \
static inline int twl_class_is_ ##class(void) \
{ \
return ((id) == (GET_TWL_REV)) ? 1 : 0; \
}
TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
/*
* Read and write single 8-bit registers
*/
int twl4030_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
int twl4030_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
/*
* Read and write several 8-bit registers at once.
*
* IMPORTANT: For twl4030_i2c_write(), allocate num_bytes + 1
* IMPORTANT: For twl_i2c_write(), allocate num_bytes + 1
* for the value, and populate your data starting at offset 1.
*/
int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
int twl6030_interrupt_unmask(u8 bit_mask, u8 offset);
int twl6030_interrupt_mask(u8 bit_mask, u8 offset);
/*----------------------------------------------------------------------*/
@ -221,6 +305,38 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
/*----------------------------------------------------------------------*/
/*
* Accessory Interrupts
*/
#define TWL5031_ACIIMR_LSB 0x05
#define TWL5031_ACIIMR_MSB 0x06
#define TWL5031_ACIIDR_LSB 0x07
#define TWL5031_ACIIDR_MSB 0x08
#define TWL5031_ACCISR1 0x0F
#define TWL5031_ACCIMR1 0x10
#define TWL5031_ACCISR2 0x11
#define TWL5031_ACCIMR2 0x12
#define TWL5031_ACCSIR 0x13
#define TWL5031_ACCEDR1 0x14
#define TWL5031_ACCSIHCTRL 0x15
/*----------------------------------------------------------------------*/
/*
* Battery Charger Controller
*/
#define TWL5031_INTERRUPTS_BCIISR1 0x0
#define TWL5031_INTERRUPTS_BCIIMR1 0x1
#define TWL5031_INTERRUPTS_BCIISR2 0x2
#define TWL5031_INTERRUPTS_BCIIMR2 0x3
#define TWL5031_INTERRUPTS_BCISIR 0x4
#define TWL5031_INTERRUPTS_BCIEDR1 0x5
#define TWL5031_INTERRUPTS_BCIEDR2 0x6
#define TWL5031_INTERRUPTS_BCISIHCTRL 0x7
/*----------------------------------------------------------------------*/
/* Power bus message definitions */
/* The TWL4030/5030 splits its power-management resources (the various
@ -250,6 +366,7 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
#define RES_TYPE_ALL 0x7
/* Resource states */
#define RES_STATE_WRST 0xF
#define RES_STATE_ACTIVE 0xE
#define RES_STATE_SLEEP 0x8
@ -310,8 +427,18 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
#define MSG_SINGULAR(devgrp, id, state) \
((devgrp) << 13 | 0 << 12 | (id) << 4 | (state))
#define MSG_BROADCAST_ALL(devgrp, state) \
((devgrp) << 5 | (state))
#define MSG_BROADCAST_REF MSG_BROADCAST_ALL
#define MSG_BROADCAST_PROV MSG_BROADCAST_ALL
#define MSG_BROADCAST__CLK_RST MSG_BROADCAST_ALL
/*----------------------------------------------------------------------*/
struct twl4030_clock_init_data {
bool ck32k_lowpwr_enable;
};
struct twl4030_bci_platform_data {
int *battery_tmp_tbl;
unsigned int tblsize;
@ -391,12 +518,15 @@ struct twl4030_resconfig {
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 */
u8 remap_off; /* off state remapping */
u8 remap_sleep; /* sleep state remapping */
};
struct twl4030_power_data {
struct twl4030_script **scripts;
unsigned num;
struct twl4030_resconfig *resource_config;
#define TWL4030_RESCONFIG_UNDEF ((u8)-1)
};
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
@ -421,6 +551,7 @@ struct twl4030_codec_data {
struct twl4030_platform_data {
unsigned irq_base, irq_end;
struct twl4030_clock_init_data *clock;
struct twl4030_bci_platform_data *bci;
struct twl4030_gpio_platform_data *gpio;
struct twl4030_madc_platform_data *madc;
@ -429,19 +560,31 @@ struct twl4030_platform_data {
struct twl4030_power_data *power;
struct twl4030_codec_data *codec;
/* LDO regulators */
/* Common LDO regulators for TWL4030/TWL6030 */
struct regulator_init_data *vdac;
struct regulator_init_data *vaux1;
struct regulator_init_data *vaux2;
struct regulator_init_data *vaux3;
/* TWL4030 LDO regulators */
struct regulator_init_data *vpll1;
struct regulator_init_data *vpll2;
struct regulator_init_data *vmmc1;
struct regulator_init_data *vmmc2;
struct regulator_init_data *vsim;
struct regulator_init_data *vaux1;
struct regulator_init_data *vaux2;
struct regulator_init_data *vaux3;
struct regulator_init_data *vaux4;
/* REVISIT more to come ... _nothing_ should be hard-wired */
struct regulator_init_data *vio;
struct regulator_init_data *vdd1;
struct regulator_init_data *vdd2;
struct regulator_init_data *vintana1;
struct regulator_init_data *vintana2;
struct regulator_init_data *vintdig;
/* TWL6030 LDO regulators */
struct regulator_init_data *vmmc;
struct regulator_init_data *vpp;
struct regulator_init_data *vusim;
struct regulator_init_data *vana;
struct regulator_init_data *vcxio;
struct regulator_init_data *vusb;
};
/*----------------------------------------------------------------------*/
@ -473,6 +616,7 @@ int twl4030_sih_setup(int module);
* VIO is generally fixed.
*/
/* TWL4030 SMPS/LDO's */
/* EXTERNAL dc-to-dc buck converters */
#define TWL4030_REG_VDD1 0
#define TWL4030_REG_VDD2 1
@ -499,4 +643,31 @@ int twl4030_sih_setup(int module);
#define TWL4030_REG_VUSB1V8 18
#define TWL4030_REG_VUSB3V1 19
/* TWL6030 SMPS/LDO's */
/* EXTERNAL dc-to-dc buck convertor contollable via SR */
#define TWL6030_REG_VDD1 30
#define TWL6030_REG_VDD2 31
#define TWL6030_REG_VDD3 32
/* Non SR compliant dc-to-dc buck convertors */
#define TWL6030_REG_VMEM 33
#define TWL6030_REG_V2V1 34
#define TWL6030_REG_V1V29 35
#define TWL6030_REG_V1V8 36
/* EXTERNAL LDOs */
#define TWL6030_REG_VAUX1_6030 37
#define TWL6030_REG_VAUX2_6030 38
#define TWL6030_REG_VAUX3_6030 39
#define TWL6030_REG_VMMC 40
#define TWL6030_REG_VPP 41
#define TWL6030_REG_VUSIM 42
#define TWL6030_REG_VANA 43
#define TWL6030_REG_VCXIO 44
#define TWL6030_REG_VDAC 45
#define TWL6030_REG_VUSB 46
/* INTERNAL LDOs */
#define TWL6030_REG_VRTC 47
#endif /* End of __TWL4030_H */

View File

@ -0,0 +1,217 @@
/*
* Marvell 88PM8607 Interface
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.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.
*/
#ifndef __LINUX_MFD_88PM8607_H
#define __LINUX_MFD_88PM8607_H
enum {
PM8607_ID_BUCK1 = 0,
PM8607_ID_BUCK2,
PM8607_ID_BUCK3,
PM8607_ID_LDO1,
PM8607_ID_LDO2,
PM8607_ID_LDO3,
PM8607_ID_LDO4,
PM8607_ID_LDO5,
PM8607_ID_LDO6,
PM8607_ID_LDO7,
PM8607_ID_LDO8,
PM8607_ID_LDO9,
PM8607_ID_LDO10,
PM8607_ID_LDO12,
PM8607_ID_LDO14,
PM8607_ID_RG_MAX,
};
#define CHIP_ID (0x40)
#define CHIP_ID_MASK (0xF8)
/* Interrupt Registers */
#define PM8607_STATUS_1 (0x01)
#define PM8607_STATUS_2 (0x02)
#define PM8607_INT_STATUS1 (0x03)
#define PM8607_INT_STATUS2 (0x04)
#define PM8607_INT_STATUS3 (0x05)
#define PM8607_INT_MASK_1 (0x06)
#define PM8607_INT_MASK_2 (0x07)
#define PM8607_INT_MASK_3 (0x08)
/* Regulator Control Registers */
#define PM8607_LDO1 (0x10)
#define PM8607_LDO2 (0x11)
#define PM8607_LDO3 (0x12)
#define PM8607_LDO4 (0x13)
#define PM8607_LDO5 (0x14)
#define PM8607_LDO6 (0x15)
#define PM8607_LDO7 (0x16)
#define PM8607_LDO8 (0x17)
#define PM8607_LDO9 (0x18)
#define PM8607_LDO10 (0x19)
#define PM8607_LDO12 (0x1A)
#define PM8607_LDO14 (0x1B)
#define PM8607_SLEEP_MODE1 (0x1C)
#define PM8607_SLEEP_MODE2 (0x1D)
#define PM8607_SLEEP_MODE3 (0x1E)
#define PM8607_SLEEP_MODE4 (0x1F)
#define PM8607_GO (0x20)
#define PM8607_SLEEP_BUCK1 (0x21)
#define PM8607_SLEEP_BUCK2 (0x22)
#define PM8607_SLEEP_BUCK3 (0x23)
#define PM8607_BUCK1 (0x24)
#define PM8607_BUCK2 (0x25)
#define PM8607_BUCK3 (0x26)
#define PM8607_BUCK_CONTROLS (0x27)
#define PM8607_SUPPLIES_EN11 (0x2B)
#define PM8607_SUPPLIES_EN12 (0x2C)
#define PM8607_GROUP1 (0x2D)
#define PM8607_GROUP2 (0x2E)
#define PM8607_GROUP3 (0x2F)
#define PM8607_GROUP4 (0x30)
#define PM8607_GROUP5 (0x31)
#define PM8607_GROUP6 (0x32)
#define PM8607_SUPPLIES_EN21 (0x33)
#define PM8607_SUPPLIES_EN22 (0x34)
/* RTC Control Registers */
#define PM8607_RTC1 (0xA0)
#define PM8607_RTC_COUNTER1 (0xA1)
#define PM8607_RTC_COUNTER2 (0xA2)
#define PM8607_RTC_COUNTER3 (0xA3)
#define PM8607_RTC_COUNTER4 (0xA4)
#define PM8607_RTC_EXPIRE1 (0xA5)
#define PM8607_RTC_EXPIRE2 (0xA6)
#define PM8607_RTC_EXPIRE3 (0xA7)
#define PM8607_RTC_EXPIRE4 (0xA8)
#define PM8607_RTC_TRIM1 (0xA9)
#define PM8607_RTC_TRIM2 (0xAA)
#define PM8607_RTC_TRIM3 (0xAB)
#define PM8607_RTC_TRIM4 (0xAC)
#define PM8607_RTC_MISC1 (0xAD)
#define PM8607_RTC_MISC2 (0xAE)
#define PM8607_RTC_MISC3 (0xAF)
/* Misc Registers */
#define PM8607_CHIP_ID (0x00)
#define PM8607_LDO1 (0x10)
#define PM8607_DVC3 (0x26)
#define PM8607_MISC1 (0x40)
/* bit definitions for PM8607 events */
#define PM8607_EVENT_ONKEY (1 << 0)
#define PM8607_EVENT_EXTON (1 << 1)
#define PM8607_EVENT_CHG (1 << 2)
#define PM8607_EVENT_BAT (1 << 3)
#define PM8607_EVENT_RTC (1 << 4)
#define PM8607_EVENT_CC (1 << 5)
#define PM8607_EVENT_VBAT (1 << 8)
#define PM8607_EVENT_VCHG (1 << 9)
#define PM8607_EVENT_VSYS (1 << 10)
#define PM8607_EVENT_TINT (1 << 11)
#define PM8607_EVENT_GPADC0 (1 << 12)
#define PM8607_EVENT_GPADC1 (1 << 13)
#define PM8607_EVENT_GPADC2 (1 << 14)
#define PM8607_EVENT_GPADC3 (1 << 15)
#define PM8607_EVENT_AUDIO_SHORT (1 << 16)
#define PM8607_EVENT_PEN (1 << 17)
#define PM8607_EVENT_HEADSET (1 << 18)
#define PM8607_EVENT_HOOK (1 << 19)
#define PM8607_EVENT_MICIN (1 << 20)
#define PM8607_EVENT_CHG_TIMEOUT (1 << 21)
#define PM8607_EVENT_CHG_DONE (1 << 22)
#define PM8607_EVENT_CHG_FAULT (1 << 23)
/* bit definitions of Status Query Interface */
#define PM8607_STATUS_CC (1 << 3)
#define PM8607_STATUS_PEN (1 << 4)
#define PM8607_STATUS_HEADSET (1 << 5)
#define PM8607_STATUS_HOOK (1 << 6)
#define PM8607_STATUS_MICIN (1 << 7)
#define PM8607_STATUS_ONKEY (1 << 8)
#define PM8607_STATUS_EXTON (1 << 9)
#define PM8607_STATUS_CHG (1 << 10)
#define PM8607_STATUS_BAT (1 << 11)
#define PM8607_STATUS_VBUS (1 << 12)
#define PM8607_STATUS_OV (1 << 13)
/* bit definitions of BUCK3 */
#define PM8607_BUCK3_DOUBLE (1 << 6)
/* bit definitions of Misc1 */
#define PM8607_MISC1_PI2C (1 << 0)
/* Interrupt Number in 88PM8607 */
enum {
PM8607_IRQ_ONKEY = 0,
PM8607_IRQ_EXTON,
PM8607_IRQ_CHG,
PM8607_IRQ_BAT,
PM8607_IRQ_RTC,
PM8607_IRQ_VBAT = 8,
PM8607_IRQ_VCHG,
PM8607_IRQ_VSYS,
PM8607_IRQ_TINT,
PM8607_IRQ_GPADC0,
PM8607_IRQ_GPADC1,
PM8607_IRQ_GPADC2,
PM8607_IRQ_GPADC3,
PM8607_IRQ_AUDIO_SHORT = 16,
PM8607_IRQ_PEN,
PM8607_IRQ_HEADSET,
PM8607_IRQ_HOOK,
PM8607_IRQ_MICIN,
PM8607_IRQ_CHG_FAIL,
PM8607_IRQ_CHG_DONE,
PM8607_IRQ_CHG_FAULT,
};
enum {
PM8607_CHIP_A0 = 0x40,
PM8607_CHIP_A1 = 0x41,
PM8607_CHIP_B0 = 0x48,
};
struct pm8607_chip {
struct device *dev;
struct mutex io_lock;
struct i2c_client *client;
int (*read)(struct pm8607_chip *chip, int reg, int bytes, void *dest);
int (*write)(struct pm8607_chip *chip, int reg, int bytes, void *src);
int buck3_double; /* DVC ramp slope double */
unsigned char chip_id;
};
#define PM8607_MAX_REGULATOR 15 /* 3 Bucks, 12 LDOs */
enum {
GI2C_PORT = 0,
PI2C_PORT,
};
struct pm8607_platform_data {
int i2c_port; /* Controlled by GI2C or PI2C */
struct regulator_init_data *regulator[PM8607_MAX_REGULATOR];
};
extern int pm8607_reg_read(struct pm8607_chip *, int);
extern int pm8607_reg_write(struct pm8607_chip *, int, unsigned char);
extern int pm8607_bulk_read(struct pm8607_chip *, int, int,
unsigned char *);
extern int pm8607_bulk_write(struct pm8607_chip *, int, int,
unsigned char *);
extern int pm8607_set_bits(struct pm8607_chip *, int, unsigned char,
unsigned char);
#endif /* __LINUX_MFD_88PM8607_H */

262
include/linux/mfd/ab4500.h Normal file
View File

@ -0,0 +1,262 @@
/*
* Copyright (C) 2009 ST-Ericsson
*
* Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.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.
*
* AB4500 device core funtions, for client access
*/
#ifndef MFD_AB4500_H
#define MFD_AB4500_H
#include <linux/device.h>
/*
* AB4500 bank addresses
*/
#define AB4500_SYS_CTRL1_BLOCK 0x1
#define AB4500_SYS_CTRL2_BLOCK 0x2
#define AB4500_REGU_CTRL1 0x3
#define AB4500_REGU_CTRL2 0x4
#define AB4500_USB 0x5
#define AB4500_TVOUT 0x6
#define AB4500_DBI 0x7
#define AB4500_ECI_AV_ACC 0x8
#define AB4500_RESERVED 0x9
#define AB4500_GPADC 0xA
#define AB4500_CHARGER 0xB
#define AB4500_GAS_GAUGE 0xC
#define AB4500_AUDIO 0xD
#define AB4500_INTERRUPT 0xE
#define AB4500_RTC 0xF
#define AB4500_MISC 0x10
#define AB4500_DEBUG 0x12
#define AB4500_PROD_TEST 0x13
#define AB4500_OTP_EMUL 0x15
/*
* System control 1 register offsets.
* Bank = 0x01
*/
#define AB4500_TURNON_STAT_REG 0x0100
#define AB4500_RESET_STAT_REG 0x0101
#define AB4500_PONKEY1_PRESS_STAT_REG 0x0102
#define AB4500_FSM_STAT1_REG 0x0140
#define AB4500_FSM_STAT2_REG 0x0141
#define AB4500_SYSCLK_REQ_STAT_REG 0x0142
#define AB4500_USB_STAT1_REG 0x0143
#define AB4500_USB_STAT2_REG 0x0144
#define AB4500_STATUS_SPARE1_REG 0x0145
#define AB4500_STATUS_SPARE2_REG 0x0146
#define AB4500_CTRL1_REG 0x0180
#define AB4500_CTRL2_REG 0x0181
/*
* System control 2 register offsets.
* bank = 0x02
*/
#define AB4500_CTRL3_REG 0x0200
#define AB4500_MAIN_WDOG_CTRL_REG 0x0201
#define AB4500_MAIN_WDOG_TIMER_REG 0x0202
#define AB4500_LOW_BAT_REG 0x0203
#define AB4500_BATT_OK_REG 0x0204
#define AB4500_SYSCLK_TIMER_REG 0x0205
#define AB4500_SMPSCLK_CTRL_REG 0x0206
#define AB4500_SMPSCLK_SEL1_REG 0x0207
#define AB4500_SMPSCLK_SEL2_REG 0x0208
#define AB4500_SMPSCLK_SEL3_REG 0x0209
#define AB4500_SYSULPCLK_CONF_REG 0x020A
#define AB4500_SYSULPCLK_CTRL1_REG 0x020B
#define AB4500_SYSCLK_CTRL_REG 0x020C
#define AB4500_SYSCLK_REQ1_VALID_REG 0x020D
#define AB4500_SYSCLK_REQ_VALID_REG 0x020E
#define AB4500_SYSCTRL_SPARE_REG 0x020F
#define AB4500_PAD_CONF_REG 0x0210
/*
* Regu control1 register offsets
* Bank = 0x03
*/
#define AB4500_REGU_SERIAL_CTRL1_REG 0x0300
#define AB4500_REGU_SERIAL_CTRL2_REG 0x0301
#define AB4500_REGU_SERIAL_CTRL3_REG 0x0302
#define AB4500_REGU_REQ_CTRL1_REG 0x0303
#define AB4500_REGU_REQ_CTRL2_REG 0x0304
#define AB4500_REGU_REQ_CTRL3_REG 0x0305
#define AB4500_REGU_REQ_CTRL4_REG 0x0306
#define AB4500_REGU_MISC1_REG 0x0380
#define AB4500_REGU_OTGSUPPLY_CTRL_REG 0x0381
#define AB4500_REGU_VUSB_CTRL_REG 0x0382
#define AB4500_REGU_VAUDIO_SUPPLY_REG 0x0383
#define AB4500_REGU_CTRL1_SPARE_REG 0x0384
/*
* Regu control2 Vmod register offsets
*/
#define AB4500_REGU_VMOD_REGU_REG 0x0440
#define AB4500_REGU_VMOD_SEL1_REG 0x0441
#define AB4500_REGU_VMOD_SEL2_REG 0x0442
#define AB4500_REGU_CTRL_DISCH_REG 0x0443
#define AB4500_REGU_CTRL_DISCH2_REG 0x0444
/*
* USB/ULPI register offsets
* Bank : 0x5
*/
#define AB4500_USB_LINE_STAT_REG 0x0580
#define AB4500_USB_LINE_CTRL1_REG 0x0581
#define AB4500_USB_LINE_CTRL2_REG 0x0582
#define AB4500_USB_LINE_CTRL3_REG 0x0583
#define AB4500_USB_LINE_CTRL4_REG 0x0584
#define AB4500_USB_LINE_CTRL5_REG 0x0585
#define AB4500_USB_OTG_CTRL_REG 0x0587
#define AB4500_USB_OTG_STAT_REG 0x0588
#define AB4500_USB_OTG_STAT_REG 0x0588
#define AB4500_USB_CTRL_SPARE_REG 0x0589
#define AB4500_USB_PHY_CTRL_REG 0x058A
/*
* TVOUT / CTRL register offsets
* Bank : 0x06
*/
#define AB4500_TVOUT_CTRL_REG 0x0680
/*
* DBI register offsets
* Bank : 0x07
*/
#define AB4500_DBI_REG1_REG 0x0700
#define AB4500_DBI_REG2_REG 0x0701
/*
* ECI regsiter offsets
* Bank : 0x08
*/
#define AB4500_ECI_CTRL_REG 0x0800
#define AB4500_ECI_HOOKLEVEL_REG 0x0801
#define AB4500_ECI_DATAOUT_REG 0x0802
#define AB4500_ECI_DATAIN_REG 0x0803
/*
* AV Connector register offsets
* Bank : 0x08
*/
#define AB4500_AV_CONN_REG 0x0840
/*
* Accessory detection register offsets
* Bank : 0x08
*/
#define AB4500_ACC_DET_DB1_REG 0x0880
#define AB4500_ACC_DET_DB2_REG 0x0881
/*
* GPADC register offsets
* Bank : 0x0A
*/
#define AB4500_GPADC_CTRL1_REG 0x0A00
#define AB4500_GPADC_CTRL2_REG 0x0A01
#define AB4500_GPADC_CTRL3_REG 0x0A02
#define AB4500_GPADC_AUTO_TIMER_REG 0x0A03
#define AB4500_GPADC_STAT_REG 0x0A04
#define AB4500_GPADC_MANDATAL_REG 0x0A05
#define AB4500_GPADC_MANDATAH_REG 0x0A06
#define AB4500_GPADC_AUTODATAL_REG 0x0A07
#define AB4500_GPADC_AUTODATAH_REG 0x0A08
#define AB4500_GPADC_MUX_CTRL_REG 0x0A09
/*
* Charger / status register offfsets
* Bank : 0x0B
*/
#define AB4500_CH_STATUS1_REG 0x0B00
#define AB4500_CH_STATUS2_REG 0x0B01
#define AB4500_CH_USBCH_STAT1_REG 0x0B02
#define AB4500_CH_USBCH_STAT2_REG 0x0B03
#define AB4500_CH_FSM_STAT_REG 0x0B04
#define AB4500_CH_STAT_REG 0x0B05
/*
* Charger / control register offfsets
* Bank : 0x0B
*/
#define AB4500_CH_VOLT_LVL_REG 0x0B40
/*
* Charger / main control register offfsets
* Bank : 0x0B
*/
#define AB4500_MCH_CTRL1 0x0B80
#define AB4500_MCH_CTRL2 0x0B81
#define AB4500_MCH_IPT_CURLVL_REG 0x0B82
#define AB4500_CH_WD_REG 0x0B83
/*
* Charger / USB control register offsets
* Bank : 0x0B
*/
#define AB4500_USBCH_CTRL1_REG 0x0BC0
#define AB4500_USBCH_CTRL2_REG 0x0BC1
#define AB4500_USBCH_IPT_CRNTLVL_REG 0x0BC2
/*
* RTC bank register offsets
* Bank : 0xF
*/
#define AB4500_RTC_SOFF_STAT_REG 0x0F00
#define AB4500_RTC_CC_CONF_REG 0x0F01
#define AB4500_RTC_READ_REQ_REG 0x0F02
#define AB4500_RTC_WATCH_TSECMID_REG 0x0F03
#define AB4500_RTC_WATCH_TSECHI_REG 0x0F04
#define AB4500_RTC_WATCH_TMIN_LOW_REG 0x0F05
#define AB4500_RTC_WATCH_TMIN_MID_REG 0x0F06
#define AB4500_RTC_WATCH_TMIN_HI_REG 0x0F07
#define AB4500_RTC_ALRM_MIN_LOW_REG 0x0F08
#define AB4500_RTC_ALRM_MIN_MID_REG 0x0F09
#define AB4500_RTC_ALRM_MIN_HI_REG 0x0F0A
#define AB4500_RTC_STAT_REG 0x0F0B
#define AB4500_RTC_BKUP_CHG_REG 0x0F0C
#define AB4500_RTC_FORCE_BKUP_REG 0x0F0D
#define AB4500_RTC_CALIB_REG 0x0F0E
#define AB4500_RTC_SWITCH_STAT_REG 0x0F0F
/*
* PWM Out generators
* Bank: 0x10
*/
#define AB4500_PWM_OUT_CTRL1_REG 0x1060
#define AB4500_PWM_OUT_CTRL2_REG 0x1061
#define AB4500_PWM_OUT_CTRL3_REG 0x1062
#define AB4500_PWM_OUT_CTRL4_REG 0x1063
#define AB4500_PWM_OUT_CTRL5_REG 0x1064
#define AB4500_PWM_OUT_CTRL6_REG 0x1065
#define AB4500_PWM_OUT_CTRL7_REG 0x1066
#define AB4500_I2C_PAD_CTRL_REG 0x1067
#define AB4500_REV_REG 0x1080
/**
* struct ab4500
* @spi: spi device structure
* @tx_buf: transmit buffer
* @rx_buf: receive buffer
* @lock: sync primitive
*/
struct ab4500 {
struct spi_device *spi;
unsigned long tx_buf[4];
unsigned long rx_buf[4];
struct mutex lock;
};
int ab4500_write(struct ab4500 *ab4500, unsigned char block,
unsigned long addr, unsigned char data);
int ab4500_read(struct ab4500 *ab4500, unsigned char block,
unsigned long addr);
#endif /* MFD_AB4500_H */

299
include/linux/mfd/adp5520.h Normal file
View File

@ -0,0 +1,299 @@
/*
* Definitions and platform data for Analog Devices
* ADP5520/ADP5501 MFD PMICs (Backlight, LED, GPIO and Keys)
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef __LINUX_MFD_ADP5520_H
#define __LINUX_MFD_ADP5520_H
#define ID_ADP5520 5520
#define ID_ADP5501 5501
/*
* ADP5520/ADP5501 Register Map
*/
#define ADP5520_MODE_STATUS 0x00
#define ADP5520_INTERRUPT_ENABLE 0x01
#define ADP5520_BL_CONTROL 0x02
#define ADP5520_BL_TIME 0x03
#define ADP5520_BL_FADE 0x04
#define ADP5520_DAYLIGHT_MAX 0x05
#define ADP5520_DAYLIGHT_DIM 0x06
#define ADP5520_OFFICE_MAX 0x07
#define ADP5520_OFFICE_DIM 0x08
#define ADP5520_DARK_MAX 0x09
#define ADP5520_DARK_DIM 0x0A
#define ADP5520_BL_VALUE 0x0B
#define ADP5520_ALS_CMPR_CFG 0x0C
#define ADP5520_L2_TRIP 0x0D
#define ADP5520_L2_HYS 0x0E
#define ADP5520_L3_TRIP 0x0F
#define ADP5520_L3_HYS 0x10
#define ADP5520_LED_CONTROL 0x11
#define ADP5520_LED_TIME 0x12
#define ADP5520_LED_FADE 0x13
#define ADP5520_LED1_CURRENT 0x14
#define ADP5520_LED2_CURRENT 0x15
#define ADP5520_LED3_CURRENT 0x16
/*
* ADP5520 Register Map
*/
#define ADP5520_GPIO_CFG_1 0x17
#define ADP5520_GPIO_CFG_2 0x18
#define ADP5520_GPIO_IN 0x19
#define ADP5520_GPIO_OUT 0x1A
#define ADP5520_GPIO_INT_EN 0x1B
#define ADP5520_GPIO_INT_STAT 0x1C
#define ADP5520_GPIO_INT_LVL 0x1D
#define ADP5520_GPIO_DEBOUNCE 0x1E
#define ADP5520_GPIO_PULLUP 0x1F
#define ADP5520_KP_INT_STAT_1 0x20
#define ADP5520_KP_INT_STAT_2 0x21
#define ADP5520_KR_INT_STAT_1 0x22
#define ADP5520_KR_INT_STAT_2 0x23
#define ADP5520_KEY_STAT_1 0x24
#define ADP5520_KEY_STAT_2 0x25
/*
* MODE_STATUS bits
*/
#define ADP5520_nSTNBY (1 << 7)
#define ADP5520_BL_EN (1 << 6)
#define ADP5520_DIM_EN (1 << 5)
#define ADP5520_OVP_INT (1 << 4)
#define ADP5520_CMPR_INT (1 << 3)
#define ADP5520_GPI_INT (1 << 2)
#define ADP5520_KR_INT (1 << 1)
#define ADP5520_KP_INT (1 << 0)
/*
* INTERRUPT_ENABLE bits
*/
#define ADP5520_AUTO_LD_EN (1 << 4)
#define ADP5520_CMPR_IEN (1 << 3)
#define ADP5520_OVP_IEN (1 << 2)
#define ADP5520_KR_IEN (1 << 1)
#define ADP5520_KP_IEN (1 << 0)
/*
* BL_CONTROL bits
*/
#define ADP5520_BL_LVL ((x) << 5)
#define ADP5520_BL_LAW ((x) << 4)
#define ADP5520_BL_AUTO_ADJ (1 << 3)
#define ADP5520_OVP_EN (1 << 2)
#define ADP5520_FOVR (1 << 1)
#define ADP5520_KP_BL_EN (1 << 0)
/*
* ALS_CMPR_CFG bits
*/
#define ADP5520_L3_OUT (1 << 3)
#define ADP5520_L2_OUT (1 << 2)
#define ADP5520_L3_EN (1 << 1)
#define ADP5020_MAX_BRIGHTNESS 0x7F
#define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4))
#define BL_CTRL_VAL(law, auto) (((1 & (auto)) << 3) | ((0x3 & (law)) << 4))
#define ALS_CMPR_CFG_VAL(filt, l3_en) (((0x7 & filt) << 5) | l3_en)
/*
* LEDs subdevice bits and masks
*/
#define ADP5520_01_MAXLEDS 3
#define ADP5520_FLAG_LED_MASK 0x3
#define ADP5520_FLAG_OFFT_SHIFT 8
#define ADP5520_FLAG_OFFT_MASK 0x3
#define ADP5520_R3_MODE (1 << 5)
#define ADP5520_C3_MODE (1 << 4)
#define ADP5520_LED_LAW (1 << 3)
#define ADP5520_LED3_EN (1 << 2)
#define ADP5520_LED2_EN (1 << 1)
#define ADP5520_LED1_EN (1 << 0)
/*
* GPIO subdevice bits and masks
*/
#define ADP5520_MAXGPIOS 8
#define ADP5520_GPIO_C3 (1 << 7) /* LED2 or GPIO7 aka C3 */
#define ADP5520_GPIO_C2 (1 << 6)
#define ADP5520_GPIO_C1 (1 << 5)
#define ADP5520_GPIO_C0 (1 << 4)
#define ADP5520_GPIO_R3 (1 << 3) /* LED3 or GPIO3 aka R3 */
#define ADP5520_GPIO_R2 (1 << 2)
#define ADP5520_GPIO_R1 (1 << 1)
#define ADP5520_GPIO_R0 (1 << 0)
struct adp5520_gpio_platform_data {
unsigned gpio_start;
u8 gpio_en_mask;
u8 gpio_pullup_mask;
};
/*
* Keypad subdevice bits and masks
*/
#define ADP5520_MAXKEYS 16
#define ADP5520_COL_C3 (1 << 7) /* LED2 or GPIO7 aka C3 */
#define ADP5520_COL_C2 (1 << 6)
#define ADP5520_COL_C1 (1 << 5)
#define ADP5520_COL_C0 (1 << 4)
#define ADP5520_ROW_R3 (1 << 3) /* LED3 or GPIO3 aka R3 */
#define ADP5520_ROW_R2 (1 << 2)
#define ADP5520_ROW_R1 (1 << 1)
#define ADP5520_ROW_R0 (1 << 0)
#define ADP5520_KEY(row, col) (col + row * 4)
#define ADP5520_KEYMAPSIZE ADP5520_MAXKEYS
struct adp5520_keys_platform_data {
int rows_en_mask; /* Number of rows */
int cols_en_mask; /* Number of columns */
const unsigned short *keymap; /* Pointer to keymap */
unsigned short keymapsize; /* Keymap size */
unsigned repeat:1; /* Enable key repeat */
};
/*
* LEDs subdevice platform data
*/
#define FLAG_ID_ADP5520_LED1_ADP5501_LED0 1 /* ADP5520 PIN ILED */
#define FLAG_ID_ADP5520_LED2_ADP5501_LED1 2 /* ADP5520 PIN C3 */
#define FLAG_ID_ADP5520_LED3_ADP5501_LED2 3 /* ADP5520 PIN R3 */
#define ADP5520_LED_DIS_BLINK (0 << ADP5520_FLAG_OFFT_SHIFT)
#define ADP5520_LED_OFFT_600ms (1 << ADP5520_FLAG_OFFT_SHIFT)
#define ADP5520_LED_OFFT_800ms (2 << ADP5520_FLAG_OFFT_SHIFT)
#define ADP5520_LED_OFFT_1200ms (3 << ADP5520_FLAG_OFFT_SHIFT)
#define ADP5520_LED_ONT_200ms 0
#define ADP5520_LED_ONT_600ms 1
#define ADP5520_LED_ONT_800ms 2
#define ADP5520_LED_ONT_1200ms 3
struct adp5520_leds_platform_data {
int num_leds;
struct led_info *leds;
u8 fade_in; /* Backlight Fade-In Timer */
u8 fade_out; /* Backlight Fade-Out Timer */
u8 led_on_time;
};
/*
* Backlight subdevice platform data
*/
#define ADP5520_FADE_T_DIS 0 /* Fade Timer Disabled */
#define ADP5520_FADE_T_300ms 1 /* 0.3 Sec */
#define ADP5520_FADE_T_600ms 2
#define ADP5520_FADE_T_900ms 3
#define ADP5520_FADE_T_1200ms 4
#define ADP5520_FADE_T_1500ms 5
#define ADP5520_FADE_T_1800ms 6
#define ADP5520_FADE_T_2100ms 7
#define ADP5520_FADE_T_2400ms 8
#define ADP5520_FADE_T_2700ms 9
#define ADP5520_FADE_T_3000ms 10
#define ADP5520_FADE_T_3500ms 11
#define ADP5520_FADE_T_4000ms 12
#define ADP5520_FADE_T_4500ms 13
#define ADP5520_FADE_T_5000ms 14
#define ADP5520_FADE_T_5500ms 15 /* 5.5 Sec */
#define ADP5520_BL_LAW_LINEAR 0
#define ADP5520_BL_LAW_SQUARE 1
#define ADP5520_BL_LAW_CUBIC1 2
#define ADP5520_BL_LAW_CUBIC2 3
#define ADP5520_BL_AMBL_FILT_80ms 0 /* Light sensor filter time */
#define ADP5520_BL_AMBL_FILT_160ms 1
#define ADP5520_BL_AMBL_FILT_320ms 2
#define ADP5520_BL_AMBL_FILT_640ms 3
#define ADP5520_BL_AMBL_FILT_1280ms 4
#define ADP5520_BL_AMBL_FILT_2560ms 5
#define ADP5520_BL_AMBL_FILT_5120ms 6
#define ADP5520_BL_AMBL_FILT_10240ms 7 /* 10.24 sec */
/*
* Blacklight current 0..30mA
*/
#define ADP5520_BL_CUR_mA(I) ((I * 127) / 30)
/*
* L2 comparator current 0..1000uA
*/
#define ADP5520_L2_COMP_CURR_uA(I) ((I * 255) / 1000)
/*
* L3 comparator current 0..127uA
*/
#define ADP5520_L3_COMP_CURR_uA(I) ((I * 255) / 127)
struct adp5520_backlight_platform_data {
u8 fade_in; /* Backlight Fade-In Timer */
u8 fade_out; /* Backlight Fade-Out Timer */
u8 fade_led_law; /* fade-on/fade-off transfer characteristic */
u8 en_ambl_sens; /* 1 = enable ambient light sensor */
u8 abml_filt; /* Light sensor filter time */
u8 l1_daylight_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
u8 l1_daylight_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
u8 l2_office_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
u8 l2_office_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
u8 l3_dark_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
u8 l3_dark_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
u8 l2_trip; /* use L2_COMP_CURR_uA(I) 0 <= I <= 1000 uA */
u8 l2_hyst; /* use L2_COMP_CURR_uA(I) 0 <= I <= 1000 uA */
u8 l3_trip; /* use L3_COMP_CURR_uA(I) 0 <= I <= 127 uA */
u8 l3_hyst; /* use L3_COMP_CURR_uA(I) 0 <= I <= 127 uA */
};
/*
* MFD chip platform data
*/
struct adp5520_platform_data {
struct adp5520_keys_platform_data *keys;
struct adp5520_gpio_platform_data *gpio;
struct adp5520_leds_platform_data *leds;
struct adp5520_backlight_platform_data *backlight;
};
/*
* MFD chip functions
*/
extern int adp5520_read(struct device *dev, int reg, uint8_t *val);
extern int adp5520_write(struct device *dev, int reg, u8 val);
extern int adp5520_clr_bits(struct device *dev, int reg, uint8_t bit_mask);
extern int adp5520_set_bits(struct device *dev, int reg, uint8_t bit_mask);
extern int adp5520_register_notifier(struct device *dev,
struct notifier_block *nb, unsigned int events);
extern int adp5520_unregister_notifier(struct device *dev,
struct notifier_block *nb, unsigned int events);
#endif /* __LINUX_MFD_ADP5520_H */

View File

@ -231,9 +231,6 @@ void pcap_set_ts_bits(struct pcap_chip *, u32);
#define PCAP_LED_4MA 1
#define PCAP_LED_5MA 2
#define PCAP_LED_9MA 3
#define PCAP_LED_GPIO_VAL_MASK 0x00ffffff
#define PCAP_LED_GPIO_EN 0x01000000
#define PCAP_LED_GPIO_INVERT 0x02000000
#define PCAP_LED_T_MASK 0xf
#define PCAP_LED_C_MASK 0x3
#define PCAP_BL_MASK 0x1f

View File

@ -24,52 +24,23 @@
#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
#include <linux/interrupt.h>
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;
struct spi_device *spidev;
struct mutex lock;
int irq;
unsigned int flags;
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;
irq_handler_t irqhandler[MC13783_NUM_IRQ];
void *irqdata[MC13783_NUM_IRQ];
/* XXX these should go as platformdata to the regulator subdevice */
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
@ -136,55 +107,6 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
#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
*/
@ -284,113 +206,15 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
#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)
static inline int mc13783_set_bits(struct mc13783 *mc13783, unsigned int offset,
u32 mask, u32 val)
{
int ret;
mc13783_lock(mc13783);
ret = mc13783_reg_rmw(mc13783, offset, mask, val);
mc13783_unlock(mc13783);
#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)
return ret;
}
#endif /* __LINUX_MFD_MC13783_PRIV_H */

View File

@ -1,28 +1,50 @@
/*
* Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
* Copyright 2009 Pengutronix
* Uwe Kleine-Koenig <u.kleine-koenig@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.
* 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.
*/
#ifndef __LINUX_MFD_MC13783_H
#define __LINUX_MFD_MC13783_H
#ifndef __INCLUDE_LINUX_MFD_MC13783_H
#define __INCLUDE_LINUX_MFD_MC13783_H
#include <linux/interrupt.h>
struct mc13783;
void mc13783_lock(struct mc13783 *mc13783);
void mc13783_unlock(struct mc13783 *mc13783);
int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val);
int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
u32 mask, u32 val);
int mc13783_irq_request(struct mc13783 *mc13783, int irq,
irq_handler_t handler, const char *name, void *dev);
int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
irq_handler_t handler, const char *name, void *dev);
int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev);
int mc13783_ackirq(struct mc13783 *mc13783, int irq);
int mc13783_mask(struct mc13783 *mc13783, int irq);
int mc13783_unmask(struct mc13783 *mc13783, int irq);
#define MC13783_ADC0 43
#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_ADINC1 (1 << 16)
#define MC13783_ADC0_ADINC2 (1 << 17)
#define MC13783_ADC0_TSMOD_MASK (MC13783_ADC0_TSMOD0 | \
MC13783_ADC0_TSMOD1 | \
MC13783_ADC0_TSMOD2)
/* to be cleaned up */
struct regulator_init_data;
struct mc13783_regulator_init_data {
@ -30,23 +52,30 @@ struct mc13783_regulator_init_data {
struct regulator_init_data *init_data;
};
struct mc13783_platform_data {
struct mc13783_regulator_init_data *regulators;
struct mc13783_regulator_platform_data {
int num_regulators;
unsigned int flags;
struct mc13783_regulator_init_data *regulators;
};
/* mc13783_platform_data flags */
struct mc13783_platform_data {
int num_regulators;
struct mc13783_regulator_init_data *regulators;
#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)
unsigned int flags;
};
#define MC13783_ADC_MODE_TS 1
#define MC13783_ADC_MODE_SINGLE_CHAN 2
#define MC13783_ADC_MODE_MULT_CHAN 3
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
@ -80,5 +109,46 @@ void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
#define MC13783_REGU_V3 29
#define MC13783_REGU_V4 30
#endif /* __INCLUDE_LINUX_MFD_MC13783_H */
#define MC13783_IRQ_ADCDONE 0
#define MC13783_IRQ_ADCBISDONE 1
#define MC13783_IRQ_TS 2
#define MC13783_IRQ_WHIGH 3
#define MC13783_IRQ_WLOW 4
#define MC13783_IRQ_CHGDET 6
#define MC13783_IRQ_CHGOV 7
#define MC13783_IRQ_CHGREV 8
#define MC13783_IRQ_CHGSHORT 9
#define MC13783_IRQ_CCCV 10
#define MC13783_IRQ_CHGCURR 11
#define MC13783_IRQ_BPON 12
#define MC13783_IRQ_LOBATL 13
#define MC13783_IRQ_LOBATH 14
#define MC13783_IRQ_UDP 15
#define MC13783_IRQ_USB 16
#define MC13783_IRQ_ID 19
#define MC13783_IRQ_SE1 21
#define MC13783_IRQ_CKDET 22
#define MC13783_IRQ_UDM 23
#define MC13783_IRQ_1HZ 24
#define MC13783_IRQ_TODA 25
#define MC13783_IRQ_ONOFD1 27
#define MC13783_IRQ_ONOFD2 28
#define MC13783_IRQ_ONOFD3 29
#define MC13783_IRQ_SYSRST 30
#define MC13783_IRQ_RTCRST 31
#define MC13783_IRQ_PC 32
#define MC13783_IRQ_WARM 33
#define MC13783_IRQ_MEMHLD 34
#define MC13783_IRQ_PWRRDY 35
#define MC13783_IRQ_THWARNL 36
#define MC13783_IRQ_THWARNH 37
#define MC13783_IRQ_CLK 38
#define MC13783_IRQ_SEMAF 39
#define MC13783_IRQ_MC2B 41
#define MC13783_IRQ_HSDET 42
#define MC13783_IRQ_HSL 43
#define MC13783_IRQ_ALSPTH 44
#define MC13783_IRQ_AHSSHORT 45
#define MC13783_NUM_IRQ 46
#endif /* __LINUX_MFD_MC13783_H */

View File

@ -40,10 +40,6 @@ struct pcf50633_platform_data {
u8 resumers[5];
};
struct pcf50633_subdev_pdata {
struct pcf50633 *pcf;
};
struct pcf50633_irq {
void (*handler) (int, void *);
void *data;
@ -217,5 +213,9 @@ enum pcf50633_reg_int5 {
#define PCF50633_REG_LEDCTL 0x2a
#define PCF50633_REG_LEDDIM 0x2b
#endif
static inline struct pcf50633 *dev_to_pcf50633(struct device *dev)
{
return dev_get_drvdata(dev);
}
#endif

View File

@ -16,7 +16,6 @@
#define __MFD_WM831X_CORE_H__
#include <linux/interrupt.h>
#include <linux/workqueue.h>
/*
* Register values.
@ -117,6 +116,7 @@
#define WM831X_DC3_SLEEP_CONTROL 0x4063
#define WM831X_DC4_CONTROL 0x4064
#define WM831X_DC4_SLEEP_CONTROL 0x4065
#define WM832X_DC4_SLEEP_CONTROL 0x4067
#define WM831X_EPE1_CONTROL 0x4066
#define WM831X_EPE2_CONTROL 0x4067
#define WM831X_LDO1_CONTROL 0x4068
@ -235,6 +235,8 @@
struct regulator_dev;
#define WM831X_NUM_IRQ_REGS 5
struct wm831x {
struct mutex io_lock;
@ -248,10 +250,11 @@ struct wm831x {
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];
int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
int num_gpio;
struct mutex auxadc_lock;
@ -278,12 +281,30 @@ int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
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);
static inline int __must_check wm831x_request_irq(struct wm831x *wm831x,
unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev)
{
return request_threaded_irq(irq, NULL, handler, flags, name, dev);
}
static inline void wm831x_free_irq(struct wm831x *wm831x,
unsigned int irq, void *dev)
{
free_irq(irq, dev);
}
static inline void wm831x_disable_irq(struct wm831x *wm831x, int irq)
{
disable_irq(irq);
}
static inline void wm831x_enable_irq(struct wm831x *wm831x, int irq)
{
enable_irq(irq);
}
#endif

View File

@ -91,6 +91,7 @@ struct wm831x_pdata {
/** Called after subdevices are set up */
int (*post_init)(struct wm831x *wm831x);
int irq_base;
int gpio_base;
struct wm831x_backlight_pdata *backlight;
struct wm831x_backup_pdata *backup;

View File

@ -15,7 +15,7 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/mfd/wm8350/audio.h>
#include <linux/mfd/wm8350/gpio.h>
@ -601,7 +601,7 @@ extern const u16 wm8352_mode3_defaults[];
struct wm8350;
struct wm8350_irq {
void (*handler) (struct wm8350 *, int, void *);
irq_handler_t handler;
void *data;
};
@ -646,10 +646,12 @@ struct wm8350 {
* @init: Function called during driver initialisation. Should be
* used by the platform to configure GPIO functions and similar.
* @irq_high: Set if WM8350 IRQ is active high.
* @irq_base: Base IRQ for genirq (not currently used).
*/
struct wm8350_platform_data {
int (*init)(struct wm8350 *wm8350);
int irq_high;
int irq_base;
};
@ -676,11 +678,13 @@ int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src);
* WM8350 internal interrupts
*/
int wm8350_register_irq(struct wm8350 *wm8350, int irq,
void (*handler) (struct wm8350 *, int, void *),
void *data);
irq_handler_t handler, unsigned long flags,
const char *name, void *data);
int wm8350_free_irq(struct wm8350 *wm8350, int irq);
int wm8350_mask_irq(struct wm8350 *wm8350, int irq);
int wm8350_unmask_irq(struct wm8350 *wm8350, int irq);
int wm8350_irq_init(struct wm8350 *wm8350, int irq,
struct wm8350_platform_data *pdata);
int wm8350_irq_exit(struct wm8350 *wm8350);
#endif

View File

@ -172,6 +172,24 @@
#define WM8350_GPIO_DEBOUNCE_OFF 0
#define WM8350_GPIO_DEBOUNCE_ON 1
/*
* R30 (0x1E) - GPIO Interrupt Status
*/
#define WM8350_GP12_EINT 0x1000
#define WM8350_GP11_EINT 0x0800
#define WM8350_GP10_EINT 0x0400
#define WM8350_GP9_EINT 0x0200
#define WM8350_GP8_EINT 0x0100
#define WM8350_GP7_EINT 0x0080
#define WM8350_GP6_EINT 0x0040
#define WM8350_GP5_EINT 0x0020
#define WM8350_GP4_EINT 0x0010
#define WM8350_GP3_EINT 0x0008
#define WM8350_GP2_EINT 0x0004
#define WM8350_GP1_EINT 0x0002
#define WM8350_GP0_EINT 0x0001
/*
* R128 (0x80) - GPIO Debounce
*/

View File

@ -26,7 +26,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#include <linux/i2c/twl.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -175,7 +175,7 @@ static int twl4030_write(struct snd_soc_codec *codec,
{
twl4030_write_reg_cache(codec, reg, value);
if (likely(reg < TWL4030_REG_SW_SHADOW))
return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value,
return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value,
reg);
else
return 0;
@ -261,7 +261,7 @@ static void twl4030_power_up(struct snd_soc_codec *codec)
do {
/* this takes a little while, so don't slam i2c */
udelay(2000);
twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
TWL4030_REG_ANAMICL);
} while ((i++ < 100) &&
((byte & TWL4030_CNCL_OFFSET_START) ==
@ -542,7 +542,7 @@ static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
break; \
case SND_SOC_DAPM_POST_PMD: \
reg_val = twl4030_read_reg_cache(w->codec, reg); \
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
reg_val & (~mask), \
reg); \
break; \
@ -679,7 +679,7 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
twl4030->sysclk) + 1);
/* Bypass the reg_cache to mute the headset */
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
hs_gain & (~0x0f),
TWL4030_REG_HS_GAIN_SET);

View File

@ -1340,9 +1340,10 @@ static int wm8350_resume(struct platform_device *pdev)
return 0;
}
static void wm8350_hp_jack_handler(struct wm8350 *wm8350, int irq, void *data)
static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
{
struct wm8350_data *priv = data;
struct wm8350 *wm8350 = priv->codec.control_data;
u16 reg;
int report;
int mask;
@ -1365,7 +1366,7 @@ static void wm8350_hp_jack_handler(struct wm8350 *wm8350, int irq, void *data)
if (!jack->jack) {
dev_warn(wm8350->dev, "Jack interrupt called with no jack\n");
return;
return IRQ_NONE;
}
/* Debounce */
@ -1378,6 +1379,8 @@ static void wm8350_hp_jack_handler(struct wm8350 *wm8350, int irq, void *data)
report = 0;
snd_soc_jack_report(jack->jack, report, jack->report);
return IRQ_HANDLED;
}
/**
@ -1421,9 +1424,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
/* Sync status */
wm8350_hp_jack_handler(wm8350, irq, priv);
wm8350_unmask_irq(wm8350, irq);
wm8350_hp_jack_handler(irq, priv);
return 0;
}
@ -1482,12 +1483,16 @@ static int wm8350_probe(struct platform_device *pdev)
wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
/* Make sure jack detect is disabled to start off with */
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
WM8350_JDL_ENA | WM8350_JDR_ENA);
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
wm8350_hp_jack_handler, priv);
wm8350_hp_jack_handler, 0, "Left jack detect",
priv);
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
wm8350_hp_jack_handler, priv);
wm8350_hp_jack_handler, 0, "Right jack detect",
priv);
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@ -1516,8 +1521,6 @@ static int wm8350_remove(struct platform_device *pdev)
WM8350_JDL_ENA | WM8350_JDR_ENA);
wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);