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