1 /* $NetBSD: gemini_lpchc.c,v 1.5 2020/11/20 18:10:07 thorpej 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.5 2020/11/20 18:10:07 thorpej 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/kmem.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 = kmem_zalloc(sizeof(*iqp), KM_SLEEP); 98 iqp->iq_func = func; 99 iqp->iq_arg = arg; 100 iqp->iq_bit = bit; 101 iqp->iq_isedge = isedge; 102 SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q); 103 104 return (void *)iqp; 105 } 106 107 static inline void 108 gemini_lpchc_intrq_remove(gemini_lpchc_softc_t *sc, void *cookie) 109 { 110 gemini_lpchc_intrq_t *iqp; 111 112 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) { 113 if ((void *)iqp == cookie) { 114 SIMPLEQ_REMOVE(&sc->sc_intrq, 115 iqp, gemini_lpchc_intrq, iq_q); 116 kmem_free(iqp, sizeof(*iqp)); 117 return; 118 } 119 } 120 } 121 122 static inline int 123 gemini_lpchc_intrq_dispatch(gemini_lpchc_softc_t *sc) 124 { 125 gemini_lpchc_intrq_t *iqp; 126 uint32_t r; 127 int rv = 0; 128 129 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_SERIRQSTS); 130 r &= ~LPCHC_SERIRQSTS_RESV; 131 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) { 132 if ((r & iqp->iq_bit) != 0) { 133 if (iqp->iq_isedge) { 134 gemini_lpchc_sirq_ack(sc->sc_iot, sc->sc_ioh, 135 iqp->iq_bit); 136 } 137 rv |= (*iqp->iq_func)(iqp->iq_arg); 138 } 139 } 140 return (rv != 0); 141 } 142 143 void 144 gemini_lpchc_init(lpcintrtag_t tag) 145 { 146 gemini_lpchc_softc_t *sc = tag; 147 uint32_t r; 148 149 gemini_lpchc_intrq_init(sc); 150 151 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR); 152 r |= LPCHC_CSR_BEN; 153 bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR, r); 154 } 155 156 void * 157 gemini_lpchc_intr_establish(lpcintrtag_t tag, uint irq, 158 int ipl, int type, int (*func)(void *), void *arg) 159 { 160 gemini_lpchc_softc_t *sc = tag; 161 bus_space_tag_t iot = sc->sc_iot; 162 bus_space_handle_t ioh = sc->sc_ioh; 163 uint32_t bit; 164 boolean_t isedge; 165 boolean_t ishigh; 166 void *ih; 167 168 isedge = ((type == IST_EDGE_RISING) || (type == IST_EDGE_FALLING)); 169 ishigh = ((type == IST_EDGE_RISING) || (type == IST_LEVEL_HIGH)); 170 171 if (irq >= GEMINI_LPCHC_NSERIRQ) { 172 printf("%s: bad irq %d\n", __FUNCTION__, irq); 173 return NULL; 174 } 175 #if 0 176 bit = 1 << irq; 177 #else 178 bit = (1 << GEMINI_LPCHC_NSERIRQ) -1; /* XXX */ 179 #endif 180 181 /* set IRQ type */ 182 gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQTYP, 183 bit, isedge); 184 185 /* set IRQ polarity */ 186 gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQPOLARITY, 187 bit, ishigh); 188 189 /* ack a-priori edge status */ 190 if (isedge) 191 gemini_lpchc_sirq_ack(iot, ioh, bit); 192 193 if (gemini_lpchc_intrq_empty(sc)) 194 gemini_lpchc_sirq_enable(iot, ioh); 195 196 ih = gemini_lpchc_intrq_insert(sc, func, arg, bit, isedge); 197 return ih; 198 } 199 200 void 201 gemini_lpchc_intr_disestablish(lpcintrtag_t tag, void *ih) 202 { 203 gemini_lpchc_softc_t *sc = tag; 204 205 gemini_lpchc_intrq_remove(sc, ih); 206 if (gemini_lpchc_intrq_empty(sc)) 207 gemini_lpchc_sirq_disable(sc->sc_iot, sc->sc_ioh); 208 } 209 210 int 211 gemini_lpchc_intr(void *arg) 212 { 213 gemini_lpchc_softc_t *sc = arg; 214 int rv; 215 216 printf("%s: enter\n", __FUNCTION__); 217 rv = gemini_lpchc_intrq_dispatch(sc); 218 printf("%s: exit\n", __FUNCTION__); 219 220 return rv; 221 } 222