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