1/* 2 * arch/arm/plat-spear/shirq.c 3 * 4 * SPEAr platform shared irq layer source file 5 * 6 * Copyright (C) 2009 ST Microelectronics 7 * Viresh Kumar<viresh.kumar@st.com> 8 * 9 * This file is licensed under the terms of the GNU General Public 10 * License version 2. This program is licensed "as is" without any 11 * warranty of any kind, whether express or implied. 12 */ 13 14#include <linux/err.h> 15#include <linux/io.h> 16#include <linux/irq.h> 17#include <linux/spinlock.h> 18#include <plat/shirq.h> 19 20struct spear_shirq *shirq; 21static DEFINE_SPINLOCK(lock); 22 23static void shirq_irq_mask(unsigned irq) 24{ 25 struct spear_shirq *shirq = get_irq_chip_data(irq); 26 u32 val, id = irq - shirq->dev_config[0].virq; 27 unsigned long flags; 28 29 if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) 30 return; 31 32 spin_lock_irqsave(&lock, flags); 33 val = readl(shirq->regs.base + shirq->regs.enb_reg); 34 if (shirq->regs.reset_to_enb) 35 val |= shirq->dev_config[id].enb_mask; 36 else 37 val &= ~(shirq->dev_config[id].enb_mask); 38 writel(val, shirq->regs.base + shirq->regs.enb_reg); 39 spin_unlock_irqrestore(&lock, flags); 40} 41 42static void shirq_irq_unmask(unsigned irq) 43{ 44 struct spear_shirq *shirq = get_irq_chip_data(irq); 45 u32 val, id = irq - shirq->dev_config[0].virq; 46 unsigned long flags; 47 48 if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) 49 return; 50 51 spin_lock_irqsave(&lock, flags); 52 val = readl(shirq->regs.base + shirq->regs.enb_reg); 53 if (shirq->regs.reset_to_enb) 54 val &= ~(shirq->dev_config[id].enb_mask); 55 else 56 val |= shirq->dev_config[id].enb_mask; 57 writel(val, shirq->regs.base + shirq->regs.enb_reg); 58 spin_unlock_irqrestore(&lock, flags); 59} 60 61static struct irq_chip shirq_chip = { 62 .name = "spear_shirq", 63 .ack = shirq_irq_mask, 64 .mask = shirq_irq_mask, 65 .unmask = shirq_irq_unmask, 66}; 67 68static void shirq_handler(unsigned irq, struct irq_desc *desc) 69{ 70 u32 i, val, mask; 71 struct spear_shirq *shirq = get_irq_data(irq); 72 73 desc->chip->ack(irq); 74 while ((val = readl(shirq->regs.base + shirq->regs.status_reg) & 75 shirq->regs.status_reg_mask)) { 76 for (i = 0; (i < shirq->dev_count) && val; i++) { 77 if (!(shirq->dev_config[i].status_mask & val)) 78 continue; 79 80 generic_handle_irq(shirq->dev_config[i].virq); 81 82 /* clear interrupt */ 83 val &= ~shirq->dev_config[i].status_mask; 84 if ((shirq->regs.clear_reg == -1) || 85 shirq->dev_config[i].clear_mask == -1) 86 continue; 87 mask = readl(shirq->regs.base + shirq->regs.clear_reg); 88 if (shirq->regs.reset_to_clear) 89 mask &= ~shirq->dev_config[i].clear_mask; 90 else 91 mask |= shirq->dev_config[i].clear_mask; 92 writel(mask, shirq->regs.base + shirq->regs.clear_reg); 93 } 94 } 95 desc->chip->unmask(irq); 96} 97 98int spear_shirq_register(struct spear_shirq *shirq) 99{ 100 int i; 101 102 if (!shirq || !shirq->dev_config || !shirq->regs.base) 103 return -EFAULT; 104 105 if (!shirq->dev_count) 106 return -EINVAL; 107 108 set_irq_chained_handler(shirq->irq, shirq_handler); 109 for (i = 0; i < shirq->dev_count; i++) { 110 set_irq_chip(shirq->dev_config[i].virq, &shirq_chip); 111 set_irq_handler(shirq->dev_config[i].virq, handle_simple_irq); 112 set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID); 113 set_irq_chip_data(shirq->dev_config[i].virq, shirq); 114 } 115 116 set_irq_data(shirq->irq, shirq); 117 return 0; 118} 119