/* $OpenBSD: z8530ms.c,v 1.1 2009/05/20 18:22:33 miod Exp $ */ /* $NetBSD: ms.c,v 1.12 1997/07/17 01:17:47 jtk Exp $ */ /* * Copyright (c) 2002, 2009, Miodrag Vallat * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ms.c 8.1 (Berkeley) 6/11/93 */ /* * Zilog Z8530 Dual UART driver (mouse interface) * * This is the "slave" driver that will be attached to * the "zsc" driver for a Sun mouse. */ #include #include #include #include #include #include #include #include #ifndef __sparc64__ /* until zs driver is unified... */ #include #else #include #endif #include #include #include /* * How many input characters we can buffer. * Note: must be a power of two! */ #define MS_RX_RING_SIZE 256 #define MS_RX_RING_MASK (MS_RX_RING_SIZE-1) struct zsms_softc { struct sunms_softc sc_base; struct zs_chanstate *sc_cs; /* Flags to communicate with zsms_softint() */ volatile int sc_intr_flags; #define INTR_RX_OVERRUN 0x01 #define INTR_TX_EMPTY 0x02 #define INTR_ST_CHECK 0x04 #define INTR_BPS_CHANGE 0x08 /* * The receive ring buffer. */ uint sc_rbget; /* ring buffer `get' index */ volatile uint sc_rbput; /* ring buffer `put' index */ uint16_t sc_rbuf[MS_RX_RING_SIZE]; /* rr1, data pairs */ }; /* * autoconf glue. */ int zsms_match(struct device *, void *, void *); void zsms_attach(struct device *, struct device *, void *); const struct cfattach zsms_ca = { sizeof(struct zsms_softc), zsms_match, zsms_attach }; struct cfdriver zsms_cd = { NULL, "zsms", DV_DULL }; /* * wsmouse accessops. */ void zsms_disable(void *); int zsms_enable(void *); const struct wsmouse_accessops zsms_accessops = { zsms_enable, sunms_ioctl, zsms_disable }; /* * zs glue. */ void zsms_rxint(struct zs_chanstate *); void zsms_softint(struct zs_chanstate *); void zsms_stint(struct zs_chanstate *, int); void zsms_txint(struct zs_chanstate *); struct zsops zsops_ms = { zsms_rxint, /* receive char available */ zsms_stint, /* external/status */ zsms_txint, /* xmit buffer empty */ zsms_softint /* process software interrupt */ }; void zsms_speed_change(void *, uint); /* * autoconf glue. */ int zsms_match(struct device *parent, void *vcf, void *aux) { struct cfdata *cf = vcf; struct zsc_attach_args *args = aux; int rc; /* If we're not looking for a mouse, just exit */ if (strcmp(args->type, "mouse") != 0) return 0; rc = 10; /* Exact match is better than wildcard. */ if (cf->cf_loc[ZSCCF_CHANNEL] == args->channel) rc += 2; /* This driver accepts wildcard. */ if (cf->cf_loc[ZSCCF_CHANNEL] == ZSCCF_CHANNEL_DEFAULT) rc += 1; return rc; } void zsms_attach(struct device *parent, struct device *self, void *aux) { struct zsc_softc *zsc = (struct zsc_softc *)parent; struct zsms_softc *sc = (struct zsms_softc *)self; struct zsc_attach_args *args = aux; struct zs_chanstate *cs; int channel; int s; channel = args->channel; #ifndef __sparc64__ /* until driver is unified... */ cs = &zsc->zsc_cs[channel]; #else cs = zsc->zsc_cs[channel]; #endif cs->cs_private = sc; cs->cs_ops = &zsops_ms; sc->sc_cs = cs; /* Initialize hardware. */ s = splzs(); zs_write_reg(cs, 9, channel == 0 ? ZSWR9_A_RESET : ZSWR9_B_RESET); /* disable interrupts until the mouse is enabled */ CLR(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE | ZSWR1_TIE); /* 8 data bits is already our default */ /* no parity, 2 stop bits */ CLR(cs->cs_preg[4], ZSWR4_SBMASK | ZSWR4_PARMASK); SET(cs->cs_preg[4], ZSWR4_TWOSB); (void)zs_set_speed(cs, INIT_SPEED); zs_loadchannelregs(cs); splx(s); sc->sc_base.sc_speed_change = zsms_speed_change; sunms_attach(&sc->sc_base, &zsms_accessops); } /* * wsmouse accessops. */ void zsms_disable(void *v) { struct zsms_softc *sc = v; struct zs_chanstate *cs = sc->sc_cs; int s; s = splzs(); /* disable RX and status change interrupts */ CLR(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE); zs_loadchannelregs(cs); splx(s); } int zsms_enable(void *v) { struct zsms_softc *sc = v; struct zs_chanstate *cs = sc->sc_cs; int s; s = splzs(); /* enable RX and status change interrupts */ SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE); zs_loadchannelregs(cs); splx(s); return 0; } /* * zs glue. */ void zsms_rxint(struct zs_chanstate *cs) { struct zsms_softc *sc = cs->cs_private; int put, put_next; u_char rr0, rr1, c; put = sc->sc_rbput; for (;;) { /* * First read the status, because reading the received char * destroys the status of this char. */ rr1 = zs_read_reg(cs, 1); c = zs_read_data(cs); /* * Note that we do not try to change speed upon encountering * framing errors, as this is not as reliable as breaks. */ if (ISSET(rr1, ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { /* Clear the receive error. */ zs_write_csr(cs, ZSWR0_RESET_ERRORS); } if (sc->sc_base.sc_state != STATE_RATE_CHANGE) { sc->sc_rbuf[put] = (c << 8) | rr1; put_next = (put + 1) & MS_RX_RING_MASK; /* Would overrun if increment makes (put==get). */ if (put_next == sc->sc_rbget) { sc->sc_intr_flags |= INTR_RX_OVERRUN; break; } else { /* OK, really increment. */ put = put_next; } } rr0 = zs_read_csr(cs); if (!ISSET(rr0, ZSRR0_RX_READY)) break; } /* Done reading. */ sc->sc_rbput = put; cs->cs_softreq = 1; } void zsms_txint(struct zs_chanstate *cs) { /* * This function should never be invoked as we don't accept TX * interrupts. If someone alters our configuration behind our * back, just disable TX interrupts again. */ zs_write_csr(cs, ZSWR0_RESET_TXINT); /* disable tx interrupts */ CLR(cs->cs_preg[1], ZSWR1_TIE); zs_loadchannelregs(cs); } void zsms_stint(struct zs_chanstate *cs, int force) { struct zsms_softc *sc = cs->cs_private; uint8_t rr0, delta; rr0 = zs_read_csr(cs); zs_write_csr(cs, ZSWR0_RESET_STATUS); /* * A break can occur if the speed is not correct. * However, we do not change speed until we get the second * break, for switching speed when the mouse is unplugged * will trigger a break and thus we'd loop changing speeds * until the mouse is plugged again. */ if (!force && ISSET(rr0, ZSRR0_BREAK)) { if (sc->sc_base.sc_state != STATE_RATE_CHANGE && ++sc->sc_base.sc_brk > 1) { sc->sc_intr_flags |= INTR_BPS_CHANGE; sc->sc_base.sc_state = STATE_RATE_CHANGE; cs->cs_softreq = 1; #ifdef DEBUG printf("%s: break detected, changing speed\n", sc->sc_base.sc_dev.dv_xname); #endif } } if (!force) delta = rr0 ^ cs->cs_rr0; else delta = cs->cs_rr0_mask; cs->cs_rr0 = rr0; if (ISSET(delta, cs->cs_rr0_mask)) { SET(cs->cs_rr0_delta, delta); sc->sc_intr_flags |= INTR_ST_CHECK; cs->cs_softreq = 1; } } void zsms_softint(struct zs_chanstate *cs) { struct zsms_softc *sc; int get, c, s, s2; int intr_flags; u_short ring_data; sc = cs->cs_private; /* Atomically get and clear flags. */ s = spltty(); s2 = splzs(); intr_flags = sc->sc_intr_flags; sc->sc_intr_flags = 0; /* Now lower to spltty for the rest. */ splx(s2); /* * If we have a baud rate change pending, do it now. * This will reset the rx ring, so we can proceed safely. */ if (ISSET(intr_flags, INTR_BPS_CHANGE)) { CLR(intr_flags, INTR_RX_OVERRUN); sunms_speed_change(&sc->sc_base); splx(s); } /* * Copy data from the receive ring, if any, to the event layer. */ get = sc->sc_rbget; while (get != sc->sc_rbput) { ring_data = sc->sc_rbuf[get]; get = (get + 1) & MS_RX_RING_MASK; /* low byte of ring_data is rr1 */ c = (ring_data >> 8) & 0xff; if (ring_data & ZSRR1_DO) SET(intr_flags, INTR_RX_OVERRUN); /* Pass this up to the "middle" layer. */ sunms_input(&sc->sc_base, c); } if (ISSET(intr_flags, INTR_RX_OVERRUN)) log(LOG_ERR, "%s: input overrun\n", sc->sc_base.sc_dev.dv_xname); sc->sc_rbget = get; /* * Status line change. Not expected except for break conditions. */ if (ISSET(intr_flags, INTR_ST_CHECK)) { cs->cs_rr0_delta = 0; } splx(s); } /* * Reinitialize the line to a different speed. Invoked at spltty(). */ void zsms_speed_change(void *v, uint bps) { struct zsms_softc *sc = v; struct zs_chanstate *cs = sc->sc_cs; uint8_t rr0; int s; s = splzs(); /* * Eat everything on the line. */ for (;;) { rr0 = zs_read_csr(cs); if (!ISSET(rr0, ZSRR0_RX_READY)) break; (void)zs_read_data(cs); } (void)zs_set_speed(cs, sc->sc_base.sc_bps); zs_loadchannelregs(cs); zsms_stint(cs, 1); sc->sc_rbget = sc->sc_rbput = 0; splx(s); }