1 /* $NetBSD: bpp.c,v 1.38 2008/04/28 20:23:57 martin Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: bpp.c,v 1.38 2008/04/28 20:23:57 martin Exp $");
35 #include <sys/param.h>
36 #include <sys/ioctl.h>
37 #include <sys/fcntl.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/vnode.h>
42 #include <sys/select.h>
43 #include <sys/malloc.h>
45 #include <sys/signalvar.h>
47 #include <sys/errno.h>
48 #include <sys/device.h>
52 #include <machine/autoconf.h>
54 #include <dev/ic/lsi64854reg.h>
55 #include <dev/ic/lsi64854var.h>
57 #include <dev/sbus/sbusvar.h>
58 #include <dev/sbus/bppreg.h>
62 #define splbpp() spltty() /* XXX */
65 #define DPRINTF(x) do { if (bppdebug) printf x ; } while (0)
73 int bpp_dss
; /* data setup to strobe */
74 int bpp_dsw
; /* data strobe width */
75 int bpp_outputpins
; /* Select/Autofeed/Init pins */
76 int bpp_inputpins
; /* Error/Select/Paperout pins */
81 uint16_t hw_hcr
; /* Hardware config register */
82 uint16_t hw_ocr
; /* Operation config register */
83 uint8_t hw_tcr
; /* Transfer Control register */
84 uint8_t hw_or
; /* Output register */
85 uint16_t hw_irq
; /* IRQ; polarity bits only */
89 struct lsi64854_softc sc_lsi64854
; /* base device */
91 size_t sc_bufsz
; /* temp buffer */
94 int sc_error
; /* bottom-half error */
96 #define BPP_OPEN 0x01 /* Device is open */
97 #define BPP_XCLUDE 0x02 /* Exclusive-open mode */
98 #define BPP_ASYNC 0x04 /* Asynchronous I/O mode */
99 #define BPP_LOCKED 0x08 /* DMA in progress */
100 #define BPP_WANT 0x10 /* Waiting for DMA */
102 struct selinfo sc_rsel
;
103 struct selinfo sc_wsel
;
104 struct proc
*sc_asyncproc
; /* Process to notify if async */
108 struct hwstate sc_hwdefault
;
109 struct hwstate sc_hwcurrent
;
112 static int bppmatch(device_t
, cfdata_t
, void *);
113 static void bppattach(device_t
, device_t
, void *);
114 static int bppintr(void *);
115 static void bppsoftintr(void *);
116 static void bpp_setparams(struct bpp_softc
*, struct hwstate
*);
118 CFATTACH_DECL_NEW(bpp
, sizeof(struct bpp_softc
),
119 bppmatch
, bppattach
, NULL
, NULL
);
121 dev_type_open(bppopen
);
122 dev_type_close(bppclose
);
123 dev_type_write(bppwrite
);
124 dev_type_ioctl(bppioctl
);
125 dev_type_poll(bpppoll
);
126 dev_type_kqfilter(bppkqfilter
);
128 const struct cdevsw bpp_cdevsw
= {
129 bppopen
, bppclose
, noread
, bppwrite
, bppioctl
,
130 nostop
, notty
, bpppoll
, nommap
, bppkqfilter
, D_TTY
133 #define BPPUNIT(dev) (minor(dev))
137 bppmatch(device_t parent
, cfdata_t cf
, void *aux
)
139 struct sbus_attach_args
*sa
= aux
;
141 return strcmp("SUNW,bpp", sa
->sa_name
) == 0;
145 bppattach(device_t parent
, device_t self
, void *aux
)
147 struct bpp_softc
*dsc
= device_private(self
);
148 struct lsi64854_softc
*sc
= &dsc
->sc_lsi64854
;
149 struct sbus_softc
*sbsc
= device_private(parent
);
150 struct sbus_attach_args
*sa
= aux
;
151 int burst
, sbusburst
;
156 selinit(&dsc
->sc_rsel
);
157 selinit(&dsc
->sc_wsel
);
158 dsc
->sc_sih
= softint_establish(SOFTINT_CLOCK
, bppsoftintr
, dsc
);
160 sc
->sc_bustag
= sa
->sa_bustag
;
161 sc
->sc_dmatag
= sa
->sa_dmatag
;
164 /* Map device registers */
165 if (sbus_bus_map(sa
->sa_bustag
,
166 sa
->sa_slot
, sa
->sa_offset
, sa
->sa_size
,
167 0, &sc
->sc_regs
) != 0) {
168 aprint_error(": cannot map registers\n");
173 * Get transfer burst size from PROM and plug it into the
174 * controller registers. This is needed on the Sun4m; do
175 * others need it too?
177 sbusburst
= sbsc
->sc_burst
;
179 sbusburst
= SBUS_BURST_32
- 1; /* 1->16 */
181 burst
= prom_getpropint(node
, "burst-sizes", -1);
183 /* take SBus burst sizes */
186 /* Clamp at parent's burst sizes */
188 sc
->sc_burst
= (burst
& SBUS_BURST_32
) ? 32 :
189 (burst
& SBUS_BURST_16
) ? 16 : 0;
191 /* Initialize the DMA channel */
192 sc
->sc_channel
= L64854_CHANNEL_PP
;
195 /* Establish interrupt handler */
197 sc
->sc_intrchain
= bppintr
;
198 sc
->sc_intrchainarg
= dsc
;
199 (void)bus_intr_establish(sa
->sa_bustag
, sa
->sa_pri
, IPL_TTY
,
203 /* Allocate buffer XXX - should actually use dmamap_uio() */
204 dsc
->sc_bufsz
= 1024;
205 dsc
->sc_buf
= malloc(dsc
->sc_bufsz
, M_DEVBUF
, M_NOWAIT
);
207 /* XXX read default state */
209 bus_space_handle_t h
= sc
->sc_regs
;
210 struct hwstate
*hw
= &dsc
->sc_hwdefault
;
211 int ack_rate
= sa
->sa_frequency
/ 1000000;
213 hw
->hw_hcr
= bus_space_read_2(sc
->sc_bustag
, h
, L64854_REG_HCR
);
214 hw
->hw_ocr
= bus_space_read_2(sc
->sc_bustag
, h
, L64854_REG_OCR
);
215 hw
->hw_tcr
= bus_space_read_1(sc
->sc_bustag
, h
, L64854_REG_TCR
);
216 hw
->hw_or
= bus_space_read_1(sc
->sc_bustag
, h
, L64854_REG_OR
);
218 DPRINTF(("bpp: hcr %x ocr %x tcr %x or %x\n",
219 hw
->hw_hcr
, hw
->hw_ocr
, hw
->hw_tcr
, hw
->hw_or
));
220 /* Set these to sane values */
221 hw
->hw_hcr
= ((ack_rate
<<BPP_HCR_DSS_SHFT
)&BPP_HCR_DSS_MASK
)
222 | ((ack_rate
<<BPP_HCR_DSW_SHFT
)&BPP_HCR_DSW_MASK
);
223 hw
->hw_ocr
|= BPP_OCR_ACK_OP
;
228 bpp_setparams(struct bpp_softc
*sc
, struct hwstate
*hw
)
231 bus_space_tag_t t
= sc
->sc_lsi64854
.sc_bustag
;
232 bus_space_handle_t h
= sc
->sc_lsi64854
.sc_regs
;
234 bus_space_write_2(t
, h
, L64854_REG_HCR
, hw
->hw_hcr
);
235 bus_space_write_2(t
, h
, L64854_REG_OCR
, hw
->hw_ocr
);
236 bus_space_write_1(t
, h
, L64854_REG_TCR
, hw
->hw_tcr
);
237 bus_space_write_1(t
, h
, L64854_REG_OR
, hw
->hw_or
);
239 /* Only change IRP settings in interrupt status register */
240 irq
= bus_space_read_2(t
, h
, L64854_REG_ICR
);
242 irq
|= (hw
->hw_irq
& BPP_ALLIRP
);
243 bus_space_write_2(t
, h
, L64854_REG_ICR
, irq
);
244 DPRINTF(("bpp_setparams: hcr %x ocr %x tcr %x or %x, irq %x\n",
245 hw
->hw_hcr
, hw
->hw_ocr
, hw
->hw_tcr
, hw
->hw_or
, irq
));
249 bppopen(dev_t dev
, int flags
, int mode
, struct lwp
*l
)
251 int unit
= BPPUNIT(dev
);
252 struct bpp_softc
*sc
;
253 struct lsi64854_softc
*lsi
;
257 if (unit
>= bpp_cd
.cd_ndevs
)
259 sc
= device_lookup_private(&bpp_cd
, unit
);
261 if ((sc
->sc_flags
& (BPP_OPEN
|BPP_XCLUDE
)) == (BPP_OPEN
|BPP_XCLUDE
))
264 lsi
= &sc
->sc_lsi64854
;
266 /* Set default parameters */
267 sc
->sc_hwcurrent
= sc
->sc_hwdefault
;
269 bpp_setparams(sc
, &sc
->sc_hwdefault
);
272 /* Enable interrupts */
273 irq
= BPP_ERR_IRQ_EN
;
274 irq
|= sc
->sc_hwdefault
.hw_irq
;
275 bus_space_write_2(lsi
->sc_bustag
, lsi
->sc_regs
, L64854_REG_ICR
, irq
);
280 bppclose(dev_t dev
, int flags
, int mode
, struct lwp
*l
)
282 struct bpp_softc
*sc
;
283 struct lsi64854_softc
*lsi
;
286 sc
= device_lookup_private(&bpp_cd
, BPPUNIT(dev
));
287 lsi
= &sc
->sc_lsi64854
;
289 /* Turn off all interrupt enables */
290 irq
= sc
->sc_hwdefault
.hw_irq
| BPP_ALLIRQ
;
292 bus_space_write_2(lsi
->sc_bustag
, lsi
->sc_regs
, L64854_REG_ICR
, irq
);
294 mutex_enter(proc_lock
);
295 sc
->sc_asyncproc
= NULL
;
296 mutex_exit(proc_lock
);
302 bppwrite(dev_t dev
, struct uio
*uio
, int flags
)
304 struct bpp_softc
*sc
;
305 struct lsi64854_softc
*lsi
;
309 sc
= device_lookup_private(&bpp_cd
, BPPUNIT(dev
));
310 lsi
= &sc
->sc_lsi64854
;
313 * Wait until the DMA engine is free.
316 while ((sc
->sc_flags
& BPP_LOCKED
) != 0) {
317 if ((flags
& IO_NDELAY
) != 0) {
322 sc
->sc_flags
|= BPP_WANT
;
323 error
= tsleep(sc
->sc_buf
, PZERO
|PCATCH
, "bppwrite", 0);
329 sc
->sc_flags
|= BPP_LOCKED
;
333 * Move data from user space into our private buffer
336 while (uio
->uio_resid
> 0) {
337 uint8_t *bp
= sc
->sc_buf
;
338 size_t len
= min(sc
->sc_bufsz
, uio
->uio_resid
);
340 if ((error
= uiomove(bp
, len
, uio
)) != 0)
346 DMA_SETUP(lsi
, &bp
, &len
, 0, &size
);
352 printf("bpp: writing %ld : ", len
);
353 for (i
= 0; i
< len
; i
++)
354 printf("%c(0x%x)", b
[i
], b
[i
]);
359 /* Clear direction control bit */
360 tcr
= bus_space_read_1(lsi
->sc_bustag
, lsi
->sc_regs
,
363 bus_space_write_1(lsi
->sc_bustag
, lsi
->sc_regs
,
364 L64854_REG_TCR
, tcr
);
369 error
= tsleep(sc
, PZERO
|PCATCH
, "bppdma", 0);
374 /* Bail out if bottom half reported an error */
375 if ((error
= sc
->sc_error
) != 0)
379 * lsi64854_pp_intr() does this part.
387 DPRINTF(("bpp done %x\n", error
));
389 sc
->sc_flags
&= ~BPP_LOCKED
;
390 if ((sc
->sc_flags
& BPP_WANT
) != 0) {
391 sc
->sc_flags
&= ~BPP_WANT
;
398 /* move to header: */
399 #define BPPIOCSPARAM _IOW('P', 0x1, struct hwstate)
400 #define BPPIOCGPARAM _IOR('P', 0x2, struct hwstate)
403 bppioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
405 struct bpp_softc
*sc
;
406 struct proc
*p
= l
->l_proc
;
407 struct hwstate
*hw
, *chw
;
411 sc
= device_lookup_private(&bpp_cd
, BPPUNIT(dev
));
415 chw
= &sc
->sc_hwcurrent
;
416 hw
= (struct hwstate
*)data
;
419 * Extract and store user-settable bits.
421 #define _bpp_set(reg,mask) do { \
422 chw->reg &= ~(mask); \
423 chw->reg |= (hw->reg & (mask)); \
424 } while (/* CONSTCOND */ 0)
425 _bpp_set(hw_hcr
, BPP_HCR_DSS_MASK
|BPP_HCR_DSW_MASK
);
426 _bpp_set(hw_ocr
, BPP_OCR_USER
);
427 _bpp_set(hw_tcr
, BPP_TCR_USER
);
428 _bpp_set(hw_or
, BPP_OR_USER
);
429 _bpp_set(hw_irq
, BPP_IRQ_USER
);
434 bpp_setparams(sc
, chw
);
438 *((struct hwstate
*)data
) = sc
->sc_hwcurrent
;
442 sc
->sc_flags
|= BPP_XCLUDE
;
447 sc
->sc_flags
&= ~BPP_XCLUDE
;
451 mutex_enter(proc_lock
);
453 if (sc
->sc_asyncproc
!= NULL
)
456 sc
->sc_asyncproc
= p
;
458 sc
->sc_asyncproc
= NULL
;
459 mutex_exit(proc_lock
);
469 bpppoll(dev_t dev
, int events
, struct lwp
*l
)
471 struct bpp_softc
*sc
;
474 sc
= device_lookup_private(&bpp_cd
, BPPUNIT(dev
));
476 if (events
& (POLLIN
| POLLRDNORM
)) {
477 /* read is not yet implemented */
480 if (events
& (POLLOUT
| POLLWRNORM
)) {
481 if ((sc
->sc_flags
& BPP_LOCKED
) == 0)
482 revents
|= (POLLOUT
| POLLWRNORM
);
486 if (events
& (POLLIN
| POLLRDNORM
))
487 selrecord(l
, &sc
->sc_rsel
);
488 if (events
& (POLLOUT
| POLLWRNORM
))
489 selrecord(l
, &sc
->sc_wsel
);
496 filt_bpprdetach(struct knote
*kn
)
498 struct bpp_softc
*sc
= kn
->kn_hook
;
502 SLIST_REMOVE(&sc
->sc_rsel
.sel_klist
, kn
, knote
, kn_selnext
);
507 filt_bppread(struct knote
*kn
, long hint
)
509 /* XXX Read not yet implemented. */
513 static const struct filterops bppread_filtops
=
514 { 1, NULL
, filt_bpprdetach
, filt_bppread
};
517 filt_bppwdetach(struct knote
*kn
)
519 struct bpp_softc
*sc
= kn
->kn_hook
;
523 SLIST_REMOVE(&sc
->sc_wsel
.sel_klist
, kn
, knote
, kn_selnext
);
528 filt_bpfwrite(struct knote
*kn
, long hint
)
530 struct bpp_softc
*sc
= kn
->kn_hook
;
532 if (sc
->sc_flags
& BPP_LOCKED
)
535 kn
->kn_data
= 0; /* XXXLUKEM (thorpej): what to put here? */
539 static const struct filterops bppwrite_filtops
=
540 { 1, NULL
, filt_bppwdetach
, filt_bpfwrite
};
543 bppkqfilter(dev_t dev
, struct knote
*kn
)
545 struct bpp_softc
*sc
;
549 sc
= device_lookup_private(&bpp_cd
, BPPUNIT(dev
));
551 switch (kn
->kn_filter
) {
553 klist
= &sc
->sc_rsel
.sel_klist
;
554 kn
->kn_fop
= &bppread_filtops
;
558 klist
= &sc
->sc_wsel
.sel_klist
;
559 kn
->kn_fop
= &bppwrite_filtops
;
569 SLIST_INSERT_HEAD(klist
, kn
, kn_selnext
);
578 struct bpp_softc
*sc
= arg
;
579 struct lsi64854_softc
*lsi
= &sc
->sc_lsi64854
;
582 /* First handle any possible DMA interrupts */
583 if (lsi64854_pp_intr((void *)lsi
) == -1)
586 irq
= bus_space_read_2(lsi
->sc_bustag
, lsi
->sc_regs
, L64854_REG_ICR
);
587 /* Ack all interrupts */
588 bus_space_write_2(lsi
->sc_bustag
, lsi
->sc_regs
, L64854_REG_ICR
,
591 DPRINTF(("%s: %x\n", __func__
, irq
));
592 /* Did our device interrupt? */
593 if ((irq
& BPP_ALLIRQ
) == 0)
596 if ((sc
->sc_flags
& BPP_LOCKED
) != 0)
598 else if ((sc
->sc_flags
& BPP_WANT
) != 0) {
599 sc
->sc_flags
&= ~BPP_WANT
;
602 selnotify(&sc
->sc_wsel
, 0, 0);
603 if (sc
->sc_asyncproc
!= NULL
)
604 softint_schedule(sc
->sc_sih
);
610 bppsoftintr(void *cookie
)
612 struct bpp_softc
*sc
= cookie
;
614 mutex_enter(proc_lock
);
615 if (sc
->sc_asyncproc
)
616 psignal(sc
->sc_asyncproc
, SIGIO
);
617 mutex_exit(proc_lock
);