1 /* $OpenBSD: xlights.c,v 1.11 2022/03/13 12:33:01 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/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 const 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
xlights_match(struct device * parent,void * arg,void * aux)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
xlights_attach(struct device * parent,struct device * self,void * aux)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
xlights_deferred(void * v)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
xlights_theosDOT(void * v)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
xlights_startdma(struct xlights_softc * sc)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
xlights_timeout(void * v)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
xlights_intr(void * v)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