xref: /openbsd/sys/dev/sbus/bpp.c (revision 274d7c50)
1 /*	$OpenBSD: bpp.c,v 1.5 2016/03/14 18:01:18 stefan Exp $	*/
2 /*	$NetBSD: bpp.c,v 1.25 2005/12/11 12:23:44 christos Exp $ */
3 
4 /*-
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Paul Kranenburg.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/ioctl.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/errno.h>
38 #include <sys/device.h>
39 #include <sys/malloc.h>
40 #include <sys/proc.h>
41 #include <sys/vnode.h>
42 #include <sys/conf.h>
43 
44 #include <machine/autoconf.h>
45 #include <machine/bus.h>
46 #include <machine/conf.h>
47 #include <machine/intr.h>
48 
49 #include <dev/ic/lsi64854reg.h>
50 #include <dev/ic/lsi64854var.h>
51 
52 #include <dev/sbus/sbusvar.h>
53 #include <dev/sbus/bppreg.h>
54 
55 #define splbpp()	spltty()	/* XXX */
56 
57 #ifdef DEBUG
58 #define DPRINTF(x) do { if (bppdebug) printf x ; } while (0)
59 int bppdebug = 1;
60 #else
61 #define DPRINTF(x)
62 #endif
63 
64 #if 0
65 struct bpp_param {
66 	int	bpp_dss;		/* data setup to strobe */
67 	int	bpp_dsw;		/* data strobe width */
68 	int	bpp_outputpins;		/* Select/Autofeed/Init pins */
69 	int	bpp_inputpins;		/* Error/Select/Paperout pins */
70 };
71 #endif
72 
73 struct hwstate {
74 	u_int16_t	hw_hcr;		/* Hardware config register */
75 	u_int16_t	hw_ocr;		/* Operation config register */
76 	u_int8_t	hw_tcr;		/* Transfer Control register */
77 	u_int8_t	hw_or;		/* Output register */
78 	u_int16_t	hw_irq;		/* IRQ; polarity bits only */
79 };
80 
81 struct bpp_softc {
82 	struct lsi64854_softc	sc_lsi64854;	/* base device */
83 
84 	size_t		sc_bufsz;		/* temp buffer */
85 	caddr_t		sc_buf;
86 
87 	int		sc_error;		/* bottom-half error */
88 	int		sc_flags;
89 #define BPP_LOCKED	0x01		/* DMA in progress */
90 #define BPP_WANT	0x02		/* Waiting for DMA */
91 
92 	/* Hardware state */
93 	struct hwstate		sc_hwstate;
94 };
95 
96 int	bppmatch(struct device *, void *, void *);
97 void	bppattach(struct device *, struct device *, void *);
98 int	bppintr		(void *);
99 void	bpp_setparams(struct bpp_softc *, struct hwstate *);
100 
101 const struct cfattach bpp_ca = {
102 	sizeof(struct bpp_softc), bppmatch, bppattach
103 };
104 
105 struct cfdriver bpp_cd = {
106 	NULL, "bpp", DV_DULL
107 };
108 
109 #define BPPUNIT(dev)	(minor(dev))
110 
111 int
112 bppmatch(struct device *parent, void *vcf, void *aux)
113 {
114 	struct sbus_attach_args *sa = aux;
115 
116 	return (strcmp("SUNW,bpp", sa->sa_name) == 0);
117 }
118 
119 void
120 bppattach(struct device *parent, struct device *self, void *aux)
121 {
122 	struct sbus_attach_args *sa = aux;
123 	struct bpp_softc *dsc = (void *)self;
124 	struct lsi64854_softc *sc = &dsc->sc_lsi64854;
125 	int burst, sbusburst;
126 	int node;
127 
128 	node = sa->sa_node;
129 
130 	sc->sc_bustag = sa->sa_bustag;
131 	sc->sc_dmatag = sa->sa_dmatag;
132 
133 	/* Map device registers */
134 	if (sa->sa_npromvaddrs != 0) {
135 		if (sbus_bus_map(sa->sa_bustag, 0, sa->sa_promvaddrs[0],
136 		    sa->sa_size,		/* ???? */
137 		    BUS_SPACE_MAP_PROMADDRESS, 0, &sc->sc_regs) != 0) {
138 			printf(": cannot map registers\n");
139 			return;
140 		}
141 	} else if (sbus_bus_map(sa->sa_bustag, sa->sa_slot, sa->sa_offset,
142 	    sa->sa_size, 0, 0, &sc->sc_regs) != 0) {
143 		printf(": cannot map registers\n");
144 		return;
145 	}
146 
147 	/* Check for the interrupt property */
148 	if (sa->sa_nintr == 0) {
149 		printf(": no interrupt property\n");
150 		return;
151 	}
152 
153 	/*
154 	 * Get transfer burst size from PROM and plug it into the
155 	 * controller registers. This is needed on the Sun4m; do
156 	 * others need it too?
157 	 */
158 	sbusburst = ((struct sbus_softc *)parent)->sc_burst;
159 	if (sbusburst == 0)
160 		sbusburst = SBUS_BURST_32 - 1; /* 1->16 */
161 
162 	burst = getpropint(node, "burst-sizes", -1);
163 	if (burst == -1)
164 		/* take SBus burst sizes */
165 		burst = sbusburst;
166 
167 	/* Clamp at parent's burst sizes */
168 	burst &= sbusburst;
169 	sc->sc_burst = (burst & SBUS_BURST_32) ? 32 :
170 		       (burst & SBUS_BURST_16) ? 16 : 0;
171 
172 	/* Initialize the DMA channel */
173 	sc->sc_channel = L64854_CHANNEL_PP;
174 	if (lsi64854_attach(sc) != 0)
175 		return;
176 
177 	/* Establish interrupt handler */
178 	sc->sc_intrchain = bppintr;
179 	sc->sc_intrchainarg = dsc;
180 	(void)bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_TTY, 0,
181 	    bppintr, sc, self->dv_xname);
182 
183 	/* Allocate buffer XXX - should actually use dmamap_uio() */
184 	dsc->sc_bufsz = 1024;
185 	dsc->sc_buf = malloc(dsc->sc_bufsz, M_DEVBUF, M_NOWAIT);
186 
187 	/* XXX read default state */
188     {
189 	bus_space_handle_t h = sc->sc_regs;
190 	struct hwstate *hw = &dsc->sc_hwstate;
191 	int ack_rate = sa->sa_frequency/1000000;
192 
193 	hw->hw_hcr = bus_space_read_2(sc->sc_bustag, h, L64854_REG_HCR);
194 	hw->hw_ocr = bus_space_read_2(sc->sc_bustag, h, L64854_REG_OCR);
195 	hw->hw_tcr = bus_space_read_1(sc->sc_bustag, h, L64854_REG_TCR);
196 	hw->hw_or = bus_space_read_1(sc->sc_bustag, h, L64854_REG_OR);
197 
198 	DPRINTF(("bpp: hcr %x ocr %x tcr %x or %x\n",
199 		 hw->hw_hcr, hw->hw_ocr, hw->hw_tcr, hw->hw_or));
200 	/* Set these to sane values */
201 	hw->hw_hcr = ((ack_rate<<BPP_HCR_DSS_SHFT)&BPP_HCR_DSS_MASK)
202 		| ((ack_rate<<BPP_HCR_DSW_SHFT)&BPP_HCR_DSW_MASK);
203 	hw->hw_ocr |= BPP_OCR_ACK_OP;
204     }
205 }
206 
207 void
208 bpp_setparams(struct bpp_softc *sc, struct hwstate *hw)
209 {
210 	u_int16_t irq;
211 	bus_space_tag_t t = sc->sc_lsi64854.sc_bustag;
212 	bus_space_handle_t h = sc->sc_lsi64854.sc_regs;
213 
214 	bus_space_write_2(t, h, L64854_REG_HCR, hw->hw_hcr);
215 	bus_space_write_2(t, h, L64854_REG_OCR, hw->hw_ocr);
216 	bus_space_write_1(t, h, L64854_REG_TCR, hw->hw_tcr);
217 	bus_space_write_1(t, h, L64854_REG_OR, hw->hw_or);
218 
219 	/* Only change IRP settings in interrupt status register */
220 	irq = bus_space_read_2(t, h, L64854_REG_ICR);
221 	irq &= ~BPP_ALLIRP;
222 	irq |= (hw->hw_irq & BPP_ALLIRP);
223 	bus_space_write_2(t, h, L64854_REG_ICR, irq);
224 	DPRINTF(("bpp_setparams: hcr %x ocr %x tcr %x or %x, irq %x\n",
225 		 hw->hw_hcr, hw->hw_ocr, hw->hw_tcr, hw->hw_or, irq));
226 }
227 
228 int
229 bppopen(dev_t dev, int flags, int mode, struct proc *p)
230 {
231 	int unit = BPPUNIT(dev);
232 	struct bpp_softc *sc;
233 	struct lsi64854_softc *lsi;
234 	u_int16_t irq;
235 	int s;
236 
237 	if (unit >= bpp_cd.cd_ndevs)
238 		return (ENXIO);
239 	if ((sc = bpp_cd.cd_devs[unit]) == NULL)
240 		return (ENXIO);
241 
242 	lsi = &sc->sc_lsi64854;
243 
244 	/* Set default parameters */
245 	s = splbpp();
246 	bpp_setparams(sc, &sc->sc_hwstate);
247 	splx(s);
248 
249 	/* Enable interrupts */
250 	irq = BPP_ERR_IRQ_EN;
251 	irq |= sc->sc_hwstate.hw_irq;
252 	bus_space_write_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR, irq);
253 	return (0);
254 }
255 
256 int
257 bppclose(dev_t dev, int flags, int mode, struct proc *p)
258 {
259 	struct bpp_softc *sc = bpp_cd.cd_devs[BPPUNIT(dev)];
260 	struct lsi64854_softc *lsi = &sc->sc_lsi64854;
261 	u_int16_t irq;
262 
263 	/* Turn off all interrupt enables */
264 	irq = sc->sc_hwstate.hw_irq | BPP_ALLIRQ;
265 	irq &= ~BPP_ALLEN;
266 	bus_space_write_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR, irq);
267 
268 	sc->sc_flags = 0;
269 	return (0);
270 }
271 
272 int
273 bppwrite(dev_t dev, struct uio *uio, int flags)
274 {
275 	struct bpp_softc *sc = bpp_cd.cd_devs[BPPUNIT(dev)];
276 	struct lsi64854_softc *lsi = &sc->sc_lsi64854;
277 	int error = 0;
278 	int s;
279 
280 	/*
281 	 * Wait until the DMA engine is free.
282 	 */
283 	s = splbpp();
284 	while ((sc->sc_flags & BPP_LOCKED) != 0) {
285 		if ((flags & IO_NDELAY) != 0) {
286 			splx(s);
287 			return (EWOULDBLOCK);
288 		}
289 
290 		sc->sc_flags |= BPP_WANT;
291 		error = tsleep(sc->sc_buf, PZERO | PCATCH, "bppwrite", 0);
292 		if (error != 0) {
293 			splx(s);
294 			return (error);
295 		}
296 	}
297 	sc->sc_flags |= BPP_LOCKED;
298 	splx(s);
299 
300 	/*
301 	 * Move data from user space into our private buffer
302 	 * and start DMA.
303 	 */
304 	while (uio->uio_resid > 0) {
305 		caddr_t bp = sc->sc_buf;
306 		size_t len = ulmin(sc->sc_bufsz, uio->uio_resid);
307 
308 		if ((error = uiomove(bp, len, uio)) != 0)
309 			break;
310 
311 		while (len > 0) {
312 			u_int8_t tcr;
313 			size_t size = len;
314 			DMA_SETUP(lsi, &bp, &len, 0, &size);
315 
316 #ifdef DEBUG
317 			if (bppdebug) {
318 				size_t i;
319 				printf("bpp: writing %ld : ", len);
320 				for (i=0; i<len; i++) printf("%c(0x%x)", bp[i], bp[i]);
321 				printf("\n");
322 			}
323 #endif
324 
325 			/* Clear direction control bit */
326 			tcr = bus_space_read_1(lsi->sc_bustag, lsi->sc_regs,
327 						L64854_REG_TCR);
328 			tcr &= ~BPP_TCR_DIR;
329 			bus_space_write_1(lsi->sc_bustag, lsi->sc_regs,
330 					  L64854_REG_TCR, tcr);
331 
332 			/* Enable DMA */
333 			s = splbpp();
334 			DMA_GO(lsi);
335 			error = tsleep(sc, PZERO | PCATCH, "bppdma", 0);
336 			splx(s);
337 			if (error != 0)
338 				goto out;
339 
340 			/* Bail out if bottom half reported an error */
341 			if ((error = sc->sc_error) != 0)
342 				goto out;
343 
344 			/*
345 			 * DMA_INTR() does this part.
346 			 *
347 			 * len -= size;
348 			 */
349 		}
350 	}
351 
352 out:
353 	DPRINTF(("bpp done %x\n", error));
354 	s = splbpp();
355 	sc->sc_flags &= ~BPP_LOCKED;
356 	if ((sc->sc_flags & BPP_WANT) != 0) {
357 		sc->sc_flags &= ~BPP_WANT;
358 		wakeup(sc->sc_buf);
359 	}
360 	splx(s);
361 	return (error);
362 }
363 
364 int
365 bppioctl(dev_t dev, u_long cmd, caddr_t	data, int flag, struct proc *p)
366 {
367 	int error = 0;
368 
369 	switch(cmd) {
370 	default:
371 		error = ENODEV;
372 		break;
373 	}
374 
375 	return (error);
376 }
377 
378 int
379 bppintr(void *arg)
380 {
381 	struct bpp_softc *sc = arg;
382 	struct lsi64854_softc *lsi = &sc->sc_lsi64854;
383 	u_int16_t irq;
384 
385 	/* First handle any possible DMA interrupts */
386 	if (DMA_INTR(lsi) == -1)
387 		sc->sc_error = 1;
388 
389 	irq = bus_space_read_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR);
390 	/* Ack all interrupts */
391 	bus_space_write_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR,
392 			  irq | BPP_ALLIRQ);
393 
394 	DPRINTF(("bpp_intr: %x\n", irq));
395 	/* Did our device interrupt? */
396 	if ((irq & BPP_ALLIRQ) == 0)
397 		return (0);
398 
399 	if ((sc->sc_flags & BPP_LOCKED) != 0)
400 		wakeup(sc);
401 	else if ((sc->sc_flags & BPP_WANT) != 0) {
402 		sc->sc_flags &= ~BPP_WANT;
403 		wakeup(sc->sc_buf);
404 	}
405 	return (1);
406 }
407