Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 330005
b: refs/heads/master
c: eea6b7c
h: refs/heads/master
i:
  330003: 71a7211
v: v3
  • Loading branch information
Milo Kim authored and Samuel Ortiz committed Sep 23, 2012
1 parent b9d1770 commit 1d6ca1d
Show file tree
Hide file tree
Showing 7 changed files with 872 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 804971ec3793d30f40c1a74775dd3fe89deb461a
refs/heads/master: eea6b7cc53aaecf868e1643058159807c744e04e
10 changes: 10 additions & 0 deletions trunk/drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,16 @@ config PMIC_ADP5520
individual components like LCD backlight, LEDs, GPIOs and Kepad
under the corresponding menus.

config MFD_LP8788
bool "Texas Instruments LP8788 Power Management Unit Driver"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select IRQ_DOMAIN
help
TI LP8788 PMU supports regulators, battery charger, RTC,
ADC, backlight driver and current sinks.

config MFD_MAX77686
bool "Maxim Semiconductor MAX77686 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/mfd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o

obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o

obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
obj-$(CONFIG_MFD_MAX8907) += max8907.o
Expand Down
198 changes: 198 additions & 0 deletions trunk/drivers/mfd/lp8788-irq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* TI LP8788 MFD - interrupt handler
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.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/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/device.h>
#include <linux/mfd/lp8788.h>
#include <linux/module.h>
#include <linux/slab.h>

/* register address */
#define LP8788_INT_1 0x00
#define LP8788_INTEN_1 0x03

#define BASE_INTEN_ADDR LP8788_INTEN_1
#define SIZE_REG 8
#define NUM_REGS 3

/*
* struct lp8788_irq_data
* @lp : used for accessing to lp8788 registers
* @irq_lock : mutex for enabling/disabling the interrupt
* @domain : IRQ domain for handling nested interrupt
* @enabled : status of enabled interrupt
*/
struct lp8788_irq_data {
struct lp8788 *lp;
struct mutex irq_lock;
struct irq_domain *domain;
int enabled[LP8788_INT_MAX];
};

static inline u8 _irq_to_addr(enum lp8788_int_id id)
{
return id / SIZE_REG;
}

static inline u8 _irq_to_enable_addr(enum lp8788_int_id id)
{
return _irq_to_addr(id) + BASE_INTEN_ADDR;
}

static inline u8 _irq_to_mask(enum lp8788_int_id id)
{
return 1 << (id % SIZE_REG);
}

static inline u8 _irq_to_val(enum lp8788_int_id id, int enable)
{
return enable << (id % SIZE_REG);
}

static void lp8788_irq_enable(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
irqd->enabled[data->hwirq] = 1;
}

static void lp8788_irq_disable(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
irqd->enabled[data->hwirq] = 0;
}

static void lp8788_irq_bus_lock(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);

mutex_lock(&irqd->irq_lock);
}

static void lp8788_irq_bus_sync_unlock(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
enum lp8788_int_id irq = data->hwirq;
u8 addr, mask, val;

addr = _irq_to_enable_addr(irq);
mask = _irq_to_mask(irq);
val = _irq_to_val(irq, irqd->enabled[irq]);

lp8788_update_bits(irqd->lp, addr, mask, val);

mutex_unlock(&irqd->irq_lock);
}

static struct irq_chip lp8788_irq_chip = {
.name = "lp8788",
.irq_enable = lp8788_irq_enable,
.irq_disable = lp8788_irq_disable,
.irq_bus_lock = lp8788_irq_bus_lock,
.irq_bus_sync_unlock = lp8788_irq_bus_sync_unlock,
};

static irqreturn_t lp8788_irq_handler(int irq, void *ptr)
{
struct lp8788_irq_data *irqd = ptr;
struct lp8788 *lp = irqd->lp;
u8 status[NUM_REGS], addr, mask;
bool handled;
int i;

if (lp8788_read_multi_bytes(lp, LP8788_INT_1, status, NUM_REGS))
return IRQ_NONE;

for (i = 0 ; i < LP8788_INT_MAX ; i++) {
addr = _irq_to_addr(i);
mask = _irq_to_mask(i);

/* reporting only if the irq is enabled */
if (status[addr] & mask) {
handle_nested_irq(irq_find_mapping(irqd->domain, i));
handled = true;
}
}

return handled ? IRQ_HANDLED : IRQ_NONE;
}

static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
struct lp8788_irq_data *irqd = d->host_data;
struct irq_chip *chip = &lp8788_irq_chip;

irq_set_chip_data(virq, irqd);
irq_set_chip_and_handler(virq, chip, handle_edge_irq);
irq_set_nested_thread(virq, 1);

#ifdef CONFIG_ARM
set_irq_flags(virq, IRQF_VALID);
#else
irq_set_noprobe(virq);
#endif

return 0;
}

static struct irq_domain_ops lp8788_domain_ops = {
.map = lp8788_irq_map,
};

int lp8788_irq_init(struct lp8788 *lp, int irq)
{
struct lp8788_irq_data *irqd;
int ret;

if (irq <= 0) {
dev_warn(lp->dev, "invalid irq number: %d\n", irq);
return 0;
}

irqd = devm_kzalloc(lp->dev, sizeof(*irqd), GFP_KERNEL);
if (!irqd)
return -ENOMEM;

irqd->lp = lp;
irqd->domain = irq_domain_add_linear(lp->dev->of_node, LP8788_INT_MAX,
&lp8788_domain_ops, irqd);
if (!irqd->domain) {
dev_err(lp->dev, "failed to add irq domain err\n");
return -EINVAL;
}

lp->irqdm = irqd->domain;
mutex_init(&irqd->irq_lock);

ret = request_threaded_irq(irq, NULL, lp8788_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"lp8788-irq", irqd);
if (ret) {
dev_err(lp->dev, "failed to create a thread for IRQ_N\n");
return ret;
}

lp->irq = irq;

return 0;
}

void lp8788_irq_exit(struct lp8788 *lp)
{
if (lp->irq)
free_irq(lp->irq, lp->irqdm);
}
Loading

0 comments on commit 1d6ca1d

Please sign in to comment.