ip27-irq.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:13k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * ip27-irq.c: Highlevel interrupt handling for IP27 architecture.
  3.  *
  4.  * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
  5.  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
  6.  * Copyright (C) 1999 - 2001 Kanoj Sarcar
  7.  */
  8. #include <linux/config.h>
  9. #include <linux/init.h>
  10. #include <linux/errno.h>
  11. #include <linux/signal.h>
  12. #include <linux/sched.h>
  13. #include <linux/types.h>
  14. #include <linux/interrupt.h>
  15. #include <linux/ioport.h>
  16. #include <linux/timex.h>
  17. #include <linux/slab.h>
  18. #include <linux/random.h>
  19. #include <linux/smp_lock.h>
  20. #include <linux/kernel_stat.h>
  21. #include <linux/delay.h>
  22. #include <asm/bitops.h>
  23. #include <asm/bootinfo.h>
  24. #include <asm/io.h>
  25. #include <asm/mipsregs.h>
  26. #include <asm/system.h>
  27. #include <asm/irq.h>
  28. #include <asm/ptrace.h>
  29. #include <asm/processor.h>
  30. #include <asm/pci/bridge.h>
  31. #include <asm/sn/sn0/hub.h>
  32. #include <asm/sn/sn0/ip27.h>
  33. #include <asm/sn/addrs.h>
  34. #include <asm/sn/agent.h>
  35. #include <asm/sn/arch.h>
  36. #include <asm/sn/intr.h>
  37. #include <asm/sn/intr_public.h>
  38. #undef DEBUG_IRQ
  39. #ifdef DEBUG_IRQ
  40. #define DBG(x...) printk(x)
  41. #else
  42. #define DBG(x...)
  43. #endif
  44. /* These should die */
  45. unsigned char bus_to_wid[256]; /* widget id for linux pci bus */
  46. unsigned char bus_to_nid[256]; /* nasid for linux pci bus */
  47. unsigned char num_bridges; /* number of bridges in the system */
  48. /*
  49.  * Linux has a controller-independent x86 interrupt architecture.
  50.  * every controller has a 'controller-template', that is used
  51.  * by the main code to do the right thing. Each driver-visible
  52.  * interrupt source is transparently wired to the apropriate
  53.  * controller. Thus drivers need not be aware of the
  54.  * interrupt-controller.
  55.  *
  56.  * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
  57.  * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
  58.  * (IO-APICs assumed to be messaging to Pentium local-APICs)
  59.  *
  60.  * the code is designed to be easily extended with new/different
  61.  * interrupt controllers, without having to do assembly magic.
  62.  */
  63. extern asmlinkage void ip27_irq(void);
  64. extern void do_IRQ(int irq, struct pt_regs *regs);
  65. extern int irq_to_bus[], irq_to_slot[], bus_to_cpu[];
  66. int intr_connect_level(int cpu, int bit);
  67. int intr_disconnect_level(int cpu, int bit);
  68. /*
  69.  * There is a single intpend register per node, and we want to have
  70.  * distinct levels for intercpu intrs for both cpus A and B on a node.
  71.  */
  72. int node_level_to_irq[MAX_COMPACT_NODES][PERNODE_LEVELS];
  73. /*
  74.  * use these macros to get the encoded nasid and widget id
  75.  * from the irq value
  76.  */
  77. #define IRQ_TO_BUS(i) irq_to_bus[(i)]
  78. #define IRQ_TO_CPU(i) bus_to_cpu[IRQ_TO_BUS(i)]
  79. #define NASID_FROM_PCI_IRQ(i) bus_to_nid[IRQ_TO_BUS(i)]
  80. #define WID_FROM_PCI_IRQ(i) bus_to_wid[IRQ_TO_BUS(i)]
  81. #define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]
  82. static inline int alloc_level(cpuid_t cpunum, int irq)
  83. {
  84. cnodeid_t nodenum = CPUID_TO_COMPACT_NODEID(cpunum);
  85. int j = LEAST_LEVEL + 3; /* resched & crosscall entries taken */
  86. while (++j < PERNODE_LEVELS) {
  87. if (node_level_to_irq[nodenum][j] == -1) {
  88. node_level_to_irq[nodenum][j] = irq;
  89. return j;
  90. }
  91. }
  92. printk("Cpu %ld flooded with devicesn", cpunum);
  93. while(1);
  94. return -1;
  95. }
  96. static inline int find_level(cpuid_t *cpunum, int irq)
  97. {
  98. int j;
  99. cnodeid_t nodenum = INVALID_CNODEID;
  100. while (++nodenum < MAX_COMPACT_NODES) {
  101. j = LEAST_LEVEL + 3; /* resched & crosscall entries taken */
  102. while (++j < PERNODE_LEVELS)
  103. if (node_level_to_irq[nodenum][j] == irq) {
  104. *cpunum = 0; /* XXX Fixme */
  105. return(j);
  106. }
  107. }
  108. printk("Could not identify cpu/level for irq %dn", irq);
  109. while(1);
  110. return(-1);
  111. }
  112. /*
  113.  * Find first bit set
  114.  */
  115. static int ms1bit(unsigned long x)
  116. {
  117. int b = 0, s;
  118. s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s;
  119. s =  8; if (x >>  8 == 0) s = 0; b += s; x >>= s;
  120. s =  4; if (x >>  4 == 0) s = 0; b += s; x >>= s;
  121. s =  2; if (x >>  2 == 0) s = 0; b += s; x >>= s;
  122. s =  1; if (x >>  1 == 0) s = 0; b += s;
  123. return b;
  124. }
  125. /*
  126.  * This code is unnecessarily complex, because we do SA_INTERRUPT
  127.  * intr enabling. Basically, once we grab the set of intrs we need
  128.  * to service, we must mask _all_ these interrupts; firstly, to make
  129.  * sure the same intr does not intr again, causing recursion that
  130.  * can lead to stack overflow. Secondly, we can not just mask the
  131.  * one intr we are do_IRQing, because the non-masked intrs in the
  132.  * first set might intr again, causing multiple servicings of the
  133.  * same intr. This effect is mostly seen for intercpu intrs.
  134.  * Kanoj 05.13.00
  135.  */
  136. void ip27_do_irq(struct pt_regs *regs)
  137. {
  138. int irq, swlevel;
  139. hubreg_t pend0, mask0;
  140. cpuid_t thiscpu = smp_processor_id();
  141. int pi_int_mask0 = ((cputoslice(thiscpu) == 0) ?
  142. PI_INT_MASK0_A : PI_INT_MASK0_B);
  143. /* copied from Irix intpend0() */
  144. while (((pend0 = LOCAL_HUB_L(PI_INT_PEND0)) &
  145. (mask0 = LOCAL_HUB_L(pi_int_mask0))) != 0) {
  146. pend0 &= mask0; /* Pick intrs we should look at */
  147. if (pend0) {
  148. /* Prevent any of the picked intrs from recursing */
  149. LOCAL_HUB_S(pi_int_mask0, mask0 & ~(pend0));
  150. do {
  151. swlevel = ms1bit(pend0);
  152. LOCAL_HUB_CLR_INTR(swlevel);
  153. /* "map" swlevel to irq */
  154. irq = LEVEL_TO_IRQ(thiscpu, swlevel);
  155. do_IRQ(irq, regs);
  156. /* clear bit in pend0 */
  157. pend0 ^= 1ULL << swlevel;
  158. } while(pend0);
  159. /* Now allow the set of serviced intrs again */
  160. LOCAL_HUB_S(pi_int_mask0, mask0);
  161. LOCAL_HUB_L(PI_INT_PEND0);
  162. }
  163. }
  164. }
  165. /* Startup one of the (PCI ...) IRQs routes over a bridge.  */
  166. static unsigned int startup_bridge_irq(unsigned int irq)
  167. {
  168. bridgereg_t device;
  169. bridge_t *bridge;
  170. int pin, swlevel;
  171. cpuid_t cpu;
  172. nasid_t master = NASID_FROM_PCI_IRQ(irq);
  173. if (irq < BASE_PCI_IRQ)
  174. return 0;
  175.         bridge = (bridge_t *) NODE_SWIN_BASE(master, WID_FROM_PCI_IRQ(irq));
  176. pin = SLOT_FROM_PCI_IRQ(irq);
  177. cpu = IRQ_TO_CPU(irq);
  178. DBG("bridge_startup(): irq= 0x%x  pin=%dn", irq, pin);
  179. /*
  180.  * "map" irq to a swlevel greater than 6 since the first 6 bits
  181.  * of INT_PEND0 are taken
  182.  */
  183. swlevel = alloc_level(cpu, irq);
  184. intr_connect_level(cpu, swlevel);
  185. bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (master << 8));
  186. bridge->b_int_enable |= (1 << pin);
  187. /* more stuff in int_enable reg */
  188. bridge->b_int_enable |= 0x7ffffe00;
  189. /*
  190.  * XXX This only works if b_int_device is initialized to 0!
  191.  * We program the bridge to have a 1:1 mapping between devices
  192.  * (slots) and intr pins.
  193.  */
  194. device = bridge->b_int_device;
  195. device |= (pin << (pin*3));
  196. bridge->b_int_device = device;
  197.         bridge->b_widget.w_tflush;                      /* Flush */
  198.         return 0;       /* Never anything pending.  */
  199. }
  200. /* Shutdown one of the (PCI ...) IRQs routes over a bridge.  */
  201. static unsigned int shutdown_bridge_irq(unsigned int irq)
  202. {
  203. bridge_t *bridge;
  204. int pin, swlevel;
  205. cpuid_t cpu;
  206. if (irq < BASE_PCI_IRQ)
  207. return 0;
  208. bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_PCI_IRQ(irq),
  209.                                      WID_FROM_PCI_IRQ(irq));
  210. DBG("bridge_shutdown: irq 0x%xn", irq);
  211. pin = SLOT_FROM_PCI_IRQ(irq);
  212. /*
  213.  * map irq to a swlevel greater than 6 since the first 6 bits
  214.  * of INT_PEND0 are taken
  215.  */
  216. swlevel = find_level(&cpu, irq);
  217. intr_disconnect_level(cpu, swlevel);
  218. LEVEL_TO_IRQ(cpu, swlevel) = -1;
  219. bridge->b_int_enable &= ~(1 << pin);
  220. bridge->b_widget.w_tflush;                      /* Flush */
  221. return 0;       /* Never anything pending.  */
  222. }
  223. static inline void enable_bridge_irq(unsigned int irq)
  224. {
  225. /* All the braindamage happens magically for us in ip27_do_irq */
  226. }
  227. static void disable_bridge_irq(unsigned int irq)
  228. {
  229. /* All the braindamage happens magically for us in ip27_do_irq */
  230. }
  231. static void mask_and_ack_bridge_irq(unsigned int irq)
  232. {
  233. /* All the braindamage happens magically for us in ip27_do_irq */
  234. }
  235. static void end_bridge_irq (unsigned int irq)
  236. {
  237. if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
  238. enable_bridge_irq(irq);
  239. }
  240. static struct hw_interrupt_type bridge_irq_type = {
  241. "bridge",
  242. startup_bridge_irq,
  243. shutdown_bridge_irq,
  244. enable_bridge_irq,
  245. disable_bridge_irq,
  246. mask_and_ack_bridge_irq,
  247. end_bridge_irq
  248. };
  249. void irq_debug(void)
  250. {
  251. bridge_t *bridge = (bridge_t *) 0x9200000008000000;
  252. printk("bridge->b_int_status = 0x%xn", bridge->b_int_status);
  253. printk("bridge->b_int_enable = 0x%xn", bridge->b_int_enable);
  254. printk("PI_INT_PEND0   = 0x%lxn", LOCAL_HUB_L(PI_INT_PEND0));
  255. printk("PI_INT_MASK0_A = 0x%lxn", LOCAL_HUB_L(PI_INT_MASK0_A));
  256. }
  257. void __init init_IRQ(void)
  258. {
  259. int i;
  260. set_except_vector(0, ip27_irq);
  261. /*
  262.  * Right now the bridge irq is our kitchen sink interrupt type
  263.  */
  264. for (i = 0; i <= NR_IRQS; i++) {
  265. irq_desc[i].status = IRQ_DISABLED;
  266. irq_desc[i].action = 0;
  267. irq_desc[i].depth = 1;
  268. irq_desc[i].handler = &bridge_irq_type;
  269. }
  270. }
  271. /*
  272.  * Get values that vary depending on which CPU and bit we're operating on.
  273.  */
  274. static hub_intmasks_t *intr_get_ptrs(cpuid_t cpu, int bit, int *new_bit,
  275. hubreg_t **intpend_masks, int *ip)
  276. {
  277. hub_intmasks_t *hub_intmasks;
  278. hub_intmasks = &cpu_data[cpu].p_intmasks;
  279. if (bit < N_INTPEND_BITS) {
  280. *intpend_masks = hub_intmasks->intpend0_masks;
  281. *ip = 0;
  282. *new_bit = bit;
  283. } else {
  284. *intpend_masks = hub_intmasks->intpend1_masks;
  285. *ip = 1;
  286. *new_bit = bit - N_INTPEND_BITS;
  287. }
  288. return hub_intmasks;
  289. }
  290. int intr_connect_level(int cpu, int bit)
  291. {
  292. int ip;
  293. int slice = cputoslice(cpu);
  294. volatile hubreg_t *mask_reg;
  295. hubreg_t *intpend_masks;
  296. nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
  297. (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
  298. /* Make sure it's not already pending when we connect it. */
  299. REMOTE_HUB_CLR_INTR(nasid, bit + ip * N_INTPEND_BITS);
  300. intpend_masks[0] |= (1ULL << (u64)bit);
  301. if (ip == 0) {
  302. mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
  303. PI_INT_MASK_OFFSET * slice);
  304. } else {
  305. mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
  306. PI_INT_MASK_OFFSET * slice);
  307. }
  308. HUB_S(mask_reg, intpend_masks[0]);
  309. return(0);
  310. }
  311. int intr_disconnect_level(int cpu, int bit)
  312. {
  313. int ip;
  314. int slice = cputoslice(cpu);
  315. volatile hubreg_t *mask_reg;
  316. hubreg_t *intpend_masks;
  317. nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
  318. (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
  319. intpend_masks[0] &= ~(1ULL << (u64)bit);
  320. if (ip == 0) {
  321. mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
  322. PI_INT_MASK_OFFSET * slice);
  323. } else {
  324. mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
  325. PI_INT_MASK_OFFSET * slice);
  326. }
  327. HUB_S(mask_reg, intpend_masks[0]);
  328. return(0);
  329. }
  330. void handle_resched_intr(int irq, void *dev_id, struct pt_regs *regs)
  331. {
  332. /* Nothing, the return from intr will work for us */
  333. }
  334. #ifdef CONFIG_SMP
  335. void core_send_ipi(int destid, unsigned int action)
  336. {
  337. int irq;
  338. #if (CPUS_PER_NODE == 2)
  339. switch (action) {
  340. case SMP_RESCHEDULE_YOURSELF:
  341. irq = CPU_RESCHED_A_IRQ;
  342. break;
  343. case SMP_CALL_FUNCTION:
  344. irq = CPU_CALL_A_IRQ;
  345. break;
  346. default:
  347. panic("sendintr");
  348. }
  349. irq += cputoslice(destid);
  350. /*
  351.  * Convert the compact hub number to the NASID to get the correct
  352.  * part of the address space.  Then set the interrupt bit associated
  353.  * with the CPU we want to send the interrupt to.
  354.  */
  355. REMOTE_HUB_SEND_INTR(COMPACT_TO_NASID_NODEID(cputocnode(destid)),
  356. FAST_IRQ_TO_LEVEL(irq));
  357. #else
  358. << Bomb!  Must redefine this for more than 2 CPUS. >>
  359. #endif
  360. }
  361. #endif
  362. extern void smp_call_function_interrupt(void);
  363. void install_cpuintr(int cpu)
  364. {
  365. #ifdef CONFIG_SMP
  366. #if (CPUS_PER_NODE == 2)
  367. static int done = 0;
  368. /*
  369.  * This is a hack till we have a pernode irqlist. Currently,
  370.  * just have the master cpu set up the handlers for the per
  371.  * cpu irqs.
  372.  */
  373. if (done == 0) {
  374. int j;
  375. if (request_irq(CPU_RESCHED_A_IRQ, handle_resched_intr,
  376. 0, "resched", 0))
  377. panic("intercpu intr unconnectible");
  378. if (request_irq(CPU_RESCHED_B_IRQ, handle_resched_intr,
  379. 0, "resched", 0))
  380. panic("intercpu intr unconnectible");
  381. if (request_irq(CPU_CALL_A_IRQ, smp_call_function_interrupt,
  382. 0, "callfunc", 0))
  383. panic("intercpu intr unconnectible");
  384. if (request_irq(CPU_CALL_B_IRQ, smp_call_function_interrupt,
  385. 0, "callfunc", 0))
  386. panic("intercpu intr unconnectible");
  387. for (j = 0; j < PERNODE_LEVELS; j++)
  388. LEVEL_TO_IRQ(0, j) = -1;
  389. LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ)) =
  390. CPU_RESCHED_A_IRQ;
  391. LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_B_IRQ)) =
  392. CPU_RESCHED_B_IRQ;
  393. LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ)) =
  394. CPU_CALL_A_IRQ;
  395. LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_B_IRQ)) =
  396. CPU_CALL_B_IRQ;
  397. for (j = 1; j < MAX_COMPACT_NODES; j++)
  398. memcpy(&node_level_to_irq[j][0],
  399. &node_level_to_irq[0][0],
  400. sizeof(node_level_to_irq[0][0])*PERNODE_LEVELS);
  401. done = 1;
  402. }
  403. intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ +
  404. cputoslice(cpu)));
  405. intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ +
  406. cputoslice(cpu)));
  407. #else /* CPUS_PER_NODE */
  408. #error Must redefine this for more than 2 CPUS.
  409. #endif /* CPUS_PER_NODE */
  410. #endif /* CONFIG_SMP */
  411. }
  412. void install_tlbintr(int cpu)
  413. {
  414. #if 0
  415. int intr_bit = N_INTPEND_BITS + TLB_INTR_A + cputoslice(cpu);
  416. intr_connect_level(cpu, intr_bit);
  417. #endif
  418. }