1 /* 2 * Copyright (c) 2020, Mars Li <mengshi.li.mars@gmail.com> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <sys/param.h> 18 #include <sys/systm.h> 19 #include <sys/queue.h> 20 #include <sys/malloc.h> 21 #include <sys/device.h> 22 #include <sys/evcount.h> 23 24 #include <machine/bus.h> 25 #include <machine/fdt.h> 26 #include <machine/riscvreg.h> 27 28 #include <dev/ofw/openfirm.h> 29 #include <dev/ofw/fdt.h> 30 31 #include "riscv64/dev/plic.h" 32 #include "riscv_cpu_intc.h" 33 34 struct intrhand { 35 int (*ih_func)(void *); /* handler */ 36 void *ih_arg; /* arg for handler */ 37 int ih_irq; /* IRQ number */ 38 char *ih_name; 39 }; 40 41 struct intrhand* intc_handler[INTC_NIRQS] = {NULL}; 42 struct interrupt_controller intc_ic; 43 44 int riscv_intc_match(struct device *, void *, void *); 45 void riscv_intc_attach(struct device *, struct device *, void *); 46 47 void riscv_intc_irq_handler(void *); 48 void *riscv_intc_intr_establish(int, int, int (*)(void *), 49 void *, char *); 50 void riscv_intc_intr_disestablish(void *); 51 52 53 struct cfattach intc_ca = { 54 sizeof (struct device), riscv_intc_match, riscv_intc_attach 55 }; 56 57 struct cfdriver intc_cd = { 58 NULL, "rv_cpu_intc", DV_DULL 59 }; 60 61 int 62 riscv_intc_match(struct device *parent, void *match, void *aux) 63 { 64 struct fdt_attach_args *faa = aux; 65 int node = faa->fa_node; 66 return (OF_getproplen(node, "interrupt-controller") >= 0 && 67 OF_is_compatible(node, "riscv,cpu-intc")); 68 } 69 70 void 71 riscv_intc_attach(struct device *parent, struct device *self, void *aux) 72 { 73 struct fdt_attach_args *faa = aux;/* should only use fa_node field */ 74 75 riscv_init_smask(); 76 77 /* hook the intr_handler */ 78 riscv_set_intr_handler(riscv_intc_irq_handler); 79 80 intc_ic.ic_node = faa->fa_node; 81 intc_ic.ic_cookie = &intc_ic; 82 83 /* 84 * only allow install/uninstall handler to/from global vector 85 * by calling riscv_intc_intr_establish/disestablish 86 */ 87 intc_ic.ic_establish = NULL; 88 intc_ic.ic_disestablish = NULL; 89 90 riscv_intr_register_fdt(&intc_ic); 91 92 /* 93 * XXX right time to enable interrupts ?? 94 * might need to postpone untile autoconf is finished 95 */ 96 enable_interrupts(); 97 } 98 99 100 /* global interrupt handler */ 101 void 102 riscv_intc_irq_handler(void *frame) 103 { 104 int irq; 105 struct intrhand *ih; 106 struct trapframe *_frame; 107 _frame = (struct trapframe*) frame; 108 109 KASSERTMSG(_frame->tf_scause & EXCP_INTR, 110 "riscv_cpu_intr: wrong frame passed"); 111 112 irq = (_frame->tf_scause & EXCP_MASK); 113 #ifdef DEBUG_INTC 114 printf("irq %d fired\n", irq); 115 #endif 116 117 ih = intc_handler[irq]; 118 if (ih->ih_func(frame) == 0) 119 #ifdef DEBUG_INTC 120 printf("fail in handling irq %d %s\n", irq, ih->ih_name); 121 #else 122 ; 123 #endif /* DEBUG_INTC */ 124 } 125 126 void * 127 riscv_intc_intr_establish(int irqno, int dummy_level, int (*func)(void *), 128 void *arg, char *name) 129 { 130 int sie; 131 struct intrhand *ih; 132 133 if (irqno < 0 || irqno >= INTC_NIRQS) 134 panic("intc_intr_establish: bogus irqnumber %d: %s", 135 irqno, name); 136 sie = disable_interrupts(); 137 138 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 139 ih->ih_func = func; 140 ih->ih_arg = arg; 141 ih->ih_irq = irqno; 142 ih->ih_name = name; 143 144 intc_handler[irqno] = ih; 145 #ifdef DEBUG_INTC 146 printf("\nintc_intr_establish irq %d [%s]\n", irqno, name); 147 #endif 148 restore_interrupts(sie); 149 return (ih); 150 } 151 152 void 153 riscv_intc_intr_disestablish(void *cookie) 154 { 155 int sie; 156 struct intrhand *ih = cookie; 157 int irqno = ih->ih_irq; 158 sie = disable_interrupts(); 159 160 intc_handler[irqno] = NULL; 161 free(ih, M_DEVBUF, 0); 162 163 restore_interrupts(sie); 164 } 165