1155324Simp/*-
2155324Simp * Copyright (c) 2005 M. Warner Losh
3158531Scognet * Copyright (c) 2005 Olivier Houchard
4155324Simp * All rights reserved.
5155324Simp *
6155324Simp * Redistribution and use in source and binary forms, with or without
7155324Simp * modification, are permitted provided that the following conditions
8155324Simp * are met:
9155324Simp *
10155324Simp * 1. Redistributions of source code must retain the above copyright
11155324Simp *    notice, this list of conditions and the following disclaimer.
12155324Simp * 2. Redistributions in binary form must reproduce the above copyright
13155324Simp *    notice, this list of conditions and the following disclaimer in the
14155324Simp *    documentation and/or other materials provided with the distribution.
15155324Simp *
16185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19185265Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26185265Simp * SUCH DAMAGE.
27155324Simp */
28155324Simp
29155324Simp#include <sys/cdefs.h>
30155324Simp__FBSDID("$FreeBSD$");
31155324Simp
32155324Simp#include <sys/param.h>
33155324Simp#include <sys/systm.h>
34155324Simp#include <sys/bus.h>
35155324Simp#include <sys/conf.h>
36155324Simp#include <sys/cons.h>
37155324Simp#include <sys/tty.h>
38155324Simp#include <machine/bus.h>
39155324Simp
40155324Simp#include <dev/uart/uart.h>
41155324Simp#include <dev/uart/uart_cpu.h>
42155324Simp#include <dev/uart/uart_bus.h>
43155324Simp#include <arm/at91/at91_usartreg.h>
44157560Simp#include <arm/at91/at91_pdcreg.h>
45187602Simp#include <arm/at91/at91var.h>
46155324Simp
47155324Simp#include "uart_if.h"
48155324Simp
49187602Simp#define DEFAULT_RCLK		at91_master_clock
50160071Simp#define	USART_BUFFER_SIZE	128
51160071Simp
52157560Simp/*
53157560Simp * High-level UART interface.
54157560Simp */
55160071Simpstruct at91_usart_rx {
56160071Simp	bus_addr_t	pa;
57236088Smarius	uint8_t		*buffer;
58160071Simp	bus_dmamap_t	map;
59160071Simp};
60160071Simp
61157560Simpstruct at91_usart_softc {
62157560Simp	struct uart_softc base;
63236088Smarius	bus_dma_tag_t tx_tag;
64157560Simp	bus_dmamap_t tx_map;
65160071Simp	uint32_t flags;
66259753Simp#define	HAS_TIMEOUT		0x1
67259753Simp#define	NEEDS_RXRDY		0x4
68236088Smarius	bus_dma_tag_t rx_tag;
69160071Simp	struct at91_usart_rx ping_pong[2];
70160071Simp	struct at91_usart_rx *ping;
71160071Simp	struct at91_usart_rx *pong;
72157560Simp};
73155324Simp
74157560Simp#define	RD4(bas, reg)		\
75157560Simp	bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg))
76157560Simp#define	WR4(bas, reg, value)	\
77157560Simp	bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value)
78157560Simp
79155324Simp#define	SIGCHG(c, i, s, d)				\
80155324Simp	do {						\
81155324Simp		if (c) {				\
82155324Simp			i |= (i & s) ? s : s | d;	\
83155324Simp		} else {				\
84155324Simp			i = (i & s) ? (i & ~s) | d : i;	\
85155324Simp		}					\
86155324Simp	} while (0);
87155324Simp
88160071Simp#define BAUD2DIVISOR(b) \
89160071Simp	((((DEFAULT_RCLK * 10) / ((b) * 16)) + 5) / 10)
90160071Simp
91155324Simp/*
92155324Simp * Low-level UART interface.
93155324Simp */
94155324Simpstatic int at91_usart_probe(struct uart_bas *bas);
95155324Simpstatic void at91_usart_init(struct uart_bas *bas, int, int, int, int);
96155324Simpstatic void at91_usart_term(struct uart_bas *bas);
97155324Simpstatic void at91_usart_putc(struct uart_bas *bas, int);
98166100Smariusstatic int at91_usart_rxready(struct uart_bas *bas);
99236088Smariusstatic int at91_usart_getc(struct uart_bas *bas, struct mtx *hwmtx);
100155324Simp
101155324Simpextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
102155324Simp
103155324Simpstatic int
104155324Simpat91_usart_param(struct uart_bas *bas, int baudrate, int databits,
105155324Simp    int stopbits, int parity)
106155324Simp{
107155324Simp	uint32_t mr;
108155324Simp
109155324Simp	/*
110236088Smarius	 * Assume 3-wire RS-232 configuration.
111155324Simp	 * XXX Not sure how uart will present the other modes to us, so
112155324Simp	 * XXX they are unimplemented.  maybe ioctl?
113155324Simp	 */
114155324Simp	mr = USART_MR_MODE_NORMAL;
115155324Simp	mr |= USART_MR_USCLKS_MCK;	/* Assume MCK */
116155324Simp
117155324Simp	/*
118155324Simp	 * Or in the databits requested
119155324Simp	 */
120155324Simp	if (databits < 9)
121155324Simp		mr &= ~USART_MR_MODE9;
122155324Simp	switch (databits) {
123155324Simp	case 5:
124155324Simp		mr |= USART_MR_CHRL_5BITS;
125155324Simp		break;
126155324Simp	case 6:
127155324Simp		mr |= USART_MR_CHRL_6BITS;
128155324Simp		break;
129155324Simp	case 7:
130155324Simp		mr |= USART_MR_CHRL_7BITS;
131155324Simp		break;
132155324Simp	case 8:
133155324Simp		mr |= USART_MR_CHRL_8BITS;
134155324Simp		break;
135155324Simp	case 9:
136155324Simp		mr |= USART_MR_CHRL_8BITS | USART_MR_MODE9;
137155324Simp		break;
138155324Simp	default:
139155324Simp		return (EINVAL);
140155324Simp	}
141155324Simp
142155324Simp	/*
143155324Simp	 * Or in the parity
144155324Simp	 */
145155324Simp	switch (parity) {
146155324Simp	case UART_PARITY_NONE:
147155324Simp		mr |= USART_MR_PAR_NONE;
148155324Simp		break;
149155324Simp	case UART_PARITY_ODD:
150155324Simp		mr |= USART_MR_PAR_ODD;
151155324Simp		break;
152155324Simp	case UART_PARITY_EVEN:
153155324Simp		mr |= USART_MR_PAR_EVEN;
154155324Simp		break;
155155324Simp	case UART_PARITY_MARK:
156155324Simp		mr |= USART_MR_PAR_MARK;
157155324Simp		break;
158155324Simp	case UART_PARITY_SPACE:
159155324Simp		mr |= USART_MR_PAR_SPACE;
160155324Simp		break;
161155324Simp	default:
162155324Simp		return (EINVAL);
163155324Simp	}
164155324Simp
165155324Simp	/*
166160071Simp	 * Or in the stop bits.  Note: The hardware supports 1.5 stop
167160071Simp	 * bits in async mode, but there's no way to specify that
168160071Simp	 * AFAICT.  Instead, rely on the convention documented at
169160071Simp	 * http://www.lammertbies.nl/comm/info/RS-232_specs.html which
170160071Simp	 * states that 1.5 stop bits are used for 5 bit bytes and
171160071Simp	 * 2 stop bits only for longer bytes.
172155324Simp	 */
173160071Simp	if (stopbits == 1)
174160071Simp		mr |= USART_MR_NBSTOP_1;
175160071Simp	else if (databits > 5)
176155324Simp		mr |= USART_MR_NBSTOP_2;
177155324Simp	else
178160071Simp		mr |= USART_MR_NBSTOP_1_5;
179155324Simp
180155324Simp	/*
181155324Simp	 * We want normal plumbing mode too, none of this fancy
182155324Simp	 * loopback or echo mode.
183155324Simp	 */
184155324Simp	mr |= USART_MR_CHMODE_NORMAL;
185155324Simp
186155324Simp	mr &= ~USART_MR_MSBF;	/* lsb first */
187155324Simp	mr &= ~USART_MR_CKLO_SCK;	/* Don't drive SCK */
188155324Simp
189160071Simp	WR4(bas, USART_MR, mr);
190160071Simp
191160071Simp	/*
192213496Scognet	 * Set the baud rate (only if we know our master clock rate)
193160071Simp	 */
194213496Scognet	if (DEFAULT_RCLK != 0)
195213496Scognet		WR4(bas, USART_BRGR, BAUD2DIVISOR(baudrate));
196160071Simp
197155324Simp	/* XXX Need to take possible synchronous mode into account */
198155324Simp	return (0);
199155324Simp}
200155324Simp
201168281Smarcelstatic struct uart_ops at91_usart_ops = {
202155324Simp	.probe = at91_usart_probe,
203155324Simp	.init = at91_usart_init,
204155324Simp	.term = at91_usart_term,
205155324Simp	.putc = at91_usart_putc,
206166100Smarius	.rxready = at91_usart_rxready,
207155324Simp	.getc = at91_usart_getc,
208155324Simp};
209155324Simp
210155324Simpstatic int
211155324Simpat91_usart_probe(struct uart_bas *bas)
212155324Simp{
213236088Smarius
214155324Simp	/* We know that this is always here */
215155324Simp	return (0);
216155324Simp}
217155324Simp
218155324Simp/*
219160071Simp * Initialize this device for use as a console.
220155324Simp */
221155324Simpstatic void
222155324Simpat91_usart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
223155324Simp    int parity)
224155324Simp{
225157023Scognet
226155324Simp	at91_usart_param(bas, baudrate, databits, stopbits, parity);
227155324Simp
228160071Simp	/* Reset the rx and tx buffers and turn on rx and tx */
229160071Simp	WR4(bas, USART_CR, USART_CR_RSTSTA | USART_CR_RSTRX | USART_CR_RSTTX);
230157560Simp	WR4(bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN);
231160071Simp	WR4(bas, USART_IDR, 0xffffffff);
232155324Simp}
233155324Simp
234155324Simp/*
235155324Simp * Free resources now that we're no longer the console.  This appears to
236155324Simp * be never called, and I'm unsure quite what to do if I am called.
237155324Simp */
238155324Simpstatic void
239155324Simpat91_usart_term(struct uart_bas *bas)
240155324Simp{
241236088Smarius
242155324Simp	/* XXX */
243155324Simp}
244155324Simp
245155324Simp/*
246155324Simp * Put a character of console output (so we do it here polling rather than
247155324Simp * interrutp driven).
248155324Simp */
249155324Simpstatic void
250155324Simpat91_usart_putc(struct uart_bas *bas, int c)
251155324Simp{
252155324Simp
253236088Smarius	while (!(RD4(bas, USART_CSR) & USART_CSR_TXRDY))
254160071Simp		continue;
255157560Simp	WR4(bas, USART_THR, c);
256155324Simp}
257155324Simp
258155324Simp/*
259166100Smarius * Check for a character available.
260155324Simp */
261155324Simpstatic int
262166100Smariusat91_usart_rxready(struct uart_bas *bas)
263155324Simp{
264155324Simp
265166100Smarius	return ((RD4(bas, USART_CSR) & USART_CSR_RXRDY) != 0 ? 1 : 0);
266155324Simp}
267155324Simp
268155324Simp/*
269155324Simp * Block waiting for a character.
270155324Simp */
271155324Simpstatic int
272236088Smariusat91_usart_getc(struct uart_bas *bas, struct mtx *hwmtx)
273155324Simp{
274155324Simp	int c;
275155324Simp
276236088Smarius	uart_lock(hwmtx);
277236088Smarius	while (!(RD4(bas, USART_CSR) & USART_CSR_RXRDY)) {
278236088Smarius		uart_unlock(hwmtx);
279236088Smarius		DELAY(4);
280236088Smarius		uart_lock(hwmtx);
281236088Smarius	}
282236088Smarius	c = RD4(bas, USART_RHR) & 0xff;
283236088Smarius	uart_unlock(hwmtx);
284155324Simp	return (c);
285155324Simp}
286155324Simp
287155324Simpstatic int at91_usart_bus_probe(struct uart_softc *sc);
288155324Simpstatic int at91_usart_bus_attach(struct uart_softc *sc);
289155324Simpstatic int at91_usart_bus_flush(struct uart_softc *, int);
290155324Simpstatic int at91_usart_bus_getsig(struct uart_softc *);
291155324Simpstatic int at91_usart_bus_ioctl(struct uart_softc *, int, intptr_t);
292155324Simpstatic int at91_usart_bus_ipend(struct uart_softc *);
293155324Simpstatic int at91_usart_bus_param(struct uart_softc *, int, int, int, int);
294155324Simpstatic int at91_usart_bus_receive(struct uart_softc *);
295155324Simpstatic int at91_usart_bus_setsig(struct uart_softc *, int);
296155324Simpstatic int at91_usart_bus_transmit(struct uart_softc *);
297155324Simp
298155324Simpstatic kobj_method_t at91_usart_methods[] = {
299155324Simp	KOBJMETHOD(uart_probe,		at91_usart_bus_probe),
300236088Smarius	KOBJMETHOD(uart_attach,		at91_usart_bus_attach),
301155324Simp	KOBJMETHOD(uart_flush,		at91_usart_bus_flush),
302155324Simp	KOBJMETHOD(uart_getsig,		at91_usart_bus_getsig),
303155324Simp	KOBJMETHOD(uart_ioctl,		at91_usart_bus_ioctl),
304155324Simp	KOBJMETHOD(uart_ipend,		at91_usart_bus_ipend),
305155324Simp	KOBJMETHOD(uart_param,		at91_usart_bus_param),
306155324Simp	KOBJMETHOD(uart_receive,	at91_usart_bus_receive),
307155324Simp	KOBJMETHOD(uart_setsig,		at91_usart_bus_setsig),
308155324Simp	KOBJMETHOD(uart_transmit,	at91_usart_bus_transmit),
309236088Smarius
310236088Smarius	KOBJMETHOD_END
311155324Simp};
312155324Simp
313155324Simpint
314155324Simpat91_usart_bus_probe(struct uart_softc *sc)
315155324Simp{
316196246Sstas
317196246Sstas	sc->sc_txfifosz = USART_BUFFER_SIZE;
318196246Sstas	sc->sc_rxfifosz = USART_BUFFER_SIZE;
319196246Sstas	sc->sc_hwiflow = 0;
320155324Simp	return (0);
321155324Simp}
322155324Simp
323160071Simpstatic void
324160071Simpat91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
325160071Simp{
326236088Smarius
327160071Simp	if (error != 0)
328160071Simp		return;
329160071Simp	*(bus_addr_t *)arg = segs[0].ds_addr;
330160071Simp}
331160071Simp
332155324Simpstatic int
333155324Simpat91_usart_bus_attach(struct uart_softc *sc)
334155324Simp{
335164969Scognet	int err;
336164969Scognet	int i;
337160071Simp	uint32_t cr;
338157560Simp	struct at91_usart_softc *atsc;
339157560Simp
340157560Simp	atsc = (struct at91_usart_softc *)sc;
341157560Simp
342160071Simp	/*
343160357Simp	 * See if we have a TIMEOUT bit.  We disable all interrupts as
344160357Simp	 * a side effect.  Boot loaders may have enabled them.  Since
345160357Simp	 * a TIMEOUT interrupt can't happen without other setup, the
346160357Simp	 * apparent race here can't actually happen.
347160071Simp	 */
348160071Simp	WR4(&sc->sc_bas, USART_IDR, 0xffffffff);
349160071Simp	WR4(&sc->sc_bas, USART_IER, USART_CSR_TIMEOUT);
350160071Simp	if (RD4(&sc->sc_bas, USART_IMR) & USART_CSR_TIMEOUT)
351160071Simp		atsc->flags |= HAS_TIMEOUT;
352160071Simp	WR4(&sc->sc_bas, USART_IDR, 0xffffffff);
353160071Simp
354157560Simp	/*
355236088Smarius	 * Allocate transmit DMA tag and map.  We allow a transmit buffer
356236088Smarius	 * to be any size, but it must map to a single contiguous physical
357236088Smarius	 * extent.
358157560Simp	 */
359185049Sstas	err = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
360183670Simp	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
361236088Smarius	    BUS_SPACE_MAXSIZE_32BIT, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,
362236088Smarius	    NULL, &atsc->tx_tag);
363157560Simp	if (err != 0)
364157560Simp		goto errout;
365236088Smarius	err = bus_dmamap_create(atsc->tx_tag, 0, &atsc->tx_map);
366157560Simp	if (err != 0)
367160357Simp		goto errout;
368236088Smarius
369160071Simp	if (atsc->flags & HAS_TIMEOUT) {
370236088Smarius		/*
371236088Smarius		 * Allocate receive DMA tags, maps, and buffers.
372236088Smarius		 * The receive buffers should be aligned to arm_dcache_align,
373236088Smarius		 * otherwise partial cache line flushes on every receive
374236088Smarius		 * interrupt are pretty much guaranteed.
375236088Smarius		 */
376236088Smarius		err = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev),
377236088Smarius		    arm_dcache_align, 0, BUS_SPACE_MAXADDR_32BIT,
378236088Smarius		    BUS_SPACE_MAXADDR, NULL, NULL, sc->sc_rxfifosz, 1,
379236088Smarius		    sc->sc_rxfifosz, BUS_DMA_ALLOCNOW, NULL, NULL,
380236088Smarius		    &atsc->rx_tag);
381236088Smarius		if (err != 0)
382236088Smarius			goto errout;
383160071Simp		for (i = 0; i < 2; i++) {
384236088Smarius			err = bus_dmamem_alloc(atsc->rx_tag,
385236088Smarius			    (void **)&atsc->ping_pong[i].buffer,
386236088Smarius			    BUS_DMA_NOWAIT, &atsc->ping_pong[i].map);
387160071Simp			if (err != 0)
388160071Simp				goto errout;
389236088Smarius			err = bus_dmamap_load(atsc->rx_tag,
390160071Simp			    atsc->ping_pong[i].map,
391160071Simp			    atsc->ping_pong[i].buffer, sc->sc_rxfifosz,
392160071Simp			    at91_getaddr, &atsc->ping_pong[i].pa, 0);
393160071Simp			if (err != 0)
394160071Simp				goto errout;
395236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->ping_pong[i].map,
396160071Simp			    BUS_DMASYNC_PREREAD);
397160071Simp		}
398160071Simp		atsc->ping = &atsc->ping_pong[0];
399160071Simp		atsc->pong = &atsc->ping_pong[1];
400160071Simp	}
401160071Simp
402160071Simp	/* Turn on rx and tx */
403160071Simp	cr = USART_CR_RSTSTA | USART_CR_RSTRX | USART_CR_RSTTX;
404160071Simp	WR4(&sc->sc_bas, USART_CR, cr);
405160071Simp	WR4(&sc->sc_bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN);
406160071Simp
407160071Simp	/*
408160071Simp	 * Setup the PDC to receive data.  We use the ping-pong buffers
409160071Simp	 * so that we can more easily bounce between the two and so that
410160071Simp	 * we get an interrupt 1/2 way through the software 'fifo' we have
411160071Simp	 * to avoid overruns.
412160071Simp	 */
413160071Simp	if (atsc->flags & HAS_TIMEOUT) {
414160071Simp		WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa);
415160071Simp		WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz);
416160071Simp		WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa);
417160071Simp		WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz);
418160071Simp		WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN);
419160071Simp
420236088Smarius		/*
421236088Smarius		 * Set the receive timeout to be 1.5 character times
422236088Smarius		 * assuming 8N1.
423236088Smarius		 */
424236088Smarius		WR4(&sc->sc_bas, USART_RTOR, 15);
425160071Simp		WR4(&sc->sc_bas, USART_CR, USART_CR_STTTO);
426160071Simp		WR4(&sc->sc_bas, USART_IER, USART_CSR_TIMEOUT |
427160071Simp		    USART_CSR_RXBUFF | USART_CSR_ENDRX);
428160071Simp	} else {
429259753Simp		/*
430259753Simp		 * Defer turning on the RXRDY bit until we're opened. This is to make the
431259753Simp		 * mountroot prompt work before we've opened the console. This is a workaround
432259753Simp		 * for not being able to change the UART interface for the 10.0 release.
433259753Simp		 */
434259753Simp		atsc->flags |= NEEDS_RXRDY;
435259753Simp		/* WR4(&sc->sc_bas, USART_IER, USART_CSR_RXRDY); */
436160071Simp	}
437160071Simp	WR4(&sc->sc_bas, USART_IER, USART_CSR_RXBRK);
438237093Smariuserrout:
439157560Simp	return (err);
440155324Simp}
441157560Simp
442155324Simpstatic int
443155324Simpat91_usart_bus_transmit(struct uart_softc *sc)
444155324Simp{
445157560Simp	bus_addr_t addr;
446157560Simp	struct at91_usart_softc *atsc;
447236088Smarius	int err;
448155324Simp
449236088Smarius	err = 0;
450157560Simp	atsc = (struct at91_usart_softc *)sc;
451157570Scognet	uart_lock(sc->sc_hwmtx);
452236088Smarius	if (bus_dmamap_load(atsc->tx_tag, atsc->tx_map, sc->sc_txbuf,
453236088Smarius	    sc->sc_txdatasz, at91_getaddr, &addr, 0) != 0) {
454236088Smarius		err = EAGAIN;
455236088Smarius		goto errout;
456236088Smarius	}
457236088Smarius	bus_dmamap_sync(atsc->tx_tag, atsc->tx_map, BUS_DMASYNC_PREWRITE);
458155324Simp	sc->sc_txbusy = 1;
459157560Simp	/*
460157560Simp	 * Setup the PDC to transfer the data and interrupt us when it
461157560Simp	 * is done.  We've already requested the interrupt.
462157560Simp	 */
463157560Simp	WR4(&sc->sc_bas, PDC_TPR, addr);
464157560Simp	WR4(&sc->sc_bas, PDC_TCR, sc->sc_txdatasz);
465157560Simp	WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_TXTEN);
466160071Simp	WR4(&sc->sc_bas, USART_IER, USART_CSR_ENDTX);
467236088Smariuserrout:
468157570Scognet	uart_unlock(sc->sc_hwmtx);
469236088Smarius	return (err);
470155324Simp}
471236088Smarius
472155324Simpstatic int
473155324Simpat91_usart_bus_setsig(struct uart_softc *sc, int sig)
474155324Simp{
475155324Simp	uint32_t new, old, cr;
476155324Simp	struct uart_bas *bas;
477155324Simp
478155324Simp	do {
479155324Simp		old = sc->sc_hwsig;
480155324Simp		new = old;
481155324Simp		if (sig & SER_DDTR)
482155324Simp			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
483155324Simp		if (sig & SER_DRTS)
484155324Simp			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
485155324Simp	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
486155324Simp	bas = &sc->sc_bas;
487157570Scognet	uart_lock(sc->sc_hwmtx);
488160071Simp	cr = 0;
489155324Simp	if (new & SER_DTR)
490155324Simp		cr |= USART_CR_DTREN;
491155324Simp	else
492155324Simp		cr |= USART_CR_DTRDIS;
493155324Simp	if (new & SER_RTS)
494155324Simp		cr |= USART_CR_RTSEN;
495155324Simp	else
496155324Simp		cr |= USART_CR_RTSDIS;
497157560Simp	WR4(bas, USART_CR, cr);
498157570Scognet	uart_unlock(sc->sc_hwmtx);
499155324Simp	return (0);
500155324Simp}
501236088Smarius
502155324Simpstatic int
503155324Simpat91_usart_bus_receive(struct uart_softc *sc)
504155324Simp{
505160071Simp
506155324Simp	return (0);
507155324Simp}
508236088Smarius
509155324Simpstatic int
510155324Simpat91_usart_bus_param(struct uart_softc *sc, int baudrate, int databits,
511155324Simp    int stopbits, int parity)
512155324Simp{
513160071Simp
514155324Simp	return (at91_usart_param(&sc->sc_bas, baudrate, databits, stopbits,
515155324Simp	    parity));
516155324Simp}
517160533Scognet
518160533Scognetstatic __inline void
519160533Scognetat91_rx_put(struct uart_softc *sc, int key)
520160533Scognet{
521178766Speter
522225214Srwatson#if defined(KDB)
523225214Srwatson	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE)
524225214Srwatson		kdb_alt_break(key, &sc->sc_altbrk);
525160533Scognet#endif
526236088Smarius	uart_rx_put(sc, key);
527160533Scognet}
528160533Scognet
529155324Simpstatic int
530155324Simpat91_usart_bus_ipend(struct uart_softc *sc)
531155324Simp{
532157560Simp	struct at91_usart_softc *atsc;
533160071Simp	struct at91_usart_rx *p;
534236088Smarius	int i, ipend, len;
535236088Smarius	uint32_t csr;
536157560Simp
537236088Smarius	ipend = 0;
538236088Smarius	atsc = (struct at91_usart_softc *)sc;
539236088Smarius	uart_lock(sc->sc_hwmtx);
540259753Simp
541259753Simp	/* Kludge -- Enable the RXRDY we deferred in attach */
542259753Simp	if (sc->sc_opened && (atsc->flags & NEEDS_RXRDY)) {
543259753Simp		WR4(&sc->sc_bas, USART_IER, USART_CSR_RXRDY);
544259753Simp		atsc->flags &= ~NEEDS_RXRDY;
545259753Simp	}
546259753Simp
547236088Smarius	csr = RD4(&sc->sc_bas, USART_CSR);
548157560Simp	if (csr & USART_CSR_ENDTX) {
549236088Smarius		bus_dmamap_sync(atsc->tx_tag, atsc->tx_map,
550157560Simp		    BUS_DMASYNC_POSTWRITE);
551236088Smarius		bus_dmamap_unload(atsc->tx_tag, atsc->tx_map);
552157560Simp	}
553236088Smarius	if (csr & (USART_CSR_TXRDY | USART_CSR_ENDTX)) {
554160071Simp		if (sc->sc_txbusy)
555160071Simp			ipend |= SER_INT_TXIDLE;
556236088Smarius		WR4(&sc->sc_bas, USART_IDR, csr & (USART_CSR_TXRDY |
557236088Smarius		    USART_CSR_ENDTX));
558160071Simp	}
559160071Simp
560160071Simp	/*
561160071Simp	 * Due to the contraints of the DMA engine present in the
562160071Simp	 * atmel chip, I can't just say I have a rx interrupt pending
563160071Simp	 * and do all the work elsewhere.  I need to look at the CSR
564160071Simp	 * bits right now and do things based on them to avoid races.
565160071Simp	 */
566236088Smarius	if (atsc->flags & HAS_TIMEOUT) {
567236088Smarius		if (csr & USART_CSR_RXBUFF) {
568236088Smarius			/*
569236088Smarius			 * We have a buffer overflow.  Copy all data from both
570236088Smarius			 * ping and pong.  Insert overflow character.  Reset
571236088Smarius			 * ping and pong and re-enable the PDC to receive
572236088Smarius			 * characters again.
573236088Smarius			 */
574236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->ping->map,
575236088Smarius			    BUS_DMASYNC_POSTREAD);
576236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->pong->map,
577236088Smarius			    BUS_DMASYNC_POSTREAD);
578236088Smarius			for (i = 0; i < sc->sc_rxfifosz; i++)
579236088Smarius				at91_rx_put(sc, atsc->ping->buffer[i]);
580236088Smarius			for (i = 0; i < sc->sc_rxfifosz; i++)
581236088Smarius				at91_rx_put(sc, atsc->pong->buffer[i]);
582236088Smarius			uart_rx_put(sc, UART_STAT_OVERRUN);
583236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->ping->map,
584236088Smarius			    BUS_DMASYNC_PREREAD);
585236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->pong->map,
586236088Smarius			    BUS_DMASYNC_PREREAD);
587236088Smarius			WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa);
588236088Smarius			WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz);
589236088Smarius			WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa);
590236088Smarius			WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz);
591236088Smarius			WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN);
592236088Smarius			ipend |= SER_INT_RXREADY;
593236088Smarius		} else if (csr & USART_CSR_ENDRX) {
594236088Smarius			/*
595236088Smarius			 * Shuffle data from ping of ping pong buffer, but
596236088Smarius			 * leave current pong in place, as it has become the
597236088Smarius			 * new ping.  We need to copy data and setup the old
598236088Smarius			 * ping as the new pong when we're done.
599236088Smarius			 */
600236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->ping->map,
601236088Smarius			    BUS_DMASYNC_POSTREAD);
602236088Smarius			for (i = 0; i < sc->sc_rxfifosz; i++)
603236088Smarius				at91_rx_put(sc, atsc->ping->buffer[i]);
604236088Smarius			p = atsc->ping;
605236088Smarius			atsc->ping = atsc->pong;
606236088Smarius			atsc->pong = p;
607236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->pong->map,
608236088Smarius			    BUS_DMASYNC_PREREAD);
609236088Smarius			WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa);
610236088Smarius			WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz);
611236088Smarius			ipend |= SER_INT_RXREADY;
612236088Smarius		} else if (csr & USART_CSR_TIMEOUT) {
613236088Smarius			/*
614236088Smarius			 * We have one partial buffer.  We need to stop the
615236088Smarius			 * PDC, get the number of characters left and from
616236088Smarius			 * that compute number of valid characters.  We then
617236088Smarius			 * need to reset ping and pong and reenable the PDC.
618236088Smarius			 * Not sure if there's a race here at fast baud rates
619236088Smarius			 * we need to worry about.
620236088Smarius			 */
621236088Smarius			WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTDIS);
622236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->ping->map,
623236088Smarius			    BUS_DMASYNC_POSTREAD);
624236088Smarius			len = sc->sc_rxfifosz - RD4(&sc->sc_bas, PDC_RCR);
625236088Smarius			for (i = 0; i < len; i++)
626236088Smarius				at91_rx_put(sc, atsc->ping->buffer[i]);
627236088Smarius			bus_dmamap_sync(atsc->rx_tag, atsc->ping->map,
628236088Smarius			    BUS_DMASYNC_PREREAD);
629236088Smarius			WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa);
630236088Smarius			WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz);
631236088Smarius			WR4(&sc->sc_bas, USART_CR, USART_CR_STTTO);
632236088Smarius			WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN);
633236088Smarius			ipend |= SER_INT_RXREADY;
634236088Smarius		}
635236088Smarius	} else if (csr & USART_CSR_RXRDY) {
636236088Smarius		/*
637236088Smarius		 * We have another charater in a device that doesn't support
638236088Smarius		 * timeouts, so we do it one character at a time.
639236088Smarius		 */
640160533Scognet		at91_rx_put(sc, RD4(&sc->sc_bas, USART_RHR) & 0xff);
641160071Simp		ipend |= SER_INT_RXREADY;
642160071Simp	}
643160071Simp
644157023Scognet	if (csr & USART_CSR_RXBRK) {
645157023Scognet		ipend |= SER_INT_BREAK;
646236088Smarius		WR4(&sc->sc_bas, USART_CR, USART_CR_RSTSTA);
647157023Scognet	}
648157570Scognet	uart_unlock(sc->sc_hwmtx);
649155324Simp	return (ipend);
650155324Simp}
651236088Smarius
652155324Simpstatic int
653155324Simpat91_usart_bus_flush(struct uart_softc *sc, int what)
654155324Simp{
655236088Smarius
656155324Simp	return (0);
657155324Simp}
658155324Simp
659155324Simpstatic int
660155324Simpat91_usart_bus_getsig(struct uart_softc *sc)
661155324Simp{
662236088Smarius	uint32_t csr, new, sig;
663155324Simp
664157570Scognet	uart_lock(sc->sc_hwmtx);
665157560Simp	csr = RD4(&sc->sc_bas, USART_CSR);
666155324Simp	sig = 0;
667155324Simp	if (csr & USART_CSR_CTS)
668155324Simp		sig |= SER_CTS;
669155324Simp	if (csr & USART_CSR_DCD)
670155324Simp		sig |= SER_DCD;
671155324Simp	if (csr & USART_CSR_DSR)
672155324Simp		sig |= SER_DSR;
673155324Simp	if (csr & USART_CSR_RI)
674155324Simp		sig |= SER_RI;
675156094Scognet	new = sig & ~SER_MASK_DELTA;
676155324Simp	sc->sc_hwsig = new;
677157570Scognet	uart_unlock(sc->sc_hwmtx);
678155324Simp	return (sig);
679155324Simp}
680155324Simp
681155324Simpstatic int
682155324Simpat91_usart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
683155324Simp{
684236088Smarius
685160071Simp	switch (request) {
686160071Simp	case UART_IOCTL_BREAK:
687160071Simp	case UART_IOCTL_IFLOW:
688160071Simp	case UART_IOCTL_OFLOW:
689160071Simp		break;
690160071Simp	case UART_IOCTL_BAUD:
691213496Scognet		/* only if we know our master clock rate */
692213496Scognet		if (DEFAULT_RCLK != 0)
693213496Scognet			WR4(&sc->sc_bas, USART_BRGR,
694236088Smarius			    BAUD2DIVISOR(*(int *)data));
695160071Simp		return (0);
696160071Simp	}
697155324Simp	return (EINVAL);
698155324Simp}
699168281Smarcel
700155324Simpstruct uart_class at91_usart_class = {
701168281Smarcel	"at91_usart",
702155324Simp	at91_usart_methods,
703157560Simp	sizeof(struct at91_usart_softc),
704168281Smarcel	.uc_ops = &at91_usart_ops,
705187602Simp	.uc_range = 8
706155324Simp};
707