Commit b03b4d7c authored by Mark Brown's avatar Mark Brown Committed by Samuel Ortiz

mfd: Ensure WM831x charger interrupts are acknowledged when suspending

The charger interrupts on the WM831x are unconditionally a wake source
for the system. If the power driver is not able to monitor them (for
example, due to the IRQ line not having been wired up on the system)
then any charger interrupt will prevent the system suspending for any
meaningful amount of time since nothing will ack them.

Avoid this issue by manually acknowledging these interrupts when we
suspend the WM831x core device if they are masked. If software is
actually using the interrupts then they will be unmasked and this
change will have no effect.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent 0d7e0e39
...@@ -1494,6 +1494,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ...@@ -1494,6 +1494,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8310: case WM8310:
parent = WM8310; parent = WM8310;
wm831x->num_gpio = 16; wm831x->num_gpio = 16;
wm831x->charger_irq_wake = 1;
if (rev > 0) { if (rev > 0) {
wm831x->has_gpio_ena = 1; wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1; wm831x->has_cs_sts = 1;
...@@ -1505,6 +1506,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ...@@ -1505,6 +1506,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8311: case WM8311:
parent = WM8311; parent = WM8311;
wm831x->num_gpio = 16; wm831x->num_gpio = 16;
wm831x->charger_irq_wake = 1;
if (rev > 0) { if (rev > 0) {
wm831x->has_gpio_ena = 1; wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1; wm831x->has_cs_sts = 1;
...@@ -1516,6 +1518,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ...@@ -1516,6 +1518,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8312: case WM8312:
parent = WM8312; parent = WM8312;
wm831x->num_gpio = 16; wm831x->num_gpio = 16;
wm831x->charger_irq_wake = 1;
if (rev > 0) { if (rev > 0) {
wm831x->has_gpio_ena = 1; wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1; wm831x->has_cs_sts = 1;
...@@ -1654,6 +1657,42 @@ static void wm831x_device_exit(struct wm831x *wm831x) ...@@ -1654,6 +1657,42 @@ static void wm831x_device_exit(struct wm831x *wm831x)
kfree(wm831x); kfree(wm831x);
} }
static int wm831x_device_suspend(struct wm831x *wm831x)
{
int reg, mask;
/* If the charger IRQs are a wake source then make sure we ack
* them even if they're not actively being used (eg, no power
* driver or no IRQ line wired up) then acknowledge the
* interrupts otherwise suspend won't last very long.
*/
if (wm831x->charger_irq_wake) {
reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
mask = WM831X_CHG_BATT_HOT_EINT |
WM831X_CHG_BATT_COLD_EINT |
WM831X_CHG_BATT_FAIL_EINT |
WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
WM831X_CHG_START_EINT;
/* If any of the interrupts are masked read the statuses */
if (reg & mask)
reg = wm831x_reg_read(wm831x,
WM831X_INTERRUPT_STATUS_2);
if (reg & mask) {
dev_info(wm831x->dev,
"Acknowledging masked charger IRQs: %x\n",
reg & mask);
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
reg & mask);
}
}
return 0;
}
static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *dest) int bytes, void *dest)
{ {
...@@ -1728,6 +1767,13 @@ static int wm831x_i2c_remove(struct i2c_client *i2c) ...@@ -1728,6 +1767,13 @@ static int wm831x_i2c_remove(struct i2c_client *i2c)
return 0; return 0;
} }
static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
{
struct wm831x *wm831x = i2c_get_clientdata(i2c);
return wm831x_device_suspend(wm831x);
}
static const struct i2c_device_id wm831x_i2c_id[] = { static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8310", WM8310 }, { "wm8310", WM8310 },
{ "wm8311", WM8311 }, { "wm8311", WM8311 },
...@@ -1745,6 +1791,7 @@ static struct i2c_driver wm831x_i2c_driver = { ...@@ -1745,6 +1791,7 @@ static struct i2c_driver wm831x_i2c_driver = {
}, },
.probe = wm831x_i2c_probe, .probe = wm831x_i2c_probe,
.remove = wm831x_i2c_remove, .remove = wm831x_i2c_remove,
.suspend = wm831x_i2c_suspend,
.id_table = wm831x_i2c_id, .id_table = wm831x_i2c_id,
}; };
......
...@@ -256,8 +256,9 @@ struct wm831x { ...@@ -256,8 +256,9 @@ struct wm831x {
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */ int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
/* Chip revision based flags */ /* Chip revision based flags */
unsigned has_gpio_ena:1; /* Has GPIO enable bit */ unsigned has_gpio_ena:1; /* Has GPIO enable bit */
unsigned has_cs_sts:1; /* Has current sink status bit */ unsigned has_cs_sts:1; /* Has current sink status bit */
unsigned charger_irq_wake:1; /* Are charger IRQs a wake source? */
int num_gpio; int num_gpio;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment