xref: /openbsd/sys/arch/sparc64/dev/comms_ebus.c (revision a7f5b4e9)
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 
83 struct 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 
106 int	comms_match(struct device *, void *, void *);
107 void	comms_attach(struct device *, struct device *, void *);
108 
109 const struct cfattach comms_ca = {
110 	sizeof(struct comms_softc), comms_match, comms_attach
111 };
112 
113 struct cfdriver comms_cd = {
114 	NULL, "comms", DV_DULL
115 };
116 
117 /*
118  * wsmouse accessops.
119  */
120 
121 void	comms_disable(void *);
122 int	comms_enable(void *);
123 
124 const struct wsmouse_accessops comms_accessops = {
125 	comms_enable,
126 	sunms_ioctl,
127 	comms_disable
128 };
129 
130 /*
131  *  com glue.
132  */
133 
134 int	comms_hardintr(void *);
135 int	comms_ismouse(int);
136 void	comms_softintr(void *);
137 void	comms_speed_change(void *, uint);
138 
139 /*
140  * autoconf glue.
141  */
142 
143 static const char *comms_names[] = {
144 	"su",
145 	"su_pnp",
146 	NULL
147 };
148 
149 int
comms_ismouse(int node)150 comms_ismouse(int node)
151 {
152 	if (OF_getproplen(node, "mouse") == 0)
153 		return 10;
154 	return 0;
155 }
156 
157 int
comms_match(struct device * parent,void * match,void * aux)158 comms_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 
181 void
comms_attach(struct device * parent,struct device * self,void * aux)182 comms_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 
237 void
comms_disable(void * v)238 comms_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 
249 int
comms_enable(void * v)250 comms_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 
267 void
comms_softintr(void * v)268 comms_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 
293 int
comms_hardintr(void * v)294 comms_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  */
350 void
comms_speed_change(void * v,uint bps)351 comms_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