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