diff options
Diffstat (limited to 'sys/arch/sparc/dev/magma.c')
-rw-r--r-- | sys/arch/sparc/dev/magma.c | 537 |
1 files changed, 403 insertions, 134 deletions
diff --git a/sys/arch/sparc/dev/magma.c b/sys/arch/sparc/dev/magma.c index 4a0fc416a4c..58bb91018da 100644 --- a/sys/arch/sparc/dev/magma.c +++ b/sys/arch/sparc/dev/magma.c @@ -64,6 +64,7 @@ #include <dev/ic/cd1400reg.h> #include <dev/ic/cd1190reg.h> +#include "bppioctl.h" #include "magmareg.h" /* @@ -470,9 +471,24 @@ int needsoftint = 0; int port = rivr >> 4; if( rivr & (1<<3) ) { /* parallel port */ - struct mbpp_port *mbpp = &sc->ms_mbpp->ms_port[port]; + struct mbpp_port *mbpp; + int n_chars; + mbpp = &sc->ms_mbpp->ms_port[port]; cd = mbpp->mp_cd1400; + + /* don't think we have to handle exceptions */ + n_chars = cd1400_read_reg(cd, CD1400_RDCR); + while( n_chars-- ) { + if( mbpp->mp_cnt == 0 ) { + SET(mbpp->mp_flags, MBPPF_WAKEUP); + needsoftint = 1; + break; + } + *mbpp->mp_ptr = cd1400_read_reg(cd, CD1400_RDSR); + mbpp->mp_ptr++; + mbpp->mp_cnt--; + } } else { /* serial port */ register struct mtty_port *mtty; register u_char *ptr, n_chars, line_stat; @@ -544,27 +560,23 @@ int needsoftint = 0; mbpp = &sc->ms_mbpp->ms_port[port]; cd = mbpp->mp_cd1400; - /* if we have anything to send, then send what we can.. otherwise - * shut off the interrupts and signal for a wakeup (can't be done - * at this spl because its sleeping in spltty() in mbppwrite - */ - - if( mbpp->mp_txc ) { + if( mbpp->mp_cnt ) { int count = 0; - while( count++ < CD1400_PAR_FIFO_SIZE && mbpp->mp_txc ) { - cd1400_write_reg(cd, CD1400_TDR, *mbpp->mp_txp); - mbpp->mp_txc--; - mbpp->mp_txp++; + /* fill the fifo */ + while( mbpp->mp_cnt && count++ < CD1400_PAR_FIFO_SIZE ) { + cd1400_write_reg(cd, CD1400_TDR, *mbpp->mp_ptr); + mbpp->mp_ptr++; + mbpp->mp_cnt--; } } else { - register int srer; - - srer = cd1400_read_reg(cd, CD1400_SRER); - CLR(srer, CD1400_SRER_TXRDY); - cd1400_write_reg(cd, CD1400_SRER, srer); - - SET(mbpp->mp_flags, MBPPF_DONE); + /* fifo is empty and we got no more data to send, so shut + * off interrupts and signal for a wakeup, which can't be + * done here in case we beat mbpp_send to the tsleep call + * (we are running at >spltty) + */ + cd1400_write_reg(cd, CD1400_SRER, 0); + SET(mbpp->mp_flags, MBPPF_WAKEUP); needsoftint = 1; } } else { /* serial port */ @@ -675,78 +687,81 @@ int s, flags; /* * check the tty ports (if any) to see what needs doing */ - if( mtty ) for( port = 0 ; port < mtty->ms_nports ; port++ ) { - struct mtty_port *mp = &mtty->ms_port[port]; - struct tty *tp = mp->mp_tty; + if( mtty ) { + for( port = 0 ; port < mtty->ms_nports ; port++ ) { + struct mtty_port *mp = &mtty->ms_port[port]; + struct tty *tp = mp->mp_tty; - if( !ISSET(tp->t_state, TS_ISOPEN) ) continue; + if( !ISSET(tp->t_state, TS_ISOPEN) ) continue; - /* - * handle any received data - */ - while( mp->mp_rget != mp->mp_rput ) { - u_char stat; - int data; + /* + * handle any received data + */ + while( mp->mp_rget != mp->mp_rput ) { + u_char stat; + int data; - stat = mp->mp_rget[0]; - data = mp->mp_rget[1]; - mp->mp_rget = ((mp->mp_rget + 2) == mp->mp_rend) ? mp->mp_rbuf : (mp->mp_rget + 2); + stat = mp->mp_rget[0]; + data = mp->mp_rget[1]; + mp->mp_rget = ((mp->mp_rget + 2) == mp->mp_rend) ? mp->mp_rbuf : (mp->mp_rget + 2); - if( stat & (CD1400_RDSR_BREAK | CD1400_RDSR_FE) ) - data |= TTY_FE; - if( stat & CD1400_RDSR_PE ) - data |= TTY_PE; + if( stat & (CD1400_RDSR_BREAK | CD1400_RDSR_FE) ) + data |= TTY_FE; + if( stat & CD1400_RDSR_PE ) + data |= TTY_PE; - if( stat & CD1400_RDSR_OE ) - log(LOG_WARNING, "%s%x: fifo overflow\n", mtty->ms_dev.dv_xname, port); + if( stat & CD1400_RDSR_OE ) + log(LOG_WARNING, "%s%x: fifo overflow\n", mtty->ms_dev.dv_xname, port); - (*linesw[tp->t_line].l_rint)(data, tp); - serviced = 1; - } + (*linesw[tp->t_line].l_rint)(data, tp); + serviced = 1; + } - s = splhigh(); /* block out hard interrupt routine */ - flags = mp->mp_flags; - CLR(mp->mp_flags, MTTYF_DONE | MTTYF_CARRIER_CHANGED | MTTYF_RING_OVERFLOW); - splx(s); /* ok */ + s = splhigh(); /* block out hard interrupt routine */ + flags = mp->mp_flags; + CLR(mp->mp_flags, MTTYF_DONE | MTTYF_CARRIER_CHANGED | MTTYF_RING_OVERFLOW); + splx(s); /* ok */ - if( ISSET(flags, MTTYF_CARRIER_CHANGED) ) { - dprintf(("%s%x: cd %s\n", mtty->ms_dev.dv_xname, port, mp->mp_carrier ? "on" : "off")); - (*linesw[tp->t_line].l_modem)(tp, mp->mp_carrier); - serviced = 1; - } + if( ISSET(flags, MTTYF_CARRIER_CHANGED) ) { + dprintf(("%s%x: cd %s\n", mtty->ms_dev.dv_xname, port, mp->mp_carrier ? "on" : "off")); + (*linesw[tp->t_line].l_modem)(tp, mp->mp_carrier); + serviced = 1; + } - if( ISSET(flags, MTTYF_RING_OVERFLOW) ) { - log(LOG_WARNING, "%s%x: ring buffer overflow\n", mtty->ms_dev.dv_xname, port); - serviced = 1; - } + if( ISSET(flags, MTTYF_RING_OVERFLOW) ) { + log(LOG_WARNING, "%s%x: ring buffer overflow\n", mtty->ms_dev.dv_xname, port); + serviced = 1; + } - if( ISSET(flags, MTTYF_DONE) ) { - ndflush(&tp->t_outq, mp->mp_txp - tp->t_outq.c_cf); - CLR(tp->t_state, TS_BUSY); - (*linesw[tp->t_line].l_start)(tp); /* might be some more */ - serviced = 1; - } - } /* for(each mtty...) */ + if( ISSET(flags, MTTYF_DONE) ) { + ndflush(&tp->t_outq, mp->mp_txp - tp->t_outq.c_cf); + CLR(tp->t_state, TS_BUSY); + (*linesw[tp->t_line].l_start)(tp); /* might be some more */ + serviced = 1; + } + } /* for(each mtty...) */ + } /* * check the bpp ports (if any) to see what needs doing */ - if( mbpp ) for( port = 0 ; port < mbpp->ms_nports ; port++ ) { - struct mbpp_port *mp = &mbpp->ms_port[port]; + if( mbpp ) { + for( port = 0 ; port < mbpp->ms_nports ; port++ ) { + struct mbpp_port *mp = &mbpp->ms_port[port]; - if( !ISSET(mp->mp_flags, MBPPF_OPEN) ) continue; + if( !ISSET(mp->mp_flags, MBPPF_OPEN) ) continue; - s = splhigh(); - flags = mp->mp_flags; - CLR(mp->mp_flags, MBPPF_DONE); - splx(s); + s = splhigh(); /* block out hard intr routine */ + flags = mp->mp_flags; + CLR(mp->mp_flags, MBPPF_WAKEUP); + splx(s); - if( ISSET(flags, MBPPF_DONE) ) { - wakeup(mp); - serviced = 1; - } - - } /* for(each mbpp...) */ + if( ISSET(flags, MBPPF_WAKEUP) ) { + wakeup(mp); + serviced = 1; + } + } /* for(each mbpp...) */ + } return(serviced); } @@ -1355,6 +1370,11 @@ int s, opt; * mbppwrite write to mbpp * mbppioctl do ioctl on mbpp * mbppselect do select on mbpp + * mbpp_rw general rw routine + * mbpp_timeout rw timeout + * mbpp_start rw start after delay + * mbpp_send send data + * mbpp_recv recv data */ int @@ -1376,7 +1396,7 @@ void *args; struct magma_softc *sc = (struct magma_softc *)parent; struct mbpp_softc *ms = (struct mbpp_softc *)dev; struct mbpp_port *mp; -int port = 0; +int port; sc->ms_mbpp = ms; dprintf((" addr 0x%x", ms)); @@ -1388,17 +1408,6 @@ int port = 0; mp->mp_cd1190 = &sc->ms_cd1190[port]; else mp->mp_cd1400 = &sc->ms_cd1400[0]; - - mp->mp_txbuf = malloc(MBPP_TXBUF_SIZE, M_DEVBUF, M_NOWAIT); - if( mp->mp_txbuf == NULL ) break; - - /* - * default strobe and timeout settings - */ - mp->mp_read_strobe = 1000; /* 1 microsecond */ - mp->mp_read_timeout = 5 * hz; /* 5 seconds */ - mp->mp_write_strobe = 1000; /* 1 microsecond */ - mp->mp_write_timeout = 5 * hz; /* 5 seconds */ } ms->ms_nports = port; @@ -1419,7 +1428,7 @@ int card = MAGMA_CARD(dev); int port = MAGMA_PORT(dev); struct mbpp_softc *ms; struct mbpp_port *mp; -int error = 0, s; +int s; if( card >= mbpp_cd.cd_ndevs || (ms = mbpp_cd.cd_devs[card]) == NULL || port >= ms->ms_nports ) return(ENXIO); @@ -1434,22 +1443,27 @@ int error = 0, s; SET(mp->mp_flags, MBPPF_OPEN); splx(s); - if( mp->mp_cd1400 ) { /* CD1400 chip */ + /* set defaults */ + mp->mp_burst = BPP_BURST; + mp->mp_timeout = mbpp_mstohz(BPP_TIMEOUT); + mp->mp_delay = mbpp_mstohz(BPP_DELAY); + + /* init chips */ + if( mp->mp_cd1400 ) { /* CD1400 */ struct cd1400 *cd = mp->mp_cd1400; /* set up CD1400 channel */ s = spltty(); cd1400_write_reg(cd, CD1400_CAR, 0); cd1400_write_ccr(cd, CD1400_CCR_CMDRESET); - cd1400_write_reg(cd, CD1400_TBPR, (mp->mp_write_strobe * cd->cd_clock / 2000)); cd1400_write_reg(cd, CD1400_LIVR, (1<<3)); - cd1400_write_ccr(cd, CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN); splx(s); - } else { /* CD1190 chip */ - error = ENXIO; + } else { /* CD1190 */ + mp->mp_flags = 0; + return(ENXIO); } - return(error); + return(0); } /* @@ -1465,19 +1479,7 @@ struct proc *p; struct mbpp_softc *ms = mbpp_cd.cd_devs[MAGMA_CARD(dev)]; struct mbpp_port *mp = &ms->ms_port[MAGMA_PORT(dev)]; - if( mp->mp_cd1400 ) { - struct cd1400 *cd = mp->mp_cd1400; - int s; - - s = spltty(); - /* turn off the channel */ - cd1400_write_reg(cd, CD1400_CAR, 0); - cd1400_write_ccr(cd, CD1400_CCR_CMDRESET); - splx(s); - } - mp->mp_flags = 0; - return(0); } @@ -1490,7 +1492,7 @@ dev_t dev; struct uio *uio; int flags; { - return(ENODEV); + return( mbpp_rw(dev, uio) ); } /* @@ -1502,33 +1504,7 @@ dev_t dev; struct uio *uio; int flags; { -struct mbpp_softc *ms = mbpp_cd.cd_devs[MAGMA_CARD(dev)]; -register struct mbpp_port *mp = &ms->ms_port[MAGMA_PORT(dev)]; -int error = 0; -int s; - - while( uio->uio_resid ) { - mp->mp_txc = MIN(uio->uio_resid, MBPP_TXBUF_SIZE); - mp->mp_txp = mp->mp_txbuf; - - error = uiomove((caddr_t)mp->mp_txp, mp->mp_txc, uio); - if( error ) break; - - s = spltty(); - if( mp->mp_cd1400 ) - cd1400_enable_transmitter(mp->mp_cd1400, 0); - error = tsleep(mp, PCATCH | PZERO, "mbppwrite", mp->mp_write_timeout); - splx(s); - - if( error ) break; - } /* while(more to send...) */ - - if( error == EWOULDBLOCK ) { - log(LOG_INFO, "%s%d: write timeout\n", ms->ms_dev.dv_xname, MAGMA_PORT(dev)); - /* XXX check paper out, printer offline etc.. */ - } - - return(error); + return( mbpp_rw(dev, uio) ); } /* @@ -1542,8 +1518,42 @@ caddr_t data; int flags; struct proc *p; { - /* we want to be able to get & set strobe and timeout values */ - return(ENODEV); +struct mbpp_softc *ms = mbpp_cd.cd_devs[MAGMA_CARD(dev)]; +register struct mbpp_port *mp = &ms->ms_port[MAGMA_PORT(dev)]; +struct bpp_param *bp; +int error = 0; +int s; + + switch(cmd) { + case BPPIOCSPARAM: + bp = (struct bpp_param *)data; + if( bp->bp_burst < BPP_BURST_MIN || bp->bp_burst > BPP_BURST_MAX || + bp->bp_delay < BPP_DELAY_MIN || bp->bp_delay > BPP_DELAY_MIN ) { + error = EINVAL; + } else { + mp->mp_burst = bp->bp_burst; + mp->mp_timeout = mbpp_mstohz(bp->bp_timeout); + mp->mp_delay = mbpp_mstohz(bp->bp_delay); + } + break; + case BPPIOCGPARAM: + bp = (struct bpp_param *)data; + bp->bp_burst = mp->mp_burst; + bp->bp_timeout = mbpp_hztoms(mp->mp_timeout); + bp->bp_delay = mbpp_hztoms(mp->mp_delay); + break; + case BPPIOCGSTAT: + /* XXX make this more generic */ + s = spltty(); + cd1400_write_reg(mp->mp_cd1400, CD1400_CAR, 0); + *(int *)data = cd1400_read_reg(mp->mp_cd1400, CD1400_PSVR); + splx(s); + break; + default: + error = ENOTTY; + } + + return(error); } /* @@ -1558,4 +1568,263 @@ struct proc *p; return(ENODEV); } +int +mbpp_rw(dev, uio) +dev_t dev; +struct uio *uio; +{ +int card = MAGMA_CARD(dev); +int port = MAGMA_PORT(dev); +struct mbpp_softc *ms = mbpp_cd.cd_devs[card]; +register struct mbpp_port *mp = &ms->ms_port[port]; +caddr_t buffer, ptr; +int buflen, cnt, len; +int s, error = 0; +int gotdata = 0; + + if( uio->uio_resid == 0 ) + return(0); + + buflen = min(uio->uio_resid, mp->mp_burst); + buffer = malloc(buflen, M_DEVBUF, M_WAITOK); + if( buffer == NULL ) + return(ENOMEM); + + SET(mp->mp_flags, MBPPF_UIO); + + /* + * start timeout, if needed + */ + if( mp->mp_timeout > 0 ) { + SET(mp->mp_flags, MBPPF_TIMEOUT); + timeout(mbpp_timeout, mp, mp->mp_timeout); + } + + len = cnt = 0; + while( uio->uio_resid > 0 ) { + len = min(buflen, uio->uio_resid); + ptr = buffer; + + if( uio->uio_rw == UIO_WRITE ) { + error = uiomove(ptr, len, uio); + if( error ) break; + } +again: /* goto bad */ + /* timed out? */ + if( !ISSET(mp->mp_flags, MBPPF_UIO) ) + break; + + /* + * perform the operation + */ + if( uio->uio_rw == UIO_WRITE ) { + cnt = mbpp_send(mp, ptr, len); + } else { + cnt = mbpp_recv(mp, ptr, len); + } + + if( uio->uio_rw == UIO_READ ) { + if( cnt ) { + error = uiomove(ptr, cnt, uio); + if( error ) break; + gotdata++; + } + else if( gotdata ) /* consider us done */ + break; + } + + /* timed out? */ + if( !ISSET(mp->mp_flags, MBPPF_UIO) ) + break; + + /* + * poll delay? + */ + if( mp->mp_delay > 0 ) { + s = splsoftclock(); + SET(mp->mp_flags, MBPPF_DELAY); + timeout(mbpp_start, mp, mp->mp_delay); + error = tsleep(mp, PCATCH | PZERO, "mbppdelay", 0); + splx(s); + if( error ) break; + } + + /* + * don't call uiomove again until we used all the data we grabbed + */ + if( uio->uio_rw == UIO_WRITE && cnt != len ) { + ptr += cnt; + len -= cnt; + cnt = 0; + goto again; + } + } + + /* + * clear timeouts + */ + s = splsoftclock(); + if( ISSET(mp->mp_flags, MBPPF_TIMEOUT) ) { + untimeout(mbpp_timeout, mp); + CLR(mp->mp_flags, MBPPF_TIMEOUT); + } + if( ISSET(mp->mp_flags, MBPPF_DELAY) ) { + untimeout(mbpp_start, mp); + CLR(mp->mp_flags, MBPPF_DELAY); + } + splx(s); + + /* + * adjust for those chars that we uiomoved but never actually wrote + */ + if( uio->uio_rw == UIO_WRITE && cnt != len ) { + uio->uio_resid += (len - cnt); + } + + free(buffer, M_DEVBUF); + return(error); +} + +void +mbpp_timeout(arg) +void *arg; +{ +struct mbpp_port *mp = arg; + + CLR(mp->mp_flags, MBPPF_UIO | MBPPF_TIMEOUT); + wakeup(mp); +} + +void +mbpp_start(arg) +void *arg; +{ +struct mbpp_port *mp = arg; + + CLR(mp->mp_flags, MBPPF_DELAY); + wakeup(mp); +} + +int +mbpp_send(mp, ptr, len) +struct mbpp_port *mp; +caddr_t ptr; +int len; +{ +int s; +struct cd1400 *cd = mp->mp_cd1400; + + /* set up io information */ + mp->mp_ptr = ptr; + mp->mp_cnt = len; + + /* start transmitting */ + s = spltty(); + if( cd ) { + cd1400_write_reg(cd, CD1400_CAR, 0); + + /* output strobe width ~1microsecond */ + cd1400_write_reg(cd, CD1400_TBPR, 10); + + /* enable channel */ + cd1400_write_ccr(cd, CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN); + cd1400_write_reg(cd, CD1400_SRER, CD1400_SRER_TXRDY); + } + + /* ZZzzz... */ + tsleep(mp, PCATCH | PZERO, "mbpp_send", 0); + + /* stop transmitting */ + if( cd ) { + cd1400_write_reg(cd, CD1400_CAR, 0); + + /* disable transmitter */ + cd1400_write_reg(cd, CD1400_SRER, 0); + cd1400_write_ccr(cd, CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTDIS); + + /* flush fifo */ + cd1400_write_ccr(cd, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); + } + splx(s); + + /* return number of chars sent */ + return(len - mp->mp_cnt); +} + +int +mbpp_recv(mp, ptr, len) +struct mbpp_port *mp; +caddr_t ptr; +int len; +{ +int s; +struct cd1400 *cd = mp->mp_cd1400; + + /* set up io information */ + mp->mp_ptr = ptr; + mp->mp_cnt = len; + + /* start receiving */ + s = spltty(); + if( cd ) { + int rcor, rbpr; + + cd1400_write_reg(cd, CD1400_CAR, 0); + + /* input strobe at 100kbaud (10microseconds) */ + cd1400_compute_baud(100000, cd->cd_clock, &rcor, &rbpr); + cd1400_write_reg(cd, CD1400_RCOR, rcor); + cd1400_write_reg(cd, CD1400_RBPR, rbpr); + + /* rx threshold */ + cd1400_write_reg(cd, CD1400_COR3, MBPP_RX_FIFO_THRESHOLD); + cd1400_write_ccr(cd, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); + + /* enable channel */ + cd1400_write_ccr(cd, CD1400_CCR_CMDCHANCTL | CD1400_CCR_RCVEN); + cd1400_write_reg(cd, CD1400_SRER, CD1400_SRER_RXDATA); + } + + /* ZZzzz... */ + tsleep(mp, PCATCH | PZERO, "mbpp_recv", 0); + + /* stop receiving */ + if( cd ) { + cd1400_write_reg(cd, CD1400_CAR, 0); + + /* disable receiving */ + cd1400_write_reg(cd, CD1400_SRER, 0); + cd1400_write_ccr(cd, CD1400_CCR_CMDCHANCTL | CD1400_CCR_RCVDIS); + } + splx(s); + + /* return number of chars received */ + return(len - mp->mp_cnt); +} + +int +mbpp_hztoms(h) +int h; +{ +int m = h; + + if( m > 0 ) + m = m * 1000 / hz; + return(m); +} + +int +mbpp_mstohz(m) +int m; +{ +int h = m; + + if( h > 0 ) { + h = h * hz / 1000; + if( h == 0 ) + h = 1000 / hz; + } + return(h); +} + #endif /* NMAGMA */ |