diff options
author | viresh kumar <viresh.kumar@st.com> | 2010-05-03 04:24:30 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-05-04 11:54:54 -0400 |
commit | 4c18e77f718cdda33363132127db4df795eadbd9 (patch) | |
tree | bbecdc08f487246049bb6d0c212d338a9056597a /arch/arm/plat-spear | |
parent | ff37f6e5910c4fd7ad42063d89c0687def665941 (diff) |
ARM: 6091/1: ST SPEAr: Adding support for shared irq layer
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>
Diffstat (limited to 'arch/arm/plat-spear')
-rw-r--r-- | arch/arm/plat-spear/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/plat-spear/include/plat/shirq.h | 73 | ||||
-rw-r--r-- | arch/arm/plat-spear/shirq.c | 118 |
3 files changed, 193 insertions, 0 deletions
diff --git a/arch/arm/plat-spear/Makefile b/arch/arm/plat-spear/Makefile index 6f4ad5e9462e..eb89540aeda9 100644 --- a/arch/arm/plat-spear/Makefile +++ b/arch/arm/plat-spear/Makefile | |||
@@ -4,3 +4,5 @@ | |||
4 | 4 | ||
5 | # Common support | 5 | # Common support |
6 | obj-y := clock.o padmux.o time.o | 6 | obj-y := clock.o padmux.o time.o |
7 | |||
8 | obj-$(CONFIG_ARCH_SPEAR3XX) += shirq.o | ||
diff --git a/arch/arm/plat-spear/include/plat/shirq.h b/arch/arm/plat-spear/include/plat/shirq.h new file mode 100644 index 000000000000..03ed8b585dcf --- /dev/null +++ b/arch/arm/plat-spear/include/plat/shirq.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * arch/arm/plat-spear/include/plat/shirq.h | ||
3 | * | ||
4 | * SPEAr platform shared irq layer header 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 | #ifndef __PLAT_SHIRQ_H | ||
15 | #define __PLAT_SHIRQ_H | ||
16 | |||
17 | #include <linux/irq.h> | ||
18 | #include <linux/types.h> | ||
19 | |||
20 | /* | ||
21 | * struct shirq_dev_config: shared irq device configuration | ||
22 | * | ||
23 | * virq: virtual irq number of device | ||
24 | * enb_mask: enable mask of device | ||
25 | * status_mask: status mask of device | ||
26 | * clear_mask: clear mask of device | ||
27 | */ | ||
28 | struct shirq_dev_config { | ||
29 | u32 virq; | ||
30 | u32 enb_mask; | ||
31 | u32 status_mask; | ||
32 | u32 clear_mask; | ||
33 | }; | ||
34 | |||
35 | /* | ||
36 | * struct shirq_regs: shared irq register configuration | ||
37 | * | ||
38 | * base: base address of shared irq register | ||
39 | * enb_reg: enable register offset | ||
40 | * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt | ||
41 | * status_reg: status register offset | ||
42 | * status_reg_mask: status register valid mask | ||
43 | * clear_reg: clear register offset | ||
44 | * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt | ||
45 | */ | ||
46 | struct shirq_regs { | ||
47 | void __iomem *base; | ||
48 | u32 enb_reg; | ||
49 | u32 reset_to_enb; | ||
50 | u32 status_reg; | ||
51 | u32 status_reg_mask; | ||
52 | u32 clear_reg; | ||
53 | u32 reset_to_clear; | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * struct spear_shirq: shared irq structure | ||
58 | * | ||
59 | * irq: hardware irq number | ||
60 | * dev_config: array of device config structures which are using "irq" line | ||
61 | * dev_count: size of dev_config array | ||
62 | * regs: register configuration for shared irq block | ||
63 | */ | ||
64 | struct spear_shirq { | ||
65 | u32 irq; | ||
66 | struct shirq_dev_config *dev_config; | ||
67 | u32 dev_count; | ||
68 | struct shirq_regs regs; | ||
69 | }; | ||
70 | |||
71 | int spear_shirq_register(struct spear_shirq *shirq); | ||
72 | |||
73 | #endif /* __PLAT_SHIRQ_H */ | ||
diff --git a/arch/arm/plat-spear/shirq.c b/arch/arm/plat-spear/shirq.c new file mode 100644 index 000000000000..2172d6946aea --- /dev/null +++ b/arch/arm/plat-spear/shirq.c | |||
@@ -0,0 +1,118 @@ | |||
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 | |||
20 | struct spear_shirq *shirq; | ||
21 | static DEFINE_SPINLOCK(lock); | ||
22 | |||
23 | static 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 | |||
42 | static 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 | |||
61 | static 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 | |||
68 | static 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 | |||
98 | int 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 | } | ||