uart_dev_imx.c revision 262649
1/*- 2 * Copyright (c) 2012 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Oleksandr Rybalko under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/10/sys/dev/uart/uart_dev_imx.c 262649 2014-03-01 04:16:54Z imp $"); 32 33#include "opt_ddb.h" 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/bus.h> 38#include <sys/conf.h> 39#include <sys/kdb.h> 40#include <machine/bus.h> 41#include <machine/fdt.h> 42 43#include <dev/uart/uart.h> 44#include <dev/uart/uart_cpu.h> 45#include <dev/uart/uart_bus.h> 46 47#include <dev/uart/uart_dev_imx5xx.h> 48 49#include "uart_if.h" 50/* 51 * Low-level UART interface. 52 */ 53static int imx_uart_probe(struct uart_bas *bas); 54static void imx_uart_init(struct uart_bas *bas, int, int, int, int); 55static void imx_uart_term(struct uart_bas *bas); 56static void imx_uart_putc(struct uart_bas *bas, int); 57static int imx_uart_rxready(struct uart_bas *bas); 58static int imx_uart_getc(struct uart_bas *bas, struct mtx *); 59 60static struct uart_ops uart_imx_uart_ops = { 61 .probe = imx_uart_probe, 62 .init = imx_uart_init, 63 .term = imx_uart_term, 64 .putc = imx_uart_putc, 65 .rxready = imx_uart_rxready, 66 .getc = imx_uart_getc, 67}; 68 69static int 70imx_uart_probe(struct uart_bas *bas) 71{ 72 73 return (0); 74} 75 76static void 77imx_uart_init(struct uart_bas *bas, int baudrate, int databits, 78 int stopbits, int parity) 79{ 80 81} 82 83static void 84imx_uart_term(struct uart_bas *bas) 85{ 86 87} 88 89static void 90imx_uart_putc(struct uart_bas *bas, int c) 91{ 92 93 while (!(IS(bas, USR2, TXFE))) 94 ; 95 SETREG(bas, REG(UTXD), c); 96} 97 98static int 99imx_uart_rxready(struct uart_bas *bas) 100{ 101 102 return ((IS(bas, USR2, RDR)) ? 1 : 0); 103} 104 105static int 106imx_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) 107{ 108 int c; 109 110 uart_lock(hwmtx); 111 while (!(IS(bas, USR2, RDR))) 112 ; 113 114 c = GETREG(bas, REG(URXD)); 115 uart_unlock(hwmtx); 116#if defined(KDB) 117 if (c & FLD(URXD, BRK)) { 118 if (kdb_break()) 119 return (0); 120 } 121#endif 122 return (c & 0xff); 123} 124 125/* 126 * High-level UART interface. 127 */ 128struct imx_uart_softc { 129 struct uart_softc base; 130}; 131 132static int imx_uart_bus_attach(struct uart_softc *); 133static int imx_uart_bus_detach(struct uart_softc *); 134static int imx_uart_bus_flush(struct uart_softc *, int); 135static int imx_uart_bus_getsig(struct uart_softc *); 136static int imx_uart_bus_ioctl(struct uart_softc *, int, intptr_t); 137static int imx_uart_bus_ipend(struct uart_softc *); 138static int imx_uart_bus_param(struct uart_softc *, int, int, int, int); 139static int imx_uart_bus_probe(struct uart_softc *); 140static int imx_uart_bus_receive(struct uart_softc *); 141static int imx_uart_bus_setsig(struct uart_softc *, int); 142static int imx_uart_bus_transmit(struct uart_softc *); 143static void imx_uart_bus_grab(struct uart_softc *); 144static void imx_uart_bus_ungrab(struct uart_softc *); 145 146static kobj_method_t imx_uart_methods[] = { 147 KOBJMETHOD(uart_attach, imx_uart_bus_attach), 148 KOBJMETHOD(uart_detach, imx_uart_bus_detach), 149 KOBJMETHOD(uart_flush, imx_uart_bus_flush), 150 KOBJMETHOD(uart_getsig, imx_uart_bus_getsig), 151 KOBJMETHOD(uart_ioctl, imx_uart_bus_ioctl), 152 KOBJMETHOD(uart_ipend, imx_uart_bus_ipend), 153 KOBJMETHOD(uart_param, imx_uart_bus_param), 154 KOBJMETHOD(uart_probe, imx_uart_bus_probe), 155 KOBJMETHOD(uart_receive, imx_uart_bus_receive), 156 KOBJMETHOD(uart_setsig, imx_uart_bus_setsig), 157 KOBJMETHOD(uart_transmit, imx_uart_bus_transmit), 158 KOBJMETHOD(uart_grab, imx_uart_bus_grab), 159 KOBJMETHOD(uart_ungrab, imx_uart_bus_ungrab), 160 { 0, 0 } 161}; 162 163struct uart_class uart_imx_class = { 164 "imx", 165 imx_uart_methods, 166 sizeof(struct imx_uart_softc), 167 .uc_ops = &uart_imx_uart_ops, 168 .uc_range = 0x100, 169 .uc_rclk = 24000000 /* TODO: get value from CCM */ 170}; 171 172#define SIGCHG(c, i, s, d) \ 173 if (c) { \ 174 i |= (i & s) ? s : s | d; \ 175 } else { \ 176 i = (i & s) ? (i & ~s) | d : i; \ 177 } 178 179static int 180imx_uart_bus_attach(struct uart_softc *sc) 181{ 182 struct uart_bas *bas; 183 struct uart_devinfo *di; 184 185 bas = &sc->sc_bas; 186 if (sc->sc_sysdev != NULL) { 187 di = sc->sc_sysdev; 188 imx_uart_init(bas, di->baudrate, di->databits, di->stopbits, 189 di->parity); 190 } else { 191 imx_uart_init(bas, 115200, 8, 1, 0); 192 } 193 194 (void)imx_uart_bus_getsig(sc); 195 196 ENA(bas, UCR4, DREN); 197 DIS(bas, UCR1, RRDYEN); 198 DIS(bas, UCR1, IDEN); 199 DIS(bas, UCR3, RXDSEN); 200 DIS(bas, UCR2, ATEN); 201 DIS(bas, UCR1, TXMPTYEN); 202 DIS(bas, UCR1, TRDYEN); 203 DIS(bas, UCR4, TCEN); 204 DIS(bas, UCR4, OREN); 205 ENA(bas, UCR4, BKEN); 206 DIS(bas, UCR4, WKEN); 207 DIS(bas, UCR1, ADEN); 208 DIS(bas, UCR3, ACIEN); 209 DIS(bas, UCR2, ESCI); 210 DIS(bas, UCR4, ENIRI); 211 DIS(bas, UCR3, AIRINTEN); 212 DIS(bas, UCR3, AWAKEN); 213 DIS(bas, UCR3, FRAERREN); 214 DIS(bas, UCR3, PARERREN); 215 DIS(bas, UCR1, RTSDEN); 216 DIS(bas, UCR2, RTSEN); 217 DIS(bas, UCR3, DTREN); 218 DIS(bas, UCR3, RI); 219 DIS(bas, UCR3, DCD); 220 DIS(bas, UCR3, DTRDEN); 221 222 /* ACK all interrupts */ 223 SETREG(bas, REG(USR1), 0xffff); 224 SETREG(bas, REG(USR2), 0xffff); 225 return (0); 226} 227 228static int 229imx_uart_bus_detach(struct uart_softc *sc) 230{ 231 232 SETREG(&sc->sc_bas, REG(UCR4), 0); 233 234 return (0); 235} 236 237static int 238imx_uart_bus_flush(struct uart_softc *sc, int what) 239{ 240 241 /* TODO */ 242 return (0); 243} 244 245static int 246imx_uart_bus_getsig(struct uart_softc *sc) 247{ 248 uint32_t new, old, sig; 249 uint8_t bes; 250 251 do { 252 old = sc->sc_hwsig; 253 sig = old; 254 uart_lock(sc->sc_hwmtx); 255 bes = GETREG(&sc->sc_bas, REG(USR2)); 256 uart_unlock(sc->sc_hwmtx); 257 /* XXX: chip can show delta */ 258 SIGCHG(bes & FLD(USR2, DCDIN), sig, SER_DCD, SER_DDCD); 259 new = sig & ~SER_MASK_DELTA; 260 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 261 262 return (sig); 263} 264 265static int 266imx_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 267{ 268 struct uart_bas *bas; 269 int error; 270 271 bas = &sc->sc_bas; 272 error = 0; 273 uart_lock(sc->sc_hwmtx); 274 switch (request) { 275 case UART_IOCTL_BREAK: 276 /* TODO */ 277 break; 278 case UART_IOCTL_BAUD: 279 /* TODO */ 280 *(int*)data = 115200; 281 break; 282 default: 283 error = EINVAL; 284 break; 285 } 286 uart_unlock(sc->sc_hwmtx); 287 288 return (error); 289} 290 291static int 292imx_uart_bus_ipend(struct uart_softc *sc) 293{ 294 struct uart_bas *bas; 295 int ipend; 296 uint32_t usr1, usr2; 297 uint32_t ucr1, ucr4; 298 299 bas = &sc->sc_bas; 300 ipend = 0; 301 302 uart_lock(sc->sc_hwmtx); 303 304 /* Read pending interrupts */ 305 usr1 = GETREG(bas, REG(USR1)); 306 usr2 = GETREG(bas, REG(USR2)); 307 /* ACK interrupts */ 308 SETREG(bas, REG(USR1), usr1); 309 SETREG(bas, REG(USR2), usr2); 310 311 ucr1 = GETREG(bas, REG(UCR1)); 312 ucr4 = GETREG(bas, REG(UCR4)); 313 314 if ((usr2 & FLD(USR2, TXFE)) && (ucr1 & FLD(UCR1, TXMPTYEN))) { 315 DIS(bas, UCR1, TXMPTYEN); 316 /* Continue TXing */ 317 ipend |= SER_INT_TXIDLE; 318 } 319 if ((usr2 & FLD(USR2, RDR)) && (ucr4 & FLD(UCR4, DREN))) { 320 DIS(bas, UCR4, DREN); 321 /* Wow, new char on input */ 322 ipend |= SER_INT_RXREADY; 323 } 324 if ((usr2 & FLD(USR2, BRCD)) && (ucr4 & FLD(UCR4, BKEN))) 325 ipend |= SER_INT_BREAK; 326 327 uart_unlock(sc->sc_hwmtx); 328 329 return (ipend); 330} 331 332static int 333imx_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, 334 int stopbits, int parity) 335{ 336 337 uart_lock(sc->sc_hwmtx); 338 imx_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity); 339 uart_unlock(sc->sc_hwmtx); 340 return (0); 341} 342 343static int 344imx_uart_bus_probe(struct uart_softc *sc) 345{ 346 int error; 347 348 error = imx_uart_probe(&sc->sc_bas); 349 if (error) 350 return (error); 351 352 sc->sc_rxfifosz = 1; 353 sc->sc_txfifosz = 1; 354 355 device_set_desc(sc->sc_dev, "imx_uart"); 356 return (0); 357} 358 359static int 360imx_uart_bus_receive(struct uart_softc *sc) 361{ 362 struct uart_bas *bas; 363 int xc, out; 364 365 bas = &sc->sc_bas; 366 uart_lock(sc->sc_hwmtx); 367 368 /* Read while we have anything in FIFO */ 369 while (IS(bas, USR2, RDR)) { 370 if (uart_rx_full(sc)) { 371 /* No space left in input buffer */ 372 sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 373 break; 374 } 375 out = 0; 376 xc = GETREG(bas, REG(URXD)); 377 378 /* We have valid char */ 379 if (xc & FLD(URXD, CHARRDY)) 380 out = xc & 0x000000ff; 381 382 if (xc & FLD(URXD, FRMERR)) 383 out |= UART_STAT_FRAMERR; 384 if (xc & FLD(URXD, PRERR)) 385 out |= UART_STAT_PARERR; 386 if (xc & FLD(URXD, OVRRUN)) 387 out |= UART_STAT_OVERRUN; 388 if (xc & FLD(URXD, BRK)) 389 out |= UART_STAT_BREAK; 390 391 uart_rx_put(sc, out); 392 } 393 /* Reenable Data Ready interrupt */ 394 ENA(bas, UCR4, DREN); 395 396 uart_unlock(sc->sc_hwmtx); 397 return (0); 398} 399 400static int 401imx_uart_bus_setsig(struct uart_softc *sc, int sig) 402{ 403 404 return (0); 405} 406 407static int 408imx_uart_bus_transmit(struct uart_softc *sc) 409{ 410 struct uart_bas *bas = &sc->sc_bas; 411 int i; 412 413 bas = &sc->sc_bas; 414 uart_lock(sc->sc_hwmtx); 415 416 /* Fill TX FIFO */ 417 for (i = 0; i < sc->sc_txdatasz; i++) { 418 SETREG(bas, REG(UTXD), sc->sc_txbuf[i] & 0xff); 419 } 420 421 sc->sc_txbusy = 1; 422 /* Call me when ready */ 423 ENA(bas, UCR1, TXMPTYEN); 424 425 uart_unlock(sc->sc_hwmtx); 426 427 return (0); 428} 429 430static void 431imx_uart_bus_grab(struct uart_softc *sc) 432{ 433 struct uart_bas *bas = &sc->sc_bas; 434 435 bas = &sc->sc_bas; 436 uart_lock(sc->sc_hwmtx); 437 DIS(bas, UCR4, DREN); 438 uart_unlock(sc->sc_hwmtx); 439} 440 441static void 442imx_uart_bus_ungrab(struct uart_softc *sc) 443{ 444 struct uart_bas *bas = &sc->sc_bas; 445 446 bas = &sc->sc_bas; 447 uart_lock(sc->sc_hwmtx); 448 ENA(bas, UCR4, DREN); 449 uart_unlock(sc->sc_hwmtx); 450} 451