uart_dev_imx.c revision 248557
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: head/sys/dev/uart/uart_dev_imx.c 248557 2013-03-20 15:39:27Z ray $"); 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 *); 143 144static kobj_method_t imx_uart_methods[] = { 145 KOBJMETHOD(uart_attach, imx_uart_bus_attach), 146 KOBJMETHOD(uart_detach, imx_uart_bus_detach), 147 KOBJMETHOD(uart_flush, imx_uart_bus_flush), 148 KOBJMETHOD(uart_getsig, imx_uart_bus_getsig), 149 KOBJMETHOD(uart_ioctl, imx_uart_bus_ioctl), 150 KOBJMETHOD(uart_ipend, imx_uart_bus_ipend), 151 KOBJMETHOD(uart_param, imx_uart_bus_param), 152 KOBJMETHOD(uart_probe, imx_uart_bus_probe), 153 KOBJMETHOD(uart_receive, imx_uart_bus_receive), 154 KOBJMETHOD(uart_setsig, imx_uart_bus_setsig), 155 KOBJMETHOD(uart_transmit, imx_uart_bus_transmit), 156 { 0, 0 } 157}; 158 159struct uart_class uart_imx_class = { 160 "imx", 161 imx_uart_methods, 162 sizeof(struct imx_uart_softc), 163 .uc_ops = &uart_imx_uart_ops, 164 .uc_range = 0x100, 165 .uc_rclk = 24000000 /* TODO: get value from CCM */ 166}; 167 168#define SIGCHG(c, i, s, d) \ 169 if (c) { \ 170 i |= (i & s) ? s : s | d; \ 171 } else { \ 172 i = (i & s) ? (i & ~s) | d : i; \ 173 } 174 175static int 176imx_uart_bus_attach(struct uart_softc *sc) 177{ 178 struct uart_bas *bas; 179 struct uart_devinfo *di; 180 181 bas = &sc->sc_bas; 182 if (sc->sc_sysdev != NULL) { 183 di = sc->sc_sysdev; 184 imx_uart_init(bas, di->baudrate, di->databits, di->stopbits, 185 di->parity); 186 } else { 187 imx_uart_init(bas, 115200, 8, 1, 0); 188 } 189 190 sc->sc_rxfifosz = 1; 191 sc->sc_txfifosz = 1; 192 193 (void)imx_uart_bus_getsig(sc); 194 195 /* XXX workaround to have working console on manut prompt */ 196 if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE){ 197 DIS(bas, UCR4, DREN); 198 } else { 199 ENA(bas, UCR4, DREN); 200 } 201 DIS(bas, UCR1, RRDYEN); 202 DIS(bas, UCR1, IDEN); 203 DIS(bas, UCR3, RXDSEN); 204 DIS(bas, UCR2, ATEN); 205 DIS(bas, UCR1, TXMPTYEN); 206 DIS(bas, UCR1, TRDYEN); 207 DIS(bas, UCR4, TCEN); 208 DIS(bas, UCR4, OREN); 209 ENA(bas, UCR4, BKEN); 210 DIS(bas, UCR4, WKEN); 211 DIS(bas, UCR1, ADEN); 212 DIS(bas, UCR3, ACIEN); 213 DIS(bas, UCR2, ESCI); 214 DIS(bas, UCR4, ENIRI); 215 DIS(bas, UCR3, AIRINTEN); 216 DIS(bas, UCR3, AWAKEN); 217 DIS(bas, UCR3, FRAERREN); 218 DIS(bas, UCR3, PARERREN); 219 DIS(bas, UCR1, RTSDEN); 220 DIS(bas, UCR2, RTSEN); 221 DIS(bas, UCR3, DTREN); 222 DIS(bas, UCR3, RI); 223 DIS(bas, UCR3, DCD); 224 DIS(bas, UCR3, DTRDEN); 225 226 /* ACK all interrupts */ 227 SETREG(bas, REG(USR1), 0xffff); 228 SETREG(bas, REG(USR2), 0xffff); 229 return (0); 230} 231 232static int 233imx_uart_bus_detach(struct uart_softc *sc) 234{ 235 236 SETREG(&sc->sc_bas, REG(UCR4), 0); 237 238 return (0); 239} 240 241static int 242imx_uart_bus_flush(struct uart_softc *sc, int what) 243{ 244 245 /* TODO */ 246 return (0); 247} 248 249static int 250imx_uart_bus_getsig(struct uart_softc *sc) 251{ 252 uint32_t new, old, sig; 253 uint8_t bes; 254 255 do { 256 old = sc->sc_hwsig; 257 sig = old; 258 uart_lock(sc->sc_hwmtx); 259 bes = GETREG(&sc->sc_bas, REG(USR2)); 260 uart_unlock(sc->sc_hwmtx); 261 /* XXX: chip can show delta */ 262 SIGCHG(bes & FLD(USR2, DCDIN), sig, SER_DCD, SER_DDCD); 263 new = sig & ~SER_MASK_DELTA; 264 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 265 266 return (sig); 267} 268 269static int 270imx_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 271{ 272 struct uart_bas *bas; 273 int error; 274 275 bas = &sc->sc_bas; 276 error = 0; 277 uart_lock(sc->sc_hwmtx); 278 switch (request) { 279 case UART_IOCTL_BREAK: 280 /* TODO */ 281 break; 282 case UART_IOCTL_BAUD: 283 /* TODO */ 284 *(int*)data = 115200; 285 break; 286 default: 287 error = EINVAL; 288 break; 289 } 290 uart_unlock(sc->sc_hwmtx); 291 292 return (error); 293} 294 295static int 296imx_uart_bus_ipend(struct uart_softc *sc) 297{ 298 struct uart_bas *bas; 299 int ipend; 300 uint32_t usr1, usr2; 301 uint32_t ucr1, ucr4; 302 303 bas = &sc->sc_bas; 304 ipend = 0; 305 306 uart_lock(sc->sc_hwmtx); 307 308 /* Read pending interrupts */ 309 usr1 = GETREG(bas, REG(USR1)); 310 usr2 = GETREG(bas, REG(USR2)); 311 /* ACK interrupts */ 312 SETREG(bas, REG(USR1), usr1); 313 SETREG(bas, REG(USR2), usr2); 314 315 ucr1 = GETREG(bas, REG(UCR1)); 316 ucr4 = GETREG(bas, REG(UCR4)); 317 318 if ((usr2 & FLD(USR2, TXFE)) && (ucr1 & FLD(UCR1, TXMPTYEN))) { 319 DIS(bas, UCR1, TXMPTYEN); 320 /* Continue TXing */ 321 ipend |= SER_INT_TXIDLE; 322 } 323 if ((usr2 & FLD(USR2, RDR)) && (ucr4 & FLD(UCR4, DREN))) { 324 DIS(bas, UCR4, DREN); 325 /* Wow, new char on input */ 326 ipend |= SER_INT_RXREADY; 327 } 328 if ((usr2 & FLD(USR2, BRCD)) && (ucr4 & FLD(UCR4, BKEN))) 329 ipend |= SER_INT_BREAK; 330 331 uart_unlock(sc->sc_hwmtx); 332 333 return (ipend); 334} 335 336static int 337imx_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, 338 int stopbits, int parity) 339{ 340 341 uart_lock(sc->sc_hwmtx); 342 imx_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity); 343 uart_unlock(sc->sc_hwmtx); 344 return (0); 345} 346 347static int 348imx_uart_bus_probe(struct uart_softc *sc) 349{ 350 int error; 351 352 error = imx_uart_probe(&sc->sc_bas); 353 if (error) 354 return (error); 355 356 device_set_desc(sc->sc_dev, "imx_uart"); 357 return (0); 358} 359 360static int 361imx_uart_bus_receive(struct uart_softc *sc) 362{ 363 struct uart_bas *bas; 364 int xc, out; 365 366 bas = &sc->sc_bas; 367 uart_lock(sc->sc_hwmtx); 368 369 /* Read while we have anything in FIFO */ 370 while (IS(bas, USR2, RDR)) { 371 if (uart_rx_full(sc)) { 372 /* No space left in input buffer */ 373 sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 374 break; 375 } 376 out = 0; 377 xc = GETREG(bas, REG(URXD)); 378 379 /* We have valid char */ 380 if (xc & FLD(URXD, CHARRDY)) 381 out = xc & 0x000000ff; 382 383 if (xc & FLD(URXD, FRMERR)) 384 out |= UART_STAT_FRAMERR; 385 if (xc & FLD(URXD, PRERR)) 386 out |= UART_STAT_PARERR; 387 if (xc & FLD(URXD, OVRRUN)) 388 out |= UART_STAT_OVERRUN; 389 if (xc & FLD(URXD, BRK)) 390 out |= UART_STAT_BREAK; 391 392 uart_rx_put(sc, out); 393 } 394 /* Reenable Data Ready interrupt */ 395 ENA(bas, UCR4, DREN); 396 397 uart_unlock(sc->sc_hwmtx); 398 return (0); 399} 400 401static int 402imx_uart_bus_setsig(struct uart_softc *sc, int sig) 403{ 404 405 /* TODO: implement (?) */ 406 407 /* XXX workaround to have working console on mount prompt */ 408 /* Enable RX interrupt */ 409 if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) 410 if (!IS(&sc->sc_bas, UCR4, DREN)) 411 ENA(&sc->sc_bas, UCR4, DREN); 412 return (0); 413} 414 415static int 416imx_uart_bus_transmit(struct uart_softc *sc) 417{ 418 struct uart_bas *bas = &sc->sc_bas; 419 int i; 420 421 bas = &sc->sc_bas; 422 uart_lock(sc->sc_hwmtx); 423 424 /* Fill TX FIFO */ 425 for (i = 0; i < sc->sc_txdatasz; i++) { 426 SETREG(bas, REG(UTXD), sc->sc_txbuf[i] & 0xff); 427 } 428 429 sc->sc_txbusy = 1; 430 /* Call me when ready */ 431 ENA(bas, UCR1, TXMPTYEN); 432 433 uart_unlock(sc->sc_hwmtx); 434 435 return (0); 436} 437