bpp.c revision 1.35 1 /* $NetBSD: bpp.c,v 1.35 2008/04/13 04:55:53 tsutsui Exp $ */
2
3 /*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Kranenburg.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: bpp.c,v 1.35 2008/04/13 04:55:53 tsutsui Exp $");
41
42 #include <sys/param.h>
43 #include <sys/ioctl.h>
44 #include <sys/fcntl.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/vnode.h>
48 #include <sys/poll.h>
49 #include <sys/select.h>
50 #include <sys/malloc.h>
51 #include <sys/proc.h>
52 #include <sys/signalvar.h>
53 #include <sys/conf.h>
54 #include <sys/errno.h>
55 #include <sys/device.h>
56
57 #include <sys/bus.h>
58 #include <sys/intr.h>
59 #include <machine/autoconf.h>
60
61 #include <dev/ic/lsi64854reg.h>
62 #include <dev/ic/lsi64854var.h>
63
64 #include <dev/sbus/sbusvar.h>
65 #include <dev/sbus/bppreg.h>
66
67 #include "ioconf.h"
68
69 #define splbpp() spltty() /* XXX */
70
71 #ifdef DEBUG
72 #define DPRINTF(x) do { if (bppdebug) printf x ; } while (0)
73 int bppdebug = 1;
74 #else
75 #define DPRINTF(x)
76 #endif
77
78 #if 0
79 struct bpp_param {
80 int bpp_dss; /* data setup to strobe */
81 int bpp_dsw; /* data strobe width */
82 int bpp_outputpins; /* Select/Autofeed/Init pins */
83 int bpp_inputpins; /* Error/Select/Paperout pins */
84 };
85 #endif
86
87 struct hwstate {
88 uint16_t hw_hcr; /* Hardware config register */
89 uint16_t hw_ocr; /* Operation config register */
90 uint8_t hw_tcr; /* Transfer Control register */
91 uint8_t hw_or; /* Output register */
92 uint16_t hw_irq; /* IRQ; polarity bits only */
93 };
94
95 struct bpp_softc {
96 struct lsi64854_softc sc_lsi64854; /* base device */
97 struct sbusdev sc_sd; /* sbus device */
98
99 size_t sc_bufsz; /* temp buffer */
100 uint8_t *sc_buf;
101
102 int sc_error; /* bottom-half error */
103 int sc_flags;
104 #define BPP_OPEN 0x01 /* Device is open */
105 #define BPP_XCLUDE 0x02 /* Exclusive-open mode */
106 #define BPP_ASYNC 0x04 /* Asynchronous I/O mode */
107 #define BPP_LOCKED 0x08 /* DMA in progress */
108 #define BPP_WANT 0x10 /* Waiting for DMA */
109
110 struct selinfo sc_rsel;
111 struct selinfo sc_wsel;
112 struct proc *sc_asyncproc; /* Process to notify if async */
113
114 /* Hardware state */
115 struct hwstate sc_hwdefault;
116 struct hwstate sc_hwcurrent;
117 };
118
119 static int bppmatch(device_t, cfdata_t, void *);
120 static void bppattach(device_t, device_t, void *);
121 static int bppintr(void *);
122 static void bpp_setparams(struct bpp_softc *, struct hwstate *);
123
124 CFATTACH_DECL_NEW(bpp, sizeof(struct bpp_softc),
125 bppmatch, bppattach, NULL, NULL);
126
127 dev_type_open(bppopen);
128 dev_type_close(bppclose);
129 dev_type_write(bppwrite);
130 dev_type_ioctl(bppioctl);
131 dev_type_poll(bpppoll);
132 dev_type_kqfilter(bppkqfilter);
133
134 const struct cdevsw bpp_cdevsw = {
135 bppopen, bppclose, noread, bppwrite, bppioctl,
136 nostop, notty, bpppoll, nommap, bppkqfilter, D_TTY
137 };
138
139 #define BPPUNIT(dev) (minor(dev))
140
141
142 int
143 bppmatch(device_t parent, cfdata_t cf, void *aux)
144 {
145 struct sbus_attach_args *sa = aux;
146
147 return strcmp("SUNW,bpp", sa->sa_name) == 0;
148 }
149
150 void
151 bppattach(device_t parent, device_t self, void *aux)
152 {
153 struct bpp_softc *dsc = device_private(self);
154 struct lsi64854_softc *sc = &dsc->sc_lsi64854;
155 struct sbus_softc *sbsc = device_private(parent);
156 struct sbus_attach_args *sa = aux;
157 int burst, sbusburst;
158 int node;
159
160 sc->sc_dev = self;
161
162 selinit(&dsc->sc_rsel);
163 selinit(&dsc->sc_wsel);
164
165 sc->sc_bustag = sa->sa_bustag;
166 sc->sc_dmatag = sa->sa_dmatag;
167 node = sa->sa_node;
168
169 /* Map device registers */
170 if (sbus_bus_map(sa->sa_bustag,
171 sa->sa_slot, sa->sa_offset, sa->sa_size,
172 0, &sc->sc_regs) != 0) {
173 aprint_error(": cannot map registers\n");
174 return;
175 }
176
177 /*
178 * Get transfer burst size from PROM and plug it into the
179 * controller registers. This is needed on the Sun4m; do
180 * others need it too?
181 */
182 sbusburst = sbsc->sc_burst;
183 if (sbusburst == 0)
184 sbusburst = SBUS_BURST_32 - 1; /* 1->16 */
185
186 burst = prom_getpropint(node, "burst-sizes", -1);
187 if (burst == -1)
188 /* take SBus burst sizes */
189 burst = sbusburst;
190
191 /* Clamp at parent's burst sizes */
192 burst &= sbusburst;
193 sc->sc_burst = (burst & SBUS_BURST_32) ? 32 :
194 (burst & SBUS_BURST_16) ? 16 : 0;
195
196 /* Join the Sbus device family */
197 dsc->sc_sd.sd_reset = NULL;
198 sbus_establish(&dsc->sc_sd, self);
199
200 /* Initialize the DMA channel */
201 sc->sc_channel = L64854_CHANNEL_PP;
202 lsi64854_attach(sc);
203
204 /* Establish interrupt handler */
205 if (sa->sa_nintr) {
206 sc->sc_intrchain = bppintr;
207 sc->sc_intrchainarg = dsc;
208 (void)bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_TTY,
209 bppintr, sc);
210 }
211
212 /* Allocate buffer XXX - should actually use dmamap_uio() */
213 dsc->sc_bufsz = 1024;
214 dsc->sc_buf = malloc(dsc->sc_bufsz, M_DEVBUF, M_NOWAIT);
215
216 /* XXX read default state */
217 {
218 bus_space_handle_t h = sc->sc_regs;
219 struct hwstate *hw = &dsc->sc_hwdefault;
220 int ack_rate = sa->sa_frequency / 1000000;
221
222 hw->hw_hcr = bus_space_read_2(sc->sc_bustag, h, L64854_REG_HCR);
223 hw->hw_ocr = bus_space_read_2(sc->sc_bustag, h, L64854_REG_OCR);
224 hw->hw_tcr = bus_space_read_1(sc->sc_bustag, h, L64854_REG_TCR);
225 hw->hw_or = bus_space_read_1(sc->sc_bustag, h, L64854_REG_OR);
226
227 DPRINTF(("bpp: hcr %x ocr %x tcr %x or %x\n",
228 hw->hw_hcr, hw->hw_ocr, hw->hw_tcr, hw->hw_or));
229 /* Set these to sane values */
230 hw->hw_hcr = ((ack_rate<<BPP_HCR_DSS_SHFT)&BPP_HCR_DSS_MASK)
231 | ((ack_rate<<BPP_HCR_DSW_SHFT)&BPP_HCR_DSW_MASK);
232 hw->hw_ocr |= BPP_OCR_ACK_OP;
233 }
234 }
235
236 void
237 bpp_setparams(struct bpp_softc *sc, struct hwstate *hw)
238 {
239 uint16_t irq;
240 bus_space_tag_t t = sc->sc_lsi64854.sc_bustag;
241 bus_space_handle_t h = sc->sc_lsi64854.sc_regs;
242
243 bus_space_write_2(t, h, L64854_REG_HCR, hw->hw_hcr);
244 bus_space_write_2(t, h, L64854_REG_OCR, hw->hw_ocr);
245 bus_space_write_1(t, h, L64854_REG_TCR, hw->hw_tcr);
246 bus_space_write_1(t, h, L64854_REG_OR, hw->hw_or);
247
248 /* Only change IRP settings in interrupt status register */
249 irq = bus_space_read_2(t, h, L64854_REG_ICR);
250 irq &= ~BPP_ALLIRP;
251 irq |= (hw->hw_irq & BPP_ALLIRP);
252 bus_space_write_2(t, h, L64854_REG_ICR, irq);
253 DPRINTF(("bpp_setparams: hcr %x ocr %x tcr %x or %x, irq %x\n",
254 hw->hw_hcr, hw->hw_ocr, hw->hw_tcr, hw->hw_or, irq));
255 }
256
257 int
258 bppopen(dev_t dev, int flags, int mode, struct lwp *l)
259 {
260 int unit = BPPUNIT(dev);
261 struct bpp_softc *sc;
262 struct lsi64854_softc *lsi;
263 uint16_t irq;
264 int s;
265
266 if (unit >= bpp_cd.cd_ndevs)
267 return ENXIO;
268 sc = device_lookup_private(&bpp_cd, unit);
269
270 if ((sc->sc_flags & (BPP_OPEN|BPP_XCLUDE)) == (BPP_OPEN|BPP_XCLUDE))
271 return EBUSY;
272
273 lsi = &sc->sc_lsi64854;
274
275 /* Set default parameters */
276 sc->sc_hwcurrent = sc->sc_hwdefault;
277 s = splbpp();
278 bpp_setparams(sc, &sc->sc_hwdefault);
279 splx(s);
280
281 /* Enable interrupts */
282 irq = BPP_ERR_IRQ_EN;
283 irq |= sc->sc_hwdefault.hw_irq;
284 bus_space_write_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR, irq);
285 return 0;
286 }
287
288 int
289 bppclose(dev_t dev, int flags, int mode, struct lwp *l)
290 {
291 struct bpp_softc *sc;
292 struct lsi64854_softc *lsi;
293 uint16_t irq;
294
295 sc = device_lookup_private(&bpp_cd, BPPUNIT(dev));
296 lsi = &sc->sc_lsi64854;
297
298 /* Turn off all interrupt enables */
299 irq = sc->sc_hwdefault.hw_irq | BPP_ALLIRQ;
300 irq &= ~BPP_ALLEN;
301 bus_space_write_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR, irq);
302
303 sc->sc_asyncproc = NULL;
304 sc->sc_flags = 0;
305 return 0;
306 }
307
308 int
309 bppwrite(dev_t dev, struct uio *uio, int flags)
310 {
311 struct bpp_softc *sc;
312 struct lsi64854_softc *lsi;
313 int error = 0;
314 int s;
315
316 sc = device_lookup_private(&bpp_cd, BPPUNIT(dev));
317 lsi = &sc->sc_lsi64854;
318
319 /*
320 * Wait until the DMA engine is free.
321 */
322 s = splbpp();
323 while ((sc->sc_flags & BPP_LOCKED) != 0) {
324 if ((flags & IO_NDELAY) != 0) {
325 splx(s);
326 return EWOULDBLOCK;
327 }
328
329 sc->sc_flags |= BPP_WANT;
330 error = tsleep(sc->sc_buf, PZERO|PCATCH, "bppwrite", 0);
331 if (error != 0) {
332 splx(s);
333 return error;
334 }
335 }
336 sc->sc_flags |= BPP_LOCKED;
337 splx(s);
338
339 /*
340 * Move data from user space into our private buffer
341 * and start DMA.
342 */
343 while (uio->uio_resid > 0) {
344 uint8_t *bp = sc->sc_buf;
345 size_t len = min(sc->sc_bufsz, uio->uio_resid);
346
347 if ((error = uiomove(bp, len, uio)) != 0)
348 break;
349
350 while (len > 0) {
351 uint8_t tcr;
352 size_t size = len;
353 DMA_SETUP(lsi, &bp, &len, 0, &size);
354
355 #ifdef DEBUG
356 if (bppdebug) {
357 int i;
358 uint8_t *b = bp;
359 printf("bpp: writing %ld : ", len);
360 for (i = 0; i < len; i++)
361 printf("%c(0x%x)", b[i], b[i]);
362 printf("\n");
363 }
364 #endif
365
366 /* Clear direction control bit */
367 tcr = bus_space_read_1(lsi->sc_bustag, lsi->sc_regs,
368 L64854_REG_TCR);
369 tcr &= ~BPP_TCR_DIR;
370 bus_space_write_1(lsi->sc_bustag, lsi->sc_regs,
371 L64854_REG_TCR, tcr);
372
373 /* Enable DMA */
374 s = splbpp();
375 DMA_GO(lsi);
376 error = tsleep(sc, PZERO|PCATCH, "bppdma", 0);
377 splx(s);
378 if (error != 0)
379 goto out;
380
381 /* Bail out if bottom half reported an error */
382 if ((error = sc->sc_error) != 0)
383 goto out;
384
385 /*
386 * lsi64854_pp_intr() does this part.
387 *
388 * len -= size;
389 */
390 }
391 }
392
393 out:
394 DPRINTF(("bpp done %x\n", error));
395 s = splbpp();
396 sc->sc_flags &= ~BPP_LOCKED;
397 if ((sc->sc_flags & BPP_WANT) != 0) {
398 sc->sc_flags &= ~BPP_WANT;
399 wakeup(sc->sc_buf);
400 }
401 splx(s);
402 return error;
403 }
404
405 /* move to header: */
406 #define BPPIOCSPARAM _IOW('P', 0x1, struct hwstate)
407 #define BPPIOCGPARAM _IOR('P', 0x2, struct hwstate)
408
409 int
410 bppioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
411 {
412 struct bpp_softc *sc;
413 struct proc *p = l->l_proc;
414 struct hwstate *hw, *chw;
415 int error = 0;
416 int s;
417
418 sc = device_lookup_private(&bpp_cd, BPPUNIT(dev));
419
420 switch(cmd) {
421 case BPPIOCSPARAM:
422 chw = &sc->sc_hwcurrent;
423 hw = (struct hwstate *)data;
424
425 /*
426 * Extract and store user-settable bits.
427 */
428 #define _bpp_set(reg,mask) do { \
429 chw->reg &= ~(mask); \
430 chw->reg |= (hw->reg & (mask)); \
431 } while (/* CONSTCOND */ 0)
432 _bpp_set(hw_hcr, BPP_HCR_DSS_MASK|BPP_HCR_DSW_MASK);
433 _bpp_set(hw_ocr, BPP_OCR_USER);
434 _bpp_set(hw_tcr, BPP_TCR_USER);
435 _bpp_set(hw_or, BPP_OR_USER);
436 _bpp_set(hw_irq, BPP_IRQ_USER);
437 #undef _bpp_set
438
439 /* Apply settings */
440 s = splbpp();
441 bpp_setparams(sc, chw);
442 splx(s);
443 break;
444 case BPPIOCGPARAM:
445 *((struct hwstate *)data) = sc->sc_hwcurrent;
446 break;
447 case TIOCEXCL:
448 s = splbpp();
449 sc->sc_flags |= BPP_XCLUDE;
450 splx(s);
451 break;
452 case TIOCNXCL:
453 s = splbpp();
454 sc->sc_flags &= ~BPP_XCLUDE;
455 splx(s);
456 break;
457 case FIOASYNC:
458 s = splbpp();
459 if (*(int *)data) {
460 if (sc->sc_asyncproc != NULL)
461 error = EBUSY;
462 else
463 sc->sc_asyncproc = p;
464 } else
465 sc->sc_asyncproc = NULL;
466 splx(s);
467 break;
468 default:
469 break;
470 }
471
472 return error;
473 }
474
475 int
476 bpppoll(dev_t dev, int events, struct lwp *l)
477 {
478 struct bpp_softc *sc;
479 int revents = 0;
480
481 sc = device_lookup_private(&bpp_cd, BPPUNIT(dev));
482
483 if (events & (POLLIN | POLLRDNORM)) {
484 /* read is not yet implemented */
485 }
486
487 if (events & (POLLOUT | POLLWRNORM)) {
488 if ((sc->sc_flags & BPP_LOCKED) == 0)
489 revents |= (POLLOUT | POLLWRNORM);
490 }
491
492 if (revents == 0) {
493 if (events & (POLLIN | POLLRDNORM))
494 selrecord(l, &sc->sc_rsel);
495 if (events & (POLLOUT | POLLWRNORM))
496 selrecord(l, &sc->sc_wsel);
497 }
498
499 return revents;
500 }
501
502 static void
503 filt_bpprdetach(struct knote *kn)
504 {
505 struct bpp_softc *sc = kn->kn_hook;
506 int s;
507
508 s = splbpp();
509 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
510 splx(s);
511 }
512
513 static int
514 filt_bppread(struct knote *kn, long hint)
515 {
516 /* XXX Read not yet implemented. */
517 return 0;
518 }
519
520 static const struct filterops bppread_filtops =
521 { 1, NULL, filt_bpprdetach, filt_bppread };
522
523 static void
524 filt_bppwdetach(struct knote *kn)
525 {
526 struct bpp_softc *sc = kn->kn_hook;
527 int s;
528
529 s = splbpp();
530 SLIST_REMOVE(&sc->sc_wsel.sel_klist, kn, knote, kn_selnext);
531 splx(s);
532 }
533
534 static int
535 filt_bpfwrite(struct knote *kn, long hint)
536 {
537 struct bpp_softc *sc = kn->kn_hook;
538
539 if (sc->sc_flags & BPP_LOCKED)
540 return 0;
541
542 kn->kn_data = 0; /* XXXLUKEM (thorpej): what to put here? */
543 return 1;
544 }
545
546 static const struct filterops bppwrite_filtops =
547 { 1, NULL, filt_bppwdetach, filt_bpfwrite };
548
549 int
550 bppkqfilter(dev_t dev, struct knote *kn)
551 {
552 struct bpp_softc *sc;
553 struct klist *klist;
554 int s;
555
556 sc = device_lookup_private(&bpp_cd, BPPUNIT(dev));
557
558 switch (kn->kn_filter) {
559 case EVFILT_READ:
560 klist = &sc->sc_rsel.sel_klist;
561 kn->kn_fop = &bppread_filtops;
562 break;
563
564 case EVFILT_WRITE:
565 klist = &sc->sc_wsel.sel_klist;
566 kn->kn_fop = &bppwrite_filtops;
567 break;
568
569 default:
570 return EINVAL;
571 }
572
573 kn->kn_hook = sc;
574
575 s = splbpp();
576 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
577 splx(s);
578
579 return 0;
580 }
581
582 int
583 bppintr(void *arg)
584 {
585 struct bpp_softc *sc = arg;
586 struct lsi64854_softc *lsi = &sc->sc_lsi64854;
587 uint16_t irq;
588
589 /* First handle any possible DMA interrupts */
590 if (lsi64854_pp_intr((void *)lsi) == -1)
591 sc->sc_error = 1;
592
593 irq = bus_space_read_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR);
594 /* Ack all interrupts */
595 bus_space_write_2(lsi->sc_bustag, lsi->sc_regs, L64854_REG_ICR,
596 irq | BPP_ALLIRQ);
597
598 DPRINTF(("%s: %x\n", __func__, irq));
599 /* Did our device interrupt? */
600 if ((irq & BPP_ALLIRQ) == 0)
601 return 0;
602
603 if ((sc->sc_flags & BPP_LOCKED) != 0)
604 wakeup(sc);
605 else if ((sc->sc_flags & BPP_WANT) != 0) {
606 sc->sc_flags &= ~BPP_WANT;
607 wakeup(sc->sc_buf);
608 } else {
609 selnotify(&sc->sc_wsel, 0, 0);
610 if (sc->sc_asyncproc != NULL) {
611 mutex_enter(&proclist_mutex);
612 psignal(sc->sc_asyncproc, SIGIO);
613 mutex_exit(&proclist_mutex);
614 }
615 }
616 return 1;
617 }
618