uart_dev_at91usart.c revision 157570
1/*- 2 * Copyright (c) 2005 M. Warner Losh 3 * Copyright (c) 2005 cognet 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/arm/at91/uart_dev_at91usart.c 157570 2006-04-06 20:47:54Z cognet $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/conf.h> 35#include <sys/cons.h> 36#include <sys/tty.h> 37#include <machine/bus.h> 38 39#include <dev/uart/uart.h> 40#include <dev/uart/uart_cpu.h> 41#include <dev/uart/uart_bus.h> 42#include <arm/at91/at91rm92reg.h> 43#include <arm/at91/at91_usartreg.h> 44#include <arm/at91/at91_pdcreg.h> 45 46#include "uart_if.h" 47 48/* 49 * High-level UART interface. 50 */ 51struct at91_usart_softc { 52 struct uart_softc base; 53 bus_dma_tag_t dmatag; /* bus dma tag for mbufs */ 54 bus_dmamap_t tx_map; 55 bus_dmamap_t rx_map; 56}; 57 58#define DEFAULT_RCLK AT91C_MASTER_CLOCK 59#define USART_BUFFER_SIZE 128 60 61#define RD4(bas, reg) \ 62 bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg)) 63#define WR4(bas, reg, value) \ 64 bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value) 65 66#define SIGCHG(c, i, s, d) \ 67 do { \ 68 if (c) { \ 69 i |= (i & s) ? s : s | d; \ 70 } else { \ 71 i = (i & s) ? (i & ~s) | d : i; \ 72 } \ 73 } while (0); 74 75/* 76 * Low-level UART interface. 77 */ 78static int at91_usart_probe(struct uart_bas *bas); 79static void at91_usart_init(struct uart_bas *bas, int, int, int, int); 80static void at91_usart_term(struct uart_bas *bas); 81static void at91_usart_putc(struct uart_bas *bas, int); 82static int at91_usart_poll(struct uart_bas *bas); 83static int at91_usart_getc(struct uart_bas *bas, struct mtx *mtx); 84 85extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; 86 87static int 88at91_usart_param(struct uart_bas *bas, int baudrate, int databits, 89 int stopbits, int parity) 90{ 91 uint32_t mr; 92 93 /* 94 * Assume 3-write RS-232 configuration. 95 * XXX Not sure how uart will present the other modes to us, so 96 * XXX they are unimplemented. maybe ioctl? 97 */ 98 mr = USART_MR_MODE_NORMAL; 99 mr |= USART_MR_USCLKS_MCK; /* Assume MCK */ 100 101 /* 102 * Or in the databits requested 103 */ 104 if (databits < 9) 105 mr &= ~USART_MR_MODE9; 106 switch (databits) { 107 case 5: 108 mr |= USART_MR_CHRL_5BITS; 109 break; 110 case 6: 111 mr |= USART_MR_CHRL_6BITS; 112 break; 113 case 7: 114 mr |= USART_MR_CHRL_7BITS; 115 break; 116 case 8: 117 mr |= USART_MR_CHRL_8BITS; 118 break; 119 case 9: 120 mr |= USART_MR_CHRL_8BITS | USART_MR_MODE9; 121 break; 122 default: 123 return (EINVAL); 124 } 125 126 /* 127 * Or in the parity 128 */ 129 switch (parity) { 130 case UART_PARITY_NONE: 131 mr |= USART_MR_PAR_NONE; 132 break; 133 case UART_PARITY_ODD: 134 mr |= USART_MR_PAR_ODD; 135 break; 136 case UART_PARITY_EVEN: 137 mr |= USART_MR_PAR_EVEN; 138 break; 139 case UART_PARITY_MARK: 140 mr |= USART_MR_PAR_MARK; 141 break; 142 case UART_PARITY_SPACE: 143 mr |= USART_MR_PAR_SPACE; 144 break; 145 default: 146 return (EINVAL); 147 } 148 149 /* 150 * Or in the stop bits. Note: The hardware supports 151 * 1.5 stop bits in async mode, but there's no way to 152 * specify that AFAICT. 153 */ 154 if (stopbits > 1) 155 mr |= USART_MR_NBSTOP_2; 156 else 157 mr |= USART_MR_NBSTOP_2; 158 /* else if (stopbits == 1.5) 159 mr |= USART_MR_NBSTOP_1_5; */ 160 161 /* 162 * We want normal plumbing mode too, none of this fancy 163 * loopback or echo mode. 164 */ 165 mr |= USART_MR_CHMODE_NORMAL; 166 167 mr &= ~USART_MR_MSBF; /* lsb first */ 168 mr &= ~USART_MR_CKLO_SCK; /* Don't drive SCK */ 169 170 /* XXX Need to take possible synchronous mode into account */ 171 return (0); 172} 173 174struct uart_ops at91_usart_ops = { 175 .probe = at91_usart_probe, 176 .init = at91_usart_init, 177 .term = at91_usart_term, 178 .putc = at91_usart_putc, 179 .poll = at91_usart_poll, 180 .getc = at91_usart_getc, 181}; 182 183static int 184at91_usart_probe(struct uart_bas *bas) 185{ 186 /* We know that this is always here */ 187 return (0); 188} 189 190/* 191 * Initialize this device (I think as the console) 192 */ 193static void 194at91_usart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, 195 int parity) 196{ 197 int cr; 198 199 at91_usart_param(bas, baudrate, databits, stopbits, parity); 200 201 /* Turn on rx and tx */ 202 cr = USART_CR_RSTSTA | USART_CR_RSTRX | USART_CR_RSTTX; 203 WR4(bas, USART_CR, cr); 204 WR4(bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN); 205 WR4(bas, USART_IER, USART_CSR_TIMEOUT | 206 USART_CSR_TXRDY | USART_CSR_RXRDY | 207 USART_CSR_RXBRK | USART_CSR_ENDRX | USART_CSR_ENDTX); 208 /* Set the receive timeout to be 1.5 character times. */ 209 WR4(bas, USART_RTOR, 12); 210} 211 212/* 213 * Free resources now that we're no longer the console. This appears to 214 * be never called, and I'm unsure quite what to do if I am called. 215 */ 216static void 217at91_usart_term(struct uart_bas *bas) 218{ 219 /* XXX */ 220} 221 222/* 223 * Put a character of console output (so we do it here polling rather than 224 * interrutp driven). 225 */ 226static void 227at91_usart_putc(struct uart_bas *bas, int c) 228{ 229 230 while (!(RD4(bas, USART_CSR) & 231 USART_CSR_TXRDY)); 232 WR4(bas, USART_THR, c); 233} 234 235/* 236 * Poll for a character available 237 */ 238static int 239at91_usart_poll(struct uart_bas *bas) 240{ 241 242 if (!(RD4(bas, USART_CSR) & USART_CSR_RXRDY)) 243 return (-1); 244 return (RD4(bas, USART_RHR) & 0xff); 245} 246 247/* 248 * Block waiting for a character. 249 */ 250static int 251at91_usart_getc(struct uart_bas *bas, struct mtx *mtx) 252{ 253 int c; 254 255 while (!(RD4(bas, USART_CSR) & USART_CSR_RXRDY)) 256 ; 257 c = RD4(bas, USART_RHR); 258 c &= 0xff; 259 return (c); 260} 261 262static int at91_usart_bus_probe(struct uart_softc *sc); 263static int at91_usart_bus_attach(struct uart_softc *sc); 264static int at91_usart_bus_flush(struct uart_softc *, int); 265static int at91_usart_bus_getsig(struct uart_softc *); 266static int at91_usart_bus_ioctl(struct uart_softc *, int, intptr_t); 267static int at91_usart_bus_ipend(struct uart_softc *); 268static int at91_usart_bus_param(struct uart_softc *, int, int, int, int); 269static int at91_usart_bus_receive(struct uart_softc *); 270static int at91_usart_bus_setsig(struct uart_softc *, int); 271static int at91_usart_bus_transmit(struct uart_softc *); 272 273static kobj_method_t at91_usart_methods[] = { 274 KOBJMETHOD(uart_probe, at91_usart_bus_probe), 275 KOBJMETHOD(uart_attach, at91_usart_bus_attach), 276 KOBJMETHOD(uart_flush, at91_usart_bus_flush), 277 KOBJMETHOD(uart_getsig, at91_usart_bus_getsig), 278 KOBJMETHOD(uart_ioctl, at91_usart_bus_ioctl), 279 KOBJMETHOD(uart_ipend, at91_usart_bus_ipend), 280 KOBJMETHOD(uart_param, at91_usart_bus_param), 281 KOBJMETHOD(uart_receive, at91_usart_bus_receive), 282 KOBJMETHOD(uart_setsig, at91_usart_bus_setsig), 283 KOBJMETHOD(uart_transmit, at91_usart_bus_transmit), 284 285 { 0, 0 } 286}; 287 288int 289at91_usart_bus_probe(struct uart_softc *sc) 290{ 291 return (0); 292} 293 294static int 295at91_usart_bus_attach(struct uart_softc *sc) 296{ 297 int err; 298 struct at91_usart_softc *atsc; 299 300 atsc = (struct at91_usart_softc *)sc; 301 302 sc->sc_txfifosz = USART_BUFFER_SIZE; 303 sc->sc_rxfifosz = USART_BUFFER_SIZE; 304 sc->sc_hwiflow = 0; 305 306 /* 307 * Allocate DMA tags and maps 308 */ 309 err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, 310 BUS_SPACE_MAXADDR, NULL, NULL, USART_BUFFER_SIZE, 1, 311 USART_BUFFER_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &atsc->dmatag); 312 if (err != 0) 313 goto errout; 314 err = bus_dmamap_create(atsc->dmatag, 0, &atsc->tx_map); 315 if (err != 0) 316 goto errout; 317 err = bus_dmamap_create(atsc->dmatag, 0, &atsc->rx_map); 318 if (err != 0) 319 goto errout; 320errout:; 321 // XXX bad 322 return (err); 323} 324 325static void 326at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 327{ 328 if (error != 0) 329 return; 330 *(bus_addr_t *)arg = segs[0].ds_addr; 331} 332 333 334static int 335at91_usart_bus_transmit(struct uart_softc *sc) 336{ 337 bus_addr_t addr; 338 struct at91_usart_softc *atsc; 339 340 atsc = (struct at91_usart_softc *)sc; 341 if (bus_dmamap_load(atsc->dmatag, atsc->tx_map, sc->sc_txbuf, 342 sc->sc_txdatasz, at91_getaddr, &addr, 0) != 0) 343 return (EAGAIN); 344 bus_dmamap_sync(atsc->dmatag, atsc->tx_map, BUS_DMASYNC_PREWRITE); 345 346 uart_lock(sc->sc_hwmtx); 347 sc->sc_txbusy = 1; 348 /* 349 * Setup the PDC to transfer the data and interrupt us when it 350 * is done. We've already requested the interrupt. 351 */ 352 WR4(&sc->sc_bas, PDC_TPR, addr); 353 WR4(&sc->sc_bas, PDC_TCR, sc->sc_txdatasz); 354 WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_TXTEN); 355 uart_unlock(sc->sc_hwmtx); 356#ifdef USART0_CONSOLE 357 /* 358 * XXX: Gross hack : Skyeye doesn't raise an interrupt once the 359 * transfer is done, so simulate it. 360 */ 361 WR4(&sc->sc_bas, USART_IER, USART_CSR_TXRDY); 362#endif 363 return (0); 364} 365static int 366at91_usart_bus_setsig(struct uart_softc *sc, int sig) 367{ 368 uint32_t new, old, cr; 369 struct uart_bas *bas; 370 371 do { 372 old = sc->sc_hwsig; 373 new = old; 374 if (sig & SER_DDTR) 375 SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); 376 if (sig & SER_DRTS) 377 SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); 378 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 379 bas = &sc->sc_bas; 380 uart_lock(sc->sc_hwmtx); 381 cr = RD4(bas, USART_CR); 382 cr &= ~(USART_CR_DTREN | USART_CR_DTRDIS | USART_CR_RTSEN | 383 USART_CR_RTSDIS); 384 if (new & SER_DTR) 385 cr |= USART_CR_DTREN; 386 else 387 cr |= USART_CR_DTRDIS; 388 if (new & SER_RTS) 389 cr |= USART_CR_RTSEN; 390 else 391 cr |= USART_CR_RTSDIS; 392 WR4(bas, USART_CR, cr); 393 uart_unlock(sc->sc_hwmtx); 394 return (0); 395} 396static int 397at91_usart_bus_receive(struct uart_softc *sc) 398{ 399 400 uart_lock(sc->sc_hwmtx); 401 uart_rx_put(sc, at91_usart_getc(&sc->sc_bas, NULL)); 402 uart_unlock(sc->sc_hwmtx); 403 return (0); 404} 405static int 406at91_usart_bus_param(struct uart_softc *sc, int baudrate, int databits, 407 int stopbits, int parity) 408{ 409 return (at91_usart_param(&sc->sc_bas, baudrate, databits, stopbits, 410 parity)); 411} 412static int 413at91_usart_bus_ipend(struct uart_softc *sc) 414{ 415 int csr = RD4(&sc->sc_bas, USART_CSR); 416 int ipend = 0; 417 struct at91_usart_softc *atsc; 418 419 atsc = (struct at91_usart_softc *)sc; 420#ifdef USART0_CONSOLE 421 /* 422 * XXX: We have to cheat for skyeye, as it will return 0xff for all 423 * the devices it doesn't emulate. 424 */ 425 if (sc->sc_bas.chan != 1) 426 return (0); 427#endif 428 429 if (csr & USART_CSR_ENDTX) { 430 bus_dmamap_sync(atsc->dmatag, atsc->tx_map, 431 BUS_DMASYNC_POSTWRITE); 432 bus_dmamap_unload(atsc->dmatag, atsc->tx_map); 433 } 434 uart_lock(sc->sc_hwmtx); 435 if (csr & USART_CSR_TXRDY && sc->sc_txbusy) 436 ipend |= SER_INT_TXIDLE; 437 if (csr & USART_CSR_ENDTX && sc->sc_txbusy) 438 ipend |= SER_INT_TXIDLE; 439 if (csr & (USART_CSR_RXRDY /* | USART_CSR_ENDRX | USART_CSR_TIMEOUT */)) 440 ipend |= SER_INT_RXREADY; 441 if (csr & USART_CSR_RXBRK) { 442 unsigned int cr = USART_CR_RSTSTA; 443 444 ipend |= SER_INT_BREAK; 445 WR4(&sc->sc_bas, USART_CR, cr); 446 } 447 uart_unlock(sc->sc_hwmtx); 448 return (ipend); 449} 450static int 451at91_usart_bus_flush(struct uart_softc *sc, int what) 452{ 453 return (0); 454} 455 456static int 457at91_usart_bus_getsig(struct uart_softc *sc) 458{ 459 uint32_t new, sig; 460 uint8_t csr; 461 462 uart_lock(sc->sc_hwmtx); 463 csr = RD4(&sc->sc_bas, USART_CSR); 464 sig = 0; 465 if (csr & USART_CSR_CTS) 466 sig |= SER_CTS; 467 if (csr & USART_CSR_DCD) 468 sig |= SER_DCD; 469 if (csr & USART_CSR_DSR) 470 sig |= SER_DSR; 471 if (csr & USART_CSR_RI) 472 sig |= SER_RI; 473 new = sig & ~SER_MASK_DELTA; 474 sc->sc_hwsig = new; 475 uart_unlock(sc->sc_hwmtx); 476 return (sig); 477} 478 479static int 480at91_usart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 481{ 482 return (EINVAL); 483} 484struct uart_class at91_usart_class = { 485 "at91_usart class", 486 at91_usart_methods, 487 sizeof(struct at91_usart_softc), 488 .uc_range = 8, 489 .uc_rclk = DEFAULT_RCLK 490}; 491