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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30-rc1 828 lines 21 kB view raw
1/* 2 * Shared interrupt handling code for IPR and INTC2 types of IRQs. 3 * 4 * Copyright (C) 2007, 2008 Magnus Damm 5 * 6 * Based on intc2.c and ipr.c 7 * 8 * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi 9 * Copyright (C) 2000 Kazumoto Kojima 10 * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) 11 * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> 12 * Copyright (C) 2005, 2006 Paul Mundt 13 * 14 * This file is subject to the terms and conditions of the GNU General Public 15 * License. See the file "COPYING" in the main directory of this archive 16 * for more details. 17 */ 18#include <linux/init.h> 19#include <linux/irq.h> 20#include <linux/module.h> 21#include <linux/io.h> 22#include <linux/interrupt.h> 23#include <linux/bootmem.h> 24#include <linux/sh_intc.h> 25#include <linux/sysdev.h> 26#include <linux/list.h> 27 28#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ 29 ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ 30 ((addr_e) << 16) | ((addr_d << 24))) 31 32#define _INTC_SHIFT(h) (h & 0x1f) 33#define _INTC_WIDTH(h) ((h >> 5) & 0xf) 34#define _INTC_FN(h) ((h >> 9) & 0xf) 35#define _INTC_MODE(h) ((h >> 13) & 0x7) 36#define _INTC_ADDR_E(h) ((h >> 16) & 0xff) 37#define _INTC_ADDR_D(h) ((h >> 24) & 0xff) 38 39struct intc_handle_int { 40 unsigned int irq; 41 unsigned long handle; 42}; 43 44struct intc_desc_int { 45 struct list_head list; 46 struct sys_device sysdev; 47 unsigned long *reg; 48#ifdef CONFIG_SMP 49 unsigned long *smp; 50#endif 51 unsigned int nr_reg; 52 struct intc_handle_int *prio; 53 unsigned int nr_prio; 54 struct intc_handle_int *sense; 55 unsigned int nr_sense; 56 struct irq_chip chip; 57}; 58 59static LIST_HEAD(intc_list); 60 61#ifdef CONFIG_SMP 62#define IS_SMP(x) x.smp 63#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) 64#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1) 65#else 66#define IS_SMP(x) 0 67#define INTC_REG(d, x, c) (d->reg[(x)]) 68#define SMP_NR(d, x) 1 69#endif 70 71static unsigned int intc_prio_level[NR_IRQS]; /* for now */ 72#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) 73static unsigned long ack_handle[NR_IRQS]; 74#endif 75 76static inline struct intc_desc_int *get_intc_desc(unsigned int irq) 77{ 78 struct irq_chip *chip = get_irq_chip(irq); 79 return (void *)((char *)chip - offsetof(struct intc_desc_int, chip)); 80} 81 82static inline unsigned int set_field(unsigned int value, 83 unsigned int field_value, 84 unsigned int handle) 85{ 86 unsigned int width = _INTC_WIDTH(handle); 87 unsigned int shift = _INTC_SHIFT(handle); 88 89 value &= ~(((1 << width) - 1) << shift); 90 value |= field_value << shift; 91 return value; 92} 93 94static void write_8(unsigned long addr, unsigned long h, unsigned long data) 95{ 96 __raw_writeb(set_field(0, data, h), addr); 97} 98 99static void write_16(unsigned long addr, unsigned long h, unsigned long data) 100{ 101 __raw_writew(set_field(0, data, h), addr); 102} 103 104static void write_32(unsigned long addr, unsigned long h, unsigned long data) 105{ 106 __raw_writel(set_field(0, data, h), addr); 107} 108 109static void modify_8(unsigned long addr, unsigned long h, unsigned long data) 110{ 111 unsigned long flags; 112 local_irq_save(flags); 113 __raw_writeb(set_field(__raw_readb(addr), data, h), addr); 114 local_irq_restore(flags); 115} 116 117static void modify_16(unsigned long addr, unsigned long h, unsigned long data) 118{ 119 unsigned long flags; 120 local_irq_save(flags); 121 __raw_writew(set_field(__raw_readw(addr), data, h), addr); 122 local_irq_restore(flags); 123} 124 125static void modify_32(unsigned long addr, unsigned long h, unsigned long data) 126{ 127 unsigned long flags; 128 local_irq_save(flags); 129 __raw_writel(set_field(__raw_readl(addr), data, h), addr); 130 local_irq_restore(flags); 131} 132 133enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 }; 134 135static void (*intc_reg_fns[])(unsigned long addr, 136 unsigned long h, 137 unsigned long data) = { 138 [REG_FN_WRITE_BASE + 0] = write_8, 139 [REG_FN_WRITE_BASE + 1] = write_16, 140 [REG_FN_WRITE_BASE + 3] = write_32, 141 [REG_FN_MODIFY_BASE + 0] = modify_8, 142 [REG_FN_MODIFY_BASE + 1] = modify_16, 143 [REG_FN_MODIFY_BASE + 3] = modify_32, 144}; 145 146enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ 147 MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */ 148 MODE_DUAL_REG, /* Two registers, set bit to enable / disable */ 149 MODE_PRIO_REG, /* Priority value written to enable interrupt */ 150 MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ 151}; 152 153static void intc_mode_field(unsigned long addr, 154 unsigned long handle, 155 void (*fn)(unsigned long, 156 unsigned long, 157 unsigned long), 158 unsigned int irq) 159{ 160 fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); 161} 162 163static void intc_mode_zero(unsigned long addr, 164 unsigned long handle, 165 void (*fn)(unsigned long, 166 unsigned long, 167 unsigned long), 168 unsigned int irq) 169{ 170 fn(addr, handle, 0); 171} 172 173static void intc_mode_prio(unsigned long addr, 174 unsigned long handle, 175 void (*fn)(unsigned long, 176 unsigned long, 177 unsigned long), 178 unsigned int irq) 179{ 180 fn(addr, handle, intc_prio_level[irq]); 181} 182 183static void (*intc_enable_fns[])(unsigned long addr, 184 unsigned long handle, 185 void (*fn)(unsigned long, 186 unsigned long, 187 unsigned long), 188 unsigned int irq) = { 189 [MODE_ENABLE_REG] = intc_mode_field, 190 [MODE_MASK_REG] = intc_mode_zero, 191 [MODE_DUAL_REG] = intc_mode_field, 192 [MODE_PRIO_REG] = intc_mode_prio, 193 [MODE_PCLR_REG] = intc_mode_prio, 194}; 195 196static void (*intc_disable_fns[])(unsigned long addr, 197 unsigned long handle, 198 void (*fn)(unsigned long, 199 unsigned long, 200 unsigned long), 201 unsigned int irq) = { 202 [MODE_ENABLE_REG] = intc_mode_zero, 203 [MODE_MASK_REG] = intc_mode_field, 204 [MODE_DUAL_REG] = intc_mode_field, 205 [MODE_PRIO_REG] = intc_mode_zero, 206 [MODE_PCLR_REG] = intc_mode_field, 207}; 208 209static inline void _intc_enable(unsigned int irq, unsigned long handle) 210{ 211 struct intc_desc_int *d = get_intc_desc(irq); 212 unsigned long addr; 213 unsigned int cpu; 214 215 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { 216 addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); 217 intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ 218 [_INTC_FN(handle)], irq); 219 } 220} 221 222static void intc_enable(unsigned int irq) 223{ 224 _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); 225} 226 227static void intc_disable(unsigned int irq) 228{ 229 struct intc_desc_int *d = get_intc_desc(irq); 230 unsigned long handle = (unsigned long) get_irq_chip_data(irq); 231 unsigned long addr; 232 unsigned int cpu; 233 234 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { 235 addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); 236 intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ 237 [_INTC_FN(handle)], irq); 238 } 239} 240 241static int intc_set_wake(unsigned int irq, unsigned int on) 242{ 243 return 0; /* allow wakeup, but setup hardware in intc_suspend() */ 244} 245 246#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) 247static void intc_mask_ack(unsigned int irq) 248{ 249 struct intc_desc_int *d = get_intc_desc(irq); 250 unsigned long handle = ack_handle[irq]; 251 unsigned long addr; 252 253 intc_disable(irq); 254 255 /* read register and write zero only to the assocaited bit */ 256 257 if (handle) { 258 addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); 259 switch (_INTC_FN(handle)) { 260 case REG_FN_MODIFY_BASE + 0: /* 8bit */ 261 __raw_readb(addr); 262 __raw_writeb(0xff ^ set_field(0, 1, handle), addr); 263 break; 264 case REG_FN_MODIFY_BASE + 1: /* 16bit */ 265 __raw_readw(addr); 266 __raw_writew(0xffff ^ set_field(0, 1, handle), addr); 267 break; 268 case REG_FN_MODIFY_BASE + 3: /* 32bit */ 269 __raw_readl(addr); 270 __raw_writel(0xffffffff ^ set_field(0, 1, handle), addr); 271 break; 272 default: 273 BUG(); 274 break; 275 } 276 } 277} 278#endif 279 280static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, 281 unsigned int nr_hp, 282 unsigned int irq) 283{ 284 int i; 285 286 /* this doesn't scale well, but... 287 * 288 * this function should only be used for cerain uncommon 289 * operations such as intc_set_priority() and intc_set_sense() 290 * and in those rare cases performance doesn't matter that much. 291 * keeping the memory footprint low is more important. 292 * 293 * one rather simple way to speed this up and still keep the 294 * memory footprint down is to make sure the array is sorted 295 * and then perform a bisect to lookup the irq. 296 */ 297 298 for (i = 0; i < nr_hp; i++) { 299 if ((hp + i)->irq != irq) 300 continue; 301 302 return hp + i; 303 } 304 305 return NULL; 306} 307 308int intc_set_priority(unsigned int irq, unsigned int prio) 309{ 310 struct intc_desc_int *d = get_intc_desc(irq); 311 struct intc_handle_int *ihp; 312 313 if (!intc_prio_level[irq] || prio <= 1) 314 return -EINVAL; 315 316 ihp = intc_find_irq(d->prio, d->nr_prio, irq); 317 if (ihp) { 318 if (prio >= (1 << _INTC_WIDTH(ihp->handle))) 319 return -EINVAL; 320 321 intc_prio_level[irq] = prio; 322 323 /* 324 * only set secondary masking method directly 325 * primary masking method is using intc_prio_level[irq] 326 * priority level will be set during next enable() 327 */ 328 329 if (_INTC_FN(ihp->handle) != REG_FN_ERR) 330 _intc_enable(irq, ihp->handle); 331 } 332 return 0; 333} 334 335#define VALID(x) (x | 0x80) 336 337static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { 338 [IRQ_TYPE_EDGE_FALLING] = VALID(0), 339 [IRQ_TYPE_EDGE_RISING] = VALID(1), 340 [IRQ_TYPE_LEVEL_LOW] = VALID(2), 341 /* SH7706, SH7707 and SH7709 do not support high level triggered */ 342#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ 343 !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ 344 !defined(CONFIG_CPU_SUBTYPE_SH7709) 345 [IRQ_TYPE_LEVEL_HIGH] = VALID(3), 346#endif 347}; 348 349static int intc_set_sense(unsigned int irq, unsigned int type) 350{ 351 struct intc_desc_int *d = get_intc_desc(irq); 352 unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; 353 struct intc_handle_int *ihp; 354 unsigned long addr; 355 356 if (!value) 357 return -EINVAL; 358 359 ihp = intc_find_irq(d->sense, d->nr_sense, irq); 360 if (ihp) { 361 addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); 362 intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); 363 } 364 return 0; 365} 366 367static unsigned int __init intc_get_reg(struct intc_desc_int *d, 368 unsigned long address) 369{ 370 unsigned int k; 371 372 for (k = 0; k < d->nr_reg; k++) { 373 if (d->reg[k] == address) 374 return k; 375 } 376 377 BUG(); 378 return 0; 379} 380 381static intc_enum __init intc_grp_id(struct intc_desc *desc, 382 intc_enum enum_id) 383{ 384 struct intc_group *g = desc->groups; 385 unsigned int i, j; 386 387 for (i = 0; g && enum_id && i < desc->nr_groups; i++) { 388 g = desc->groups + i; 389 390 for (j = 0; g->enum_ids[j]; j++) { 391 if (g->enum_ids[j] != enum_id) 392 continue; 393 394 return g->enum_id; 395 } 396 } 397 398 return 0; 399} 400 401static unsigned int __init intc_mask_data(struct intc_desc *desc, 402 struct intc_desc_int *d, 403 intc_enum enum_id, int do_grps) 404{ 405 struct intc_mask_reg *mr = desc->mask_regs; 406 unsigned int i, j, fn, mode; 407 unsigned long reg_e, reg_d; 408 409 for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) { 410 mr = desc->mask_regs + i; 411 412 for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { 413 if (mr->enum_ids[j] != enum_id) 414 continue; 415 416 if (mr->set_reg && mr->clr_reg) { 417 fn = REG_FN_WRITE_BASE; 418 mode = MODE_DUAL_REG; 419 reg_e = mr->clr_reg; 420 reg_d = mr->set_reg; 421 } else { 422 fn = REG_FN_MODIFY_BASE; 423 if (mr->set_reg) { 424 mode = MODE_ENABLE_REG; 425 reg_e = mr->set_reg; 426 reg_d = mr->set_reg; 427 } else { 428 mode = MODE_MASK_REG; 429 reg_e = mr->clr_reg; 430 reg_d = mr->clr_reg; 431 } 432 } 433 434 fn += (mr->reg_width >> 3) - 1; 435 return _INTC_MK(fn, mode, 436 intc_get_reg(d, reg_e), 437 intc_get_reg(d, reg_d), 438 1, 439 (mr->reg_width - 1) - j); 440 } 441 } 442 443 if (do_grps) 444 return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0); 445 446 return 0; 447} 448 449static unsigned int __init intc_prio_data(struct intc_desc *desc, 450 struct intc_desc_int *d, 451 intc_enum enum_id, int do_grps) 452{ 453 struct intc_prio_reg *pr = desc->prio_regs; 454 unsigned int i, j, fn, mode, bit; 455 unsigned long reg_e, reg_d; 456 457 for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) { 458 pr = desc->prio_regs + i; 459 460 for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) { 461 if (pr->enum_ids[j] != enum_id) 462 continue; 463 464 if (pr->set_reg && pr->clr_reg) { 465 fn = REG_FN_WRITE_BASE; 466 mode = MODE_PCLR_REG; 467 reg_e = pr->set_reg; 468 reg_d = pr->clr_reg; 469 } else { 470 fn = REG_FN_MODIFY_BASE; 471 mode = MODE_PRIO_REG; 472 if (!pr->set_reg) 473 BUG(); 474 reg_e = pr->set_reg; 475 reg_d = pr->set_reg; 476 } 477 478 fn += (pr->reg_width >> 3) - 1; 479 480 BUG_ON((j + 1) * pr->field_width > pr->reg_width); 481 482 bit = pr->reg_width - ((j + 1) * pr->field_width); 483 484 return _INTC_MK(fn, mode, 485 intc_get_reg(d, reg_e), 486 intc_get_reg(d, reg_d), 487 pr->field_width, bit); 488 } 489 } 490 491 if (do_grps) 492 return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0); 493 494 return 0; 495} 496 497#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) 498static unsigned int __init intc_ack_data(struct intc_desc *desc, 499 struct intc_desc_int *d, 500 intc_enum enum_id) 501{ 502 struct intc_mask_reg *mr = desc->ack_regs; 503 unsigned int i, j, fn, mode; 504 unsigned long reg_e, reg_d; 505 506 for (i = 0; mr && enum_id && i < desc->nr_ack_regs; i++) { 507 mr = desc->ack_regs + i; 508 509 for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { 510 if (mr->enum_ids[j] != enum_id) 511 continue; 512 513 fn = REG_FN_MODIFY_BASE; 514 mode = MODE_ENABLE_REG; 515 reg_e = mr->set_reg; 516 reg_d = mr->set_reg; 517 518 fn += (mr->reg_width >> 3) - 1; 519 return _INTC_MK(fn, mode, 520 intc_get_reg(d, reg_e), 521 intc_get_reg(d, reg_d), 522 1, 523 (mr->reg_width - 1) - j); 524 } 525 } 526 527 return 0; 528} 529#endif 530 531static unsigned int __init intc_sense_data(struct intc_desc *desc, 532 struct intc_desc_int *d, 533 intc_enum enum_id) 534{ 535 struct intc_sense_reg *sr = desc->sense_regs; 536 unsigned int i, j, fn, bit; 537 538 for (i = 0; sr && enum_id && i < desc->nr_sense_regs; i++) { 539 sr = desc->sense_regs + i; 540 541 for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { 542 if (sr->enum_ids[j] != enum_id) 543 continue; 544 545 fn = REG_FN_MODIFY_BASE; 546 fn += (sr->reg_width >> 3) - 1; 547 548 BUG_ON((j + 1) * sr->field_width > sr->reg_width); 549 550 bit = sr->reg_width - ((j + 1) * sr->field_width); 551 552 return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), 553 0, sr->field_width, bit); 554 } 555 } 556 557 return 0; 558} 559 560static void __init intc_register_irq(struct intc_desc *desc, 561 struct intc_desc_int *d, 562 intc_enum enum_id, 563 unsigned int irq) 564{ 565 struct intc_handle_int *hp; 566 unsigned int data[2], primary; 567 568 /* Prefer single interrupt source bitmap over other combinations: 569 * 1. bitmap, single interrupt source 570 * 2. priority, single interrupt source 571 * 3. bitmap, multiple interrupt sources (groups) 572 * 4. priority, multiple interrupt sources (groups) 573 */ 574 575 data[0] = intc_mask_data(desc, d, enum_id, 0); 576 data[1] = intc_prio_data(desc, d, enum_id, 0); 577 578 primary = 0; 579 if (!data[0] && data[1]) 580 primary = 1; 581 582 if (!data[0] && !data[1]) 583 pr_warning("intc: missing unique irq mask for " 584 "irq %d (vect 0x%04x)\n", irq, irq2evt(irq)); 585 586 data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1); 587 data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1); 588 589 if (!data[primary]) 590 primary ^= 1; 591 592 BUG_ON(!data[primary]); /* must have primary masking method */ 593 594 disable_irq_nosync(irq); 595 set_irq_chip_and_handler_name(irq, &d->chip, 596 handle_level_irq, "level"); 597 set_irq_chip_data(irq, (void *)data[primary]); 598 599 /* set priority level 600 * - this needs to be at least 2 for 5-bit priorities on 7780 601 */ 602 intc_prio_level[irq] = 2; 603 604 /* enable secondary masking method if present */ 605 if (data[!primary]) 606 _intc_enable(irq, data[!primary]); 607 608 /* add irq to d->prio list if priority is available */ 609 if (data[1]) { 610 hp = d->prio + d->nr_prio; 611 hp->irq = irq; 612 hp->handle = data[1]; 613 614 if (primary) { 615 /* 616 * only secondary priority should access registers, so 617 * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() 618 */ 619 620 hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); 621 hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); 622 } 623 d->nr_prio++; 624 } 625 626 /* add irq to d->sense list if sense is available */ 627 data[0] = intc_sense_data(desc, d, enum_id); 628 if (data[0]) { 629 (d->sense + d->nr_sense)->irq = irq; 630 (d->sense + d->nr_sense)->handle = data[0]; 631 d->nr_sense++; 632 } 633 634 /* irq should be disabled by default */ 635 d->chip.mask(irq); 636 637#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) 638 if (desc->ack_regs) 639 ack_handle[irq] = intc_ack_data(desc, d, enum_id); 640#endif 641} 642 643static unsigned int __init save_reg(struct intc_desc_int *d, 644 unsigned int cnt, 645 unsigned long value, 646 unsigned int smp) 647{ 648 if (value) { 649 d->reg[cnt] = value; 650#ifdef CONFIG_SMP 651 d->smp[cnt] = smp; 652#endif 653 return 1; 654 } 655 656 return 0; 657} 658 659static unsigned char *intc_evt2irq_table; 660 661unsigned int intc_evt2irq(unsigned int vector) 662{ 663 unsigned int irq = evt2irq(vector); 664 665 if (intc_evt2irq_table && intc_evt2irq_table[irq]) 666 irq = intc_evt2irq_table[irq]; 667 668 return irq; 669} 670 671void __init register_intc_controller(struct intc_desc *desc) 672{ 673 unsigned int i, k, smp; 674 struct intc_desc_int *d; 675 676 d = alloc_bootmem(sizeof(*d)); 677 678 INIT_LIST_HEAD(&d->list); 679 list_add(&d->list, &intc_list); 680 681 d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0; 682 d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0; 683 d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0; 684 685#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) 686 d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0; 687#endif 688 d->reg = alloc_bootmem(d->nr_reg * sizeof(*d->reg)); 689#ifdef CONFIG_SMP 690 d->smp = alloc_bootmem(d->nr_reg * sizeof(*d->smp)); 691#endif 692 k = 0; 693 694 if (desc->mask_regs) { 695 for (i = 0; i < desc->nr_mask_regs; i++) { 696 smp = IS_SMP(desc->mask_regs[i]); 697 k += save_reg(d, k, desc->mask_regs[i].set_reg, smp); 698 k += save_reg(d, k, desc->mask_regs[i].clr_reg, smp); 699 } 700 } 701 702 if (desc->prio_regs) { 703 d->prio = alloc_bootmem(desc->nr_vectors * sizeof(*d->prio)); 704 705 for (i = 0; i < desc->nr_prio_regs; i++) { 706 smp = IS_SMP(desc->prio_regs[i]); 707 k += save_reg(d, k, desc->prio_regs[i].set_reg, smp); 708 k += save_reg(d, k, desc->prio_regs[i].clr_reg, smp); 709 } 710 } 711 712 if (desc->sense_regs) { 713 d->sense = alloc_bootmem(desc->nr_vectors * sizeof(*d->sense)); 714 715 for (i = 0; i < desc->nr_sense_regs; i++) { 716 k += save_reg(d, k, desc->sense_regs[i].reg, 0); 717 } 718 } 719 720 d->chip.name = desc->name; 721 d->chip.mask = intc_disable; 722 d->chip.unmask = intc_enable; 723 d->chip.mask_ack = intc_disable; 724 d->chip.enable = intc_enable; 725 d->chip.disable = intc_disable; 726 d->chip.shutdown = intc_disable; 727 d->chip.set_type = intc_set_sense; 728 d->chip.set_wake = intc_set_wake; 729 730#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) 731 if (desc->ack_regs) { 732 for (i = 0; i < desc->nr_ack_regs; i++) 733 k += save_reg(d, k, desc->ack_regs[i].set_reg, 0); 734 735 d->chip.mask_ack = intc_mask_ack; 736 } 737#endif 738 739 BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ 740 741 /* keep the first vector only if same enum is used multiple times */ 742 for (i = 0; i < desc->nr_vectors; i++) { 743 struct intc_vect *vect = desc->vectors + i; 744 int first_irq = evt2irq(vect->vect); 745 746 if (!vect->enum_id) 747 continue; 748 749 for (k = i + 1; k < desc->nr_vectors; k++) { 750 struct intc_vect *vect2 = desc->vectors + k; 751 752 if (vect->enum_id != vect2->enum_id) 753 continue; 754 755 vect2->enum_id = 0; 756 757 if (!intc_evt2irq_table) 758 intc_evt2irq_table = alloc_bootmem(NR_IRQS); 759 760 if (!intc_evt2irq_table) { 761 pr_warning("intc: cannot allocate evt2irq!\n"); 762 continue; 763 } 764 765 intc_evt2irq_table[evt2irq(vect2->vect)] = first_irq; 766 } 767 } 768 769 /* register the vectors one by one */ 770 for (i = 0; i < desc->nr_vectors; i++) { 771 struct intc_vect *vect = desc->vectors + i; 772 773 if (!vect->enum_id) 774 continue; 775 776 intc_register_irq(desc, d, vect->enum_id, evt2irq(vect->vect)); 777 } 778} 779 780static int intc_suspend(struct sys_device *dev, pm_message_t state) 781{ 782 struct intc_desc_int *d; 783 struct irq_desc *desc; 784 int irq; 785 786 /* get intc controller associated with this sysdev */ 787 d = container_of(dev, struct intc_desc_int, sysdev); 788 789 /* enable wakeup irqs belonging to this intc controller */ 790 for_each_irq_desc(irq, desc) { 791 if ((desc->status & IRQ_WAKEUP) && (desc->chip == &d->chip)) 792 intc_enable(irq); 793 } 794 795 return 0; 796} 797 798static struct sysdev_class intc_sysdev_class = { 799 .name = "intc", 800 .suspend = intc_suspend, 801}; 802 803/* register this intc as sysdev to allow suspend/resume */ 804static int __init register_intc_sysdevs(void) 805{ 806 struct intc_desc_int *d; 807 int error; 808 int id = 0; 809 810 error = sysdev_class_register(&intc_sysdev_class); 811 if (!error) { 812 list_for_each_entry(d, &intc_list, list) { 813 d->sysdev.id = id; 814 d->sysdev.cls = &intc_sysdev_class; 815 error = sysdev_register(&d->sysdev); 816 if (error) 817 break; 818 id++; 819 } 820 } 821 822 if (error) 823 pr_warning("intc: sysdev registration error\n"); 824 825 return error; 826} 827 828device_initcall(register_intc_sysdevs);