Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 186877
b: refs/heads/master
c: 760e451
h: refs/heads/master
i:
  186875: afe904a
v: v3
  • Loading branch information
Mark Brown authored and Samuel Ortiz committed Mar 7, 2010
1 parent c097c0a commit f6dbc06
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 82 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 29c71b138c83c8191f1f7e46fcc28b9d6bc8a5dd
refs/heads/master: 760e4518788df6762700e6bb9dd8692379f11168
155 changes: 87 additions & 68 deletions trunk/drivers/mfd/wm8350-irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/irq.h>

#include <linux/mfd/wm8350/core.h>
#include <linux/mfd/wm8350/audio.h>
Expand All @@ -29,8 +29,6 @@
#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
Expand Down Expand Up @@ -366,27 +364,20 @@ static struct wm8350_irq_data wm8350_irqs[] = {
},
};

static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
static inline struct wm8350_irq_data *irq_to_wm8350_irq(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);
return &wm8350_irqs[irq - wm8350->irq_base];
}

/*
* 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.
* rare occurrence so we save I2C/SPI reads. We're also assuming that
* it's rare to get lots of interrupts firing simultaneously so try to
* minimise I/O.
*/
static irqreturn_t wm8350_irq(int irq, void *irq_data)
{
Expand All @@ -397,7 +388,6 @@ static irqreturn_t wm8350_irq(int irq, void *irq_data)
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);

Expand All @@ -416,93 +406,101 @@ static irqreturn_t wm8350_irq(int irq, void *irq_data)
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);
sub_reg[data->reg] &= ~wm8350->irq_masks[data->reg];
read_done[data->reg] = 1;
}

if (sub_reg[data->reg] & data->mask)
wm8350_irq_call_handler(wm8350, i);
handle_nested_irq(wm8350->irq_base + 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)
static void wm8350_irq_lock(unsigned int irq)
{
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);
struct wm8350 *wm8350 = get_irq_chip_data(irq);

return 0;
mutex_lock(&wm8350->irq_lock);
}
EXPORT_SYMBOL_GPL(wm8350_register_irq);

int wm8350_free_irq(struct wm8350 *wm8350, int irq, void *data)
static void wm8350_irq_sync_unlock(unsigned int irq)
{
if (irq < 0 || irq >= WM8350_NUM_IRQ)
return -EINVAL;
struct wm8350 *wm8350 = get_irq_chip_data(irq);
int i;

wm8350_mask_irq(wm8350, irq);
for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
if (wm8350->irq_masks[i] !=
wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i])
WARN_ON(wm8350_reg_write(wm8350,
WM8350_INT_STATUS_1_MASK + i,
wm8350->irq_masks[i]));
}

mutex_lock(&wm8350->irq_mutex);
wm8350->irq[irq].handler = NULL;
mutex_unlock(&wm8350->irq_mutex);
return 0;
mutex_unlock(&wm8350->irq_lock);
}
EXPORT_SYMBOL_GPL(wm8350_free_irq);

int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
static void wm8350_irq_enable(unsigned int irq)
{
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK +
wm8350_irqs[irq].reg,
wm8350_irqs[irq].mask);
struct wm8350 *wm8350 = get_irq_chip_data(irq);
struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq);

wm8350->irq_masks[irq_data->reg] &= ~irq_data->mask;
}
EXPORT_SYMBOL_GPL(wm8350_mask_irq);

int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
static void wm8350_irq_disable(unsigned int irq)
{
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK +
wm8350_irqs[irq].reg,
wm8350_irqs[irq].mask);
struct wm8350 *wm8350 = get_irq_chip_data(irq);
struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq);

wm8350->irq_masks[irq_data->reg] |= irq_data->mask;
}
EXPORT_SYMBOL_GPL(wm8350_unmask_irq);

static struct irq_chip wm8350_irq_chip = {
.name = "wm8350",
.bus_lock = wm8350_irq_lock,
.bus_sync_unlock = wm8350_irq_sync_unlock,
.disable = wm8350_irq_disable,
.enable = wm8350_irq_enable,
};

int wm8350_irq_init(struct wm8350 *wm8350, int irq,
struct wm8350_platform_data *pdata)
{
int ret;
int ret, cur_irq, i;
int flags = IRQF_ONESHOT;

if (!irq) {
dev_err(wm8350->dev, "No IRQ configured\n");
return -EINVAL;
dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n");
return 0;
}

if (!pdata || !pdata->irq_base) {
dev_warn(wm8350->dev, "No interrupt support, no IRQ base\n");
return 0;
}

/* Mask top level interrupts */
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);
/* Mask all individual interrupts by default and cache the
* masks. We read the masks back since there are unwritable
* bits in the mask registers. */
for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK + i,
0xFFFF);
wm8350->irq_masks[i] =
wm8350_reg_read(wm8350,
WM8350_INT_STATUS_1_MASK + i);
}

mutex_init(&wm8350->irq_lock);
wm8350->chip_irq = irq;
wm8350->irq_base = pdata->irq_base;

if (pdata && pdata->irq_high) {
if (pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;

wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
Expand All @@ -514,11 +512,32 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
WM8350_IRQ_POL);
}

/* Register with genirq */
for (cur_irq = wm8350->irq_base;
cur_irq < ARRAY_SIZE(wm8350_irqs) + wm8350->irq_base;
cur_irq++) {
set_irq_chip_data(cur_irq, wm8350);
set_irq_chip_and_handler(cur_irq, &wm8350_irq_chip,
handle_edge_irq);
set_irq_nested_thread(cur_irq, 1);

/* 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, wm8350_irq, flags,
"wm8350", wm8350);
if (ret != 0)
dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret);

/* Allow interrupts to fire */
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0);

return ret;
}

Expand Down
44 changes: 31 additions & 13 deletions trunk/include/linux/mfd/wm8350/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,8 @@

#define WM8350_NUM_IRQ 63

#define WM8350_NUM_IRQ_REGS 7

struct wm8350_reg_access {
u16 readable; /* Mask of readable bits */
u16 writable; /* Mask of writable bits */
Expand All @@ -600,11 +602,6 @@ extern const u16 wm8352_mode3_defaults[];

struct wm8350;

struct wm8350_irq {
irq_handler_t handler;
void *data;
};

struct wm8350_hwmon {
struct platform_device *pdev;
struct device *classdev;
Expand All @@ -626,9 +623,10 @@ struct wm8350 {
struct mutex auxadc_mutex;

/* Interrupt handling */
struct mutex irq_mutex; /* IRQ table mutex */
struct wm8350_irq irq[WM8350_NUM_IRQ];
struct mutex irq_lock;
int chip_irq;
int irq_base;
u16 irq_masks[WM8350_NUM_IRQ_REGS];

/* Client devices */
struct wm8350_codec codec;
Expand Down Expand Up @@ -677,13 +675,33 @@ 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,
irq_handler_t handler, unsigned long flags,
const char *name, void *data);
int wm8350_free_irq(struct wm8350 *wm8350, int irq, void *data);
static inline int wm8350_register_irq(struct wm8350 *wm8350, int irq,
irq_handler_t handler,
unsigned long flags,
const char *name, void *data)
{
if (!wm8350->irq_base)
return -ENODEV;

return request_threaded_irq(irq + wm8350->irq_base, NULL,
handler, flags, name, data);
}

static inline void wm8350_free_irq(struct wm8350 *wm8350, int irq, void *data)
{
free_irq(irq + wm8350->irq_base, data);
}

static inline void wm8350_mask_irq(struct wm8350 *wm8350, int irq)
{
disable_irq(irq + wm8350->irq_base);
}

static inline void wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
{
enable_irq(irq + wm8350->irq_base);
}

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);
Expand Down

0 comments on commit f6dbc06

Please sign in to comment.