summaryrefslogtreecommitdiff
path: root/target/linux/brcm63xx/patches-3.14/320-MIPS-BCM63XX-replace-irq-dispatch-code-with-a-generi.patch
blob: f0311ce90c2cffd9e05102a1eb7e6e5659fca76d (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
From 01bf26c51b427e24ac69f604d33f7d9360a9e470 Mon Sep 17 00:00:00 2001
From: Jonas Gorski <jogo@openwrt.org>
Date: Thu, 18 Apr 2013 21:14:49 +0200
Subject: [PATCH 31/53] MIPS: BCM63XX: replace irq dispatch code with a generic
 version

The generic version uses a variable length of u32 registers of u32/u64.
This allows easier support for longer registers without having to rewrite
verything.

This "generic" version is not slower than the old version in the best case
(= i == next set bit), and twice as fast in the worst case in 64 bits.

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

--- a/arch/mips/bcm63xx/irq.c
+++ b/arch/mips/bcm63xx/irq.c
@@ -51,47 +51,65 @@ static inline void handle_internal(int i
  * will resume the loop where it ended the last time we left this
  * function.
  */
-static void __dispatch_internal_32(void)
-{
-	u32 pending;
-	static int i;
-
-	pending = bcm_readl(irq_stat_addr) & bcm_readl(irq_mask_addr);
-
-	if (!pending)
-		return ;
-
-	while (1) {
-		int to_call = i;
 
-		i = (i + 1) & 0x1f;
-		if (pending & (1 << to_call)) {
-			handle_internal(to_call);
-			break;
-		}
-	}
+#define BUILD_IPIC_INTERNAL(width)					\
+void __dispatch_internal_##width(void)					\
+{									\
+	u32 pending[width / 32];					\
+	unsigned int src, tgt;						\
+	bool irqs_pending = false;					\
+	static int i;							\
+									\
+	/* read registers in reverse order */				\
+	for (src = 0, tgt = (width / 32); src < (width / 32); src++) {	\
+		u32 val;						\
+									\
+		val = bcm_readl(irq_stat_addr + src * sizeof(u32));	\
+		val &= bcm_readl(irq_mask_addr + src * sizeof(u32));	\
+		pending[--tgt] = val;					\
+									\
+		if (val)						\
+			irqs_pending = true;				\
+	}								\
+									\
+	if (!irqs_pending)						\
+		return;							\
+									\
+	while (1) {							\
+		int to_call = i;					\
+									\
+		i = (i + 1) & (width - 1);				\
+		if (pending[to_call / 32] & (1 << (to_call & 0x1f))) {	\
+			handle_internal(to_call);			\
+			break;						\
+		}							\
+	}								\
+}									\
+									\
+static void __internal_irq_mask_##width(unsigned int irq)		\
+{									\
+	u32 val;							\
+	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
+	unsigned bit = irq & 0x1f;					\
+									\
+	val = bcm_readl(irq_mask_addr + reg * sizeof(u32));		\
+	val &= ~(1 << bit);						\
+	bcm_writel(val, irq_mask_addr + reg * sizeof(u32));		\
+}									\
+									\
+static void __internal_irq_unmask_##width(unsigned int irq)		\
+{ 									\
+	u32 val;							\
+	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
+	unsigned bit = irq & 0x1f; 					\
+									\
+	val = bcm_readl(irq_mask_addr + reg * sizeof(u32));		\
+	val |= (1 << bit); 						\
+	bcm_writel(val, irq_mask_addr + reg * sizeof(u32));		\
 }
 
-static void __dispatch_internal_64(void)
-{
-	u64 pending;
-	static int i;
-
-	pending = bcm_readq(irq_stat_addr) & bcm_readq(irq_mask_addr);
-
-	if (!pending)
-		return ;
-
-	while (1) {
-		int to_call = i;
-
-		i = (i + 1) & 0x3f;
-		if (pending & (1ull << to_call)) {
-			handle_internal(to_call);
-			break;
-		}
-	}
-}
+BUILD_IPIC_INTERNAL(32);
+BUILD_IPIC_INTERNAL(64);
 
 asmlinkage void plat_irq_dispatch(void)
 {
@@ -128,42 +146,6 @@ asmlinkage void plat_irq_dispatch(void)
  * internal IRQs operations: only mask/unmask on PERF irq mask
  * register.
  */
-static void __internal_irq_mask_32(unsigned int irq)
-{
-	u32 mask;
-
-	mask = bcm_readl(irq_mask_addr);
-	mask &= ~(1 << irq);
-	bcm_writel(mask, irq_mask_addr);
-}
-
-static void __internal_irq_mask_64(unsigned int irq)
-{
-	u64 mask;
-
-	mask = bcm_readq(irq_mask_addr);
-	mask &= ~(1ull << irq);
-	bcm_writeq(mask, irq_mask_addr);
-}
-
-static void __internal_irq_unmask_32(unsigned int irq)
-{
-	u32 mask;
-
-	mask = bcm_readl(irq_mask_addr);
-	mask |= (1 << irq);
-	bcm_writel(mask, irq_mask_addr);
-}
-
-static void __internal_irq_unmask_64(unsigned int irq)
-{
-	u64 mask;
-
-	mask = bcm_readq(irq_mask_addr);
-	mask |= (1ull << irq);
-	bcm_writeq(mask, irq_mask_addr);
-}
-
 static void bcm63xx_internal_irq_mask(struct irq_data *d)
 {
 	internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);