xref: /netbsd-src/sys/arch/arm/at91/at91usart.c (revision dbfa10e52a5827a2b548912aaaea11468b4c7f47)
1 /*	$Id: at91usart.c,v 1.14 2022/10/26 23:38:06 riastradh Exp $	*/
2 /*	$NetBSD: at91usart.c,v 1.14 2022/10/26 23:38:06 riastradh Exp $ */
3 
4 /*
5  * Copyright (c) 2007 Embedtronics Oy. All rights reserved.
6  *
7  * Copyright (c) 1998, 1999, 2001, 2002, 2004 The NetBSD Foundation, Inc.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to The NetBSD Foundation
11  * by Jesse Off
12  *
13  * This code is derived from software contributed to The NetBSD Foundation
14  * by Ichiro FUKUHARA and Naoto Shimazaki.
15  *
16  * This code is derived from software contributed to The NetBSD Foundation
17  * by IWAMOTO Toshihiro.
18  *
19  * This code is derived from software contributed to The NetBSD Foundation
20  * by Charles M. Hannum.
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
32  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
33  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
35  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
36  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
39  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGE.
42  */
43 
44 /*
45  * Copyright (c) 1991 The Regents of the University of California.
46  * All rights reserved.
47  *
48  * Redistribution and use in source and binary forms, with or without
49  * modification, are permitted provided that the following conditions
50  * are met:
51  * 1. Redistributions of source code must retain the above copyright
52  *    notice, this list of conditions and the following disclaimer.
53  * 2. Redistributions in binary form must reproduce the above copyright
54  *    notice, this list of conditions and the following disclaimer in the
55  *    documentation and/or other materials provided with the distribution.
56  * 3. Neither the name of the University nor the names of its contributors
57  *    may be used to endorse or promote products derived from this software
58  *    without specific prior written permission.
59  *
60  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
61  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
62  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
63  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
64  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
65  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
66  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
67  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
68  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
69  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
70  * SUCH DAMAGE.
71  *
72  *      @(#)com.c       7.5 (Berkeley) 5/16/91
73  */
74 
75 /*
76  * TODO: hardware flow control
77  */
78 
79 #include <sys/cdefs.h>
80 __KERNEL_RCSID(0, "$NetBSD: at91usart.c,v 1.14 2022/10/26 23:38:06 riastradh Exp $");
81 
82 #include "opt_kgdb.h"
83 
84 #ifdef RND_COM
85 #include <sys/rndsource.h>
86 #endif
87 
88 #ifdef	NOTYET
89 /*
90  * Override cnmagic(9) macro before including <sys/systm.h>.
91  * We need to know if cn_check_magic triggered debugger, so set a flag.
92  * Callers of cn_check_magic must declare int cn_trapped = 0;
93  * XXX: this is *ugly*!
94  */
95 #define cn_trap()				\
96 	do {					\
97 		console_debugger();		\
98 		cn_trapped = 1;			\
99 	} while (/* CONSTCOND */ 0)
100 #endif	/* NOTYET */
101 
102 
103 #include <sys/param.h>
104 #include <sys/systm.h>
105 #include <sys/types.h>
106 #include <sys/conf.h>
107 #include <sys/file.h>
108 #include <sys/device.h>
109 #include <sys/kernel.h>
110 #include <sys/tty.h>
111 #include <sys/uio.h>
112 #include <sys/vnode.h>
113 #include <sys/kauth.h>
114 
115 #include <machine/intr.h>
116 #include <sys/bus.h>
117 
118 #include <ddb/db_active.h>
119 
120 #include <arm/at91/at91reg.h>
121 #include <arm/at91/at91var.h>
122 #include <arm/at91/at91usartreg.h>
123 #include <arm/at91/at91usartvar.h>
124 
125 #include <dev/cons.h>
126 
127 static int	at91usart_param(struct tty *, struct termios *);
128 static void	at91usart_start(struct tty *);
129 static int	at91usart_hwiflow(struct tty *, int);
130 
131 #if 0
132 static u_int	cflag2lcrhi(tcflag_t);
133 #endif
134 static void	at91usart_set(struct at91usart_softc *);
135 
136 #if	NOTYET
137 int             at91usart_cn_getc(dev_t);
138 void            at91usart_cn_putc(dev_t, int);
139 void            at91usart_cn_pollc(dev_t, int);
140 void            at91usart_cn_probe(struct consdev *);
141 void            at91usart_cn_init(struct consdev *);
142 
143 static struct at91usart_cons_softc {
144 	bus_space_tag_t		sc_iot;
145 	bus_space_handle_t	sc_ioh;
146 	bus_addr_t		sc_hwbase;
147 	int			sc_ospeed;
148 	tcflag_t		sc_cflag;
149 	int			sc_attached;
150 
151 	uint8_t			*sc_rx_ptr;
152 	uint8_t			sc_rx_fifo[64];
153 } usart_cn_sc;
154 
155 static struct cnm_state at91usart_cnm_state;
156 #endif	/* NOTYET */
157 
158 static void	at91usart_soft(void* arg);
159 inline static void	at91usart_txsoft(struct at91usart_softc *, struct tty *);
160 inline static void	at91usart_rxsoft(struct at91usart_softc *, struct tty *, unsigned csr);
161 
162 #define	PDC_BLOCK_SIZE	64
163 
164 //CFATTACH_DECL_NEW(at91usart, sizeof(struct at91usart_softc),
165 //	      at91usart_match, at91usart_attach, NULL, NULL);
166 
167 //#define	USART_DEBUG	10
168 
169 #ifdef	USART_DEBUG
170 int usart_debug = USART_DEBUG;
171 #define	DPRINTFN(n,fmt) if (usart_debug >= (n)) printf fmt
172 #else
173 #define	DPRINTFN(n,fmt)
174 #endif
175 
176 extern struct cfdriver at91usart_cd;
177 
178 dev_type_open(at91usart_open);
179 dev_type_close(at91usart_close);
180 dev_type_read(at91usart_read);
181 dev_type_write(at91usart_write);
182 dev_type_ioctl(at91usart_ioctl);
183 dev_type_stop(at91usart_stop);
184 dev_type_tty(at91usart_tty);
185 dev_type_poll(at91usart_poll);
186 
187 const struct cdevsw at91usart_cdevsw = {
188 	.d_open = at91usart_open,
189 	.d_close = at91usart_close,
190 	.d_read = at91usart_read,
191 	.d_write = at91usart_write,
192 	.d_ioctl = at91usart_ioctl,
193 	.d_stop = at91usart_stop,
194 	.d_tty = at91usart_tty,
195 	.d_poll = at91usart_poll,
196 	.d_mmap = nommap,
197 	.d_kqfilter = ttykqfilter,
198 	.d_discard = nodiscard,
199 	.d_flag = D_TTY
200 };
201 
202 #if	NOTYET
203 struct consdev at91usart_cons = {
204 	at91usart_cn_probe, NULL, at91usart_cn_getc, at91usart_cn_putc, at91usart_cn_pollc, NULL,
205 	NULL, NULL, NODEV, CN_REMOTE
206 };
207 #endif	/* NOTYET */
208 
209 #ifndef DEFAULT_COMSPEED
210 #define DEFAULT_COMSPEED 115200
211 #endif
212 
213 #define COMUNIT(x)	TTUNIT(x)
214 #define COMDIALOUT(x)	TTDIALOUT(x)
215 
216 #define COM_ISALIVE(sc)	((sc)->enabled != 0 && device_is_active((sc)->sc_dev))
217 
218 static inline void
at91usart_writereg(struct at91usart_softc * sc,int reg,u_int val)219 at91usart_writereg(struct at91usart_softc *sc, int reg, u_int val)
220 {
221 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
222 }
223 
224 static inline u_int
at91usart_readreg(struct at91usart_softc * sc,int reg)225 at91usart_readreg(struct at91usart_softc *sc, int reg)
226 {
227 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
228 }
229 #if 0
230 static int
231 at91usart_match(device_t parent, cfdata_t cf, void *aux)
232 {
233 	if (strcmp(cf->cf_name, "at91usart") == 0)
234 		return 1;
235 	return 0;
236 }
237 #endif
238 static int at91usart_intr(void* arg);
239 
240 void
at91usart_attach_subr(struct at91usart_softc * sc,struct at91bus_attach_args * sa)241 at91usart_attach_subr(struct at91usart_softc *sc, struct at91bus_attach_args *sa)
242 {
243 	struct tty *tp;
244 	int err;
245 
246 	printf("\n");
247 
248 	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh))
249 		panic("%s: Cannot map registers", device_xname(sc->sc_dev));
250 
251 	sc->sc_iot = sa->sa_iot;
252 	sc->sc_hwbase = sa->sa_addr;
253 	sc->sc_dmat = sa->sa_dmat;
254 	sc->sc_pid = sa->sa_pid;
255 
256 	/* allocate fifos */
257 	err = at91pdc_alloc_fifo(sc->sc_dmat, &sc->sc_rx_fifo, AT91USART_RING_SIZE, BUS_DMA_READ | BUS_DMA_STREAMING);
258 	if (err)
259 		panic("%s: cannot allocate rx fifo", device_xname(sc->sc_dev));
260 
261 	err = at91pdc_alloc_fifo(sc->sc_dmat, &sc->sc_tx_fifo, AT91USART_RING_SIZE, BUS_DMA_WRITE | BUS_DMA_STREAMING);
262 	if (err)
263 		panic("%s: cannot allocate tx fifo", device_xname(sc->sc_dev));
264 
265 	/* initialize uart */
266 	at91_peripheral_clock(sc->sc_pid, 1);
267 
268 	at91usart_writereg(sc, US_IDR, -1);
269 	at91usart_writereg(sc, US_RTOR, 12);	// 12-bit timeout
270 	at91usart_writereg(sc, US_PDC + PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
271 	at91_intr_establish(sa->sa_pid, IPL_TTY, INTR_HIGH_LEVEL, at91usart_intr, sc);
272 	USART_INIT(sc, 115200U);
273 
274 #ifdef	NOTYET
275 	if (sc->sc_iot == usart_cn_sc.sc_iot
276 	    && sc->sc_hwbase == usart_cn_sc.sc_hwbase) {
277 		usart_cn_sc.sc_attached = 1;
278 		/* Make sure the console is always "hardwired". */
279 		delay(10000);	/* wait for output to finish */
280 		SET(sc->sc_hwflags, COM_HW_CONSOLE);
281 		SET(sc->sc_swflags, TIOCFLAG_SOFTCAR);
282 		SET(sc->sc_ier, USART_INT_RXRDY);
283 		USARTREG(USART_IER) = USART_INT_RXRDY; // @@@@@
284 	}
285 #endif	// NOTYET
286 
287 	tp = tty_alloc();
288 	tp->t_oproc = at91usart_start;
289 	tp->t_param = at91usart_param;
290 	tp->t_hwiflow = at91usart_hwiflow;
291 
292 	sc->sc_tty = tp;
293 
294 	tty_attach(tp);
295 
296 #if	NOTYET
297 	if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
298 		int maj;
299 
300 		/* locate the major number */
301 		maj = cdevsw_lookup_major(&at91usart_cdevsw);
302 
303 		cn_tab->cn_dev = makedev(maj, device_unit(sc->sc_dev));
304 
305 		aprint_normal("%s: console (maj %u  min %u  cn_dev %u)\n",
306 		    device_xname(sc->sc_dev), maj, device_unit(sc->sc_dev),
307 		    cn_tab->cn_dev);
308 	}
309 #endif	/* NOTYET */
310 
311 	sc->sc_si = softint_establish(SOFTINT_SERIAL, at91usart_soft, sc);
312 
313 #ifdef RND_COM
314 	rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
315 			  RND_TYPE_TTY, RND_FLAG_DEFAULT);
316 #endif
317 
318 	/* if there are no enable/disable functions, assume the device
319 	   is always enabled */
320 	if (!sc->enable)
321 		sc->enabled = 1;
322 
323 	/* XXX configure register */
324 	/* xxx_config(sc) */
325 
326 	SET(sc->sc_hwflags, COM_HW_DEV_OK);
327 }
328 
329 static int
at91usart_param(struct tty * tp,struct termios * t)330 at91usart_param(struct tty *tp, struct termios *t)
331 {
332 	struct at91usart_softc *sc
333 		= device_lookup_private(&at91usart_cd, COMUNIT(tp->t_dev));
334 	int s;
335 
336 	if (COM_ISALIVE(sc) == 0)
337 		return (EIO);
338 
339 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
340 		return (EINVAL);
341 
342 	/*
343 	 * For the console, always force CLOCAL and !HUPCL, so that the port
344 	 * is always active.
345 	 */
346 	if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
347 	    ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
348 		SET(t->c_cflag, CLOCAL);
349 		CLR(t->c_cflag, HUPCL);
350 	}
351 
352 	/*
353 	 * If there were no changes, don't do anything.  This avoids dropping
354 	 * input and improves performance when all we did was frob things like
355 	 * VMIN and VTIME.
356 	 */
357 	if (tp->t_ospeed == t->c_ospeed &&
358 	    tp->t_cflag == t->c_cflag)
359 		return (0);
360 
361 	s = spltty();
362 
363 	sc->sc_brgr = (AT91_MSTCLK / 16 + t->c_ospeed / 2) / t->c_ospeed;
364 
365 	/* And copy to tty. */
366 	tp->t_ispeed = 0;
367 	tp->t_ospeed = t->c_ospeed;
368 	tp->t_cflag = t->c_cflag;
369 	at91usart_set(sc);
370 
371 	splx(s);
372 
373 	/*
374 	 * Update the tty layer's idea of the carrier bit.
375 	 * We tell tty the carrier is always on.
376 	 */
377 	(void) (*tp->t_linesw->l_modem)(tp, 1);
378 
379 #ifdef COM_DEBUG
380 	if (com_debug)
381 		comstatus(sc, "comparam ");
382 #endif
383 
384 	/* tell the upper layer about hwflow.. */
385 	if (sc->hwflow)
386 		(*sc->hwflow)(sc, t->c_cflag);
387 
388 	return (0);
389 }
390 
391 static int
at91usart_hwiflow(struct tty * tp,int block)392 at91usart_hwiflow(struct tty *tp, int block)
393 {
394 	if (block) {
395 		/* tty discipline wants to block */
396 	} else {
397 		/* tty discipline wants to unblock */
398 	}
399 	return (0);
400 }
401 
402 static __inline void
at91usart_start_tx(struct at91usart_softc * sc)403 at91usart_start_tx(struct at91usart_softc *sc)
404 {
405 	if (!sc->start_tx)
406 		at91usart_writereg(sc, US_PDC + PDC_PTCR, PDC_PTCR_TXTEN);
407 	else
408 		(*sc->start_tx)(sc);
409 }
410 
411 static __inline void
at91usart_stop_tx(struct at91usart_softc * sc)412 at91usart_stop_tx(struct at91usart_softc *sc)
413 {
414 	if (!sc->stop_tx)
415 		at91usart_writereg(sc, US_PDC + PDC_PTCR, PDC_PTCR_TXTDIS);
416 	else
417 		(*sc->stop_tx)(sc);
418 }
419 
420 static __inline void
at91usart_rx_started(struct at91usart_softc * sc)421 at91usart_rx_started(struct at91usart_softc *sc)
422 {
423 	if (sc->rx_started)
424 		(*sc->rx_started)(sc);
425 }
426 
427 static __inline void
at91usart_rx_stopped(struct at91usart_softc * sc)428 at91usart_rx_stopped(struct at91usart_softc *sc)
429 {
430 	if (sc->rx_stopped)
431 		(*sc->rx_stopped)(sc);
432 }
433 
434 static __inline void
at91usart_rx_rts_ctl(struct at91usart_softc * sc,int enabled)435 at91usart_rx_rts_ctl(struct at91usart_softc *sc, int enabled)
436 {
437 	if (sc->rx_rts_ctl)
438 		(*sc->rx_rts_ctl)(sc, enabled);
439 }
440 
441 static void
at91usart_filltx(struct at91usart_softc * sc)442 at91usart_filltx(struct at91usart_softc *sc)
443 {
444 	struct tty *tp = sc->sc_tty;
445 	int len;
446 	void *dst;
447 
448 	// post write handler
449 	AT91PDC_FIFO_POSTWRITE(sc->sc_iot, sc->sc_ioh, sc->sc_dmat, US_PDC,
450 				&sc->sc_tx_fifo);
451 
452 	// copy more data to fifo:
453 	if (sc->sc_tbc > 0
454 	    && (dst = AT91PDC_FIFO_WRPTR(&sc->sc_tx_fifo, &len)) != NULL) {
455 		// copy data to fifo
456 		if (len > sc->sc_tbc)
457 			len = sc->sc_tbc;
458 		memcpy(dst, sc->sc_tba, len);
459 		sc->sc_tba += len;
460 		if ((sc->sc_tbc -= len) <= 0)
461 			CLR(tp->t_state, TS_BUSY);
462 		// update fifo
463 		AT91PDC_FIFO_WRITTEN(&sc->sc_tx_fifo, len);
464 		// tell tty interface we've sent some bytes
465 		ndflush(&tp->t_outq, len);
466 	}
467 
468 	// start sending data...
469 	if (AT91PDC_FIFO_PREWRITE(sc->sc_iot, sc->sc_ioh, sc->sc_dmat,
470 				   US_PDC, &sc->sc_tx_fifo, PDC_BLOCK_SIZE)) {
471 		at91usart_start_tx(sc);
472 		SET(sc->sc_ier, US_CSR_TXEMPTY | US_CSR_ENDTX);
473 	} else {
474 		CLR(sc->sc_ier, US_CSR_ENDTX);
475 	}
476 }
477 
478 static void
at91usart_start(struct tty * tp)479 at91usart_start(struct tty *tp)
480 {
481 	struct at91usart_softc *sc
482 		= device_lookup_private(&at91usart_cd, COMUNIT(tp->t_dev));
483 	int s;
484 
485 	if (COM_ISALIVE(sc) == 0) {
486 		DPRINTFN(5, ("%s: %s / COM_ISALIVE == 0\n", device_xname(sc->sc_dev), __FUNCTION__));
487 		return;
488 	}
489 
490 	s = spltty();
491 	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
492 		DPRINTFN(5, ("%s: %s: TS_BUSY || TS_TIMEOUT || TS_TTSTOP\n", device_xname(sc->sc_dev), __FUNCTION__));
493 		goto out;
494 	}
495 
496 	if (!ttypull(tp))
497 		goto out;
498 
499 	/* Grab the first contiguous region of buffer space. */
500 	{
501 		u_char *tba;
502 		int tbc;
503 
504 		tba = tp->t_outq.c_cf;
505 		tbc = ndqb(&tp->t_outq, 0);
506 
507 		sc->sc_tba = tba;
508 		sc->sc_tbc = tbc;
509 	}
510 
511 	SET(tp->t_state, TS_BUSY);
512 
513 	/* Output the first chunk of the contiguous buffer. */
514 	at91usart_filltx(sc);
515 	at91usart_writereg(sc, US_IER, sc->sc_ier);
516 	DPRINTFN(5, ("%s: %s, ier=%08x (csr=%08x)\n", device_xname(sc->sc_dev), __FUNCTION__, sc->sc_ier, at91usart_readreg(sc, US_CSR)));
517 
518 out:
519 	splx(s);
520 
521 	return;
522 }
523 
524 static __inline__ void
at91usart_break(struct at91usart_softc * sc,int onoff)525 at91usart_break(struct at91usart_softc *sc, int onoff)
526 {
527 	at91usart_writereg(sc, US_CR, onoff ? US_CR_STTBRK : US_CR_STPBRK);
528 }
529 
530 static void
at91usart_shutdown(struct at91usart_softc * sc)531 at91usart_shutdown(struct at91usart_softc *sc)
532 {
533 	int s;
534 
535 	s = spltty();
536 
537 	/* turn of dma */
538 	at91usart_writereg(sc, US_PDC + PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
539 	at91usart_writereg(sc, US_PDC + PDC_TNCR, 0);
540 	at91usart_writereg(sc, US_PDC + PDC_TCR, 0);
541 	at91usart_writereg(sc, US_PDC + PDC_RNCR, 0);
542 	at91usart_writereg(sc, US_PDC + PDC_RCR, 0);
543 
544 	/* Turn off interrupts. */
545 	at91usart_writereg(sc, US_IDR, -1);
546 
547 	/* Clear any break condition set with TIOCSBRK. */
548 	at91usart_break(sc, 0);
549 	at91usart_set(sc);
550 
551 	if (sc->disable) {
552 #ifdef DIAGNOSTIC
553 		if (!sc->enabled)
554 			panic("at91usart_shutdown: not enabled?");
555 #endif
556 		(*sc->disable)(sc);
557 		sc->enabled = 0;
558 	}
559 	splx(s);
560 }
561 
562 int
at91usart_open(dev_t dev,int flag,int mode,struct lwp * l)563 at91usart_open(dev_t dev, int flag, int mode, struct lwp *l)
564 {
565 	struct at91usart_softc *sc;
566 	struct tty *tp;
567 	int s;
568 	int error;
569 
570 	sc = device_lookup_private(&at91usart_cd, COMUNIT(dev));
571 	if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK))
572 		return (ENXIO);
573 
574 	if (!device_is_active(sc->sc_dev))
575 		return (ENXIO);
576 
577 #ifdef KGDB
578 	/*
579 	 * If this is the kgdb port, no other use is permitted.
580 	 */
581 	if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
582 		return (EBUSY);
583 #endif
584 
585 	tp = sc->sc_tty;
586 
587 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
588 		return (EBUSY);
589 
590 	s = spltty();
591 
592 	/*
593 	 * Do the following iff this is a first open.
594 	 */
595 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
596 		struct termios t;
597 
598 		tp->t_dev = dev;
599 
600 		if (sc->enable) {
601 			if ((*sc->enable)(sc)) {
602 				splx(s);
603 				printf("%s: device enable failed\n",
604 				       device_xname(sc->sc_dev));
605 				return (EIO);
606 			}
607 			sc->enabled = 1;
608 #if 0
609 /* XXXXXXXXXXXXXXX */
610 			com_config(sc);
611 #endif
612 		}
613 
614 		/* reset fifos: */
615 		AT91PDC_RESET_FIFO(sc->sc_iot, sc->sc_ioh, sc->sc_dmat, US_PDC, &sc->sc_rx_fifo, 0);
616 		AT91PDC_RESET_FIFO(sc->sc_iot, sc->sc_ioh, sc->sc_dmat, US_PDC, &sc->sc_tx_fifo, 1);
617 
618 		/* reset receive */
619 		at91usart_writereg(sc, US_CR, US_CR_RSTSTA | US_CR_STTTO);
620 
621 		/* Turn on interrupts. */
622 		sc->sc_ier = US_CSR_ENDRX|US_CSR_RXBUFF|US_CSR_TIMEOUT|US_CSR_RXBRK;
623 		at91usart_writereg(sc, US_IER, sc->sc_ier);
624 
625 		/* enable DMA: */
626 		at91usart_writereg(sc, US_PDC + PDC_PTCR, PDC_PTCR_RXTEN);
627 
628 		/*
629 		 * Initialize the termios status to the defaults.  Add in the
630 		 * sticky bits from TIOCSFLAGS.
631 		 */
632 		t.c_ispeed = 0;
633 /*		if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
634 			t.c_ospeed = usart_cn_sc.sc_ospeed;
635 			t.c_cflag = usart_cn_sc.sc_cflag;
636 		} else*/ {
637 			t.c_ospeed = TTYDEF_SPEED;
638 			t.c_cflag = TTYDEF_CFLAG;
639 		}
640 		if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
641 			SET(t.c_cflag, CLOCAL);
642 		if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
643 			SET(t.c_cflag, CRTSCTS);
644 		if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
645 			SET(t.c_cflag, MDMBUF);
646 
647 		/* Make sure at91usart_param() will do something. */
648 		tp->t_ospeed = 0;
649 		(void) at91usart_param(tp, &t);
650 		tp->t_iflag = TTYDEF_IFLAG;
651 		tp->t_oflag = TTYDEF_OFLAG;
652 		tp->t_lflag = TTYDEF_LFLAG;
653 		ttychars(tp);
654 		ttsetwater(tp);
655 
656 		/* and unblock. */
657 		CLR(sc->sc_rx_flags, RX_ANY_BLOCK);
658 
659 #ifdef COM_DEBUG
660 		if (at91usart_debug)
661 			comstatus(sc, "at91usart_open  ");
662 #endif
663 
664 	}
665 
666 	splx(s);
667 
668 	error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
669 	if (error)
670 		goto bad;
671 
672 	error = (*tp->t_linesw->l_open)(dev, tp);
673 	if (error)
674 		goto bad;
675 
676 	return (0);
677 
678 bad:
679 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
680 		/*
681 		 * We failed to open the device, and nobody else had it opened.
682 		 * Clean up the state as appropriate.
683 		 */
684 		at91usart_shutdown(sc);
685 	}
686 
687 	return (error);
688 }
689 
690 int
at91usart_close(dev_t dev,int flag,int mode,struct lwp * l)691 at91usart_close(dev_t dev, int flag, int mode, struct lwp *l)
692 {
693 	struct at91usart_softc *sc = device_lookup_private(&at91usart_cd, COMUNIT(dev));
694 	struct tty *tp = sc->sc_tty;
695 
696 	/* XXX This is for cons.c. */
697 	if (!ISSET(tp->t_state, TS_ISOPEN))
698 		return (0);
699 
700 	(*tp->t_linesw->l_close)(tp, flag);
701 	ttyclose(tp);
702 
703 	if (COM_ISALIVE(sc) == 0)
704 		return (0);
705 
706 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
707 		/*
708 		 * Although we got a last close, the device may still be in
709 		 * use; e.g. if this was the dialout node, and there are still
710 		 * processes waiting for carrier on the non-dialout node.
711 		 */
712 		at91usart_shutdown(sc);
713 	}
714 
715 	return (0);
716 }
717 
718 int
at91usart_read(dev_t dev,struct uio * uio,int flag)719 at91usart_read(dev_t dev, struct uio *uio, int flag)
720 {
721 	struct at91usart_softc *sc = device_lookup_private(&at91usart_cd, COMUNIT(dev));
722 	struct tty *tp = sc->sc_tty;
723 
724 	if (COM_ISALIVE(sc) == 0)
725 		return (EIO);
726 
727 	return ((*tp->t_linesw->l_read)(tp, uio, flag));
728 }
729 
730 int
at91usart_write(dev_t dev,struct uio * uio,int flag)731 at91usart_write(dev_t dev, struct uio *uio, int flag)
732 {
733 	struct at91usart_softc *sc = device_lookup_private(&at91usart_cd, COMUNIT(dev));
734 	struct tty *tp = sc->sc_tty;
735 
736 	if (COM_ISALIVE(sc) == 0)
737 		return (EIO);
738 
739 	return ((*tp->t_linesw->l_write)(tp, uio, flag));
740 }
741 
742 int
at91usart_poll(dev_t dev,int events,struct lwp * l)743 at91usart_poll(dev_t dev, int events, struct lwp *l)
744 {
745 	struct at91usart_softc *sc = device_lookup_private(&at91usart_cd, COMUNIT(dev));
746 	struct tty *tp = sc->sc_tty;
747 
748 	if (COM_ISALIVE(sc) == 0)
749 		return (EIO);
750 
751 	return ((*tp->t_linesw->l_poll)(tp, events, l));
752 }
753 
754 struct tty *
at91usart_tty(dev_t dev)755 at91usart_tty(dev_t dev)
756 {
757 	struct at91usart_softc *sc = device_lookup_private(&at91usart_cd, COMUNIT(dev));
758 	struct tty *tp = sc->sc_tty;
759 
760 	return (tp);
761 }
762 
763 int
at91usart_ioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)764 at91usart_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
765 {
766 	struct at91usart_softc *sc = device_lookup_private(&at91usart_cd, COMUNIT(dev));
767 	struct tty *tp = sc->sc_tty;
768 	int error;
769 	int s;
770 
771 	if (COM_ISALIVE(sc) == 0)
772 		return (EIO);
773 
774 	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
775 	if (error != EPASSTHROUGH)
776 		return (error);
777 
778 	error = ttioctl(tp, cmd, data, flag, l);
779 	if (error != EPASSTHROUGH)
780 		return (error);
781 
782 	error = 0;
783 
784 	s = spltty();
785 
786 	switch (cmd) {
787 	case TIOCSBRK:
788 		at91usart_break(sc, 1);
789 		break;
790 
791 	case TIOCCBRK:
792 		at91usart_break(sc, 0);
793 		break;
794 
795 	case TIOCGFLAGS:
796 		*(int *)data = sc->sc_swflags;
797 		break;
798 
799 	case TIOCSFLAGS:
800 		error = kauth_authorize_device_tty(l->l_cred,
801 		    KAUTH_DEVICE_TTY_PRIVSET, tp);
802 		if (error)
803 			break;
804 		sc->sc_swflags = *(int *)data;
805 		break;
806 
807 	default:
808 		error = EPASSTHROUGH;
809 		break;
810 	}
811 
812 	splx(s);
813 
814 	return (error);
815 }
816 
817 /*
818  * Stop output on a line.
819  */
820 void
at91usart_stop(struct tty * tp,int flag)821 at91usart_stop(struct tty *tp, int flag)
822 {
823 	struct at91usart_softc *sc
824 		= device_lookup_private(&at91usart_cd, COMUNIT(tp->t_dev));
825 	int s;
826 
827 	s = spltty();
828 	if (ISSET(tp->t_state, TS_BUSY)) {
829 		/* Stop transmitting at the next chunk. */
830 		sc->sc_tbc = 0;
831 		if (!ISSET(tp->t_state, TS_TTSTOP))
832 			SET(tp->t_state, TS_FLUSH);
833 	}
834 	splx(s);
835 }
836 
837 #if 0
838 static u_int
839 cflag2lcrhi(tcflag_t cflag)
840 {
841 	uint32_t	mr;
842 
843 	switch (cflag & CSIZE) {
844 	default:
845 		mr = 0x0;
846 		break;
847 	}
848 #if 0
849 	mr |= (cflag & PARENB) ? LinCtrlHigh_PEN : 0;
850 	mr |= (cflag & PARODD) ? 0 : LinCtrlHigh_EPS;
851 	mr |= (cflag & CSTOPB) ? LinCtrlHigh_STP2 : 0;
852 	mr |= LinCtrlHigh_FEN;  /* FIFO always enabled */
853 #endif
854 	mr |= USART_MR_PAR_NONE;
855 	return (mr);
856 }
857 #endif
858 
859 
860 static void
at91usart_set(struct at91usart_softc * sc)861 at91usart_set(struct at91usart_softc *sc)
862 {
863 	at91usart_writereg(sc, US_MR, US_MR_CHRL_8 | US_MR_PAR_NONE | US_MR_NBSTOP_1);
864 	at91usart_writereg(sc, US_BRGR, sc->sc_brgr);
865 	at91usart_writereg(sc, US_CR, US_CR_TXEN | US_CR_RXEN); // @@@ just in case
866 }
867 
868 #if	NOTYET
869 int
at91usart_cn_attach(bus_space_tag_t iot,bus_addr_t iobase,bus_space_handle_t ioh,uint32_t mstclk,int ospeed,tcflag_t cflag)870 at91usart_cn_attach(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t ioh,
871 		    uint32_t mstclk, int ospeed, tcflag_t cflag)
872 {
873 	cn_tab = &at91usart_cons;
874 	cn_init_magic(&at91usart_cnm_state);
875 	cn_set_magic("\047\001");
876 
877 	usart_cn_sc.sc_iot = iot;
878 	usart_cn_sc.sc_ioh = ioh;
879 	usart_cn_sc.sc_hwbase = iobase;
880 	usart_cn_sc.sc_ospeed = ospeed;
881 	usart_cn_sc.sc_cflag = cflag;
882 
883 	USART_INIT(mstclk, ospeed);
884 
885 	return (0);
886 }
887 
888 void
at91usart_cn_probe(struct consdev * cp)889 at91usart_cn_probe(struct consdev *cp)
890 {
891 	cp->cn_pri = CN_REMOTE;
892 }
893 
894 void
at91usart_cn_pollc(dev_t dev,int on)895 at91usart_cn_pollc(dev_t dev, int on)
896 {
897 	if (on) {
898 		// enable polling mode
899 		USARTREG(US_IDR) = USART_INT_RXRDY;
900 	} else {
901 		// disable polling mode
902 		USARTREG(US_IER) = USART_INT_RXRDY;
903 	}
904 }
905 
906 void
at91usart_cn_putc(dev_t dev,int c)907 at91usart_cn_putc(dev_t dev, int c)
908 {
909 	int			s;
910 #if 0
911 	bus_space_tag_t		iot = usart_cn_sc.sc_iot;
912 	bus_space_handle_t	ioh = usart_cn_sc.sc_ioh;
913 #endif
914 	s = spltty();
915 
916 	USART_PUTC(c);
917 
918 #ifdef DEBUG
919 	if (c == '\r') {
920 		while((USARTREG(USART_SR) & USART_SR_TXEMPTY) == 0)
921 			;
922 	}
923 #endif
924 
925 	splx(s);
926 }
927 
928 int
at91usart_cn_getc(dev_t dev)929 at91usart_cn_getc(dev_t dev)
930 {
931 	int			c, sr;
932 	int			s;
933 #if 0
934 	bus_space_tag_t		iot = usart_cn_sc.sc_iot;
935 	bus_space_handle_t	ioh = usart_cn_sc.sc_ioh;
936 #endif
937 
938         s = spltty();
939 
940 	while ((c = USART_PEEKC()) == -1) {
941 	  splx(s);
942 	  s = spltty();
943 	}
944 		;
945 	sr = USARTREG(USART_SR);
946 	if (ISSET(sr, USART_SR_FRAME) && c == 0) {
947 		USARTREG(USART_CR) = USART_CR_RSTSTA;	// reset status bits
948 		c = CNC_BREAK;
949 	}
950 	if (!db_active) {
951 		int cn_trapped __unused = 0;
952 
953 		cn_check_magic(dev, c, at91usart_cnm_state);
954 	}
955 	splx(s);
956 
957 	c &= 0xff;
958 
959 	return (c);
960 }
961 #endif	/* NOTYET */
962 
963 inline static void
at91usart_rxsoft(struct at91usart_softc * sc,struct tty * tp,unsigned csr)964 at91usart_rxsoft(struct at91usart_softc *sc, struct tty *tp, unsigned csr)
965 {
966 	u_char *start, *get, *end;
967 	int cc;
968 
969 	AT91PDC_FIFO_POSTREAD(sc->sc_iot, sc->sc_ioh, sc->sc_dmat, US_PDC,
970 			      &sc->sc_rx_fifo);
971 
972 	if (ISSET(csr, US_CSR_TIMEOUT | US_CSR_RXBRK))
973 		at91usart_rx_stopped(sc);
974 
975 	while ((start = AT91PDC_FIFO_RDPTR(&sc->sc_rx_fifo, &cc)) != NULL) {
976 		int (*rint)(int, struct tty *) = tp->t_linesw->l_rint;
977 		int code;
978 
979 		if (!ISSET(csr, US_CSR_TIMEOUT | US_CSR_RXBRK))
980 			at91usart_rx_started(sc);
981 
982 		for (get = start, end = start + cc; get < end; get++) {
983 			code = *get;
984 			if ((*rint)(code, tp) == -1) {
985 				/*
986 				 * The line discipline's buffer is out of space.
987 				 */
988 				if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
989 					/*
990 					 * We're either not using flow control, or the
991 					 * line discipline didn't tell us to block for
992 					 * some reason.  Either way, we have no way to
993 					 * know when there's more space available, so
994 					 * just drop the rest of the data.
995 					 */
996 					get = end;
997 					printf("%s: receive missing data!\n",
998 					     device_xname(sc->sc_dev));
999 				} else {
1000 					/*
1001 					 * Don't schedule any more receive processing
1002 					 * until the line discipline tells us there's
1003 					 * space available (through comhwiflow()).
1004 					 * Leave the rest of the data in the input
1005 					 * buffer.
1006 					 */
1007 					SET(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
1008 				}
1009 				break;
1010 			}
1011 		}
1012 
1013 		// tell we've read some bytes...
1014 		AT91PDC_FIFO_READ(&sc->sc_rx_fifo, get - start);
1015 
1016 		if (ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED))
1017 			break;
1018 	}
1019 
1020 	// h/w flow control hook:
1021 	if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
1022 		at91usart_rx_rts_ctl(sc, (AT91PDC_FIFO_SPACE(&sc->sc_rx_fifo) > PDC_BLOCK_SIZE * 2));
1023 
1024 	// write next pointer if USART is ready:
1025 	if (AT91PDC_FIFO_PREREAD(sc->sc_iot, sc->sc_ioh, sc->sc_dmat, US_PDC,
1026 				  &sc->sc_rx_fifo, PDC_BLOCK_SIZE)) {
1027 		SET(sc->sc_ier, US_CSR_ENDRX | US_CSR_RXBUFF | US_CSR_TIMEOUT | US_CSR_RXBRK);
1028 	} else {
1029 		CLR(sc->sc_ier, US_CSR_ENDRX | US_CSR_RXBUFF | US_CSR_TIMEOUT | US_CSR_RXBRK);
1030 	}
1031 }
1032 
1033 inline static void
at91usart_txsoft(struct at91usart_softc * sc,struct tty * tp)1034 at91usart_txsoft(struct at91usart_softc *sc, struct tty *tp)
1035 {
1036 	at91usart_filltx(sc);
1037 	if (!ISSET(tp->t_state, TS_BUSY))
1038 		(*tp->t_linesw->l_start)(tp);
1039 }
1040 
1041 
1042 static void
at91usart_soft(void * arg)1043 at91usart_soft(void* arg)
1044 {
1045 	struct at91usart_softc *sc = arg;
1046 	int s;
1047 	u_int csr;
1048 
1049 	if (COM_ISALIVE(sc) == 0)
1050 		return;
1051 
1052 	s = spltty();
1053 	csr = sc->sc_csr;
1054 	while (csr != 0) {
1055 		if ((csr &= sc->sc_ier) == 0)
1056 			break;
1057 //		splx(s);
1058 		DPRINTFN(5, ("%s: %s / csr = 0x%08x\n", device_xname(sc->sc_dev), __FUNCTION__, csr));
1059 		if (ISSET(csr, US_CSR_ENDRX | US_CSR_RXBUFF | US_CSR_TIMEOUT | US_CSR_RXBRK)) {
1060 			/* receive interrupt */
1061 			if (ISSET(csr, US_CSR_RXBRK)) {
1062 				// break received!
1063 				at91usart_writereg(sc, US_CR, US_CR_RSTSTA | US_CR_STTTO);
1064 			} else if (ISSET(csr, US_CSR_TIMEOUT)) {
1065 				// timeout received
1066 				at91usart_writereg(sc, US_CR, US_CR_STTTO);
1067 			}
1068 			at91usart_rxsoft(sc, sc->sc_tty, csr);
1069 		}
1070 		if (ISSET(csr, US_CSR_TXEMPTY)) {
1071 			at91usart_stop_tx(sc);
1072 			CLR(sc->sc_ier, US_CSR_TXEMPTY);
1073 			if (AT91PDC_FIFO_EMPTY(&sc->sc_tx_fifo)) {
1074 				// everything sent!
1075 				if (ISSET(sc->sc_tty->t_state, TS_FLUSH))
1076 					CLR(sc->sc_tty->t_state, TS_FLUSH);
1077 			}
1078 		}
1079 		if (ISSET(csr, US_CSR_TXEMPTY | US_CSR_ENDTX)) {
1080 			/* transmit interrupt! */
1081 			at91usart_txsoft(sc, sc->sc_tty);
1082 		}
1083 //		s = spltty();
1084 		csr = at91usart_readreg(sc, US_CSR);
1085 	}
1086 	sc->sc_csr = 0;
1087 	at91usart_writereg(sc, US_IER, sc->sc_ier);	// re-enable interrupts
1088 	splx(s);
1089 }
1090 
1091 
1092 static int
at91usart_intr(void * arg)1093 at91usart_intr(void* arg)
1094 {
1095 	struct at91usart_softc *sc = arg;
1096 	u_int csr, imr;
1097 
1098 	// get out if interrupts are not enabled
1099 	imr = at91usart_readreg(sc, US_IMR);
1100 	if (!imr)
1101 		return 0;
1102 	// get out if pending interrupt is not enabled
1103 	csr = at91usart_readreg(sc, US_CSR);
1104 	DPRINTFN(6,("%s: csr=%08X imr=%08X\n", device_xname(sc->sc_dev), csr, imr));
1105 	if (!ISSET(csr, imr))
1106 		return 0;
1107 
1108 	// ok, we DO have some interrupts to serve! let softint do it
1109 	sc->sc_csr = csr;
1110 	at91usart_writereg(sc, US_IDR, -1);
1111 
1112 	/* Wake up the poller. */
1113 	softint_schedule(sc->sc_si);
1114 
1115 	/* we're done for now */
1116 	return (1);
1117 
1118 }
1119