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