1/*	$OpenBSD: comms_ebus.c,v 1.2 2009/06/17 06:48:38 matthieu Exp $	*/
2
3/*
4 * Copyright (c) 2009 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18/*
19 * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
20 * All rights reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the above copyright
26 *    notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 *    notice, this list of conditions and the following disclaimer in the
29 *    documentation and/or other materials provided with the distribution.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
32 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
35 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
37 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
40 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 * POSSIBILITY OF SUCH DAMAGE.
42 *
43 * Effort sponsored in part by the Defense Advanced Research Projects
44 * Agency (DARPA) and Air Force Research Laboratory, Air Force
45 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
46 *
47 */
48
49#include <sys/param.h>
50#include <sys/systm.h>
51#include <sys/conf.h>
52#include <sys/device.h>
53#include <sys/ioctl.h>
54#include <sys/kernel.h>
55#include <sys/proc.h>
56#include <sys/syslog.h>
57#include <sys/tty.h>
58
59#include <machine/bus.h>
60#include <machine/autoconf.h>
61#include <machine/openfirm.h>
62
63#include <sparc64/dev/ebusreg.h>
64#include <sparc64/dev/ebusvar.h>
65
66#include <dev/wscons/wsconsio.h>
67#include <dev/wscons/wsmousevar.h>
68
69#include <dev/sun/sunmsvar.h>
70
71#include <dev/ic/comreg.h>
72#include <dev/ic/comvar.h>
73#include <dev/ic/ns16550reg.h>
74#define	com_lcr com_cfcr
75
76#include <dev/cons.h>
77
78/* should match com_ebus.c */
79#define	BAUD_BASE	(1843200)
80
81#define	COMMS_RX_RING	64
82
83struct comms_softc {
84	struct	sunms_softc sc_base;
85
86	u_int	sc_ier;
87
88	bus_space_tag_t sc_iot;		/* bus tag */
89	bus_space_handle_t sc_ioh;	/* bus handle */
90	void *sc_ih, *sc_si;		/* interrupt vectors */
91
92	u_int sc_rxcnt;
93	u_int8_t sc_rxbuf[COMMS_RX_RING];
94	u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput;
95};
96
97#define	COM_WRITE(sc,r,v) \
98    bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
99#define	COM_READ(sc,r) \
100    bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r))
101
102/*
103 * autoconf glue.
104 */
105
106int	comms_match(struct device *, void *, void *);
107void	comms_attach(struct device *, struct device *, void *);
108
109const struct cfattach comms_ca = {
110	sizeof(struct comms_softc), comms_match, comms_attach
111};
112
113struct cfdriver comms_cd = {
114	NULL, "comms", DV_DULL
115};
116
117/*
118 * wsmouse accessops.
119 */
120
121void	comms_disable(void *);
122int	comms_enable(void *);
123
124const struct wsmouse_accessops comms_accessops = {
125	comms_enable,
126	sunms_ioctl,
127	comms_disable
128};
129
130/*
131 *  com glue.
132 */
133
134int	comms_hardintr(void *);
135int	comms_ismouse(int);
136void	comms_softintr(void *);
137void	comms_speed_change(void *, uint);
138
139/*
140 * autoconf glue.
141 */
142
143static const char *comms_names[] = {
144	"su",
145	"su_pnp",
146	NULL
147};
148
149int
150comms_ismouse(int node)
151{
152	if (OF_getproplen(node, "mouse") == 0)
153		return 10;
154	return 0;
155}
156
157int
158comms_match(struct device *parent, void *match, void *aux)
159{
160	struct ebus_attach_args *ea = aux;
161	int i;
162
163	for (i = 0; comms_names[i]; i++)
164		if (strcmp(ea->ea_name, comms_names[i]) == 0)
165			return comms_ismouse(ea->ea_node);
166
167	if (strcmp(ea->ea_name, "serial") == 0) {
168		char compat[80];
169
170		if ((i = OF_getproplen(ea->ea_node, "compatible")) &&
171		    OF_getprop(ea->ea_node, "compatible", compat,
172			sizeof(compat)) == i) {
173			if (strcmp(compat, "su16550") == 0 ||
174			    strcmp(compat, "su") == 0)
175				return comms_ismouse(ea->ea_node);
176		}
177	}
178	return 0;
179}
180
181void
182comms_attach(struct device *parent, struct device *self, void *aux)
183{
184	struct comms_softc *sc = (void *)self;
185	struct ebus_attach_args *ea = aux;
186
187	sc->sc_iot = ea->ea_memtag;
188
189	sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf;
190	sc->sc_rxend = sc->sc_rxbuf + COMMS_RX_RING;
191	sc->sc_rxcnt = 0;
192
193	/* we really want IPL_TTY here. */
194	sc->sc_si = softintr_establish(IPL_TTY, comms_softintr, sc);
195	if (sc->sc_si == NULL) {
196		printf(": can't get soft intr\n");
197		return;
198	}
199
200	/* Use prom address if available, otherwise map it. */
201	if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
202	    BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) {
203		sc->sc_iot = ea->ea_memtag;
204	} else if (ebus_bus_map(ea->ea_memtag, 0,
205	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
206	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
207		sc->sc_iot = ea->ea_memtag;
208	} else if (ebus_bus_map(ea->ea_iotag, 0,
209	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
210	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
211		sc->sc_iot = ea->ea_iotag;
212	} else {
213		printf(": can't map register space\n");
214                return;
215	}
216
217	sc->sc_ih = bus_intr_establish(sc->sc_iot,
218	    ea->ea_intrs[0], IPL_TTY, 0, comms_hardintr, sc, self->dv_xname);
219	if (sc->sc_ih == NULL) {
220		printf(": can't get hard intr\n");
221		return;
222	}
223
224	/* Initialize hardware. */
225	sc->sc_ier = 0;
226	comms_speed_change(sc, INIT_SPEED);
227
228	sc->sc_base.sc_speed_change = comms_speed_change;
229
230	sunms_attach(&sc->sc_base, &comms_accessops);
231}
232
233/*
234 * wsmouse accessops.
235 */
236
237void
238comms_disable(void *v)
239{
240	struct comms_softc *sc = v;
241	int s;
242
243	s = spltty();
244	sc->sc_ier = 0;
245	COM_WRITE(sc, com_ier, sc->sc_ier);
246	splx(s);
247}
248
249int
250comms_enable(void *v)
251{
252	struct comms_softc *sc = v;
253	int s;
254
255	s = spltty();
256	sc->sc_ier = IER_ERXRDY;
257	COM_WRITE(sc, com_ier, sc->sc_ier);
258	splx(s);
259
260	return 0;
261}
262
263/*
264 * com glue.
265 */
266
267void
268comms_softintr(void *v)
269{
270	struct comms_softc *sc = v;
271	uint8_t c;
272
273	/*
274	 * If we have a baud rate change pending, do it now.
275	 * This will reset the rx ring, so we can proceed safely.
276	 */
277	if (sc->sc_base.sc_state == STATE_RATE_CHANGE) {
278		sunms_speed_change(&sc->sc_base);
279	}
280
281	/*
282	 * Copy data from the receive ring, if any, to the event layer.
283	 */
284	while (sc->sc_rxcnt) {
285		c = *sc->sc_rxget;
286		if (++sc->sc_rxget == sc->sc_rxend)
287			sc->sc_rxget = sc->sc_rxbeg;
288		sc->sc_rxcnt--;
289		sunms_input(&sc->sc_base, c);
290	}
291}
292
293int
294comms_hardintr(void *v)
295{
296	struct comms_softc *sc = v;
297	u_int8_t iir, lsr, data;
298	int needsoft = 0;
299
300	/* Nothing to do */
301	iir = COM_READ(sc, com_iir);
302	if (ISSET(iir, IIR_NOPEND))
303		return 0;
304
305	for (;;) {
306		lsr = COM_READ(sc, com_lsr);
307
308		if (ISSET(lsr, LSR_BI)) {
309			if (sc->sc_base.sc_state != STATE_RATE_CHANGE &&
310			    ++sc->sc_base.sc_brk > 1) {
311				sc->sc_base.sc_state = STATE_RATE_CHANGE;
312				needsoft = 1;
313#ifdef DEBUG
314				printf("%s: break detected, changing speed\n",
315				    sc->sc_base.sc_dev.dv_xname);
316#endif
317			}
318		}
319
320		if (ISSET(lsr, LSR_RXRDY)) {
321			needsoft = 1;
322
323			do {
324				data = COM_READ(sc, com_data);
325				if (sc->sc_base.sc_state != STATE_RATE_CHANGE &&
326				    sc->sc_rxcnt != COMMS_RX_RING) {
327					*sc->sc_rxput = data;
328					if (++sc->sc_rxput == sc->sc_rxend)
329						sc->sc_rxput = sc->sc_rxbeg;
330					sc->sc_rxcnt++;
331				}
332				lsr = COM_READ(sc, com_lsr);
333			} while (ISSET(lsr, LSR_RXRDY));
334		}
335
336		iir = COM_READ(sc, com_iir);
337		if (ISSET(iir, IIR_NOPEND))
338			break;
339	}
340
341	if (needsoft)
342		softintr_schedule(sc->sc_si);
343
344	return 1;
345}
346
347/*
348 * Reinitialize the line to a different speed.  Invoked at spltty().
349 */
350void
351comms_speed_change(void *v, uint bps)
352{
353	struct comms_softc *sc = v;
354	int ospeed;
355
356	/*
357	 * Eat everything on the line.
358	 */
359	while (ISSET(COM_READ(sc, com_lsr), LSR_RXRDY))
360		COM_READ(sc, com_data);
361
362	ospeed = comspeed(BAUD_BASE, bps);
363
364	/* disable interrupts while the chip is reprogrammed */
365	COM_WRITE(sc, com_ier, 0);
366
367	COM_WRITE(sc, com_lcr, LCR_DLAB);
368	COM_WRITE(sc, com_dlbl, ospeed);
369	COM_WRITE(sc, com_dlbh, ospeed >> 8);
370	/* 8 data bits, no parity, 2 stop bits */
371	COM_WRITE(sc, com_lcr, LCR_8BITS | LCR_PNONE | LCR_STOPB);
372	COM_READ(sc, com_iir);
373
374	COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS);
375	/* XXX do something about the FIFO? */
376
377	COM_WRITE(sc, com_ier, sc->sc_ier);
378}
379