summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm63xx/patches-3.8/319-MIPS-BCM63XX-protect-irq-register-accesses-with-a-sp.patch
blob: 76e8c32670d7cc76009c3a2b75d5f1c7de8f13af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
From 7b8e7bc9806b61be2f07bf2bbb5e3ee6e0f333e9 Mon Sep 17 00:00:00 2001
From: Jonas Gorski <jogo@openwrt.org>
Date: Sun, 21 Apr 2013 15:38:56 +0200
Subject: [PATCH 10/13] MIPS: BCM63XX: protect irq register accesses with a
 spinlock

Since IRQs can be handled on both CPUs, we need to ensure that we
don't try to modify the IRQ registers at the same time.

Signed-off-by: Jonas Gorski <jogo@openwrt.org>
---
 arch/mips/bcm63xx/irq.c |   47 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 42 insertions(+), 5 deletions(-)

--- a/arch/mips/bcm63xx/irq.c
+++ b/arch/mips/bcm63xx/irq.c
@@ -12,6 +12,7 @@
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/irq.h>
+#include <linux/spinlock.h>
 #include <asm/irq_cpu.h>
 #include <asm/mipsregs.h>
 #include <bcm63xx_cpu.h>
@@ -26,6 +27,9 @@ static void __internal_irq_mask_64(unsig
 static void __internal_irq_unmask_32(unsigned int irq) __maybe_unused;
 static void __internal_irq_unmask_64(unsigned int irq) __maybe_unused;
 
+static DEFINE_SPINLOCK(ipic_lock);
+static DEFINE_SPINLOCK(epic_lock);
+
 #ifndef BCMCPU_RUNTIME_DETECT
 #ifdef CONFIG_BCM63XX_CPU_6328
 #define irq_stat_reg0		PERF_IRQSTAT_6328_REG(0)
@@ -290,7 +294,9 @@ void __dispatch_internal_##width(int cpu
 	static int i;							\
 	u32 irq_stat_addr = get_irq_stat_addr(cpu);			\
 	u32 irq_mask_addr = get_irq_mask_addr(cpu);			\
+	unsigned long flags;						\
 									\
+	spin_lock_irqsave(&ipic_lock, flags);				\
 	/* read registers in reverse order */				\
 	for (src = 0, tgt = (width / 32); src < (width / 32); src++) {	\
 		u32 val;						\
@@ -302,6 +308,7 @@ void __dispatch_internal_##width(int cpu
 		if (val)						\
 			irqs_pending = true;				\
 	}								\
+	spin_unlock_irqrestore(&ipic_lock, flags);			\
 									\
 	if (!irqs_pending)						\
 		return;							\
@@ -381,12 +388,20 @@ asmlinkage void plat_irq_dispatch(void)
  */
 static void bcm63xx_internal_irq_mask(struct irq_data *d)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipic_lock, flags);
 	internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);
+	spin_unlock_irqrestore(&ipic_lock, flags);
 }
 
 static void bcm63xx_internal_irq_unmask(struct irq_data *d)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipic_lock, flags);
 	internal_irq_unmask(d->irq - IRQ_INTERNAL_BASE);
+	spin_unlock_irqrestore(&ipic_lock, flags);
 }
 
 /*
@@ -397,8 +412,11 @@ static void bcm63xx_external_irq_mask(st
 {
 	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
 	u32 reg, regaddr;
+	unsigned long flags;
 
 	regaddr = get_ext_irq_perf_reg(irq);
+
+	spin_lock_irqsave(&epic_lock, flags);
 	reg = bcm_perf_readl(regaddr);
 
 	if (BCMCPU_IS_6348())
@@ -407,16 +425,24 @@ static void bcm63xx_external_irq_mask(st
 		reg &= ~EXTIRQ_CFG_MASK(irq % 4);
 
 	bcm_perf_writel(reg, regaddr);
-	if (is_ext_irq_cascaded)
-		internal_irq_mask(irq + ext_irq_start);
+	spin_unlock_irqrestore(&epic_lock, flags);
+
+	if (is_ext_irq_cascaded) {
+		struct irq_data *cd = irq_get_irq_data(irq + ext_irq_start);
+
+		bcm63xx_internal_irq_mask(cd);
+	}
 }
 
 static void bcm63xx_external_irq_unmask(struct irq_data *d)
 {
 	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
 	u32 reg, regaddr;
+	unsigned long flags;
 
 	regaddr = get_ext_irq_perf_reg(irq);
+
+	spin_lock_irqsave(&epic_lock, flags);
 	reg = bcm_perf_readl(regaddr);
 
 	if (BCMCPU_IS_6348())
@@ -425,16 +451,22 @@ static void bcm63xx_external_irq_unmask(
 		reg |= EXTIRQ_CFG_MASK(irq % 4);
 
 	bcm_perf_writel(reg, regaddr);
+	spin_unlock_irqrestore(&epic_lock, flags);
+
+	if (is_ext_irq_cascaded) {
+		struct irq_data *cd = irq_get_irq_data(irq + ext_irq_start);
 
-	if (is_ext_irq_cascaded)
-		internal_irq_unmask(irq + ext_irq_start);
+		bcm63xx_internal_irq_unmask(cd);
+	}
 }
 
 static void bcm63xx_external_irq_clear(struct irq_data *d)
 {
 	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
 	u32 reg, regaddr;
+	unsigned long flags;
 
+	spin_lock_irqsave(&epic_lock, flags);
 	regaddr = get_ext_irq_perf_reg(irq);
 	reg = bcm_perf_readl(regaddr);
 
@@ -444,6 +476,7 @@ static void bcm63xx_external_irq_clear(s
 		reg |= EXTIRQ_CFG_CLEAR(irq % 4);
 
 	bcm_perf_writel(reg, regaddr);
+	spin_unlock_irqrestore(&epic_lock, flags);
 }
 
 static int bcm63xx_external_irq_set_type(struct irq_data *d,
@@ -452,6 +485,7 @@ static int bcm63xx_external_irq_set_type
 	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
 	u32 reg, regaddr;
 	int levelsense, sense, bothedge;
+	unsigned long flags;
 
 	flow_type &= IRQ_TYPE_SENSE_MASK;
 
@@ -486,9 +520,11 @@ static int bcm63xx_external_irq_set_type
 	}
 
 	regaddr = get_ext_irq_perf_reg(irq);
-	reg = bcm_perf_readl(regaddr);
 	irq %= 4;
 
+	spin_lock_irqsave(&epic_lock, flags);
+	reg = bcm_perf_readl(regaddr);
+
 	switch (bcm63xx_get_cpu_id()) {
 	case BCM6348_CPU_ID:
 		if (levelsense)
@@ -529,6 +565,7 @@ static int bcm63xx_external_irq_set_type
 	}
 
 	bcm_perf_writel(reg, regaddr);
+	spin_unlock_irqrestore(&epic_lock, flags);
 
 	irqd_set_trigger_type(d, flow_type);
 	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))