1/*	$OpenBSD: z8530ms.c,v 1.4 2016/10/24 06:13:52 deraadt Exp $	*/
2/*	$NetBSD: ms.c,v 1.12 1997/07/17 01:17:47 jtk Exp $	*/
3
4/*
5 * Copyright (c) 2002, 2009, Miodrag Vallat
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 *
29 * Copyright (c) 1992, 1993
30 *	The Regents of the University of California.  All rights reserved.
31 *
32 * This software was developed by the Computer Systems Engineering group
33 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
34 * contributed to Berkeley.
35 *
36 * All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 *	This product includes software developed by the University of
39 *	California, Lawrence Berkeley Laboratory.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 * 3. All advertising materials mentioning features or use of this software
50 *    must display the following acknowledgement:
51 *	This product includes software developed by the University of
52 *	California, Berkeley and its contributors.
53 * 4. Neither the name of the University nor the names of its contributors
54 *    may be used to endorse or promote products derived from this software
55 *    without specific prior written permission.
56 *
57 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
58 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
59 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
60 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
61 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
62 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
63 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
64 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
65 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
66 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67 * SUCH DAMAGE.
68 *
69 *	@(#)ms.c	8.1 (Berkeley) 6/11/93
70 */
71
72/*
73 * Zilog Z8530 Dual UART driver (mouse interface)
74 *
75 * This is the "slave" driver that will be attached to
76 * the "zsc" driver for a Sun mouse.
77 */
78
79#include <sys/param.h>
80#include <sys/systm.h>
81#include <sys/conf.h>
82#include <sys/device.h>
83#include <sys/ioctl.h>
84#include <sys/kernel.h>
85#include <sys/proc.h>
86#include <sys/syslog.h>
87
88#include <dev/ic/z8530reg.h>
89#include <machine/z8530var.h>
90
91#include <dev/wscons/wsconsio.h>
92#include <dev/wscons/wsmousevar.h>
93#include <dev/sun/sunmsvar.h>
94
95/*
96 * How many input characters we can buffer.
97 * Note: must be a power of two!
98 */
99#define	MS_RX_RING_SIZE	256
100#define MS_RX_RING_MASK (MS_RX_RING_SIZE-1)
101
102struct zsms_softc {
103	struct	sunms_softc sc_base;
104	struct	zs_chanstate *sc_cs;
105
106	/* Flags to communicate with zsms_softint() */
107	volatile int sc_intr_flags;
108#define	INTR_RX_OVERRUN 	0x01
109#define INTR_TX_EMPTY   	0x02
110#define INTR_ST_CHECK   	0x04
111#define	INTR_BPS_CHANGE		0x08
112
113	/*
114	 * The receive ring buffer.
115	 */
116	uint	sc_rbget;		/* ring buffer `get' index */
117	volatile uint	sc_rbput;	/* ring buffer `put' index */
118	uint16_t sc_rbuf[MS_RX_RING_SIZE]; /* rr1, data pairs */
119};
120
121/*
122 * autoconf glue.
123 */
124
125int	zsms_match(struct device *, void *, void *);
126void	zsms_attach(struct device *, struct device *, void *);
127
128const struct cfattach zsms_ca = {
129	sizeof(struct zsms_softc), zsms_match, zsms_attach
130};
131
132struct cfdriver zsms_cd = {
133	NULL, "zsms", DV_DULL
134};
135
136/*
137 * wsmouse accessops.
138 */
139
140void	zsms_disable(void *);
141int	zsms_enable(void *);
142
143const struct wsmouse_accessops zsms_accessops = {
144	zsms_enable,
145	sunms_ioctl,
146	zsms_disable
147};
148
149/*
150 * zs glue.
151 */
152
153void	zsms_rxint(struct zs_chanstate *);
154void	zsms_softint(struct zs_chanstate *);
155void	zsms_stint(struct zs_chanstate *, int);
156void	zsms_txint(struct zs_chanstate *);
157
158struct zsops zsops_ms = {
159	zsms_rxint,	/* receive char available */
160	zsms_stint,	/* external/status */
161	zsms_txint,	/* xmit buffer empty */
162	zsms_softint	/* process software interrupt */
163};
164
165void	zsms_speed_change(void *, uint);
166
167/*
168 * autoconf glue.
169 */
170
171int
172zsms_match(struct device *parent, void *vcf, void *aux)
173{
174	struct cfdata *cf = vcf;
175	struct zsc_attach_args *args = aux;
176	int rc;
177
178	/* If we're not looking for a mouse, just exit */
179	if (strcmp(args->type, "mouse") != 0)
180		return 0;
181
182	rc = 10;
183
184	/* Exact match is better than wildcard. */
185	if (cf->cf_loc[ZSCCF_CHANNEL] == args->channel)
186		rc += 2;
187
188	/* This driver accepts wildcard. */
189	if (cf->cf_loc[ZSCCF_CHANNEL] == ZSCCF_CHANNEL_DEFAULT)
190		rc += 1;
191
192	return rc;
193}
194
195void
196zsms_attach(struct device *parent, struct device *self, void *aux)
197{
198	struct zsc_softc *zsc = (struct zsc_softc *)parent;
199	struct zsms_softc *sc = (struct zsms_softc *)self;
200	struct zsc_attach_args *args = aux;
201	struct zs_chanstate *cs;
202	int channel;
203	int s;
204
205	channel = args->channel;
206	cs = zsc->zsc_cs[channel];
207	cs->cs_private = sc;
208	cs->cs_ops = &zsops_ms;
209	sc->sc_cs = cs;
210
211	/* Initialize hardware. */
212	s = splzs();
213	zs_write_reg(cs, 9, channel == 0 ?  ZSWR9_A_RESET : ZSWR9_B_RESET);
214	/* disable interrupts until the mouse is enabled */
215	CLR(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE | ZSWR1_TIE);
216	/* 8 data bits is already our default */
217	/* no parity, 2 stop bits */
218	CLR(cs->cs_preg[4], ZSWR4_SBMASK | ZSWR4_PARMASK);
219	SET(cs->cs_preg[4], ZSWR4_TWOSB);
220	(void)zs_set_speed(cs, INIT_SPEED);
221	zs_loadchannelregs(cs);
222	splx(s);
223
224	sc->sc_base.sc_speed_change = zsms_speed_change;
225
226	sunms_attach(&sc->sc_base, &zsms_accessops);
227}
228
229/*
230 * wsmouse accessops.
231 */
232
233void
234zsms_disable(void *v)
235{
236	struct zsms_softc *sc = v;
237	struct zs_chanstate *cs = sc->sc_cs;
238	int s;
239
240	s = splzs();
241	/* disable RX and status change interrupts */
242	CLR(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE);
243	zs_loadchannelregs(cs);
244	splx(s);
245}
246
247int
248zsms_enable(void *v)
249{
250	struct zsms_softc *sc = v;
251	struct zs_chanstate *cs = sc->sc_cs;
252	int s;
253
254	s = splzs();
255	/* enable RX and status change interrupts */
256	SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE);
257	zs_loadchannelregs(cs);
258	splx(s);
259
260	return 0;
261}
262
263/*
264 * zs glue.
265 */
266
267void
268zsms_rxint(struct zs_chanstate *cs)
269{
270	struct zsms_softc *sc = cs->cs_private;
271	int put, put_next;
272	u_char rr0, rr1, c;
273
274	put = sc->sc_rbput;
275
276	for (;;) {
277		/*
278		 * First read the status, because reading the received char
279		 * destroys the status of this char.
280		 */
281		rr1 = zs_read_reg(cs, 1);
282		c = zs_read_data(cs);
283
284		/*
285		 * Note that we do not try to change speed upon encountering
286		 * framing errors, as this is not as reliable as breaks.
287		 */
288		if (ISSET(rr1, ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
289			/* Clear the receive error. */
290			zs_write_csr(cs, ZSWR0_RESET_ERRORS);
291		}
292
293		if (sc->sc_base.sc_state != STATE_RATE_CHANGE) {
294			sc->sc_rbuf[put] = (c << 8) | rr1;
295			put_next = (put + 1) & MS_RX_RING_MASK;
296
297			/* Would overrun if increment makes (put==get). */
298			if (put_next == sc->sc_rbget) {
299				sc->sc_intr_flags |= INTR_RX_OVERRUN;
300				break;
301			} else {
302				/* OK, really increment. */
303				put = put_next;
304			}
305		}
306
307		rr0 = zs_read_csr(cs);
308		if (!ISSET(rr0, ZSRR0_RX_READY))
309			break;
310	}
311
312	/* Done reading. */
313	sc->sc_rbput = put;
314
315	cs->cs_softreq = 1;
316}
317
318void
319zsms_txint(struct zs_chanstate *cs)
320{
321	/*
322	 * This function should never be invoked as we don't accept TX
323	 * interrupts.  If someone alters our configuration behind our
324	 * back, just disable TX interrupts again.
325	 */
326	zs_write_csr(cs, ZSWR0_RESET_TXINT);
327
328	/* disable tx interrupts */
329	CLR(cs->cs_preg[1], ZSWR1_TIE);
330	zs_loadchannelregs(cs);
331}
332
333void
334zsms_stint(struct zs_chanstate *cs, int force)
335{
336	struct zsms_softc *sc = cs->cs_private;
337	uint8_t rr0, delta;
338
339	rr0 = zs_read_csr(cs);
340	zs_write_csr(cs, ZSWR0_RESET_STATUS);
341
342	/*
343	 * A break can occur if the speed is not correct.
344	 * However, we do not change speed until we get the second
345	 * break, for switching speed when the mouse is unplugged
346	 * will trigger a break and thus we'd loop changing speeds
347	 * until the mouse is plugged again.
348	 */
349	if (!force && ISSET(rr0, ZSRR0_BREAK)) {
350		if (sc->sc_base.sc_state != STATE_RATE_CHANGE &&
351		    ++sc->sc_base.sc_brk > 1) {
352			sc->sc_intr_flags |= INTR_BPS_CHANGE;
353			sc->sc_base.sc_state = STATE_RATE_CHANGE;
354			cs->cs_softreq = 1;
355#ifdef DEBUG
356			printf("%s: break detected, changing speed\n",
357			    sc->sc_base.sc_dev.dv_xname);
358#endif
359		}
360	}
361
362	if (!force)
363		delta = rr0 ^ cs->cs_rr0;
364	else
365		delta = cs->cs_rr0_mask;
366	cs->cs_rr0 = rr0;
367
368	if (ISSET(delta, cs->cs_rr0_mask)) {
369		SET(cs->cs_rr0_delta, delta);
370
371		sc->sc_intr_flags |= INTR_ST_CHECK;
372		cs->cs_softreq = 1;
373	}
374}
375
376void
377zsms_softint(struct zs_chanstate *cs)
378{
379	struct zsms_softc *sc;
380	int get, c, s, s2;
381	int intr_flags;
382	u_short ring_data;
383
384	sc = cs->cs_private;
385
386	/* Atomically get and clear flags. */
387	s = spltty();
388	s2 = splzs();
389	intr_flags = sc->sc_intr_flags;
390	sc->sc_intr_flags = 0;
391	/* Now lower to spltty for the rest. */
392	splx(s2);
393
394	/*
395	 * If we have a baud rate change pending, do it now.
396	 * This will reset the rx ring, so we can proceed safely.
397	 */
398	if (ISSET(intr_flags, INTR_BPS_CHANGE)) {
399		CLR(intr_flags, INTR_RX_OVERRUN);
400		sunms_speed_change(&sc->sc_base);
401	}
402
403	/*
404	 * Copy data from the receive ring, if any, to the event layer.
405	 */
406	get = sc->sc_rbget;
407	while (get != sc->sc_rbput) {
408		ring_data = sc->sc_rbuf[get];
409		get = (get + 1) & MS_RX_RING_MASK;
410
411		/* low byte of ring_data is rr1 */
412		c = (ring_data >> 8) & 0xff;
413
414		if (ring_data & ZSRR1_DO)
415			SET(intr_flags, INTR_RX_OVERRUN);
416
417		/* Pass this up to the "middle" layer. */
418		sunms_input(&sc->sc_base, c);
419	}
420	if (ISSET(intr_flags, INTR_RX_OVERRUN))
421		log(LOG_ERR, "%s: input overrun\n",
422		    sc->sc_base.sc_dev.dv_xname);
423	sc->sc_rbget = get;
424
425	/*
426	 * Status line change.  Not expected except for break conditions.
427	 */
428	if (ISSET(intr_flags, INTR_ST_CHECK)) {
429		cs->cs_rr0_delta = 0;
430	}
431
432	splx(s);
433}
434
435/*
436 * Reinitialize the line to a different speed.  Invoked at spltty().
437 */
438void
439zsms_speed_change(void *v, uint bps)
440{
441	struct zsms_softc *sc = v;
442	struct zs_chanstate *cs = sc->sc_cs;
443	uint8_t rr0;
444	int s;
445
446	s = splzs();
447
448	/*
449	 * Eat everything on the line.
450	 */
451	for (;;) {
452		rr0 = zs_read_csr(cs);
453		if (!ISSET(rr0, ZSRR0_RX_READY))
454			break;
455		(void)zs_read_data(cs);
456	}
457
458	(void)zs_set_speed(cs, sc->sc_base.sc_bps);
459	zs_loadchannelregs(cs);
460	zsms_stint(cs, 1);
461
462	sc->sc_rbget = sc->sc_rbput = 0;
463
464	splx(s);
465}
466