Multiple peripherals in SPEAr share common hardware interrupt lines. This patch adds support for a shared irq layer, which registers hardware irqs by itself and exposes virtual irq numbers to peripherals. Signed-off-by: Viresh Kumar <viresh.kumar@st.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>master
parent
ff37f6e591
commit
4c18e77f71
@ -0,0 +1,73 @@ |
||||
/*
|
||||
* arch/arm/plat-spear/include/plat/shirq.h |
||||
* |
||||
* SPEAr platform shared irq layer header 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. |
||||
*/ |
||||
|
||||
#ifndef __PLAT_SHIRQ_H |
||||
#define __PLAT_SHIRQ_H |
||||
|
||||
#include <linux/irq.h> |
||||
#include <linux/types.h> |
||||
|
||||
/*
|
||||
* struct shirq_dev_config: shared irq device configuration |
||||
* |
||||
* virq: virtual irq number of device |
||||
* enb_mask: enable mask of device |
||||
* status_mask: status mask of device |
||||
* clear_mask: clear mask of device |
||||
*/ |
||||
struct shirq_dev_config { |
||||
u32 virq; |
||||
u32 enb_mask; |
||||
u32 status_mask; |
||||
u32 clear_mask; |
||||
}; |
||||
|
||||
/*
|
||||
* struct shirq_regs: shared irq register configuration |
||||
* |
||||
* base: base address of shared irq register |
||||
* enb_reg: enable register offset |
||||
* reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt |
||||
* status_reg: status register offset |
||||
* status_reg_mask: status register valid mask |
||||
* clear_reg: clear register offset |
||||
* reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt |
||||
*/ |
||||
struct shirq_regs { |
||||
void __iomem *base; |
||||
u32 enb_reg; |
||||
u32 reset_to_enb; |
||||
u32 status_reg; |
||||
u32 status_reg_mask; |
||||
u32 clear_reg; |
||||
u32 reset_to_clear; |
||||
}; |
||||
|
||||
/*
|
||||
* struct spear_shirq: shared irq structure |
||||
* |
||||
* irq: hardware irq number |
||||
* dev_config: array of device config structures which are using "irq" line |
||||
* dev_count: size of dev_config array |
||||
* regs: register configuration for shared irq block |
||||
*/ |
||||
struct spear_shirq { |
||||
u32 irq; |
||||
struct shirq_dev_config *dev_config; |
||||
u32 dev_count; |
||||
struct shirq_regs regs; |
||||
}; |
||||
|
||||
int spear_shirq_register(struct spear_shirq *shirq); |
||||
|
||||
#endif /* __PLAT_SHIRQ_H */ |
@ -0,0 +1,118 @@ |
||||
/*
|
||||
* 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); |
||||
|
||||
static void shirq_irq_mask(unsigned irq) |
||||
{ |
||||
struct spear_shirq *shirq = get_irq_chip_data(irq); |
||||
u32 val, id = irq - shirq->dev_config[0].virq; |
||||
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 void shirq_irq_unmask(unsigned irq) |
||||
{ |
||||
struct spear_shirq *shirq = get_irq_chip_data(irq); |
||||
u32 val, id = irq - shirq->dev_config[0].virq; |
||||
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", |
||||
.ack = shirq_irq_mask, |
||||
.mask = shirq_irq_mask, |
||||
.unmask = shirq_irq_unmask, |
||||
}; |
||||
|
||||
static void shirq_handler(unsigned irq, struct irq_desc *desc) |
||||
{ |
||||
u32 i, val, mask; |
||||
struct spear_shirq *shirq = get_irq_data(irq); |
||||
|
||||
desc->chip->ack(irq); |
||||
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); |
||||
} |
||||
} |
||||
desc->chip->unmask(irq); |
||||
} |
||||
|
||||
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; |
||||
|
||||
set_irq_chained_handler(shirq->irq, shirq_handler); |
||||
for (i = 0; i < shirq->dev_count; i++) { |
||||
set_irq_chip(shirq->dev_config[i].virq, &shirq_chip); |
||||
set_irq_handler(shirq->dev_config[i].virq, handle_simple_irq); |
||||
set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID); |
||||
set_irq_chip_data(shirq->dev_config[i].virq, shirq); |
||||
} |
||||
|
||||
set_irq_data(shirq->irq, shirq); |
||||
return 0; |
||||
} |
Loading…
Reference in new issue