diff options
| author | Jonas Gorski <jogo@openwrt.org> | 2014-07-12 06:49:39 -0400 |
|---|---|---|
| committer | Ralf Baechle <ralf@linux-mips.org> | 2014-07-30 09:29:23 -0400 |
| commit | 74b8ca3f3160b062207be6ee88785409c0a777ea (patch) | |
| tree | 0908fb60dbbd573ab17c67d8caa3dc4325a3e786 /arch/mips/bcm63xx | |
| parent | 7a9fd14d4c4796d3b0d4aec9c91183560d201b4d (diff) | |
MIPS: BCM63xx: Protect irq register accesses
Since we will have the chance of accessing the registers concurrently,
protect any accesses through a spinlock.
Signed-off-by: Jonas Gorski <jogo@openwrt.org>
Cc: linux-mips@linux-mips.org
Cc: John Crispin <blogic@openwrt.org>
Cc: Maxime Bizon <mbizon@freebox.fr>
Cc: Florian Fainelli <florian@openwrt.org>
Cc: Kevin Cernekee <cernekee@gmail.com>
Cc: Gregory Fong <gregory.0xf0@gmail.com>
Patchwork: https://patchwork.linux-mips.org/patch/7321/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/bcm63xx')
| -rw-r--r-- | arch/mips/bcm63xx/irq.c | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/arch/mips/bcm63xx/irq.c b/arch/mips/bcm63xx/irq.c index 53be291c3d94..2f1939122bc3 100644 --- a/arch/mips/bcm63xx/irq.c +++ b/arch/mips/bcm63xx/irq.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <linux/interrupt.h> | 12 | #include <linux/interrupt.h> |
| 13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
| 14 | #include <linux/irq.h> | 14 | #include <linux/irq.h> |
| 15 | #include <linux/spinlock.h> | ||
| 15 | #include <asm/irq_cpu.h> | 16 | #include <asm/irq_cpu.h> |
| 16 | #include <asm/mipsregs.h> | 17 | #include <asm/mipsregs.h> |
| 17 | #include <bcm63xx_cpu.h> | 18 | #include <bcm63xx_cpu.h> |
| @@ -20,6 +21,9 @@ | |||
| 20 | #include <bcm63xx_irq.h> | 21 | #include <bcm63xx_irq.h> |
| 21 | 22 | ||
| 22 | 23 | ||
| 24 | static DEFINE_SPINLOCK(ipic_lock); | ||
| 25 | static DEFINE_SPINLOCK(epic_lock); | ||
| 26 | |||
| 23 | static u32 irq_stat_addr[2]; | 27 | static u32 irq_stat_addr[2]; |
| 24 | static u32 irq_mask_addr[2]; | 28 | static u32 irq_mask_addr[2]; |
| 25 | static void (*dispatch_internal)(int cpu); | 29 | static void (*dispatch_internal)(int cpu); |
| @@ -62,8 +66,10 @@ void __dispatch_internal_##width(int cpu) \ | |||
| 62 | bool irqs_pending = false; \ | 66 | bool irqs_pending = false; \ |
| 63 | static unsigned int i[2]; \ | 67 | static unsigned int i[2]; \ |
| 64 | unsigned int *next = &i[cpu]; \ | 68 | unsigned int *next = &i[cpu]; \ |
| 69 | unsigned long flags; \ | ||
| 65 | \ | 70 | \ |
| 66 | /* read registers in reverse order */ \ | 71 | /* read registers in reverse order */ \ |
| 72 | spin_lock_irqsave(&ipic_lock, flags); \ | ||
| 67 | for (src = 0, tgt = (width / 32); src < (width / 32); src++) { \ | 73 | for (src = 0, tgt = (width / 32); src < (width / 32); src++) { \ |
| 68 | u32 val; \ | 74 | u32 val; \ |
| 69 | \ | 75 | \ |
| @@ -74,6 +80,7 @@ void __dispatch_internal_##width(int cpu) \ | |||
| 74 | if (val) \ | 80 | if (val) \ |
| 75 | irqs_pending = true; \ | 81 | irqs_pending = true; \ |
| 76 | } \ | 82 | } \ |
| 83 | spin_unlock_irqrestore(&ipic_lock, flags); \ | ||
| 77 | \ | 84 | \ |
| 78 | if (!irqs_pending) \ | 85 | if (!irqs_pending) \ |
| 79 | return; \ | 86 | return; \ |
| @@ -94,10 +101,13 @@ static void __internal_irq_mask_##width(unsigned int irq) \ | |||
| 94 | u32 val; \ | 101 | u32 val; \ |
| 95 | unsigned reg = (irq / 32) ^ (width/32 - 1); \ | 102 | unsigned reg = (irq / 32) ^ (width/32 - 1); \ |
| 96 | unsigned bit = irq & 0x1f; \ | 103 | unsigned bit = irq & 0x1f; \ |
| 104 | unsigned long flags; \ | ||
| 97 | \ | 105 | \ |
| 106 | spin_lock_irqsave(&ipic_lock, flags); \ | ||
| 98 | val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32)); \ | 107 | val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32)); \ |
| 99 | val &= ~(1 << bit); \ | 108 | val &= ~(1 << bit); \ |
| 100 | bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32)); \ | 109 | bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32)); \ |
| 110 | spin_unlock_irqrestore(&ipic_lock, flags); \ | ||
| 101 | } \ | 111 | } \ |
| 102 | \ | 112 | \ |
| 103 | static void __internal_irq_unmask_##width(unsigned int irq) \ | 113 | static void __internal_irq_unmask_##width(unsigned int irq) \ |
| @@ -105,10 +115,13 @@ static void __internal_irq_unmask_##width(unsigned int irq) \ | |||
| 105 | u32 val; \ | 115 | u32 val; \ |
| 106 | unsigned reg = (irq / 32) ^ (width/32 - 1); \ | 116 | unsigned reg = (irq / 32) ^ (width/32 - 1); \ |
| 107 | unsigned bit = irq & 0x1f; \ | 117 | unsigned bit = irq & 0x1f; \ |
| 118 | unsigned long flags; \ | ||
| 108 | \ | 119 | \ |
| 120 | spin_lock_irqsave(&ipic_lock, flags); \ | ||
| 109 | val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32)); \ | 121 | val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32)); \ |
| 110 | val |= (1 << bit); \ | 122 | val |= (1 << bit); \ |
| 111 | bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32)); \ | 123 | bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32)); \ |
| 124 | spin_unlock_irqrestore(&ipic_lock, flags); \ | ||
| 112 | } | 125 | } |
| 113 | 126 | ||
| 114 | BUILD_IPIC_INTERNAL(32); | 127 | BUILD_IPIC_INTERNAL(32); |
| @@ -167,8 +180,10 @@ static void bcm63xx_external_irq_mask(struct irq_data *d) | |||
| 167 | { | 180 | { |
| 168 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; | 181 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; |
| 169 | u32 reg, regaddr; | 182 | u32 reg, regaddr; |
| 183 | unsigned long flags; | ||
| 170 | 184 | ||
| 171 | regaddr = get_ext_irq_perf_reg(irq); | 185 | regaddr = get_ext_irq_perf_reg(irq); |
| 186 | spin_lock_irqsave(&epic_lock, flags); | ||
| 172 | reg = bcm_perf_readl(regaddr); | 187 | reg = bcm_perf_readl(regaddr); |
| 173 | 188 | ||
| 174 | if (BCMCPU_IS_6348()) | 189 | if (BCMCPU_IS_6348()) |
| @@ -177,6 +192,8 @@ static void bcm63xx_external_irq_mask(struct irq_data *d) | |||
| 177 | reg &= ~EXTIRQ_CFG_MASK(irq % 4); | 192 | reg &= ~EXTIRQ_CFG_MASK(irq % 4); |
| 178 | 193 | ||
| 179 | bcm_perf_writel(reg, regaddr); | 194 | bcm_perf_writel(reg, regaddr); |
| 195 | spin_unlock_irqrestore(&epic_lock, flags); | ||
| 196 | |||
| 180 | if (is_ext_irq_cascaded) | 197 | if (is_ext_irq_cascaded) |
| 181 | internal_irq_mask(irq + ext_irq_start); | 198 | internal_irq_mask(irq + ext_irq_start); |
| 182 | } | 199 | } |
| @@ -185,8 +202,10 @@ static void bcm63xx_external_irq_unmask(struct irq_data *d) | |||
| 185 | { | 202 | { |
| 186 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; | 203 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; |
| 187 | u32 reg, regaddr; | 204 | u32 reg, regaddr; |
| 205 | unsigned long flags; | ||
| 188 | 206 | ||
| 189 | regaddr = get_ext_irq_perf_reg(irq); | 207 | regaddr = get_ext_irq_perf_reg(irq); |
| 208 | spin_lock_irqsave(&epic_lock, flags); | ||
| 190 | reg = bcm_perf_readl(regaddr); | 209 | reg = bcm_perf_readl(regaddr); |
| 191 | 210 | ||
| 192 | if (BCMCPU_IS_6348()) | 211 | if (BCMCPU_IS_6348()) |
| @@ -195,6 +214,7 @@ static void bcm63xx_external_irq_unmask(struct irq_data *d) | |||
| 195 | reg |= EXTIRQ_CFG_MASK(irq % 4); | 214 | reg |= EXTIRQ_CFG_MASK(irq % 4); |
| 196 | 215 | ||
| 197 | bcm_perf_writel(reg, regaddr); | 216 | bcm_perf_writel(reg, regaddr); |
| 217 | spin_unlock_irqrestore(&epic_lock, flags); | ||
| 198 | 218 | ||
| 199 | if (is_ext_irq_cascaded) | 219 | if (is_ext_irq_cascaded) |
| 200 | internal_irq_unmask(irq + ext_irq_start); | 220 | internal_irq_unmask(irq + ext_irq_start); |
| @@ -204,8 +224,10 @@ static void bcm63xx_external_irq_clear(struct irq_data *d) | |||
| 204 | { | 224 | { |
| 205 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; | 225 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; |
| 206 | u32 reg, regaddr; | 226 | u32 reg, regaddr; |
| 227 | unsigned long flags; | ||
| 207 | 228 | ||
| 208 | regaddr = get_ext_irq_perf_reg(irq); | 229 | regaddr = get_ext_irq_perf_reg(irq); |
| 230 | spin_lock_irqsave(&epic_lock, flags); | ||
| 209 | reg = bcm_perf_readl(regaddr); | 231 | reg = bcm_perf_readl(regaddr); |
| 210 | 232 | ||
| 211 | if (BCMCPU_IS_6348()) | 233 | if (BCMCPU_IS_6348()) |
| @@ -214,6 +236,7 @@ static void bcm63xx_external_irq_clear(struct irq_data *d) | |||
| 214 | reg |= EXTIRQ_CFG_CLEAR(irq % 4); | 236 | reg |= EXTIRQ_CFG_CLEAR(irq % 4); |
| 215 | 237 | ||
| 216 | bcm_perf_writel(reg, regaddr); | 238 | bcm_perf_writel(reg, regaddr); |
| 239 | spin_unlock_irqrestore(&epic_lock, flags); | ||
| 217 | } | 240 | } |
| 218 | 241 | ||
| 219 | static int bcm63xx_external_irq_set_type(struct irq_data *d, | 242 | static int bcm63xx_external_irq_set_type(struct irq_data *d, |
| @@ -222,6 +245,7 @@ static int bcm63xx_external_irq_set_type(struct irq_data *d, | |||
| 222 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; | 245 | unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; |
| 223 | u32 reg, regaddr; | 246 | u32 reg, regaddr; |
| 224 | int levelsense, sense, bothedge; | 247 | int levelsense, sense, bothedge; |
| 248 | unsigned long flags; | ||
| 225 | 249 | ||
| 226 | flow_type &= IRQ_TYPE_SENSE_MASK; | 250 | flow_type &= IRQ_TYPE_SENSE_MASK; |
| 227 | 251 | ||
| @@ -256,6 +280,7 @@ static int bcm63xx_external_irq_set_type(struct irq_data *d, | |||
| 256 | } | 280 | } |
| 257 | 281 | ||
| 258 | regaddr = get_ext_irq_perf_reg(irq); | 282 | regaddr = get_ext_irq_perf_reg(irq); |
| 283 | spin_lock_irqsave(&epic_lock, flags); | ||
| 259 | reg = bcm_perf_readl(regaddr); | 284 | reg = bcm_perf_readl(regaddr); |
| 260 | irq %= 4; | 285 | irq %= 4; |
| 261 | 286 | ||
| @@ -300,6 +325,7 @@ static int bcm63xx_external_irq_set_type(struct irq_data *d, | |||
| 300 | } | 325 | } |
| 301 | 326 | ||
| 302 | bcm_perf_writel(reg, regaddr); | 327 | bcm_perf_writel(reg, regaddr); |
| 328 | spin_unlock_irqrestore(&epic_lock, flags); | ||
| 303 | 329 | ||
| 304 | irqd_set_trigger_type(d, flow_type); | 330 | irqd_set_trigger_type(d, flow_type); |
| 305 | if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) | 331 | if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) |
