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