mirror of https://gitee.com/openkylin/linux.git
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:
commit
76b8f82cde
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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},
|
||||
};
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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, ®_val);
|
||||
adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val);
|
||||
else
|
||||
adp5520_read(dev->master, GPIO_IN, ®_val);
|
||||
adp5520_read(dev->master, ADP5520_GPIO_IN, ®_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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ®_val_lo);
|
||||
adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, ®_val_hi);
|
||||
|
||||
keymask = (reg_val_hi << 8) | reg_val_lo;
|
||||
/* Read twice to clear */
|
||||
adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, ®_val_lo);
|
||||
adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, ®_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, ®_val_lo);
|
||||
adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, ®_val_hi);
|
||||
|
||||
keymask = (reg_val_hi << 8) | reg_val_lo;
|
||||
/* Read twice to clear */
|
||||
adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, ®_val_lo);
|
||||
adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, ®_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");
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
|
@ -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, ®_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, ®_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, ®_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, ®_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");
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
®ulator_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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(®ulators[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;
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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, ®_val);
|
||||
error = adp5520_read(data->master, ADP5520_BL_VALUE, ®_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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue