xref: /netbsd-src/sys/dev/qbus/dl.c (revision fad4c9f71477ae11cea2ee75ec82151ac770a534)
1 /*	$NetBSD: dl.c,v 1.34 2006/05/15 20:44:04 yamt Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright (c) 1997  Ben Harris.  All rights reserved.
41  * Copyright (c) 1982, 1986, 1990, 1992, 1993
42  *	The Regents of the University of California.  All rights reserved.
43  *
44  * This code is derived from software contributed to Berkeley by
45  * Ralph Campbell and Rick Macklem.
46  *
47  * Redistribution and use in source and binary forms, with or without
48  * modification, are permitted provided that the following conditions
49  * are met:
50  * 1. Redistributions of source code must retain the above copyright
51  *    notice, this list of conditions and the following disclaimer.
52  * 2. Redistributions in binary form must reproduce the above copyright
53  *    notice, this list of conditions and the following disclaimer in the
54  *    documentation and/or other materials provided with the distribution.
55  * 3. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  */
71 
72 /*
73  * Copyright (c) 1996  Ken C. Wellsch.  All rights reserved.
74  *
75  * This code is derived from software contributed to Berkeley by
76  * Ralph Campbell and Rick Macklem.
77  *
78  * Redistribution and use in source and binary forms, with or without
79  * modification, are permitted provided that the following conditions
80  * are met:
81  * 1. Redistributions of source code must retain the above copyright
82  *    notice, this list of conditions and the following disclaimer.
83  * 2. Redistributions in binary form must reproduce the above copyright
84  *    notice, this list of conditions and the following disclaimer in the
85  *    documentation and/or other materials provided with the distribution.
86  * 3. All advertising materials mentioning features or use of this software
87  *    must display the following acknowledgement:
88  *	This product includes software developed by the University of
89  *	California, Berkeley and its contributors.
90  * 4. Neither the name of the University nor the names of its contributors
91  *    may be used to endorse or promote products derived from this software
92  *    without specific prior written permission.
93  *
94  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
95  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
96  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
97  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
98  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
99  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
100  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
101  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
102  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
103  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
104  * SUCH DAMAGE.
105  */
106 
107 /*
108  * dl.c -- Device driver for the DL11 and DLV11 serial cards.
109  *
110  * OS-interface code derived from the dz and dca (hp300) drivers.
111  */
112 
113 #include <sys/cdefs.h>
114 __KERNEL_RCSID(0, "$NetBSD: dl.c,v 1.34 2006/05/15 20:44:04 yamt Exp $");
115 
116 #include <sys/param.h>
117 #include <sys/systm.h>
118 #include <sys/ioctl.h>
119 #include <sys/tty.h>
120 #include <sys/proc.h>
121 #include <sys/buf.h>
122 #include <sys/conf.h>
123 #include <sys/file.h>
124 #include <sys/uio.h>
125 #include <sys/kernel.h>
126 #include <sys/syslog.h>
127 #include <sys/device.h>
128 #include <sys/kauth.h>
129 
130 #include <machine/bus.h>
131 
132 #include <dev/qbus/ubavar.h>
133 
134 #include <dev/qbus/dlreg.h>
135 
136 #include "ioconf.h"
137 
138 struct dl_softc {
139 	struct device	sc_dev;
140 	struct evcnt	sc_rintrcnt;
141 	struct evcnt	sc_tintrcnt;
142 	bus_space_tag_t	sc_iot;
143 	bus_space_handle_t sc_ioh;
144 	struct tty	*sc_tty;
145 };
146 
147 static	int	dl_match (struct device *, struct cfdata *, void *);
148 static	void	dl_attach (struct device *, struct device *, void *);
149 static	void	dlrint (void *);
150 static	void	dlxint (void *);
151 static	void	dlstart (struct tty *);
152 static	int	dlparam (struct tty *, struct termios *);
153 static	void	dlbrk (struct dl_softc *, int);
154 
155 CFATTACH_DECL(dl, sizeof(struct dl_softc),
156     dl_match, dl_attach, NULL, NULL);
157 
158 dev_type_open(dlopen);
159 dev_type_close(dlclose);
160 dev_type_read(dlread);
161 dev_type_write(dlwrite);
162 dev_type_ioctl(dlioctl);
163 dev_type_stop(dlstop);
164 dev_type_tty(dltty);
165 dev_type_poll(dlpoll);
166 
167 const struct cdevsw dl_cdevsw = {
168 	dlopen, dlclose, dlread, dlwrite, dlioctl,
169 	dlstop, dltty, dlpoll, nommap, ttykqfilter, D_TTY
170 };
171 
172 #define	DL_READ_WORD(reg) \
173 	bus_space_read_2(sc->sc_iot, sc->sc_ioh, reg)
174 #define	DL_WRITE_WORD(reg, val) \
175 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, reg, val)
176 #define	DL_WRITE_BYTE(reg, val) \
177 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, val)
178 
179 /* Autoconfig handles: setup the controller to interrupt, */
180 /* then complete the housecleaning for full operation */
181 
182 static int
183 dl_match (struct device *parent, struct cfdata *cf, void *aux)
184 {
185 	struct uba_attach_args *ua = aux;
186 
187 #ifdef DL_DEBUG
188 	printf("Probing for dl at %lo ... ", (long)ua->ua_iaddr);
189 #endif
190 
191 	bus_space_write_2(ua->ua_iot, ua->ua_ioh, DL_UBA_XCSR, DL_XCSR_TXIE);
192 	if (bus_space_read_2(ua->ua_iot, ua->ua_ioh, DL_UBA_XCSR) !=
193 	    (DL_XCSR_TXIE | DL_XCSR_TX_READY)) {
194 #ifdef DL_DEBUG
195 		printf("failed (step 1; XCSR = %.4b)\n",
196 		    bus_space_read_2(ua->ua_iot, ua->ua_ioh, DL_UBA_XCSR),
197 		    DL_XCSR_BITS);
198 #endif
199 		return 0;
200 	}
201 
202 	/*
203 	 * We have to force an interrupt so the uba driver can work
204 	 * out where we are.  Unfortunately, the only way to make a
205 	 * DL11 interrupt is to get it to send or receive a
206 	 * character.  We'll send a NUL and hope it doesn't hurt
207 	 * anything.
208 	 */
209 
210 	bus_space_write_1(ua->ua_iot, ua->ua_ioh, DL_UBA_XBUFL, '\0');
211 #if 0 /* This test seems to fail 2/3 of the time :-( */
212 	if (dladdr->dl_xcsr != (DL_XCSR_TXIE)) {
213 #ifdef DL_DEBUG
214 		printf("failed (step 2; XCSR = %.4b)\n", dladdr->dl_xcsr,
215 		    DL_XCSR_BITS);
216 #endif
217 		return 0;
218 	}
219 #endif
220 	DELAY(100000); /* delay 1/10 s for character to transmit */
221 	if (bus_space_read_2(ua->ua_iot, ua->ua_ioh, DL_UBA_XCSR) !=
222 	    (DL_XCSR_TXIE | DL_XCSR_TX_READY)) {
223 #ifdef DL_DEBUG
224 		printf("failed (step 3; XCSR = %.4b)\n",
225 		    bus_space_read_2(ua->ua_iot, ua->ua_ioh, DL_UBA_XCSR),
226 		    DL_XCSR_BITS);
227 #endif
228 		return 0;
229 	}
230 
231 
232 	/* What else do I need to do? */
233 
234 	return 1;
235 
236 }
237 
238 static void
239 dl_attach (struct device *parent, struct device *self, void *aux)
240 {
241 	struct dl_softc *sc = device_private(self);
242 	struct uba_attach_args *ua = aux;
243 
244 	sc->sc_iot = ua->ua_iot;
245 	sc->sc_ioh = ua->ua_ioh;
246 
247 	/* Tidy up the device */
248 
249 	DL_WRITE_WORD(DL_UBA_RCSR, DL_RCSR_RXIE);
250 	DL_WRITE_WORD(DL_UBA_XCSR, DL_XCSR_TXIE);
251 
252 	/* Initialize our softc structure. Should be done in open? */
253 
254 	sc->sc_tty = ttymalloc();
255 	tty_attach(sc->sc_tty);
256 
257 	/* Now register the TX & RX interrupt handlers */
258 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
259 		dlxint, sc, &sc->sc_tintrcnt);
260 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec - 4,
261 		dlrint, sc, &sc->sc_rintrcnt);
262 	evcnt_attach_dynamic(&sc->sc_rintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
263 		sc->sc_dev.dv_xname, "rintr");
264 	evcnt_attach_dynamic(&sc->sc_tintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
265 		sc->sc_dev.dv_xname, "tintr");
266 
267 	printf("\n");
268 }
269 
270 /* Receiver Interrupt Handler */
271 
272 static void
273 dlrint(void *arg)
274 {
275 	struct dl_softc *sc = arg;
276 
277 	if (DL_READ_WORD(DL_UBA_RCSR) & DL_RCSR_RX_DONE) {
278 		struct tty *tp = sc->sc_tty;
279 		unsigned c;
280 		int cc;
281 
282 		c = DL_READ_WORD(DL_UBA_RBUF);
283 		cc = c & 0xFF;
284 
285 		if (!(tp->t_state & TS_ISOPEN)) {
286 			wakeup((caddr_t)&tp->t_rawq);
287 			return;
288 		}
289 
290 		if (c & DL_RBUF_OVERRUN_ERR) {
291 			/*
292 			 * XXX: This should really be logged somwhere
293 			 * else where we can afford the time.
294 			 */
295 			log(LOG_WARNING, "%s: rx overrun\n",
296 			    sc->sc_dev.dv_xname);
297 		}
298 		if (c & DL_RBUF_FRAMING_ERR)
299 			cc |= TTY_FE;
300 		if (c & DL_RBUF_PARITY_ERR)
301 			cc |= TTY_PE;
302 
303 		(*tp->t_linesw->l_rint)(cc, tp);
304 #if defined(DIAGNOSTIC)
305 	} else {
306 		log(LOG_WARNING, "%s: stray rx interrupt\n",
307 		    sc->sc_dev.dv_xname);
308 #endif
309 	}
310 }
311 
312 /* Transmitter Interrupt Handler */
313 
314 static void
315 dlxint(void *arg)
316 {
317 	struct dl_softc *sc = arg;
318 	struct tty *tp = sc->sc_tty;
319 
320 	tp->t_state &= ~(TS_BUSY | TS_FLUSH);
321 	(*tp->t_linesw->l_start)(tp);
322 
323 	return;
324 }
325 
326 int
327 dlopen(dev_t dev, int flag, int mode, struct lwp *l)
328 {
329 	struct proc *p = l->l_proc;
330 	struct tty *tp;
331 	struct dl_softc *sc;
332 	int unit;
333 
334 	unit = minor(dev);
335 
336 	if (unit >= dl_cd.cd_ndevs || dl_cd.cd_devs[unit] == NULL)
337 		return ENXIO;
338 	sc = dl_cd.cd_devs[unit];
339 
340 	tp = sc->sc_tty;
341 	if (tp == NULL)
342 		return ENODEV;
343 	tp->t_oproc = dlstart;
344 	tp->t_param = dlparam;
345 	tp->t_dev = dev;
346 
347 	if (!(tp->t_state & TS_ISOPEN)) {
348 		ttychars(tp);
349 		tp->t_iflag = TTYDEF_IFLAG;
350 		tp->t_oflag = TTYDEF_OFLAG;
351 		/* No modem control, so set CLOCAL. */
352 		tp->t_cflag = TTYDEF_CFLAG | CLOCAL;
353 		tp->t_lflag = TTYDEF_LFLAG;
354 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
355 
356 		dlparam(tp, &tp->t_termios);
357 		ttsetwater(tp);
358 
359 	} else if ((tp->t_state & TS_XCLUDE) &&
360 	    kauth_authorize_generic(p->p_cred, KAUTH_GENERIC_ISSUSER,
361 	    &p->p_acflag) != 0)
362 		return EBUSY;
363 
364 	return ((*tp->t_linesw->l_open)(dev, tp));
365 }
366 
367 /*ARGSUSED*/
368 int
369 dlclose(dev_t dev, int flag, int mode, struct lwp *l)
370 {
371 	struct dl_softc *sc = dl_cd.cd_devs[minor(dev)];
372 	struct tty *tp = sc->sc_tty;
373 
374 	(*tp->t_linesw->l_close)(tp, flag);
375 
376 	/* Make sure a BREAK state is not left enabled. */
377 	dlbrk(sc, 0);
378 
379 	return (ttyclose(tp));
380 }
381 
382 int
383 dlread(dev_t dev, struct uio *uio, int flag)
384 {
385 	struct dl_softc *sc = dl_cd.cd_devs[minor(dev)];
386 	struct tty *tp = sc->sc_tty;
387 
388 	return ((*tp->t_linesw->l_read)(tp, uio, flag));
389 }
390 
391 int
392 dlwrite(dev_t dev, struct uio *uio, int flag)
393 {
394 	struct dl_softc *sc = dl_cd.cd_devs[minor(dev)];
395 	struct tty *tp = sc->sc_tty;
396 
397 	return ((*tp->t_linesw->l_write)(tp, uio, flag));
398 }
399 
400 int
401 dlpoll(dev_t dev, int events, struct lwp *l)
402 {
403 	struct dl_softc *sc = dl_cd.cd_devs[minor(dev)];
404 	struct tty *tp = sc->sc_tty;
405 
406 	return ((*tp->t_linesw->l_poll)(tp, events, l));
407 }
408 
409 int
410 dlioctl(dev_t dev, unsigned long cmd, caddr_t data, int flag, struct lwp *l)
411 {
412 	struct dl_softc *sc = dl_cd.cd_devs[minor(dev)];
413 	struct tty *tp = sc->sc_tty;
414 	int error;
415 
416 
417 	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
418 	if (error != EPASSTHROUGH)
419 		return (error);
420 
421 	error = ttioctl(tp, cmd, data, flag, l);
422 	if (error != EPASSTHROUGH)
423 		return (error);
424 
425 	switch (cmd) {
426 
427 	case TIOCSBRK:
428 		dlbrk(sc, 1);
429 		break;
430 
431 	case TIOCCBRK:
432 		dlbrk(sc, 0);
433 		break;
434 
435 	case TIOCMGET:
436 		/* No modem control, assume they're all low. */
437 		*(int *)data = 0;
438 		break;
439 
440 	default:
441 		return (EPASSTHROUGH);
442 	}
443 	return (0);
444 }
445 
446 struct tty *
447 dltty(dev_t dev)
448 {
449 	struct dl_softc *sc = dl_cd.cd_devs[minor(dev)];
450 
451 	return sc->sc_tty;
452 }
453 
454 void
455 dlstop(struct tty *tp, int flag)
456 {
457 	int s = spltty();
458 
459 	if ((tp->t_state & (TS_BUSY|TS_TTSTOP)) == TS_BUSY)
460 		tp->t_state |= TS_FLUSH;
461 	splx(s);
462 }
463 
464 static void
465 dlstart(struct tty *tp)
466 {
467 	struct dl_softc *sc = dl_cd.cd_devs[minor(tp->t_dev)];
468 	int s = spltty();
469 
470 	if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
471 		goto out;
472 	if (tp->t_outq.c_cc <= tp->t_lowat) {
473 		if (tp->t_state & TS_ASLEEP) {
474 			tp->t_state &= ~TS_ASLEEP;
475 			wakeup((caddr_t)&tp->t_outq);
476 		}
477 		selwakeup(&tp->t_wsel);
478 	}
479 	if (tp->t_outq.c_cc == 0)
480 		goto out;
481 
482 
483 	if (DL_READ_WORD(DL_UBA_XCSR) & DL_XCSR_TX_READY) {
484 		tp->t_state |= TS_BUSY;
485 		DL_WRITE_BYTE(DL_UBA_XBUFL, getc(&tp->t_outq));
486 	}
487 out:
488 	splx(s);
489 	return;
490 }
491 
492 /*ARGSUSED*/
493 static int
494 dlparam(struct tty *tp, struct termios *t)
495 {
496 	/*
497 	 * All this kind of stuff (speed, character format, whatever)
498 	 * is set by jumpers on the card.  Changing it is thus rather
499 	 * tricky for a mere device driver.
500 	 */
501 	return 0;
502 }
503 
504 static void
505 dlbrk(struct dl_softc *sc, int state)
506 {
507 	int s = spltty();
508 
509 	if (state) {
510 		DL_WRITE_WORD(DL_UBA_XCSR, DL_READ_WORD(DL_UBA_XCSR) |
511 		    DL_XCSR_TX_BREAK);
512 	} else {
513 		DL_WRITE_WORD(DL_UBA_XCSR, DL_READ_WORD(DL_UBA_XCSR) &
514 		    ~DL_XCSR_TX_BREAK);
515 	}
516 	splx(s);
517 }
518