blob: 78189035e7f109202272419bee7d1169b1e38c0f [file] [log] [blame]
viresh kumar4c18e772010-05-03 09:24:30 +01001/*
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
Lennert Buytenhek0e60e112010-11-29 11:22:33 +010023static void shirq_irq_mask(struct irq_data *d)
viresh kumar4c18e772010-05-03 09:24:30 +010024{
Lennert Buytenhek0e60e112010-11-29 11:22:33 +010025 struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
26 u32 val, id = d->irq - shirq->dev_config[0].virq;
viresh kumar4c18e772010-05-03 09:24:30 +010027 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
Lennert Buytenhek0e60e112010-11-29 11:22:33 +010042static void shirq_irq_unmask(struct irq_data *d)
viresh kumar4c18e772010-05-03 09:24:30 +010043{
Lennert Buytenhek0e60e112010-11-29 11:22:33 +010044 struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
45 u32 val, id = d->irq - shirq->dev_config[0].virq;
viresh kumar4c18e772010-05-03 09:24:30 +010046 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",
Lennert Buytenhek0e60e112010-11-29 11:22:33 +010063 .irq_ack = shirq_irq_mask,
64 .irq_mask = shirq_irq_mask,
65 .irq_unmask = shirq_irq_unmask,
viresh kumar4c18e772010-05-03 09:24:30 +010066};
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
Lennert Buytenhek0e60e112010-11-29 11:22:33 +010073 desc->irq_data.chip->irq_ack(&desc->irq_data);
viresh kumar4c18e772010-05-03 09:24:30 +010074 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 }
Lennert Buytenhek0e60e112010-11-29 11:22:33 +010095 desc->irq_data.chip->irq_unmask(&desc->irq_data);
viresh kumar4c18e772010-05-03 09:24:30 +010096}
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}