Skip to content

Commit

Permalink
mfd: Deny ab8500 suspend if i2c transfer is ongoing
Browse files Browse the repository at this point in the history
If we are in the middle of an I2C transfer we need to deny suspend
of the AB8500 core. Implement an atomic reference counter for the
I2C operations to make sure we don't do this.

Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Reviewed-by: Mattias Wallin <mattias.wallin@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
  • Loading branch information
Jonas Aaberg authored and Samuel Ortiz committed May 1, 2012
1 parent 7e82d6f commit 112a80d
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 8 deletions.
38 changes: 31 additions & 7 deletions drivers/mfd/ab8500-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,13 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
static int ab8500_set_register(struct device *dev, u8 bank,
u8 reg, u8 value)
{
int ret;
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);

return set_register_interruptible(ab8500, bank, reg, value);
atomic_inc(&ab8500->transfer_ongoing);
ret = set_register_interruptible(ab8500, bank, reg, value);
atomic_dec(&ab8500->transfer_ongoing);
return ret;
}

static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
Expand Down Expand Up @@ -192,9 +196,13 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
static int ab8500_get_register(struct device *dev, u8 bank,
u8 reg, u8 *value)
{
int ret;
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);

return get_register_interruptible(ab8500, bank, reg, value);
atomic_inc(&ab8500->transfer_ongoing);
ret = get_register_interruptible(ab8500, bank, reg, value);
atomic_dec(&ab8500->transfer_ongoing);
return ret;
}

static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
Expand Down Expand Up @@ -241,11 +249,14 @@ static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
static int ab8500_mask_and_set_register(struct device *dev,
u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
{
int ret;
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);

return mask_and_set_register_interruptible(ab8500, bank, reg,
bitmask, bitvalues);

atomic_inc(&ab8500->transfer_ongoing);
ret= mask_and_set_register_interruptible(ab8500, bank, reg,
bitmask, bitvalues);
atomic_dec(&ab8500->transfer_ongoing);
return ret;
}

static struct abx500_ops ab8500_ops = {
Expand All @@ -264,6 +275,7 @@ static void ab8500_irq_lock(struct irq_data *data)
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);

mutex_lock(&ab8500->irq_lock);
atomic_inc(&ab8500->transfer_ongoing);
}

static void ab8500_irq_sync_unlock(struct irq_data *data)
Expand Down Expand Up @@ -292,7 +304,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
}

atomic_dec(&ab8500->transfer_ongoing);
mutex_unlock(&ab8500->irq_lock);
}

Expand Down Expand Up @@ -332,6 +344,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev)

dev_vdbg(ab8500->dev, "interrupt\n");

atomic_inc(&ab8500->transfer_ongoing);

for (i = 0; i < ab8500->mask_size; i++) {
int regoffset = ab8500->irq_reg_offset[i];
int status;
Expand All @@ -355,9 +369,10 @@ static irqreturn_t ab8500_irq(int irq, void *dev)

handle_nested_irq(ab8500->irq_base + line);
value &= ~(1 << bit);

} while (value);
}

atomic_dec(&ab8500->transfer_ongoing);
return IRQ_HANDLED;
}

Expand Down Expand Up @@ -411,6 +426,14 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
}
}

int ab8500_suspend(struct ab8500 *ab8500)
{
if (atomic_read(&ab8500->transfer_ongoing))
return -EINVAL;
else
return 0;
}

/* AB8500 GPIO Resources */
static struct resource __devinitdata ab8500_gpio_resources[] = {
{
Expand Down Expand Up @@ -1059,6 +1082,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)

mutex_init(&ab8500->lock);
mutex_init(&ab8500->irq_lock);
atomic_set(&ab8500->transfer_ongoing, 0);

if (version != AB8500_VERSION_UNDEFINED)
ab8500->version = version;
Expand Down
6 changes: 5 additions & 1 deletion include/linux/mfd/abx500/ab8500.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef MFD_AB8500_H
#define MFD_AB8500_H

#include <linux/atomic.h>
#include <linux/mutex.h>

struct device;
Expand Down Expand Up @@ -224,6 +225,7 @@ enum ab8500_version {
* @dev: parent device
* @lock: read/write operations lock
* @irq_lock: genirq bus lock
* @transfer_ongoing: 0 if no transfer ongoing
* @irq: irq line
* @version: chip version id (e.g. ab8500 or ab9540)
* @chip_id: chip revision id
Expand All @@ -242,7 +244,7 @@ struct ab8500 {
struct device *dev;
struct mutex lock;
struct mutex irq_lock;

atomic_t transfer_ongoing;
int irq_base;
int irq;
enum ab8500_version version;
Expand Down Expand Up @@ -288,6 +290,8 @@ extern int __devinit ab8500_init(struct ab8500 *ab8500,
enum ab8500_version version);
extern int __devexit ab8500_exit(struct ab8500 *ab8500);

extern int ab8500_suspend(struct ab8500 *ab8500);

static inline int is_ab8500(struct ab8500 *ab)
{
return ab->version == AB8500_VERSION_AB8500;
Expand Down

0 comments on commit 112a80d

Please sign in to comment.