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

[POWERPC] Add arch/powerpc driver for UIC, PPC4xx interrupt controller

This patch adds a driver to arch/powerpc/sysdev for the UIC, the
on-chip interrupt controller from IBM/AMCC 4xx chips. It uses the new
irq host mapping infrastructure.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>

authored by

David Gibson and committed by
Paul Mackerras
e58923ed f6557331

+366
+1
arch/powerpc/sysdev/Makefile
··· 21 21 ifeq ($(CONFIG_PPC_MERGE),y) 22 22 obj-$(CONFIG_PPC_I8259) += i8259.o 23 23 obj-$(CONFIG_PPC_83xx) += ipic.o 24 + obj-$(CONFIG_4xx) += uic.o 24 25 endif 25 26 26 27 # Temporary hack until we have migrated to asm-powerpc
+342
arch/powerpc/sysdev/uic.c
··· 1 + /* 2 + * arch/powerpc/sysdev/uic.c 3 + * 4 + * IBM PowerPC 4xx Universal Interrupt Controller 5 + * 6 + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation. 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms of the GNU General Public License as published by the 10 + * Free Software Foundation; either version 2 of the License, or (at your 11 + * option) any later version. 12 + */ 13 + #include <linux/kernel.h> 14 + #include <linux/init.h> 15 + #include <linux/errno.h> 16 + #include <linux/reboot.h> 17 + #include <linux/slab.h> 18 + #include <linux/stddef.h> 19 + #include <linux/sched.h> 20 + #include <linux/signal.h> 21 + #include <linux/sysdev.h> 22 + #include <linux/device.h> 23 + #include <linux/bootmem.h> 24 + #include <linux/spinlock.h> 25 + #include <linux/irq.h> 26 + #include <linux/interrupt.h> 27 + #include <asm/irq.h> 28 + #include <asm/io.h> 29 + #include <asm/prom.h> 30 + #include <asm/dcr.h> 31 + 32 + #define NR_UIC_INTS 32 33 + 34 + #define UIC_SR 0x0 35 + #define UIC_ER 0x2 36 + #define UIC_CR 0x3 37 + #define UIC_PR 0x4 38 + #define UIC_TR 0x5 39 + #define UIC_MSR 0x6 40 + #define UIC_VR 0x7 41 + #define UIC_VCR 0x8 42 + 43 + #define uic_irq_to_hw(virq) (irq_map[virq].hwirq) 44 + 45 + struct uic *primary_uic; 46 + 47 + struct uic { 48 + int index; 49 + int dcrbase; 50 + 51 + spinlock_t lock; 52 + 53 + /* The remapper for this UIC */ 54 + struct irq_host *irqhost; 55 + 56 + /* For secondary UICs, the cascade interrupt's irqaction */ 57 + struct irqaction cascade; 58 + 59 + /* The device node of the interrupt controller */ 60 + struct device_node *of_node; 61 + }; 62 + 63 + static void uic_unmask_irq(unsigned int virq) 64 + { 65 + struct uic *uic = get_irq_chip_data(virq); 66 + unsigned int src = uic_irq_to_hw(virq); 67 + unsigned long flags; 68 + u32 er; 69 + 70 + spin_lock_irqsave(&uic->lock, flags); 71 + er = mfdcr(uic->dcrbase + UIC_ER); 72 + er |= 1 << (31 - src); 73 + mtdcr(uic->dcrbase + UIC_ER, er); 74 + spin_unlock_irqrestore(&uic->lock, flags); 75 + } 76 + 77 + static void uic_mask_irq(unsigned int virq) 78 + { 79 + struct uic *uic = get_irq_chip_data(virq); 80 + unsigned int src = uic_irq_to_hw(virq); 81 + unsigned long flags; 82 + u32 er; 83 + 84 + spin_lock_irqsave(&uic->lock, flags); 85 + er = mfdcr(uic->dcrbase + UIC_ER); 86 + er &= ~(1 << (31 - src)); 87 + mtdcr(uic->dcrbase + UIC_ER, er); 88 + spin_unlock_irqrestore(&uic->lock, flags); 89 + } 90 + 91 + static void uic_ack_irq(unsigned int virq) 92 + { 93 + struct uic *uic = get_irq_chip_data(virq); 94 + unsigned int src = uic_irq_to_hw(virq); 95 + unsigned long flags; 96 + 97 + spin_lock_irqsave(&uic->lock, flags); 98 + mtdcr(uic->dcrbase + UIC_SR, 1 << (31-src)); 99 + spin_unlock_irqrestore(&uic->lock, flags); 100 + } 101 + 102 + static int uic_set_irq_type(unsigned int virq, unsigned int flow_type) 103 + { 104 + struct uic *uic = get_irq_chip_data(virq); 105 + unsigned int src = uic_irq_to_hw(virq); 106 + struct irq_desc *desc = get_irq_desc(virq); 107 + unsigned long flags; 108 + int trigger, polarity; 109 + u32 tr, pr, mask; 110 + 111 + switch (flow_type & IRQ_TYPE_SENSE_MASK) { 112 + case IRQ_TYPE_NONE: 113 + uic_mask_irq(virq); 114 + return 0; 115 + 116 + case IRQ_TYPE_EDGE_RISING: 117 + trigger = 1; polarity = 1; 118 + break; 119 + case IRQ_TYPE_EDGE_FALLING: 120 + trigger = 1; polarity = 0; 121 + break; 122 + case IRQ_TYPE_LEVEL_HIGH: 123 + trigger = 0; polarity = 1; 124 + break; 125 + case IRQ_TYPE_LEVEL_LOW: 126 + trigger = 0; polarity = 0; 127 + break; 128 + default: 129 + return -EINVAL; 130 + } 131 + 132 + mask = ~(1 << (31 - src)); 133 + 134 + spin_lock_irqsave(&uic->lock, flags); 135 + tr = mfdcr(uic->dcrbase + UIC_TR); 136 + pr = mfdcr(uic->dcrbase + UIC_PR); 137 + tr = (tr & mask) | (trigger << (31-src)); 138 + pr = (pr & mask) | (polarity << (31-src)); 139 + 140 + mtdcr(uic->dcrbase + UIC_PR, pr); 141 + mtdcr(uic->dcrbase + UIC_TR, tr); 142 + 143 + desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); 144 + desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; 145 + if (trigger) 146 + desc->status |= IRQ_LEVEL; 147 + 148 + spin_unlock_irqrestore(&uic->lock, flags); 149 + 150 + return 0; 151 + } 152 + 153 + static struct irq_chip uic_irq_chip = { 154 + .typename = " UIC ", 155 + .unmask = uic_unmask_irq, 156 + .mask = uic_mask_irq, 157 + /* .mask_ack = uic_mask_irq_and_ack, */ 158 + .ack = uic_ack_irq, 159 + .set_type = uic_set_irq_type, 160 + }; 161 + 162 + static int uic_host_match(struct irq_host *h, struct device_node *node) 163 + { 164 + struct uic *uic = h->host_data; 165 + return uic->of_node == node; 166 + } 167 + 168 + static int uic_host_map(struct irq_host *h, unsigned int virq, 169 + irq_hw_number_t hw) 170 + { 171 + struct uic *uic = h->host_data; 172 + 173 + set_irq_chip_data(virq, uic); 174 + /* Despite the name, handle_level_irq() works for both level 175 + * and edge irqs on UIC. FIXME: check this is correct */ 176 + set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq); 177 + 178 + /* Set default irq type */ 179 + set_irq_type(virq, IRQ_TYPE_NONE); 180 + 181 + return 0; 182 + } 183 + 184 + static int uic_host_xlate(struct irq_host *h, struct device_node *ct, 185 + u32 *intspec, unsigned int intsize, 186 + irq_hw_number_t *out_hwirq, unsigned int *out_type) 187 + 188 + { 189 + /* UIC intspecs must have 2 cells */ 190 + BUG_ON(intsize != 2); 191 + *out_hwirq = intspec[0]; 192 + *out_type = intspec[1]; 193 + return 0; 194 + } 195 + 196 + static struct irq_host_ops uic_host_ops = { 197 + .match = uic_host_match, 198 + .map = uic_host_map, 199 + .xlate = uic_host_xlate, 200 + }; 201 + 202 + irqreturn_t uic_cascade(int virq, void *data) 203 + { 204 + struct uic *uic = data; 205 + u32 msr; 206 + int src; 207 + int subvirq; 208 + 209 + msr = mfdcr(uic->dcrbase + UIC_MSR); 210 + src = 32 - ffs(msr); 211 + 212 + subvirq = irq_linear_revmap(uic->irqhost, src); 213 + generic_handle_irq(subvirq); 214 + 215 + return IRQ_HANDLED; 216 + } 217 + 218 + static struct uic * __init uic_init_one(struct device_node *node) 219 + { 220 + struct uic *uic; 221 + const u32 *indexp, *dcrreg; 222 + int len; 223 + 224 + BUG_ON(! device_is_compatible(node, "ibm,uic")); 225 + 226 + uic = alloc_bootmem(sizeof(*uic)); 227 + if (! uic) 228 + return NULL; /* FIXME: panic? */ 229 + 230 + memset(uic, 0, sizeof(*uic)); 231 + spin_lock_init(&uic->lock); 232 + uic->of_node = of_node_get(node); 233 + indexp = get_property(node, "cell-index", &len); 234 + if (!indexp || (len != sizeof(u32))) { 235 + printk(KERN_ERR "uic: Device node %s has missing or invalid " 236 + "cell-index property\n", node->full_name); 237 + return NULL; 238 + } 239 + uic->index = *indexp; 240 + 241 + dcrreg = get_property(node, "dcr-reg", &len); 242 + if (!dcrreg || (len != 2*sizeof(u32))) { 243 + printk(KERN_ERR "uic: Device node %s has missing or invalid " 244 + "dcr-reg property\n", node->full_name); 245 + return NULL; 246 + } 247 + uic->dcrbase = *dcrreg; 248 + 249 + uic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, NR_UIC_INTS, 250 + &uic_host_ops, -1); 251 + if (! uic->irqhost) { 252 + of_node_put(node); 253 + return NULL; /* FIXME: panic? */ 254 + } 255 + 256 + uic->irqhost->host_data = uic; 257 + 258 + /* Start with all interrupts disabled, level and non-critical */ 259 + mtdcr(uic->dcrbase + UIC_ER, 0); 260 + mtdcr(uic->dcrbase + UIC_CR, 0); 261 + mtdcr(uic->dcrbase + UIC_TR, 0); 262 + /* Clear any pending interrupts, in case the firmware left some */ 263 + mtdcr(uic->dcrbase + UIC_SR, 0xffffffff); 264 + 265 + printk ("UIC%d (%d IRQ sources) at DCR 0x%x\n", uic->index, 266 + NR_UIC_INTS, uic->dcrbase); 267 + 268 + return uic; 269 + } 270 + 271 + void __init uic_init_tree(void) 272 + { 273 + struct device_node *np; 274 + struct uic *uic; 275 + const u32 *interrupts; 276 + 277 + /* First locate and initialize the top-level UIC */ 278 + 279 + np = of_find_compatible_node(NULL, NULL, "ibm,uic"); 280 + while (np) { 281 + interrupts = get_property(np, "interrupts", NULL); 282 + if (! interrupts) 283 + break; 284 + 285 + np = of_find_compatible_node(np, NULL, "ibm,uic"); 286 + } 287 + 288 + BUG_ON(!np); /* uic_init_tree() assumes there's a UIC as the 289 + * top-level interrupt controller */ 290 + primary_uic = uic_init_one(np); 291 + if (! primary_uic) 292 + panic("Unable to initialize primary UIC %s\n", np->full_name); 293 + 294 + irq_set_default_host(primary_uic->irqhost); 295 + of_node_put(np); 296 + 297 + /* The scan again for cascaded UICs */ 298 + np = of_find_compatible_node(NULL, NULL, "ibm,uic"); 299 + while (np) { 300 + interrupts = get_property(np, "interrupts", NULL); 301 + if (interrupts) { 302 + /* Secondary UIC */ 303 + int cascade_virq; 304 + int ret; 305 + 306 + uic = uic_init_one(np); 307 + if (! uic) 308 + panic("Unable to initialize a secondary UIC %s\n", 309 + np->full_name); 310 + 311 + cascade_virq = irq_of_parse_and_map(np, 0); 312 + 313 + uic->cascade.handler = uic_cascade; 314 + uic->cascade.name = "UIC cascade"; 315 + uic->cascade.dev_id = uic; 316 + 317 + ret = setup_irq(cascade_virq, &uic->cascade); 318 + if (ret) 319 + printk(KERN_ERR "Failed to setup_irq(%d) for " 320 + "UIC%d cascade\n", cascade_virq, 321 + uic->index); 322 + 323 + /* FIXME: setup critical cascade?? */ 324 + } 325 + 326 + np = of_find_compatible_node(np, NULL, "ibm,uic"); 327 + } 328 + } 329 + 330 + /* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ 331 + unsigned int uic_get_irq(void) 332 + { 333 + u32 msr; 334 + int src; 335 + 336 + BUG_ON(! primary_uic); 337 + 338 + msr = mfdcr(primary_uic->dcrbase + UIC_MSR); 339 + src = 32 - ffs(msr); 340 + 341 + return irq_linear_revmap(primary_uic->irqhost, src); 342 + }
+23
include/asm-powerpc/uic.h
··· 1 + /* 2 + * include/asm-powerpc/uic.h 3 + * 4 + * IBM PPC4xx UIC external definitions and structure. 5 + * 6 + * Maintainer: David Gibson <dwg@au1.ibm.com> 7 + * Copyright 2007 IBM Corporation. 8 + * 9 + * This program is free software; you can redistribute it and/or modify it 10 + * under the terms of the GNU General Public License as published by the 11 + * Free Software Foundation; either version 2 of the License, or (at your 12 + * option) any later version. 13 + */ 14 + #ifndef _ASM_POWERPC_UIC_H 15 + #define _ASM_POWERPC_UIC_H 16 + 17 + #ifdef __KERNEL__ 18 + 19 + extern void __init uic_init_tree(void); 20 + extern unsigned int uic_get_irq(void); 21 + 22 + #endif /* __KERNEL__ */ 23 + #endif /* _ASM_POWERPC_UIC_H */