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>
		
			
				
	
	
		
			118 lines
		
	
	
	
		
			3.2 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			118 lines
		
	
	
	
		
			3.2 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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;
 | 
						|
}
 |