xref: /openbsd-src/sys/arch/macppc/dev/xlights.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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