2010-05-03 10:24:30 +02:00
|
|
|
/*
|
|
|
|
* arch/arm/plat-spear/shirq.c
|
|
|
|
*
|
|
|
|
* SPEAr platform shared irq layer source file
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009 ST Microelectronics
|
|
|
|
* Viresh Kumar<viresh.kumar@st.com>
|
|
|
|
*
|
|
|
|
* This file is licensed under the terms of the GNU General Public
|
|
|
|
* License version 2. This program is licensed "as is" without any
|
|
|
|
* warranty of any kind, whether express or implied.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <plat/shirq.h>
|
|
|
|
|
|
|
|
struct spear_shirq *shirq;
|
|
|
|
static DEFINE_SPINLOCK(lock);
|
|
|
|
|
2010-11-29 11:22:33 +01:00
|
|
|
static void shirq_irq_mask(struct irq_data *d)
|
2010-05-03 10:24:30 +02:00
|
|
|
{
|
2010-11-29 11:22:33 +01:00
|
|
|
struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
|
|
|
|
u32 val, id = d->irq - shirq->dev_config[0].virq;
|
2010-05-03 10:24:30 +02:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&lock, flags);
|
|
|
|
val = readl(shirq->regs.base + shirq->regs.enb_reg);
|
|
|
|
if (shirq->regs.reset_to_enb)
|
|
|
|
val |= shirq->dev_config[id].enb_mask;
|
|
|
|
else
|
|
|
|
val &= ~(shirq->dev_config[id].enb_mask);
|
|
|
|
writel(val, shirq->regs.base + shirq->regs.enb_reg);
|
|
|
|
spin_unlock_irqrestore(&lock, flags);
|
|
|
|
}
|
|
|
|
|
2010-11-29 11:22:33 +01:00
|
|
|
static void shirq_irq_unmask(struct irq_data *d)
|
2010-05-03 10:24:30 +02:00
|
|
|
{
|
2010-11-29 11:22:33 +01:00
|
|
|
struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
|
|
|
|
u32 val, id = d->irq - shirq->dev_config[0].virq;
|
2010-05-03 10:24:30 +02:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&lock, flags);
|
|
|
|
val = readl(shirq->regs.base + shirq->regs.enb_reg);
|
|
|
|
if (shirq->regs.reset_to_enb)
|
|
|
|
val &= ~(shirq->dev_config[id].enb_mask);
|
|
|
|
else
|
|
|
|
val |= shirq->dev_config[id].enb_mask;
|
|
|
|
writel(val, shirq->regs.base + shirq->regs.enb_reg);
|
|
|
|
spin_unlock_irqrestore(&lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct irq_chip shirq_chip = {
|
|
|
|
.name = "spear_shirq",
|
2010-11-29 11:22:33 +01:00
|
|
|
.irq_ack = shirq_irq_mask,
|
|
|
|
.irq_mask = shirq_irq_mask,
|
|
|
|
.irq_unmask = shirq_irq_unmask,
|
2010-05-03 10:24:30 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static void shirq_handler(unsigned irq, struct irq_desc *desc)
|
|
|
|
{
|
|
|
|
u32 i, val, mask;
|
2011-03-24 13:25:22 +01:00
|
|
|
struct spear_shirq *shirq = irq_get_handler_data(irq);
|
2010-05-03 10:24:30 +02:00
|
|
|
|
2010-11-29 11:22:33 +01:00
|
|
|
desc->irq_data.chip->irq_ack(&desc->irq_data);
|
2010-05-03 10:24:30 +02:00
|
|
|
while ((val = readl(shirq->regs.base + shirq->regs.status_reg) &
|
|
|
|
shirq->regs.status_reg_mask)) {
|
|
|
|
for (i = 0; (i < shirq->dev_count) && val; i++) {
|
|
|
|
if (!(shirq->dev_config[i].status_mask & val))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
generic_handle_irq(shirq->dev_config[i].virq);
|
|
|
|
|
|
|
|
/* clear interrupt */
|
|
|
|
val &= ~shirq->dev_config[i].status_mask;
|
|
|
|
if ((shirq->regs.clear_reg == -1) ||
|
|
|
|
shirq->dev_config[i].clear_mask == -1)
|
|
|
|
continue;
|
|
|
|
mask = readl(shirq->regs.base + shirq->regs.clear_reg);
|
|
|
|
if (shirq->regs.reset_to_clear)
|
|
|
|
mask &= ~shirq->dev_config[i].clear_mask;
|
|
|
|
else
|
|
|
|
mask |= shirq->dev_config[i].clear_mask;
|
|
|
|
writel(mask, shirq->regs.base + shirq->regs.clear_reg);
|
|
|
|
}
|
|
|
|
}
|
2010-11-29 11:22:33 +01:00
|
|
|
desc->irq_data.chip->irq_unmask(&desc->irq_data);
|
2010-05-03 10:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int spear_shirq_register(struct spear_shirq *shirq)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!shirq || !shirq->dev_config || !shirq->regs.base)
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (!shirq->dev_count)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-03-24 13:25:22 +01:00
|
|
|
irq_set_chained_handler(shirq->irq, shirq_handler);
|
2010-05-03 10:24:30 +02:00
|
|
|
for (i = 0; i < shirq->dev_count; i++) {
|
2011-03-24 13:35:09 +01:00
|
|
|
irq_set_chip_and_handler(shirq->dev_config[i].virq,
|
|
|
|
&shirq_chip, handle_simple_irq);
|
2010-05-03 10:24:30 +02:00
|
|
|
set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID);
|
2011-03-24 13:25:22 +01:00
|
|
|
irq_set_chip_data(shirq->dev_config[i].virq, shirq);
|
2010-05-03 10:24:30 +02:00
|
|
|
}
|
|
|
|
|
2011-03-24 13:25:22 +01:00
|
|
|
irq_set_handler_data(shirq->irq, shirq);
|
2010-05-03 10:24:30 +02:00
|
|
|
return 0;
|
|
|
|
}
|