summaryrefslogtreecommitdiff
path: root/target/linux/brcm63xx/patches-3.13/324-MIPS-BCM63XX-protect-irq-register-accesses.patch
blob: c38137dc8173e82c65d275469121abf3b8aa6737 (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
From 85257b702e1d4c6dcc839c737833c42ca53bae93 Mon Sep 17 00:00:00 2001
From: Jonas Gorski <jogo@openwrt.org>
Date: Sun, 21 Apr 2013 15:38:56 +0200
Subject: [PATCH 35/53] MIPS: BCM63XX: protect irq register accesses

---
 arch/mips/bcm63xx/irq.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

--- 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>
@@ -20,6 +21,9 @@
 #include <bcm63xx_irq.h>
 
 
+static DEFINE_SPINLOCK(ipic_lock);
+static DEFINE_SPINLOCK(epic_lock);
+
 static u32 irq_stat_addr[2];
 static u32 irq_mask_addr[2];
 static void (*dispatch_internal)(int pin);
@@ -62,8 +66,10 @@ void __dispatch_internal_##width(int pin
 	bool irqs_pending = false;					\
 	static int i[2];						\
 	int *next = &i[pin];						\
+	unsigned long flags;						\
 									\
 	/* read registers in reverse order */				\
+	spin_lock_irqsave(&ipic_lock, flags);				\
 	for (src = 0, tgt = (width / 32); src < (width / 32); src++) {	\
 		u32 val;						\
 									\
@@ -74,6 +80,7 @@ void __dispatch_internal_##width(int pin
 		if (val)						\
 			irqs_pending = true;				\
 	}								\
+	spin_unlock_irqrestore(&ipic_lock, flags);			\
 									\
 	if (!irqs_pending)						\
 		return;							\
@@ -94,10 +101,13 @@ static void __internal_irq_mask_##width(
 	u32 val;							\
 	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
 	unsigned bit = irq & 0x1f;					\
+	unsigned long flags;						\
 									\
+	spin_lock_irqsave(&ipic_lock, flags);				\
 	val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32));		\
 	val &= ~(1 << bit);						\
 	bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32));		\
+	spin_unlock_irqrestore(&ipic_lock, flags);			\
 }									\
 									\
 static void __internal_irq_unmask_##width(unsigned int irq)		\
@@ -105,10 +115,13 @@ static void __internal_irq_unmask_##widt
 	u32 val;							\
 	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
 	unsigned bit = irq & 0x1f; 					\
+	unsigned long flags;						\
 									\
+	spin_lock_irqsave(&ipic_lock, flags);				\
 	val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32));		\
 	val |= (1 << bit); 						\
 	bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32));		\
+	spin_unlock_irqrestore(&ipic_lock, flags);			\
 }
 
 BUILD_IPIC_INTERNAL(32);
@@ -167,8 +180,10 @@ 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())
@@ -177,6 +192,8 @@ static void bcm63xx_external_irq_mask(st
 		reg &= ~EXTIRQ_CFG_MASK(irq % 4);
 
 	bcm_perf_writel(reg, regaddr);
+	spin_unlock_irqrestore(&epic_lock, flags);
+
 	if (is_ext_irq_cascaded)
 		internal_irq_mask(irq + ext_irq_start);
 }
@@ -185,8 +202,10 @@ static void bcm63xx_external_irq_unmask(
 {
 	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())
@@ -195,6 +214,7 @@ 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)
 		internal_irq_unmask(irq + ext_irq_start);
@@ -204,8 +224,10 @@ static void bcm63xx_external_irq_clear(s
 {
 	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())
@@ -214,6 +236,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,
@@ -222,6 +245,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;
 
@@ -256,6 +280,7 @@ static int bcm63xx_external_irq_set_type
 	}
 
 	regaddr = get_ext_irq_perf_reg(irq);
+	spin_lock_irqsave(&epic_lock, flags);
 	reg = bcm_perf_readl(regaddr);
 	irq %= 4;
 
@@ -300,6 +325,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))