1#include <loongson.h> 2#include <irq.h> 3#include <linux/interrupt.h> 4#include <linux/module.h> 5 6#include <asm/irq_cpu.h> 7#include <asm/i8259.h> 8#include <asm/mipsregs.h> 9 10#include "smp.h" 11 12unsigned int ht_irq[] = {1, 3, 4, 5, 6, 7, 8, 12, 14, 15}; 13 14static void ht_irqdispatch(void) 15{ 16 unsigned int i, irq; 17 18 irq = LOONGSON_HT1_INT_VECTOR(0); 19 LOONGSON_HT1_INT_VECTOR(0) = irq; /* Acknowledge the IRQs */ 20 21 for (i = 0; i < ARRAY_SIZE(ht_irq); i++) { 22 if (irq & (0x1 << ht_irq[i])) 23 do_IRQ(ht_irq[i]); 24 } 25} 26 27void mach_irq_dispatch(unsigned int pending) 28{ 29 if (pending & CAUSEF_IP7) 30 do_IRQ(LOONGSON_TIMER_IRQ); 31#if defined(CONFIG_SMP) 32 else if (pending & CAUSEF_IP6) 33 loongson3_ipi_interrupt(NULL); 34#endif 35 else if (pending & CAUSEF_IP3) 36 ht_irqdispatch(); 37 else if (pending & CAUSEF_IP2) 38 do_IRQ(LOONGSON_UART_IRQ); 39 else { 40 pr_err("%s : spurious interrupt\n", __func__); 41 spurious_interrupt(); 42 } 43} 44 45static struct irqaction cascade_irqaction = { 46 .handler = no_action, 47 .name = "cascade", 48}; 49 50static inline void mask_loongson_irq(struct irq_data *d) 51{ 52 clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); 53 irq_disable_hazard(); 54 55 /* Workaround: UART IRQ may deliver to any core */ 56 if (d->irq == LOONGSON_UART_IRQ) { 57 int cpu = smp_processor_id(); 58 int node_id = cpu / loongson_sysconf.cores_per_node; 59 int core_id = cpu % loongson_sysconf.cores_per_node; 60 u64 intenclr_addr = smp_group[node_id] | 61 (u64)(&LOONGSON_INT_ROUTER_INTENCLR); 62 u64 introuter_lpc_addr = smp_group[node_id] | 63 (u64)(&LOONGSON_INT_ROUTER_LPC); 64 65 *(volatile u32 *)intenclr_addr = 1 << 10; 66 *(volatile u8 *)introuter_lpc_addr = 0x10 + (1<<core_id); 67 } 68} 69 70static inline void unmask_loongson_irq(struct irq_data *d) 71{ 72 /* Workaround: UART IRQ may deliver to any core */ 73 if (d->irq == LOONGSON_UART_IRQ) { 74 int cpu = smp_processor_id(); 75 int node_id = cpu / loongson_sysconf.cores_per_node; 76 int core_id = cpu % loongson_sysconf.cores_per_node; 77 u64 intenset_addr = smp_group[node_id] | 78 (u64)(&LOONGSON_INT_ROUTER_INTENSET); 79 u64 introuter_lpc_addr = smp_group[node_id] | 80 (u64)(&LOONGSON_INT_ROUTER_LPC); 81 82 *(volatile u32 *)intenset_addr = 1 << 10; 83 *(volatile u8 *)introuter_lpc_addr = 0x10 + (1<<core_id); 84 } 85 86 set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); 87 irq_enable_hazard(); 88} 89 90 /* For MIPS IRQs which shared by all cores */ 91static struct irq_chip loongson_irq_chip = { 92 .name = "Loongson", 93 .irq_ack = mask_loongson_irq, 94 .irq_mask = mask_loongson_irq, 95 .irq_mask_ack = mask_loongson_irq, 96 .irq_unmask = unmask_loongson_irq, 97 .irq_eoi = unmask_loongson_irq, 98}; 99 100void irq_router_init(void) 101{ 102 int i; 103 104 /* route LPC int to cpu core0 int 0 */ 105 LOONGSON_INT_ROUTER_LPC = LOONGSON_INT_CORE0_INT0; 106 /* route HT1 int0 ~ int7 to cpu core0 INT1*/ 107 for (i = 0; i < 8; i++) 108 LOONGSON_INT_ROUTER_HT1(i) = LOONGSON_INT_CORE0_INT1; 109 /* enable HT1 interrupt */ 110 LOONGSON_HT1_INTN_EN(0) = 0xffffffff; 111 /* enable router interrupt intenset */ 112 LOONGSON_INT_ROUTER_INTENSET = 113 LOONGSON_INT_ROUTER_INTEN | (0xffff << 16) | 0x1 << 10; 114} 115 116void __init mach_init_irq(void) 117{ 118 clear_c0_status(ST0_IM | ST0_BEV); 119 120 irq_router_init(); 121 mips_cpu_irq_init(); 122 init_i8259_irqs(); 123 irq_set_chip_and_handler(LOONGSON_UART_IRQ, 124 &loongson_irq_chip, handle_level_irq); 125 126 /* setup HT1 irq */ 127 setup_irq(LOONGSON_HT1_IRQ, &cascade_irqaction); 128 129 set_c0_status(STATUSF_IP2 | STATUSF_IP6); 130} 131 132#ifdef CONFIG_HOTPLUG_CPU 133 134void fixup_irqs(void) 135{ 136 irq_cpu_offline(); 137 clear_c0_status(ST0_IM); 138} 139 140#endif 141