Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

metag: Internal and external irqchips

Meta core internal interrupts (from HWSTATMETA and friends) are vectored
onto the TR1 core trigger for the current thread. This is demultiplexed
in irq-metag.c to individual Linux IRQs for each internal interrupt.

External SoC interrupts (from HWSTATEXT and friends) are vectored onto
the TR2 core trigger for the current thread. This is demultiplexed in
irq-metag-ext.c to individual Linux IRQs for each external SoC interrupt.
The external irqchip has devicetree bindings for configuring the number
of irq banks and the type of masking available.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Dom Cobley <popcornmix@gmail.com>
Cc: Simon Arlott <simon@fire.lp0.eu>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: linux-doc@vger.kernel.org

+1359
+82
Documentation/devicetree/bindings/metag/meta-intc.txt
··· 1 + * Meta External Trigger Controller Binding 2 + 3 + This binding specifies what properties must be available in the device tree 4 + representation of a Meta external trigger controller. 5 + 6 + Required properties: 7 + 8 + - compatible: Specifies the compatibility list for the interrupt controller. 9 + The type shall be <string> and the value shall include "img,meta-intc". 10 + 11 + - num-banks: Specifies the number of interrupt banks (each of which can 12 + handle 32 interrupt sources). 13 + 14 + - interrupt-controller: The presence of this property identifies the node 15 + as an interupt controller. No property value shall be defined. 16 + 17 + - #interrupt-cells: Specifies the number of cells needed to encode an 18 + interrupt source. The type shall be a <u32> and the value shall be 2. 19 + 20 + - #address-cells: Specifies the number of cells needed to encode an 21 + address. The type shall be <u32> and the value shall be 0. As such, 22 + 'interrupt-map' nodes do not have to specify a parent unit address. 23 + 24 + Optional properties: 25 + 26 + - no-mask: The controller doesn't have any mask registers. 27 + 28 + * Interrupt Specifier Definition 29 + 30 + Interrupt specifiers consists of 2 cells encoded as follows: 31 + 32 + - <1st-cell>: The interrupt-number that identifies the interrupt source. 33 + 34 + - <2nd-cell>: The Linux interrupt flags containing level-sense information, 35 + encoded as follows: 36 + 1 = edge triggered 37 + 4 = level-sensitive 38 + 39 + * Examples 40 + 41 + Example 1: 42 + 43 + /* 44 + * Meta external trigger block 45 + */ 46 + intc: intc { 47 + // This is an interrupt controller node. 48 + interrupt-controller; 49 + 50 + // No address cells so that 'interrupt-map' nodes which 51 + // reference this interrupt controller node do not need a parent 52 + // address specifier. 53 + #address-cells = <0>; 54 + 55 + // Two cells to encode interrupt sources. 56 + #interrupt-cells = <2>; 57 + 58 + // Number of interrupt banks 59 + num-banks = <2>; 60 + 61 + // No HWMASKEXT is available (specify on Chorus2 and Comet ES1) 62 + no-mask; 63 + 64 + // Compatible with Meta hardware trigger block. 65 + compatible = "img,meta-intc"; 66 + }; 67 + 68 + Example 2: 69 + 70 + /* 71 + * An interrupt generating device that is wired to a Meta external 72 + * trigger block. 73 + */ 74 + uart1: uart@0x02004c00 { 75 + // Interrupt source '5' that is level-sensitive. 76 + // Note that there are only two cells as specified in the 77 + // interrupt parent's '#interrupt-cells' property. 78 + interrupts = <5 4 /* level */>; 79 + 80 + // The interrupt controller that this device is wired to. 81 + interrupt-parent = <&intc>; 82 + };
+2
MAINTAINERS
··· 5040 5040 F: Documentation/metag/ 5041 5041 F: Documentation/devicetree/bindings/metag/ 5042 5042 F: drivers/clocksource/metag_generic.c 5043 + F: drivers/irqchip/irq-metag.c 5044 + F: drivers/irqchip/irq-metag-ext.c 5043 5045 5044 5046 MICROBLAZE ARCHITECTURE 5045 5047 M: Michal Simek <monstr@monstr.eu>
+5
arch/metag/kernel/irq.c
··· 6 6 #include <linux/kernel.h> 7 7 #include <linux/interrupt.h> 8 8 #include <linux/init.h> 9 + #include <linux/irqchip/metag-ext.h> 10 + #include <linux/irqchip/metag.h> 9 11 #include <linux/irqdomain.h> 10 12 #include <linux/ratelimit.h> 11 13 ··· 259 257 panic("init_IRQ: cannot add root IRQ domain"); 260 258 261 259 irq_ctx_init(smp_processor_id()); 260 + 261 + init_internal_IRQ(); 262 + init_external_IRQ(); 262 263 263 264 if (machine_desc->init_irq) 264 265 machine_desc->init_irq();
+2
drivers/irqchip/Makefile
··· 1 1 obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o 2 + obj-$(CONFIG_METAG) += irq-metag-ext.o 3 + obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o 2 4 obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o 3 5 obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o 4 6 obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
+868
drivers/irqchip/irq-metag-ext.c
··· 1 + /* 2 + * Meta External interrupt code. 3 + * 4 + * Copyright (C) 2005-2012 Imagination Technologies Ltd. 5 + * 6 + * External interrupts on Meta are configured at two-levels, in the CPU core and 7 + * in the external trigger block. Interrupts from SoC peripherals are 8 + * multiplexed onto a single Meta CPU "trigger" - traditionally it has always 9 + * been trigger 2 (TR2). For info on how de-multiplexing happens check out 10 + * meta_intc_irq_demux(). 11 + */ 12 + 13 + #include <linux/interrupt.h> 14 + #include <linux/irqchip/metag-ext.h> 15 + #include <linux/irqdomain.h> 16 + #include <linux/io.h> 17 + #include <linux/of.h> 18 + #include <linux/slab.h> 19 + #include <linux/syscore_ops.h> 20 + 21 + #include <asm/irq.h> 22 + #include <asm/hwthread.h> 23 + 24 + #define HWSTAT_STRIDE 8 25 + #define HWVEC_BLK_STRIDE 0x1000 26 + 27 + /** 28 + * struct meta_intc_priv - private meta external interrupt data 29 + * @nr_banks: Number of interrupt banks 30 + * @domain: IRQ domain for all banks of external IRQs 31 + * @unmasked: Record of unmasked IRQs 32 + * @levels_altered: Record of altered level bits 33 + */ 34 + struct meta_intc_priv { 35 + unsigned int nr_banks; 36 + struct irq_domain *domain; 37 + 38 + unsigned long unmasked[4]; 39 + 40 + #ifdef CONFIG_METAG_SUSPEND_MEM 41 + unsigned long levels_altered[4]; 42 + #endif 43 + }; 44 + 45 + /* Private data for the one and only external interrupt controller */ 46 + static struct meta_intc_priv meta_intc_priv; 47 + 48 + /** 49 + * meta_intc_offset() - Get the offset into the bank of a hardware IRQ number 50 + * @hw: Hardware IRQ number (within external trigger block) 51 + * 52 + * Returns: Bit offset into the IRQ's bank registers 53 + */ 54 + static unsigned int meta_intc_offset(irq_hw_number_t hw) 55 + { 56 + return hw & 0x1f; 57 + } 58 + 59 + /** 60 + * meta_intc_bank() - Get the bank number of a hardware IRQ number 61 + * @hw: Hardware IRQ number (within external trigger block) 62 + * 63 + * Returns: Bank number indicating which register the IRQ's bits are 64 + */ 65 + static unsigned int meta_intc_bank(irq_hw_number_t hw) 66 + { 67 + return hw >> 5; 68 + } 69 + 70 + /** 71 + * meta_intc_stat_addr() - Get the address of a HWSTATEXT register 72 + * @hw: Hardware IRQ number (within external trigger block) 73 + * 74 + * Returns: Address of a HWSTATEXT register containing the status bit for 75 + * the specified hardware IRQ number 76 + */ 77 + static void __iomem *meta_intc_stat_addr(irq_hw_number_t hw) 78 + { 79 + return (void __iomem *)(HWSTATEXT + 80 + HWSTAT_STRIDE * meta_intc_bank(hw)); 81 + } 82 + 83 + /** 84 + * meta_intc_level_addr() - Get the address of a HWLEVELEXT register 85 + * @hw: Hardware IRQ number (within external trigger block) 86 + * 87 + * Returns: Address of a HWLEVELEXT register containing the sense bit for 88 + * the specified hardware IRQ number 89 + */ 90 + static void __iomem *meta_intc_level_addr(irq_hw_number_t hw) 91 + { 92 + return (void __iomem *)(HWLEVELEXT + 93 + HWSTAT_STRIDE * meta_intc_bank(hw)); 94 + } 95 + 96 + /** 97 + * meta_intc_mask_addr() - Get the address of a HWMASKEXT register 98 + * @hw: Hardware IRQ number (within external trigger block) 99 + * 100 + * Returns: Address of a HWMASKEXT register containing the mask bit for the 101 + * specified hardware IRQ number 102 + */ 103 + static void __iomem *meta_intc_mask_addr(irq_hw_number_t hw) 104 + { 105 + return (void __iomem *)(HWMASKEXT + 106 + HWSTAT_STRIDE * meta_intc_bank(hw)); 107 + } 108 + 109 + /** 110 + * meta_intc_vec_addr() - Get the vector address of a hardware interrupt 111 + * @hw: Hardware IRQ number (within external trigger block) 112 + * 113 + * Returns: Address of a HWVECEXT register controlling the core trigger to 114 + * vector the IRQ onto 115 + */ 116 + static inline void __iomem *meta_intc_vec_addr(irq_hw_number_t hw) 117 + { 118 + return (void __iomem *)(HWVEC0EXT + 119 + HWVEC_BLK_STRIDE * meta_intc_bank(hw) + 120 + HWVECnEXT_STRIDE * meta_intc_offset(hw)); 121 + } 122 + 123 + /** 124 + * meta_intc_startup_irq() - set up an external irq 125 + * @data: data for the external irq to start up 126 + * 127 + * Multiplex interrupts for irq onto TR2. Clear any pending interrupts and 128 + * unmask irq, both using the appropriate callbacks. 129 + */ 130 + static unsigned int meta_intc_startup_irq(struct irq_data *data) 131 + { 132 + irq_hw_number_t hw = data->hwirq; 133 + void __iomem *vec_addr = meta_intc_vec_addr(hw); 134 + int thread = hard_processor_id(); 135 + 136 + /* Perform any necessary acking. */ 137 + if (data->chip->irq_ack) 138 + data->chip->irq_ack(data); 139 + 140 + /* Wire up this interrupt to the core with HWVECxEXT. */ 141 + metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); 142 + 143 + /* Perform any necessary unmasking. */ 144 + data->chip->irq_unmask(data); 145 + 146 + return 0; 147 + } 148 + 149 + /** 150 + * meta_intc_shutdown_irq() - turn off an external irq 151 + * @data: data for the external irq to turn off 152 + * 153 + * Mask irq using the appropriate callback and stop muxing it onto TR2. 154 + */ 155 + static void meta_intc_shutdown_irq(struct irq_data *data) 156 + { 157 + irq_hw_number_t hw = data->hwirq; 158 + void __iomem *vec_addr = meta_intc_vec_addr(hw); 159 + 160 + /* Mask the IRQ */ 161 + data->chip->irq_mask(data); 162 + 163 + /* 164 + * Disable the IRQ at the core by removing the interrupt from 165 + * the HW vector mapping. 166 + */ 167 + metag_out32(0, vec_addr); 168 + } 169 + 170 + /** 171 + * meta_intc_ack_irq() - acknowledge an external irq 172 + * @data: data for the external irq to ack 173 + * 174 + * Clear down an edge interrupt in the status register. 175 + */ 176 + static void meta_intc_ack_irq(struct irq_data *data) 177 + { 178 + irq_hw_number_t hw = data->hwirq; 179 + unsigned int bit = 1 << meta_intc_offset(hw); 180 + void __iomem *stat_addr = meta_intc_stat_addr(hw); 181 + 182 + /* Ack the int, if it is still 'on'. 183 + * NOTE - this only works for edge triggered interrupts. 184 + */ 185 + if (metag_in32(stat_addr) & bit) 186 + metag_out32(bit, stat_addr); 187 + } 188 + 189 + /** 190 + * record_irq_is_masked() - record the IRQ masked so it doesn't get handled 191 + * @data: data for the external irq to record 192 + * 193 + * This should get called whenever an external IRQ is masked (by whichever 194 + * callback is used). It records the IRQ masked so that it doesn't get handled 195 + * if it still shows up in the status register. 196 + */ 197 + static void record_irq_is_masked(struct irq_data *data) 198 + { 199 + struct meta_intc_priv *priv = &meta_intc_priv; 200 + irq_hw_number_t hw = data->hwirq; 201 + 202 + clear_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]); 203 + } 204 + 205 + /** 206 + * record_irq_is_unmasked() - record the IRQ unmasked so it can be handled 207 + * @data: data for the external irq to record 208 + * 209 + * This should get called whenever an external IRQ is unmasked (by whichever 210 + * callback is used). It records the IRQ unmasked so that it gets handled if it 211 + * shows up in the status register. 212 + */ 213 + static void record_irq_is_unmasked(struct irq_data *data) 214 + { 215 + struct meta_intc_priv *priv = &meta_intc_priv; 216 + irq_hw_number_t hw = data->hwirq; 217 + 218 + set_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]); 219 + } 220 + 221 + /* 222 + * For use by wrapper IRQ drivers 223 + */ 224 + 225 + /** 226 + * meta_intc_mask_irq_simple() - minimal mask used by wrapper IRQ drivers 227 + * @data: data for the external irq being masked 228 + * 229 + * This should be called by any wrapper IRQ driver mask functions. it doesn't do 230 + * any masking but records the IRQ as masked so that the core code knows the 231 + * mask has taken place. It is the callers responsibility to ensure that the IRQ 232 + * won't trigger an interrupt to the core. 233 + */ 234 + void meta_intc_mask_irq_simple(struct irq_data *data) 235 + { 236 + record_irq_is_masked(data); 237 + } 238 + 239 + /** 240 + * meta_intc_unmask_irq_simple() - minimal unmask used by wrapper IRQ drivers 241 + * @data: data for the external irq being unmasked 242 + * 243 + * This should be called by any wrapper IRQ driver unmask functions. it doesn't 244 + * do any unmasking but records the IRQ as unmasked so that the core code knows 245 + * the unmask has taken place. It is the callers responsibility to ensure that 246 + * the IRQ can now trigger an interrupt to the core. 247 + */ 248 + void meta_intc_unmask_irq_simple(struct irq_data *data) 249 + { 250 + record_irq_is_unmasked(data); 251 + } 252 + 253 + 254 + /** 255 + * meta_intc_mask_irq() - mask an external irq using HWMASKEXT 256 + * @data: data for the external irq to mask 257 + * 258 + * This is a default implementation of a mask function which makes use of the 259 + * HWMASKEXT registers available in newer versions. 260 + * 261 + * Earlier versions without these registers should use SoC level IRQ masking 262 + * which call the meta_intc_*_simple() functions above, or if that isn't 263 + * available should use the fallback meta_intc_*_nomask() functions below. 264 + */ 265 + static void meta_intc_mask_irq(struct irq_data *data) 266 + { 267 + irq_hw_number_t hw = data->hwirq; 268 + unsigned int bit = 1 << meta_intc_offset(hw); 269 + void __iomem *mask_addr = meta_intc_mask_addr(hw); 270 + unsigned long flags; 271 + 272 + record_irq_is_masked(data); 273 + 274 + /* update the interrupt mask */ 275 + __global_lock2(flags); 276 + metag_out32(metag_in32(mask_addr) & ~bit, mask_addr); 277 + __global_unlock2(flags); 278 + } 279 + 280 + /** 281 + * meta_intc_unmask_irq() - unmask an external irq using HWMASKEXT 282 + * @data: data for the external irq to unmask 283 + * 284 + * This is a default implementation of an unmask function which makes use of the 285 + * HWMASKEXT registers available on new versions. It should be paired with 286 + * meta_intc_mask_irq() above. 287 + */ 288 + static void meta_intc_unmask_irq(struct irq_data *data) 289 + { 290 + irq_hw_number_t hw = data->hwirq; 291 + unsigned int bit = 1 << meta_intc_offset(hw); 292 + void __iomem *mask_addr = meta_intc_mask_addr(hw); 293 + unsigned long flags; 294 + 295 + record_irq_is_unmasked(data); 296 + 297 + /* update the interrupt mask */ 298 + __global_lock2(flags); 299 + metag_out32(metag_in32(mask_addr) | bit, mask_addr); 300 + __global_unlock2(flags); 301 + } 302 + 303 + /** 304 + * meta_intc_mask_irq_nomask() - mask an external irq by unvectoring 305 + * @data: data for the external irq to mask 306 + * 307 + * This is the version of the mask function for older versions which don't have 308 + * HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the IRQ is 309 + * unvectored from the core and retriggered if necessary later. 310 + */ 311 + static void meta_intc_mask_irq_nomask(struct irq_data *data) 312 + { 313 + irq_hw_number_t hw = data->hwirq; 314 + void __iomem *vec_addr = meta_intc_vec_addr(hw); 315 + 316 + record_irq_is_masked(data); 317 + 318 + /* there is no interrupt mask, so unvector the interrupt */ 319 + metag_out32(0, vec_addr); 320 + } 321 + 322 + /** 323 + * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring 324 + * @data: data for the external irq to unmask 325 + * 326 + * This is the version of the unmask function for older versions which don't 327 + * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the 328 + * IRQ is revectored back to the core and retriggered if necessary. 329 + * 330 + * The retriggering done by this function is specific to edge interrupts. 331 + */ 332 + static void meta_intc_unmask_edge_irq_nomask(struct irq_data *data) 333 + { 334 + irq_hw_number_t hw = data->hwirq; 335 + unsigned int bit = 1 << meta_intc_offset(hw); 336 + void __iomem *stat_addr = meta_intc_stat_addr(hw); 337 + void __iomem *vec_addr = meta_intc_vec_addr(hw); 338 + unsigned int thread = hard_processor_id(); 339 + 340 + record_irq_is_unmasked(data); 341 + 342 + /* there is no interrupt mask, so revector the interrupt */ 343 + metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); 344 + 345 + /* 346 + * Re-trigger interrupt 347 + * 348 + * Writing a 1 toggles, and a 0->1 transition triggers. We only 349 + * retrigger if the status bit is already set, which means we 350 + * need to clear it first. Retriggering is fundamentally racy 351 + * because if the interrupt fires again after we clear it we 352 + * could end up clearing it again and the interrupt handler 353 + * thinking it hasn't fired. Therefore we need to keep trying to 354 + * retrigger until the bit is set. 355 + */ 356 + if (metag_in32(stat_addr) & bit) { 357 + metag_out32(bit, stat_addr); 358 + while (!(metag_in32(stat_addr) & bit)) 359 + metag_out32(bit, stat_addr); 360 + } 361 + } 362 + 363 + /** 364 + * meta_intc_unmask_level_irq_nomask() - unmask a level irq by revectoring 365 + * @data: data for the external irq to unmask 366 + * 367 + * This is the version of the unmask function for older versions which don't 368 + * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the 369 + * IRQ is revectored back to the core and retriggered if necessary. 370 + * 371 + * The retriggering done by this function is specific to level interrupts. 372 + */ 373 + static void meta_intc_unmask_level_irq_nomask(struct irq_data *data) 374 + { 375 + irq_hw_number_t hw = data->hwirq; 376 + unsigned int bit = 1 << meta_intc_offset(hw); 377 + void __iomem *stat_addr = meta_intc_stat_addr(hw); 378 + void __iomem *vec_addr = meta_intc_vec_addr(hw); 379 + unsigned int thread = hard_processor_id(); 380 + 381 + record_irq_is_unmasked(data); 382 + 383 + /* there is no interrupt mask, so revector the interrupt */ 384 + metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); 385 + 386 + /* Re-trigger interrupt */ 387 + /* Writing a 1 triggers interrupt */ 388 + if (metag_in32(stat_addr) & bit) 389 + metag_out32(bit, stat_addr); 390 + } 391 + 392 + /** 393 + * meta_intc_irq_set_type() - set the type of an external irq 394 + * @data: data for the external irq to set the type of 395 + * @flow_type: new irq flow type 396 + * 397 + * Set the flow type of an external interrupt. This updates the irq chip and irq 398 + * handler depending on whether the irq is edge or level sensitive (the polarity 399 + * is ignored), and also sets up the bit in HWLEVELEXT so the hardware knows 400 + * when to trigger. 401 + */ 402 + static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type) 403 + { 404 + #ifdef CONFIG_METAG_SUSPEND_MEM 405 + struct meta_intc_priv *priv = &meta_intc_priv; 406 + #endif 407 + unsigned int irq = data->irq; 408 + irq_hw_number_t hw = data->hwirq; 409 + unsigned int bit = 1 << meta_intc_offset(hw); 410 + void __iomem *level_addr = meta_intc_level_addr(hw); 411 + unsigned long flags; 412 + unsigned int level; 413 + 414 + /* update the chip/handler */ 415 + if (flow_type & IRQ_TYPE_LEVEL_MASK) 416 + __irq_set_chip_handler_name_locked(irq, &meta_intc_level_chip, 417 + handle_level_irq, NULL); 418 + else 419 + __irq_set_chip_handler_name_locked(irq, &meta_intc_edge_chip, 420 + handle_edge_irq, NULL); 421 + 422 + /* and clear/set the bit in HWLEVELEXT */ 423 + __global_lock2(flags); 424 + level = metag_in32(level_addr); 425 + if (flow_type & IRQ_TYPE_LEVEL_MASK) 426 + level |= bit; 427 + else 428 + level &= ~bit; 429 + metag_out32(level, level_addr); 430 + #ifdef CONFIG_METAG_SUSPEND_MEM 431 + priv->levels_altered[meta_intc_bank(hw)] |= bit; 432 + #endif 433 + __global_unlock2(flags); 434 + 435 + return 0; 436 + } 437 + 438 + /** 439 + * meta_intc_irq_demux() - external irq de-multiplexer 440 + * @irq: the virtual interrupt number 441 + * @desc: the interrupt description structure for this irq 442 + * 443 + * The cpu receives an interrupt on TR2 when a SoC interrupt has occurred. It is 444 + * this function's job to demux this irq and figure out exactly which external 445 + * irq needs servicing. 446 + * 447 + * Whilst using TR2 to detect external interrupts is a software convention it is 448 + * (hopefully) unlikely to change. 449 + */ 450 + static void meta_intc_irq_demux(unsigned int irq, struct irq_desc *desc) 451 + { 452 + struct meta_intc_priv *priv = &meta_intc_priv; 453 + irq_hw_number_t hw; 454 + unsigned int bank, irq_no, status; 455 + void __iomem *stat_addr = meta_intc_stat_addr(0); 456 + 457 + /* 458 + * Locate which interrupt has caused our handler to run. 459 + */ 460 + for (bank = 0; bank < priv->nr_banks; ++bank) { 461 + /* Which interrupts are currently pending in this bank? */ 462 + recalculate: 463 + status = metag_in32(stat_addr) & priv->unmasked[bank]; 464 + 465 + for (hw = bank*32; status; status >>= 1, ++hw) { 466 + if (status & 0x1) { 467 + /* 468 + * Map the hardware IRQ number to a virtual 469 + * Linux IRQ number. 470 + */ 471 + irq_no = irq_linear_revmap(priv->domain, hw); 472 + 473 + /* 474 + * Only fire off external interrupts that are 475 + * registered to be handled by the kernel. 476 + * Other external interrupts are probably being 477 + * handled by other Meta hardware threads. 478 + */ 479 + generic_handle_irq(irq_no); 480 + 481 + /* 482 + * The handler may have re-enabled interrupts 483 + * which could have caused a nested invocation 484 + * of this code and make the copy of the 485 + * status register we are using invalid. 486 + */ 487 + goto recalculate; 488 + } 489 + } 490 + stat_addr += HWSTAT_STRIDE; 491 + } 492 + } 493 + 494 + #ifdef CONFIG_SMP 495 + /** 496 + * meta_intc_set_affinity() - set the affinity for an interrupt 497 + * @data: data for the external irq to set the affinity of 498 + * @cpumask: cpu mask representing cpus which can handle the interrupt 499 + * @force: whether to force (ignored) 500 + * 501 + * Revector the specified external irq onto a specific cpu's TR2 trigger, so 502 + * that that cpu tends to be the one who handles it. 503 + */ 504 + static int meta_intc_set_affinity(struct irq_data *data, 505 + const struct cpumask *cpumask, bool force) 506 + { 507 + irq_hw_number_t hw = data->hwirq; 508 + void __iomem *vec_addr = meta_intc_vec_addr(hw); 509 + unsigned int cpu, thread; 510 + 511 + /* 512 + * Wire up this interrupt from HWVECxEXT to the Meta core. 513 + * 514 + * Note that we can't wire up HWVECxEXT to interrupt more than 515 + * one cpu (the interrupt code doesn't support it), so we just 516 + * pick the first cpu we find in 'cpumask'. 517 + */ 518 + cpu = cpumask_any(cpumask); 519 + thread = cpu_2_hwthread_id[cpu]; 520 + 521 + metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); 522 + 523 + return 0; 524 + } 525 + #else 526 + #define meta_intc_set_affinity NULL 527 + #endif 528 + 529 + #ifdef CONFIG_PM_SLEEP 530 + #define META_INTC_CHIP_FLAGS (IRQCHIP_MASK_ON_SUSPEND \ 531 + | IRQCHIP_SKIP_SET_WAKE) 532 + #else 533 + #define META_INTC_CHIP_FLAGS 0 534 + #endif 535 + 536 + /* public edge/level irq chips which SoCs can override */ 537 + 538 + struct irq_chip meta_intc_edge_chip = { 539 + .irq_startup = meta_intc_startup_irq, 540 + .irq_shutdown = meta_intc_shutdown_irq, 541 + .irq_ack = meta_intc_ack_irq, 542 + .irq_mask = meta_intc_mask_irq, 543 + .irq_unmask = meta_intc_unmask_irq, 544 + .irq_set_type = meta_intc_irq_set_type, 545 + .irq_set_affinity = meta_intc_set_affinity, 546 + .flags = META_INTC_CHIP_FLAGS, 547 + }; 548 + 549 + struct irq_chip meta_intc_level_chip = { 550 + .irq_startup = meta_intc_startup_irq, 551 + .irq_shutdown = meta_intc_shutdown_irq, 552 + .irq_set_type = meta_intc_irq_set_type, 553 + .irq_mask = meta_intc_mask_irq, 554 + .irq_unmask = meta_intc_unmask_irq, 555 + .irq_set_affinity = meta_intc_set_affinity, 556 + .flags = META_INTC_CHIP_FLAGS, 557 + }; 558 + 559 + /** 560 + * meta_intc_map() - map an external irq 561 + * @d: irq domain of external trigger block 562 + * @irq: virtual irq number 563 + * @hw: hardware irq number within external trigger block 564 + * 565 + * This sets up a virtual irq for a specified hardware interrupt. The irq chip 566 + * and handler is configured, using the HWLEVELEXT registers to determine 567 + * edge/level flow type. These registers will have been set when the irq type is 568 + * set (or set to a default at init time). 569 + */ 570 + static int meta_intc_map(struct irq_domain *d, unsigned int irq, 571 + irq_hw_number_t hw) 572 + { 573 + unsigned int bit = 1 << meta_intc_offset(hw); 574 + void __iomem *level_addr = meta_intc_level_addr(hw); 575 + 576 + /* Go by the current sense in the HWLEVELEXT register */ 577 + if (metag_in32(level_addr) & bit) 578 + irq_set_chip_and_handler(irq, &meta_intc_level_chip, 579 + handle_level_irq); 580 + else 581 + irq_set_chip_and_handler(irq, &meta_intc_edge_chip, 582 + handle_edge_irq); 583 + return 0; 584 + } 585 + 586 + static const struct irq_domain_ops meta_intc_domain_ops = { 587 + .map = meta_intc_map, 588 + .xlate = irq_domain_xlate_twocell, 589 + }; 590 + 591 + #ifdef CONFIG_METAG_SUSPEND_MEM 592 + 593 + /** 594 + * struct meta_intc_context - suspend context 595 + * @levels: State of HWLEVELEXT registers 596 + * @masks: State of HWMASKEXT registers 597 + * @vectors: State of HWVECEXT registers 598 + * @txvecint: State of TxVECINT registers 599 + * 600 + * This structure stores the IRQ state across suspend. 601 + */ 602 + struct meta_intc_context { 603 + u32 levels[4]; 604 + u32 masks[4]; 605 + u8 vectors[4*32]; 606 + 607 + u8 txvecint[4][4]; 608 + }; 609 + 610 + /* suspend context */ 611 + static struct meta_intc_context *meta_intc_context; 612 + 613 + /** 614 + * meta_intc_suspend() - store irq state 615 + * 616 + * To avoid interfering with other threads we only save the IRQ state of IRQs in 617 + * use by Linux. 618 + */ 619 + static int meta_intc_suspend(void) 620 + { 621 + struct meta_intc_priv *priv = &meta_intc_priv; 622 + int i, j; 623 + irq_hw_number_t hw; 624 + unsigned int bank; 625 + unsigned long flags; 626 + struct meta_intc_context *context; 627 + void __iomem *level_addr, *mask_addr, *vec_addr; 628 + u32 mask, bit; 629 + 630 + context = kzalloc(sizeof(*context), GFP_ATOMIC); 631 + if (!context) 632 + return -ENOMEM; 633 + 634 + hw = 0; 635 + level_addr = meta_intc_level_addr(0); 636 + mask_addr = meta_intc_mask_addr(0); 637 + for (bank = 0; bank < priv->nr_banks; ++bank) { 638 + vec_addr = meta_intc_vec_addr(hw); 639 + 640 + /* create mask of interrupts in use */ 641 + mask = 0; 642 + for (bit = 1; bit; bit <<= 1) { 643 + i = irq_linear_revmap(priv->domain, hw); 644 + /* save mapped irqs which are enabled or have actions */ 645 + if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || 646 + irq_has_action(i))) { 647 + mask |= bit; 648 + 649 + /* save trigger vector */ 650 + context->vectors[hw] = metag_in32(vec_addr); 651 + } 652 + 653 + ++hw; 654 + vec_addr += HWVECnEXT_STRIDE; 655 + } 656 + 657 + /* save level state if any IRQ levels altered */ 658 + if (priv->levels_altered[bank]) 659 + context->levels[bank] = metag_in32(level_addr); 660 + /* save mask state if any IRQs in use */ 661 + if (mask) 662 + context->masks[bank] = metag_in32(mask_addr); 663 + 664 + level_addr += HWSTAT_STRIDE; 665 + mask_addr += HWSTAT_STRIDE; 666 + } 667 + 668 + /* save trigger matrixing */ 669 + __global_lock2(flags); 670 + for (i = 0; i < 4; ++i) 671 + for (j = 0; j < 4; ++j) 672 + context->txvecint[i][j] = metag_in32(T0VECINT_BHALT + 673 + TnVECINT_STRIDE*i + 674 + 8*j); 675 + __global_unlock2(flags); 676 + 677 + meta_intc_context = context; 678 + return 0; 679 + } 680 + 681 + /** 682 + * meta_intc_resume() - restore saved irq state 683 + * 684 + * Restore the saved IRQ state and drop it. 685 + */ 686 + static void meta_intc_resume(void) 687 + { 688 + struct meta_intc_priv *priv = &meta_intc_priv; 689 + int i, j; 690 + irq_hw_number_t hw; 691 + unsigned int bank; 692 + unsigned long flags; 693 + struct meta_intc_context *context = meta_intc_context; 694 + void __iomem *level_addr, *mask_addr, *vec_addr; 695 + u32 mask, bit, tmp; 696 + 697 + meta_intc_context = NULL; 698 + 699 + hw = 0; 700 + level_addr = meta_intc_level_addr(0); 701 + mask_addr = meta_intc_mask_addr(0); 702 + for (bank = 0; bank < priv->nr_banks; ++bank) { 703 + vec_addr = meta_intc_vec_addr(hw); 704 + 705 + /* create mask of interrupts in use */ 706 + mask = 0; 707 + for (bit = 1; bit; bit <<= 1) { 708 + i = irq_linear_revmap(priv->domain, hw); 709 + /* restore mapped irqs, enabled or with actions */ 710 + if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || 711 + irq_has_action(i))) { 712 + mask |= bit; 713 + 714 + /* restore trigger vector */ 715 + metag_out32(context->vectors[hw], vec_addr); 716 + } 717 + 718 + ++hw; 719 + vec_addr += HWVECnEXT_STRIDE; 720 + } 721 + 722 + if (mask) { 723 + /* restore mask state */ 724 + __global_lock2(flags); 725 + tmp = metag_in32(mask_addr); 726 + tmp = (tmp & ~mask) | (context->masks[bank] & mask); 727 + metag_out32(tmp, mask_addr); 728 + __global_unlock2(flags); 729 + } 730 + 731 + mask = priv->levels_altered[bank]; 732 + if (mask) { 733 + /* restore level state */ 734 + __global_lock2(flags); 735 + tmp = metag_in32(level_addr); 736 + tmp = (tmp & ~mask) | (context->levels[bank] & mask); 737 + metag_out32(tmp, level_addr); 738 + __global_unlock2(flags); 739 + } 740 + 741 + level_addr += HWSTAT_STRIDE; 742 + mask_addr += HWSTAT_STRIDE; 743 + } 744 + 745 + /* restore trigger matrixing */ 746 + __global_lock2(flags); 747 + for (i = 0; i < 4; ++i) { 748 + for (j = 0; j < 4; ++j) { 749 + metag_out32(context->txvecint[i][j], 750 + T0VECINT_BHALT + 751 + TnVECINT_STRIDE*i + 752 + 8*j); 753 + } 754 + } 755 + __global_unlock2(flags); 756 + 757 + kfree(context); 758 + } 759 + 760 + static struct syscore_ops meta_intc_syscore_ops = { 761 + .suspend = meta_intc_suspend, 762 + .resume = meta_intc_resume, 763 + }; 764 + 765 + static void __init meta_intc_init_syscore_ops(struct meta_intc_priv *priv) 766 + { 767 + register_syscore_ops(&meta_intc_syscore_ops); 768 + } 769 + #else 770 + #define meta_intc_init_syscore_ops(priv) do {} while (0) 771 + #endif 772 + 773 + /** 774 + * meta_intc_init_cpu() - register with a Meta cpu 775 + * @priv: private interrupt controller data 776 + * @cpu: the CPU to register on 777 + * 778 + * Configure @cpu's TR2 irq so that we can demux external irqs. 779 + */ 780 + static void __init meta_intc_init_cpu(struct meta_intc_priv *priv, int cpu) 781 + { 782 + unsigned int thread = cpu_2_hwthread_id[cpu]; 783 + unsigned int signum = TBID_SIGNUM_TR2(thread); 784 + int irq = tbisig_map(signum); 785 + 786 + /* Register the multiplexed IRQ handler */ 787 + irq_set_chained_handler(irq, meta_intc_irq_demux); 788 + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); 789 + } 790 + 791 + /** 792 + * meta_intc_no_mask() - indicate lack of HWMASKEXT registers 793 + * 794 + * Called from SoC code (or init code below) to dynamically indicate the lack of 795 + * HWMASKEXT registers (for example depending on some SoC revision register). 796 + * This alters the irq mask and unmask callbacks to use the fallback 797 + * unvectoring/retriggering technique instead of using HWMASKEXT registers. 798 + */ 799 + void __init meta_intc_no_mask(void) 800 + { 801 + meta_intc_edge_chip.irq_mask = meta_intc_mask_irq_nomask; 802 + meta_intc_edge_chip.irq_unmask = meta_intc_unmask_edge_irq_nomask; 803 + meta_intc_level_chip.irq_mask = meta_intc_mask_irq_nomask; 804 + meta_intc_level_chip.irq_unmask = meta_intc_unmask_level_irq_nomask; 805 + } 806 + 807 + /** 808 + * init_external_IRQ() - initialise the external irq controller 809 + * 810 + * Set up the external irq controller using device tree properties. This is 811 + * called from init_IRQ(). 812 + */ 813 + int __init init_external_IRQ(void) 814 + { 815 + struct meta_intc_priv *priv = &meta_intc_priv; 816 + struct device_node *node; 817 + int ret, cpu; 818 + u32 val; 819 + bool no_masks = false; 820 + 821 + node = of_find_compatible_node(NULL, NULL, "img,meta-intc"); 822 + if (!node) 823 + return -ENOENT; 824 + 825 + /* Get number of banks */ 826 + ret = of_property_read_u32(node, "num-banks", &val); 827 + if (ret) { 828 + pr_err("meta-intc: No num-banks property found\n"); 829 + return ret; 830 + } 831 + if (val < 1 || val > 4) { 832 + pr_err("meta-intc: num-banks (%u) out of range\n", val); 833 + return -EINVAL; 834 + } 835 + priv->nr_banks = val; 836 + 837 + /* Are any mask registers present? */ 838 + if (of_get_property(node, "no-mask", NULL)) 839 + no_masks = true; 840 + 841 + /* No HWMASKEXT registers present? */ 842 + if (no_masks) 843 + meta_intc_no_mask(); 844 + 845 + /* Set up an IRQ domain */ 846 + /* 847 + * This is a legacy IRQ domain for now until all the platform setup code 848 + * has been converted to devicetree. 849 + */ 850 + priv->domain = irq_domain_add_linear(node, priv->nr_banks*32, 851 + &meta_intc_domain_ops, priv); 852 + if (unlikely(!priv->domain)) { 853 + pr_err("meta-intc: cannot add IRQ domain\n"); 854 + return -ENOMEM; 855 + } 856 + 857 + /* Setup TR2 for all cpus. */ 858 + for_each_possible_cpu(cpu) 859 + meta_intc_init_cpu(priv, cpu); 860 + 861 + /* Set up system suspend/resume callbacks */ 862 + meta_intc_init_syscore_ops(priv); 863 + 864 + pr_info("meta-intc: External IRQ controller initialised (%u IRQs)\n", 865 + priv->nr_banks*32); 866 + 867 + return 0; 868 + }
+343
drivers/irqchip/irq-metag.c
··· 1 + /* 2 + * Meta internal (HWSTATMETA) interrupt code. 3 + * 4 + * Copyright (C) 2011-2012 Imagination Technologies Ltd. 5 + * 6 + * This code is based on the code in SoC/common/irq.c and SoC/comet/irq.c 7 + * The code base could be generalised/merged as a lot of the functionality is 8 + * similar. Until this is done, we try to keep the code simple here. 9 + */ 10 + 11 + #include <linux/interrupt.h> 12 + #include <linux/io.h> 13 + #include <linux/irqdomain.h> 14 + 15 + #include <asm/irq.h> 16 + #include <asm/hwthread.h> 17 + 18 + #define PERF0VECINT 0x04820580 19 + #define PERF1VECINT 0x04820588 20 + #define PERF0TRIG_OFFSET 16 21 + #define PERF1TRIG_OFFSET 17 22 + 23 + /** 24 + * struct metag_internal_irq_priv - private meta internal interrupt data 25 + * @domain: IRQ domain for all internal Meta IRQs (HWSTATMETA) 26 + * @unmasked: Record of unmasked IRQs 27 + */ 28 + struct metag_internal_irq_priv { 29 + struct irq_domain *domain; 30 + 31 + unsigned long unmasked; 32 + }; 33 + 34 + /* Private data for the one and only internal interrupt controller */ 35 + static struct metag_internal_irq_priv metag_internal_irq_priv; 36 + 37 + static unsigned int metag_internal_irq_startup(struct irq_data *data); 38 + static void metag_internal_irq_shutdown(struct irq_data *data); 39 + static void metag_internal_irq_ack(struct irq_data *data); 40 + static void metag_internal_irq_mask(struct irq_data *data); 41 + static void metag_internal_irq_unmask(struct irq_data *data); 42 + #ifdef CONFIG_SMP 43 + static int metag_internal_irq_set_affinity(struct irq_data *data, 44 + const struct cpumask *cpumask, bool force); 45 + #endif 46 + 47 + static struct irq_chip internal_irq_edge_chip = { 48 + .name = "HWSTATMETA-IRQ", 49 + .irq_startup = metag_internal_irq_startup, 50 + .irq_shutdown = metag_internal_irq_shutdown, 51 + .irq_ack = metag_internal_irq_ack, 52 + .irq_mask = metag_internal_irq_mask, 53 + .irq_unmask = metag_internal_irq_unmask, 54 + #ifdef CONFIG_SMP 55 + .irq_set_affinity = metag_internal_irq_set_affinity, 56 + #endif 57 + }; 58 + 59 + /* 60 + * metag_hwvec_addr - get the address of *VECINT regs of irq 61 + * 62 + * This function is a table of supported triggers on HWSTATMETA 63 + * Could do with a structure, but better keep it simple. Changes 64 + * in this code should be rare. 65 + */ 66 + static inline void __iomem *metag_hwvec_addr(irq_hw_number_t hw) 67 + { 68 + void __iomem *addr; 69 + 70 + switch (hw) { 71 + case PERF0TRIG_OFFSET: 72 + addr = (void __iomem *)PERF0VECINT; 73 + break; 74 + case PERF1TRIG_OFFSET: 75 + addr = (void __iomem *)PERF1VECINT; 76 + break; 77 + default: 78 + addr = NULL; 79 + break; 80 + } 81 + return addr; 82 + } 83 + 84 + /* 85 + * metag_internal_startup - setup an internal irq 86 + * @irq: the irq to startup 87 + * 88 + * Multiplex interrupts for @irq onto TR1. Clear any pending 89 + * interrupts. 90 + */ 91 + static unsigned int metag_internal_irq_startup(struct irq_data *data) 92 + { 93 + /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ 94 + metag_internal_irq_ack(data); 95 + 96 + /* Enable the interrupt by unmasking it */ 97 + metag_internal_irq_unmask(data); 98 + 99 + return 0; 100 + } 101 + 102 + /* 103 + * metag_internal_irq_shutdown - turn off the irq 104 + * @irq: the irq number to turn off 105 + * 106 + * Mask @irq and clear any pending interrupts. 107 + * Stop muxing @irq onto TR1. 108 + */ 109 + static void metag_internal_irq_shutdown(struct irq_data *data) 110 + { 111 + /* Disable the IRQ at the core by masking it. */ 112 + metag_internal_irq_mask(data); 113 + 114 + /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ 115 + metag_internal_irq_ack(data); 116 + } 117 + 118 + /* 119 + * metag_internal_irq_ack - acknowledge irq 120 + * @irq: the irq to ack 121 + */ 122 + static void metag_internal_irq_ack(struct irq_data *data) 123 + { 124 + irq_hw_number_t hw = data->hwirq; 125 + unsigned int bit = 1 << hw; 126 + 127 + if (metag_in32(HWSTATMETA) & bit) 128 + metag_out32(bit, HWSTATMETA); 129 + } 130 + 131 + /** 132 + * metag_internal_irq_mask() - mask an internal irq by unvectoring 133 + * @data: data for the internal irq to mask 134 + * 135 + * HWSTATMETA has no mask register. Instead the IRQ is unvectored from the core 136 + * and retriggered if necessary later. 137 + */ 138 + static void metag_internal_irq_mask(struct irq_data *data) 139 + { 140 + struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 141 + irq_hw_number_t hw = data->hwirq; 142 + void __iomem *vec_addr = metag_hwvec_addr(hw); 143 + 144 + clear_bit(hw, &priv->unmasked); 145 + 146 + /* there is no interrupt mask, so unvector the interrupt */ 147 + metag_out32(0, vec_addr); 148 + } 149 + 150 + /** 151 + * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring 152 + * @data: data for the internal irq to unmask 153 + * 154 + * HWSTATMETA has no mask register. Instead the IRQ is revectored back to the 155 + * core and retriggered if necessary. 156 + */ 157 + static void metag_internal_irq_unmask(struct irq_data *data) 158 + { 159 + struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 160 + irq_hw_number_t hw = data->hwirq; 161 + unsigned int bit = 1 << hw; 162 + void __iomem *vec_addr = metag_hwvec_addr(hw); 163 + unsigned int thread = hard_processor_id(); 164 + 165 + set_bit(hw, &priv->unmasked); 166 + 167 + /* there is no interrupt mask, so revector the interrupt */ 168 + metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), vec_addr); 169 + 170 + /* 171 + * Re-trigger interrupt 172 + * 173 + * Writing a 1 toggles, and a 0->1 transition triggers. We only 174 + * retrigger if the status bit is already set, which means we 175 + * need to clear it first. Retriggering is fundamentally racy 176 + * because if the interrupt fires again after we clear it we 177 + * could end up clearing it again and the interrupt handler 178 + * thinking it hasn't fired. Therefore we need to keep trying to 179 + * retrigger until the bit is set. 180 + */ 181 + if (metag_in32(HWSTATMETA) & bit) { 182 + metag_out32(bit, HWSTATMETA); 183 + while (!(metag_in32(HWSTATMETA) & bit)) 184 + metag_out32(bit, HWSTATMETA); 185 + } 186 + } 187 + 188 + #ifdef CONFIG_SMP 189 + /* 190 + * metag_internal_irq_set_affinity - set the affinity for an interrupt 191 + */ 192 + static int metag_internal_irq_set_affinity(struct irq_data *data, 193 + const struct cpumask *cpumask, bool force) 194 + { 195 + unsigned int cpu, thread; 196 + irq_hw_number_t hw = data->hwirq; 197 + /* 198 + * Wire up this interrupt from *VECINT to the Meta core. 199 + * 200 + * Note that we can't wire up *VECINT to interrupt more than 201 + * one cpu (the interrupt code doesn't support it), so we just 202 + * pick the first cpu we find in 'cpumask'. 203 + */ 204 + cpu = cpumask_any(cpumask); 205 + thread = cpu_2_hwthread_id[cpu]; 206 + 207 + metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), 208 + metag_hwvec_addr(hw)); 209 + 210 + return 0; 211 + } 212 + #endif 213 + 214 + /* 215 + * metag_internal_irq_demux - irq de-multiplexer 216 + * @irq: the interrupt number 217 + * @desc: the interrupt description structure for this irq 218 + * 219 + * The cpu receives an interrupt on TR1 when an interrupt has 220 + * occurred. It is this function's job to demux this irq and 221 + * figure out exactly which trigger needs servicing. 222 + */ 223 + static void metag_internal_irq_demux(unsigned int irq, struct irq_desc *desc) 224 + { 225 + struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc); 226 + irq_hw_number_t hw; 227 + unsigned int irq_no; 228 + u32 status; 229 + 230 + recalculate: 231 + status = metag_in32(HWSTATMETA) & priv->unmasked; 232 + 233 + for (hw = 0; status != 0; status >>= 1, ++hw) { 234 + if (status & 0x1) { 235 + /* 236 + * Map the hardware IRQ number to a virtual Linux IRQ 237 + * number. 238 + */ 239 + irq_no = irq_linear_revmap(priv->domain, hw); 240 + 241 + /* 242 + * Only fire off interrupts that are 243 + * registered to be handled by the kernel. 244 + * Other interrupts are probably being 245 + * handled by other Meta hardware threads. 246 + */ 247 + generic_handle_irq(irq_no); 248 + 249 + /* 250 + * The handler may have re-enabled interrupts 251 + * which could have caused a nested invocation 252 + * of this code and make the copy of the 253 + * status register we are using invalid. 254 + */ 255 + goto recalculate; 256 + } 257 + } 258 + } 259 + 260 + /** 261 + * internal_irq_map() - Map an internal meta IRQ to a virtual IRQ number. 262 + * @hw: Number of the internal IRQ. Must be in range. 263 + * 264 + * Returns: The virtual IRQ number of the Meta internal IRQ specified by 265 + * @hw. 266 + */ 267 + int internal_irq_map(unsigned int hw) 268 + { 269 + struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 270 + if (!priv->domain) 271 + return -ENODEV; 272 + return irq_create_mapping(priv->domain, hw); 273 + } 274 + 275 + /** 276 + * metag_internal_irq_init_cpu - regsister with the Meta cpu 277 + * @cpu: the CPU to register on 278 + * 279 + * Configure @cpu's TR1 irq so that we can demux irqs. 280 + */ 281 + static void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv, 282 + int cpu) 283 + { 284 + unsigned int thread = cpu_2_hwthread_id[cpu]; 285 + unsigned int signum = TBID_SIGNUM_TR1(thread); 286 + int irq = tbisig_map(signum); 287 + 288 + /* Register the multiplexed IRQ handler */ 289 + irq_set_handler_data(irq, priv); 290 + irq_set_chained_handler(irq, metag_internal_irq_demux); 291 + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); 292 + } 293 + 294 + /** 295 + * metag_internal_intc_map() - map an internal irq 296 + * @d: irq domain of internal trigger block 297 + * @irq: virtual irq number 298 + * @hw: hardware irq number within internal trigger block 299 + * 300 + * This sets up a virtual irq for a specified hardware interrupt. The irq chip 301 + * and handler is configured. 302 + */ 303 + static int metag_internal_intc_map(struct irq_domain *d, unsigned int irq, 304 + irq_hw_number_t hw) 305 + { 306 + /* only register interrupt if it is mapped */ 307 + if (!metag_hwvec_addr(hw)) 308 + return -EINVAL; 309 + 310 + irq_set_chip_and_handler(irq, &internal_irq_edge_chip, 311 + handle_edge_irq); 312 + return 0; 313 + } 314 + 315 + static const struct irq_domain_ops metag_internal_intc_domain_ops = { 316 + .map = metag_internal_intc_map, 317 + }; 318 + 319 + /** 320 + * metag_internal_irq_register - register internal IRQs 321 + * 322 + * Register the irq chip and handler function for all internal IRQs 323 + */ 324 + int __init init_internal_IRQ(void) 325 + { 326 + struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 327 + unsigned int cpu; 328 + 329 + /* Set up an IRQ domain */ 330 + priv->domain = irq_domain_add_linear(NULL, 32, 331 + &metag_internal_intc_domain_ops, 332 + priv); 333 + if (unlikely(!priv->domain)) { 334 + pr_err("meta-internal-intc: cannot add IRQ domain\n"); 335 + return -ENOMEM; 336 + } 337 + 338 + /* Setup TR1 for all cpus. */ 339 + for_each_possible_cpu(cpu) 340 + metag_internal_irq_init_cpu(priv, cpu); 341 + 342 + return 0; 343 + };
+33
include/linux/irqchip/metag-ext.h
··· 1 + /* 2 + * Copyright (C) 2012 Imagination Technologies 3 + */ 4 + 5 + #ifndef _LINUX_IRQCHIP_METAG_EXT_H_ 6 + #define _LINUX_IRQCHIP_METAG_EXT_H_ 7 + 8 + struct irq_data; 9 + struct platform_device; 10 + 11 + /* called from core irq code at init */ 12 + int init_external_IRQ(void); 13 + 14 + /* 15 + * called from SoC init_irq() callback to dynamically indicate the lack of 16 + * HWMASKEXT registers. 17 + */ 18 + void meta_intc_no_mask(void); 19 + 20 + /* 21 + * These allow SoCs to specialise the interrupt controller from their init_irq 22 + * callbacks. 23 + */ 24 + 25 + extern struct irq_chip meta_intc_edge_chip; 26 + extern struct irq_chip meta_intc_level_chip; 27 + 28 + /* this should be called in the mask callback */ 29 + void meta_intc_mask_irq_simple(struct irq_data *data); 30 + /* this should be called in the unmask callback */ 31 + void meta_intc_unmask_irq_simple(struct irq_data *data); 32 + 33 + #endif /* _LINUX_IRQCHIP_METAG_EXT_H_ */
+24
include/linux/irqchip/metag.h
··· 1 + /* 2 + * Copyright (C) 2011 Imagination Technologies 3 + */ 4 + 5 + #ifndef _LINUX_IRQCHIP_METAG_H_ 6 + #define _LINUX_IRQCHIP_METAG_H_ 7 + 8 + #include <linux/errno.h> 9 + 10 + #ifdef CONFIG_METAG_PERFCOUNTER_IRQS 11 + extern int init_internal_IRQ(void); 12 + extern int internal_irq_map(unsigned int hw); 13 + #else 14 + static inline int init_internal_IRQ(void) 15 + { 16 + return 0; 17 + } 18 + static inline int internal_irq_map(unsigned int hw) 19 + { 20 + return -EINVAL; 21 + } 22 + #endif 23 + 24 + #endif /* _LINUX_IRQCHIP_METAG_H_ */