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[] = {0, 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 .flags = IRQF_NO_SUSPEND, 48 .name = "cascade", 49}; 50 51static inline void mask_loongson_irq(struct irq_data *d) 52{ 53 clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); 54 irq_disable_hazard(); 55 56 /* Workaround: UART IRQ may deliver to any core */ 57 if (d->irq == LOONGSON_UART_IRQ) { 58 int cpu = smp_processor_id(); 59 int node_id = cpu_logical_map(cpu) / loongson_sysconf.cores_per_node; 60 int core_id = cpu_logical_map(cpu) % loongson_sysconf.cores_per_node; 61 u64 intenclr_addr = smp_group[node_id] | 62 (u64)(&LOONGSON_INT_ROUTER_INTENCLR); 63 u64 introuter_lpc_addr = smp_group[node_id] | 64 (u64)(&LOONGSON_INT_ROUTER_LPC); 65 66 *(volatile u32 *)intenclr_addr = 1 << 10; 67 *(volatile u8 *)introuter_lpc_addr = 0x10 + (1<<core_id); 68 } 69} 70 71static inline void unmask_loongson_irq(struct irq_data *d) 72{ 73 /* Workaround: UART IRQ may deliver to any core */ 74 if (d->irq == LOONGSON_UART_IRQ) { 75 int cpu = smp_processor_id(); 76 int node_id = cpu_logical_map(cpu) / loongson_sysconf.cores_per_node; 77 int core_id = cpu_logical_map(cpu) % loongson_sysconf.cores_per_node; 78 u64 intenset_addr = smp_group[node_id] | 79 (u64)(&LOONGSON_INT_ROUTER_INTENSET); 80 u64 introuter_lpc_addr = smp_group[node_id] | 81 (u64)(&LOONGSON_INT_ROUTER_LPC); 82 83 *(volatile u32 *)intenset_addr = 1 << 10; 84 *(volatile u8 *)introuter_lpc_addr = 0x10 + (1<<core_id); 85 } 86 87 set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); 88 irq_enable_hazard(); 89} 90 91 /* For MIPS IRQs which shared by all cores */ 92static struct irq_chip loongson_irq_chip = { 93 .name = "Loongson", 94 .irq_ack = mask_loongson_irq, 95 .irq_mask = mask_loongson_irq, 96 .irq_mask_ack = mask_loongson_irq, 97 .irq_unmask = unmask_loongson_irq, 98 .irq_eoi = unmask_loongson_irq, 99}; 100 101void irq_router_init(void) 102{ 103 int i; 104 105 /* route LPC int to cpu core0 int 0 */ 106 LOONGSON_INT_ROUTER_LPC = 107 LOONGSON_INT_COREx_INTy(loongson_sysconf.boot_cpu_id, 0); 108 /* route HT1 int0 ~ int7 to cpu core0 INT1*/ 109 for (i = 0; i < 8; i++) 110 LOONGSON_INT_ROUTER_HT1(i) = 111 LOONGSON_INT_COREx_INTy(loongson_sysconf.boot_cpu_id, 1); 112 /* enable HT1 interrupt */ 113 LOONGSON_HT1_INTN_EN(0) = 0xffffffff; 114 /* enable router interrupt intenset */ 115 LOONGSON_INT_ROUTER_INTENSET = 116 LOONGSON_INT_ROUTER_INTEN | (0xffff << 16) | 0x1 << 10; 117} 118 119void __init mach_init_irq(void) 120{ 121 clear_c0_status(ST0_IM | ST0_BEV); 122 123 irq_router_init(); 124 mips_cpu_irq_init(); 125 init_i8259_irqs(); 126 irq_set_chip_and_handler(LOONGSON_UART_IRQ, 127 &loongson_irq_chip, handle_level_irq); 128 129 /* setup HT1 irq */ 130 setup_irq(LOONGSON_HT1_IRQ, &cascade_irqaction); 131 132 set_c0_status(STATUSF_IP2 | STATUSF_IP6); 133} 134 135#ifdef CONFIG_HOTPLUG_CPU 136 137void fixup_irqs(void) 138{ 139 irq_cpu_offline(); 140 clear_c0_status(ST0_IM); 141} 142 143#endif 144