1 /* $OpenBSD: xlights.c,v 1.10 2020/01/10 04:13:31 cheloha Exp $ */ 2 /* 3 * Copyright (c) 2007 Gordon Willem Klok <gwk@openbsd,org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/param.h> 18 #include <sys/systm.h> 19 #include <sys/proc.h> 20 #include <sys/device.h> 21 #include <sys/kthread.h> 22 #include <sys/timeout.h> 23 #include <dev/ofw/openfirm.h> 24 25 #include <machine/bus.h> 26 #include <machine/autoconf.h> 27 #include <macppc/dev/dbdma.h> 28 #include <macppc/dev/i2sreg.h> 29 #include <macppc/pci/macobio.h> 30 31 struct xlights_softc { 32 struct device sc_dev; 33 int sc_node; 34 int sc_intr; 35 uint32_t sc_freq; 36 int sc_dmasts; 37 38 u_char *sc_reg; 39 dbdma_regmap_t *sc_dma; 40 bus_dma_tag_t sc_dmat; 41 bus_dmamap_t sc_bufmap; 42 bus_dma_segment_t sc_bufseg[1]; 43 dbdma_t sc_dbdma; 44 dbdma_command_t *sc_dmacmd; 45 uint32_t *sc_buf; 46 uint32_t *sc_bufpos; 47 48 struct timeout sc_tmo; 49 }; 50 51 int xlights_match(struct device *, void *, void *); 52 void xlights_attach(struct device *, struct device *, void *); 53 int xlights_intr(void *); 54 void xlights_startdma(struct xlights_softc *); 55 void xlights_deferred(void *); 56 void xlights_theosDOT(void *); 57 void xlights_timeout(void *); 58 59 struct cfattach xlights_ca = { 60 sizeof(struct xlights_softc), xlights_match, 61 xlights_attach 62 }; 63 64 struct cfdriver xlights_cd = { 65 NULL, "xlights", DV_DULL 66 }; 67 68 #define BL_BUFSZ PAGE_SIZE 69 #define BL_DBDMA_CMDS 2 70 71 int 72 xlights_match(struct device *parent, void *arg, void *aux) 73 { 74 struct confargs *ca = aux; 75 int soundbus, soundchip, error; 76 char compat[32]; 77 78 if (strcmp(ca->ca_name, "i2s") != 0) 79 return 0; 80 if ((soundbus = OF_child(ca->ca_node)) == 0) 81 return 0; 82 if ((soundchip = OF_child(soundbus)) == 0) 83 return 0; 84 85 error = OF_getprop(soundchip, "virtual", compat, sizeof(compat)); 86 if (error == -1) { 87 error = OF_getprop(soundchip, "name", compat, 88 sizeof(compat)); 89 90 if (error == -1 || (strcmp(compat, "lightshow")) != 0) 91 return 0; 92 } 93 94 /* we require at least 4 registers */ 95 if (ca->ca_nreg / sizeof(int) < 4) 96 return 0; 97 /* we require at least 3 interrupts */ 98 if (ca->ca_nintr / sizeof(int) < 6) 99 return 0; 100 101 return 1; 102 } 103 104 void 105 xlights_attach(struct device *parent, struct device *self, void *aux) 106 { 107 struct xlights_softc *sc = (struct xlights_softc *)self; 108 struct confargs *ca = aux; 109 int nseg, error, intr[6]; 110 u_int32_t reg[4]; 111 int type; 112 113 sc->sc_node = OF_child(ca->ca_node); 114 115 OF_getprop(sc->sc_node, "reg", reg, sizeof(reg)); 116 ca->ca_reg[0] += ca->ca_baseaddr; 117 ca->ca_reg[2] += ca->ca_baseaddr; 118 119 if ((sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1])) == NULL) { 120 printf(": cannot map registers\n"); 121 return; 122 } 123 sc->sc_dmat = ca->ca_dmat; 124 125 if ((sc->sc_dma = mapiodev(ca->ca_reg[2], ca->ca_reg[3])) == NULL) { 126 printf(": cannot map DMA registers\n"); 127 goto nodma; 128 } 129 130 if ((sc->sc_dbdma = dbdma_alloc(sc->sc_dmat, BL_DBDMA_CMDS)) == NULL) { 131 printf(": cannot alloc DMA descriptors\n"); 132 goto nodbdma; 133 } 134 sc->sc_dmacmd = sc->sc_dbdma->d_addr; 135 136 if ((error = bus_dmamem_alloc(sc->sc_dmat, BL_BUFSZ, 0, 0, 137 sc->sc_bufseg, 1, &nseg, BUS_DMA_NOWAIT))) { 138 printf(": cannot allocate DMA mem (%d)\n", error); 139 goto nodmamem; 140 } 141 142 if ((error = bus_dmamem_map(sc->sc_dmat, sc->sc_bufseg, nseg, 143 BL_BUFSZ, (caddr_t *)&sc->sc_buf, BUS_DMA_NOWAIT))) { 144 printf(": cannot map DMA mem (%d)\n", error); 145 goto nodmamap; 146 } 147 sc->sc_bufpos = sc->sc_buf; 148 149 if ((error = bus_dmamap_create(sc->sc_dmat, BL_BUFSZ, 1, BL_BUFSZ, 0, 150 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_bufmap))) { 151 printf(": cannot create DMA map (%d)\n", error); 152 goto nodmacreate; 153 } 154 155 if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_bufmap, sc->sc_buf, 156 BL_BUFSZ, NULL, BUS_DMA_NOWAIT))) { 157 printf(": cannot load DMA map (%d)\n", error); 158 goto nodmaload; 159 } 160 /* XXX: Should probably extract this from the clock data 161 * property of the soundchip node */ 162 sc->sc_freq = 16384; 163 164 OF_getprop(sc->sc_node, "interrupts", intr, sizeof(intr)); 165 /* output interrupt */ 166 sc->sc_intr = intr[2]; 167 type = intr[3] ? IST_LEVEL : IST_EDGE; 168 169 printf(": irq %d\n", sc->sc_intr); 170 171 macobio_enable(I2SClockOffset, I2S0EN); 172 out32rb(sc->sc_reg + I2S_INT, I2S_INT_CLKSTOPPEND); 173 macobio_disable(I2SClockOffset, I2S0CLKEN); 174 for (error = 0; error < 1000; error++) { 175 if (in32rb(sc->sc_reg + I2S_INT) & I2S_INT_CLKSTOPPEND) { 176 error = 0; 177 break; 178 } 179 delay(1); 180 } 181 if (error) { 182 printf("%s: i2s timeout\n", sc->sc_dev.dv_xname); 183 goto nodmaload; 184 } 185 186 mac_intr_establish(parent, sc->sc_intr, intr[3] ? IST_LEVEL : 187 type, IPL_TTY, xlights_intr, sc, sc->sc_dev.dv_xname); 188 189 out32rb(sc->sc_reg + I2S_FORMAT, CLKSRC_VS); 190 macobio_enable(I2SClockOffset, I2S0CLKEN); 191 192 kthread_create_deferred(xlights_deferred, sc); 193 timeout_set(&sc->sc_tmo, xlights_timeout, sc); 194 return; 195 nodmaload: 196 bus_dmamap_destroy(sc->sc_dmat, sc->sc_bufmap); 197 nodmacreate: 198 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_buf, BL_BUFSZ); 199 nodmamap: 200 bus_dmamem_free(sc->sc_dmat, sc->sc_bufseg, nseg); 201 nodmamem: 202 dbdma_free(sc->sc_dbdma); 203 nodbdma: 204 unmapiodev((void *)sc->sc_dma, ca->ca_reg[3]); 205 nodma: 206 unmapiodev(sc->sc_reg, ca->ca_reg[1]); 207 } 208 209 void 210 xlights_deferred(void *v) 211 { 212 struct xlights_softc *sc = (struct xlights_softc *)v; 213 214 kthread_create(xlights_theosDOT, v, NULL, sc->sc_dev.dv_xname); 215 } 216 217 /* 218 * xserv has two rows of leds laid out as follows 219 * 25 26 27 28 29 30 31 00 220 * 17 18 19 20 21 22 23 24 221 */ 222 223 char ledfollow_0[16] = { 25, 26, 27, 28, 29, 30, 31, 00, 224 24, 23, 22, 21, 20, 19, 18, 17 }; 225 char ledfollow_1[16] = { 17, 25, 26, 27, 28, 29, 30, 31, 226 00, 24, 23, 22, 21, 20, 19, 18 }; 227 char ledfollow_2[16] = { 18, 17, 25, 26, 27, 28, 29, 30, 228 31, 00, 24, 23, 22, 21, 20, 19 }; 229 void 230 xlights_theosDOT(void *v) 231 { 232 struct xlights_softc *sc = (struct xlights_softc *)v; 233 uint32_t *p; 234 int k, nsamp; 235 int ledpos, ledpos_high, ledpos_med, ledpos_dim; 236 uint32_t val; 237 238 while (1) { 239 /* 240 * ldavg 0 - .5 sec -> (8192 / 16) 241 * ldavg 1 - 1 sec -> (16384 / 16) 242 * ldavg 2 - 1.5 sec -> (24576 / 16) 243 */ 244 nsamp = sc->sc_freq + 245 sc->sc_freq / FSCALE * averunnable.ldavg[0]; 246 nsamp /= 16; /* scale, per led */ 247 nsamp /= 4; /* scale, why?, sizeof(uint32_t)? */ 248 for (ledpos = 0; ledpos < 16; ledpos++) { 249 ledpos_high = ledfollow_0[ledpos]; 250 ledpos_med = ledfollow_1[ledpos]; 251 ledpos_dim = ledfollow_2[ledpos]; 252 p = sc->sc_bufpos; 253 254 for (k = 0; k < nsamp;) { 255 if (p - sc->sc_buf < 256 BL_BUFSZ / sizeof(uint32_t)) { 257 val = (1 << ledpos_high); 258 if ((k % 4) == 0) 259 val |= (1 << ledpos_med); 260 if ((k % 16) == 0) 261 val |= (1 << ledpos_dim); 262 *p = val; 263 264 p++; 265 k++; 266 } else { 267 xlights_startdma(sc); 268 while (sc->sc_dmasts) 269 tsleep_nsec(sc->sc_buf, PWAIT, 270 "blinken", INFSLP); 271 p = sc->sc_buf; 272 } 273 } 274 sc->sc_bufpos = p; 275 } 276 } 277 } 278 279 void 280 xlights_startdma(struct xlights_softc *sc) 281 { 282 dbdma_command_t *cmdp = sc->sc_dmacmd; 283 284 sc->sc_dmasts = 1; 285 timeout_add_msec(&sc->sc_tmo, 2500); 286 287 DBDMA_BUILD(cmdp, DBDMA_CMD_OUT_LAST, 0, 288 sc->sc_bufmap->dm_segs[0].ds_len, 289 sc->sc_bufmap->dm_segs[0].ds_addr, DBDMA_INT_ALWAYS, 290 DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); 291 cmdp++; 292 293 DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_NEVER, 294 DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); 295 296 dbdma_start(sc->sc_dma, sc->sc_dbdma); 297 } 298 299 void 300 xlights_timeout(void *v) 301 { 302 struct xlights_softc *sc = (struct xlights_softc *)v; 303 304 dbdma_reset(sc->sc_dma); 305 timeout_del(&sc->sc_tmo); 306 sc->sc_dmasts = 0; 307 wakeup(sc->sc_buf); 308 } 309 310 int 311 xlights_intr(void *v) 312 { 313 struct xlights_softc *sc = (struct xlights_softc *)v; 314 int status; 315 dbdma_command_t *cmd; 316 317 cmd = sc->sc_dmacmd; 318 status = dbdma_ld16(&cmd->d_status); 319 if (sc->sc_dmasts) { 320 sc->sc_dmasts = 0; 321 timeout_del(&sc->sc_tmo); 322 wakeup(sc->sc_buf); 323 return (1); 324 } 325 return (0); 326 } 327