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