Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | // SPDX-License-Identifier: GPL-2.0 #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/sched/debug.h> #include <linux/types.h> #include <linux/ioport.h> #include <asm/hwtest.h> #include <asm/irq.h> #include <asm/irq_regs.h> #include <asm/processor.h> #include <asm/virt.h> #define GFPIC_REG_IRQ_PENDING 0x04 #define GFPIC_REG_IRQ_DISABLE_ALL 0x08 #define GFPIC_REG_IRQ_DISABLE 0x0c #define GFPIC_REG_IRQ_ENABLE 0x10 static struct resource picres[6]; static const char *picname[6] = { "goldfish_pic.0", "goldfish_pic.1", "goldfish_pic.2", "goldfish_pic.3", "goldfish_pic.4", "goldfish_pic.5" }; /* * 6 goldfish-pic for CPU IRQ #1 to IRQ #6 * CPU IRQ #1 -> PIC #1 * IRQ #1 to IRQ #31 -> unused * IRQ #32 -> goldfish-tty * CPU IRQ #2 -> PIC #2 * IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32 * CPU IRQ #3 -> PIC #3 * IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64 * CPU IRQ #4 -> PIC #4 * IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96 * CPU IRQ #5 -> PIC #5 * IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128 * CPU IRQ #6 -> PIC #6 * IRQ #1 -> goldfish-timer * IRQ #2 -> goldfish-rtc * IRQ #3 to IRQ #32 -> unused * CPU IRQ #7 -> NMI */ static u32 gfpic_read(int pic, int reg) { void __iomem *base = (void __iomem *)(virt_bi_data.pic.mmio + pic * 0x1000); return ioread32be(base + reg); } static void gfpic_write(u32 value, int pic, int reg) { void __iomem *base = (void __iomem *)(virt_bi_data.pic.mmio + pic * 0x1000); iowrite32be(value, base + reg); } #define GF_PIC(irq) ((irq - IRQ_USER) / 32) #define GF_IRQ(irq) ((irq - IRQ_USER) % 32) static void virt_irq_enable(struct irq_data *data) { gfpic_write(BIT(GF_IRQ(data->irq)), GF_PIC(data->irq), GFPIC_REG_IRQ_ENABLE); } static void virt_irq_disable(struct irq_data *data) { gfpic_write(BIT(GF_IRQ(data->irq)), GF_PIC(data->irq), GFPIC_REG_IRQ_DISABLE); } static unsigned int virt_irq_startup(struct irq_data *data) { virt_irq_enable(data); return 0; } static irqreturn_t virt_nmi_handler(int irq, void *dev_id) { static int in_nmi; if (READ_ONCE(in_nmi)) return IRQ_HANDLED; WRITE_ONCE(in_nmi, 1); pr_warn("Non-Maskable Interrupt\n"); show_registers(get_irq_regs()); WRITE_ONCE(in_nmi, 0); return IRQ_HANDLED; } static struct irq_chip virt_irq_chip = { .name = "virt", .irq_enable = virt_irq_enable, .irq_disable = virt_irq_disable, .irq_startup = virt_irq_startup, .irq_shutdown = virt_irq_disable, }; static void goldfish_pic_irq(struct irq_desc *desc) { u32 irq_pending; unsigned int irq_num; unsigned int pic = desc->irq_data.irq - 1; irq_pending = gfpic_read(pic, GFPIC_REG_IRQ_PENDING); irq_num = IRQ_USER + pic * 32; do { if (irq_pending & 1) generic_handle_irq(irq_num); ++irq_num; irq_pending >>= 1; } while (irq_pending); } void __init virt_init_IRQ(void) { unsigned int i; m68k_setup_irq_controller(&virt_irq_chip, handle_simple_irq, IRQ_USER, NUM_VIRT_SOURCES - IRQ_USER); for (i = 0; i < 6; i++) { picres[i] = (struct resource) DEFINE_RES_MEM_NAMED(virt_bi_data.pic.mmio + i * 0x1000, 0x1000, picname[i]); if (request_resource(&iomem_resource, &picres[i])) { pr_err("Cannot allocate %s resource\n", picname[i]); return; } irq_set_chained_handler(virt_bi_data.pic.irq + i, goldfish_pic_irq); } if (request_irq(IRQ_AUTO_7, virt_nmi_handler, 0, "NMI", virt_nmi_handler)) pr_err("Couldn't register NMI\n"); } |