1 /* $NetBSD: gemini_lpchc.c,v 1.3 2019/01/08 19:41:10 jdolecek Exp $ */ 2 3 /* 4 * GEMINI LPC Host Controller 5 */ 6 #include "opt_gemini.h" 7 #include "locators.h" 8 9 #include <sys/cdefs.h> 10 __KERNEL_RCSID(0, "$NetBSD: gemini_lpchc.c,v 1.3 2019/01/08 19:41:10 jdolecek Exp $"); 11 12 #include <sys/param.h> 13 #include <sys/callout.h> 14 #include <sys/cdefs.h> 15 #include <sys/device.h> 16 #include <sys/kernel.h> 17 #include <sys/systm.h> 18 #include <sys/malloc.h> 19 20 #include <sys/bus.h> 21 22 #include <arm/gemini/gemini_lpchcvar.h> 23 #include <arm/gemini/gemini_lpcvar.h> 24 #include <arm/gemini/gemini_reg.h> 25 26 static inline void 27 gemini_lpchc_sirq_cfg(bus_space_tag_t iot, bus_space_handle_t ioh, 28 bus_size_t offset, uint32_t bit, boolean_t doset) 29 { 30 uint32_t r; 31 32 r = bus_space_read_4(iot, ioh, offset); 33 if (doset) 34 r |= bit; 35 else 36 r &= ~bit; 37 bus_space_write_4(iot, ioh, offset, r); 38 } 39 40 static inline void 41 gemini_lpchc_sirq_ack(bus_space_tag_t iot, bus_space_handle_t ioh, 42 uint32_t bit) 43 { 44 uint32_t r; 45 46 r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_SERIRQSTS); 47 r &= bit; 48 if (r != 0) 49 bus_space_write_4(iot, ioh, GEMINI_LPCHC_SERIRQSTS, r); 50 } 51 52 static inline void 53 gemini_lpchc_sirq_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 54 { 55 uint32_t r; 56 57 r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_IRQCTL); 58 r &= ~LPCHC_IRQCTL_SIRQEN; 59 bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r); 60 } 61 62 static inline void 63 gemini_lpchc_sirq_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 64 { 65 uint32_t r; 66 67 r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_IRQCTL); 68 r |= LPCHC_IRQCTL_SIRQEN; 69 r |= LPCHC_IRQCTL_SIRQMS; /* XXX "continuous mode" */ 70 r |= IRQCTL_SIRQFW_8; /* XXX SIRW Frame Width 8 clocks */ 71 bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r); 72 #if 0 73 delay(10); /* wait 1 serial IRQ cycle */ 74 r &= ~LPCHC_IRQCTL_SIRQMS; /* XXX "quiet mode" */ 75 bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r); 76 #endif 77 } 78 79 static inline void 80 gemini_lpchc_intrq_init(gemini_lpchc_softc_t *sc) 81 { 82 SIMPLEQ_INIT(&sc->sc_intrq); 83 } 84 85 static inline int 86 gemini_lpchc_intrq_empty(gemini_lpchc_softc_t *sc) 87 { 88 return SIMPLEQ_EMPTY(&sc->sc_intrq); 89 } 90 91 static inline void * 92 gemini_lpchc_intrq_insert(gemini_lpchc_softc_t *sc, int (*func)(void *), 93 void *arg, uint32_t bit, boolean_t isedge) 94 { 95 gemini_lpchc_intrq_t *iqp; 96 97 iqp = malloc(sizeof(*iqp), M_DEVBUF, M_NOWAIT|M_ZERO); 98 if (iqp == NULL) { 99 printf("gemini_lpchc_intrq_insert: malloc failed\n"); 100 return NULL; 101 } 102 103 iqp->iq_func = func; 104 iqp->iq_arg = arg; 105 iqp->iq_bit = bit; 106 iqp->iq_isedge = isedge; 107 SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q); 108 109 return (void *)iqp; 110 } 111 112 static inline void 113 gemini_lpchc_intrq_remove(gemini_lpchc_softc_t *sc, void *cookie) 114 { 115 gemini_lpchc_intrq_t *iqp; 116 117 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) { 118 if ((void *)iqp == cookie) { 119 SIMPLEQ_REMOVE(&sc->sc_intrq, 120 iqp, gemini_lpchc_intrq, iq_q); 121 free(iqp, M_DEVBUF); 122 return; 123 } 124 } 125 } 126 127 static inline int 128 gemini_lpchc_intrq_dispatch(gemini_lpchc_softc_t *sc) 129 { 130 gemini_lpchc_intrq_t *iqp; 131 uint32_t r; 132 int rv = 0; 133 134 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_SERIRQSTS); 135 r &= ~LPCHC_SERIRQSTS_RESV; 136 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) { 137 if ((r & iqp->iq_bit) != 0) { 138 if (iqp->iq_isedge) { 139 gemini_lpchc_sirq_ack(sc->sc_iot, sc->sc_ioh, 140 iqp->iq_bit); 141 } 142 rv |= (*iqp->iq_func)(iqp->iq_arg); 143 } 144 } 145 return (rv != 0); 146 } 147 148 void 149 gemini_lpchc_init(lpcintrtag_t tag) 150 { 151 gemini_lpchc_softc_t *sc = tag; 152 uint32_t r; 153 154 gemini_lpchc_intrq_init(sc); 155 156 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR); 157 r |= LPCHC_CSR_BEN; 158 bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR, r); 159 } 160 161 void * 162 gemini_lpchc_intr_establish(lpcintrtag_t tag, uint irq, 163 int ipl, int type, int (*func)(void *), void *arg) 164 { 165 gemini_lpchc_softc_t *sc = tag; 166 bus_space_tag_t iot = sc->sc_iot; 167 bus_space_handle_t ioh = sc->sc_ioh; 168 uint32_t bit; 169 boolean_t isedge; 170 boolean_t ishigh; 171 void *ih; 172 173 isedge = ((type == IST_EDGE_RISING) || (type == IST_EDGE_FALLING)); 174 ishigh = ((type == IST_EDGE_RISING) || (type == IST_LEVEL_HIGH)); 175 176 if (irq >= GEMINI_LPCHC_NSERIRQ) { 177 printf("%s: bad irq %d\n", __FUNCTION__, irq); 178 return NULL; 179 } 180 #if 0 181 bit = 1 << irq; 182 #else 183 bit = (1 << GEMINI_LPCHC_NSERIRQ) -1; /* XXX */ 184 #endif 185 186 /* set IRQ type */ 187 gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQTYP, 188 bit, isedge); 189 190 /* set IRQ polarity */ 191 gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQPOLARITY, 192 bit, ishigh); 193 194 /* ack a-priori edge status */ 195 if (isedge) 196 gemini_lpchc_sirq_ack(iot, ioh, bit); 197 198 if (gemini_lpchc_intrq_empty(sc)) 199 gemini_lpchc_sirq_enable(iot, ioh); 200 201 ih = gemini_lpchc_intrq_insert(sc, func, arg, bit, isedge); 202 if (ih == NULL) 203 if (gemini_lpchc_intrq_empty(sc)) 204 gemini_lpchc_sirq_disable(iot, ioh); 205 206 return ih; 207 } 208 209 void 210 gemini_lpchc_intr_disestablish(lpcintrtag_t tag, void *ih) 211 { 212 gemini_lpchc_softc_t *sc = tag; 213 214 gemini_lpchc_intrq_remove(sc, ih); 215 if (gemini_lpchc_intrq_empty(sc)) 216 gemini_lpchc_sirq_disable(sc->sc_iot, sc->sc_ioh); 217 } 218 219 int 220 gemini_lpchc_intr(void *arg) 221 { 222 gemini_lpchc_softc_t *sc = arg; 223 int rv; 224 225 printf("%s: enter\n", __FUNCTION__); 226 rv = gemini_lpchc_intrq_dispatch(sc); 227 printf("%s: exit\n", __FUNCTION__); 228 229 return rv; 230 } 231 232