1 /* $NetBSD: meson_uart.c,v 1.5 2021/01/27 03:10:18 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matt Thomas of 3am Software Foundry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "opt_console.h" 33 #include "locators.h" 34 35 #include <sys/cdefs.h> 36 37 __KERNEL_RCSID(1, "$NetBSD: meson_uart.c,v 1.5 2021/01/27 03:10:18 thorpej Exp $"); 38 39 #define cn_trap() \ 40 do { \ 41 console_debugger(); \ 42 cn_trapped = 1; \ 43 } while (/* CONSTCOND */ 0) 44 45 #include <sys/param.h> 46 #include <sys/bus.h> 47 #include <sys/device.h> 48 #include <sys/conf.h> 49 #include <sys/intr.h> 50 #include <sys/systm.h> 51 #include <sys/time.h> 52 #include <sys/termios.h> 53 #include <sys/kauth.h> 54 #include <sys/lwp.h> 55 #include <sys/tty.h> 56 57 #include <dev/cons.h> 58 59 #include <dev/fdt/fdtvar.h> 60 61 #include <arm/amlogic/meson_uart.h> 62 63 static int meson_uart_match(device_t, cfdata_t, void *); 64 static void meson_uart_attach(device_t, device_t, void *); 65 66 static int meson_uart_intr(void *); 67 static void meson_uart_rxsoft(void *); 68 69 static int meson_uart_cngetc(dev_t); 70 static void meson_uart_cnputc(dev_t, int); 71 static void meson_uart_cnpollc(dev_t, int); 72 73 static void meson_uart_start(struct tty *); 74 static int meson_uart_param(struct tty *, struct termios *); 75 76 extern struct cfdriver mesonuart_cd; 77 78 static const struct device_compatible_entry compat_data[] = { 79 { .compat = "amlogic,meson6-uart" }, 80 { .compat = "amlogic,meson8-uart" }, 81 { .compat = "amlogic,meson8b-uart" }, 82 { .compat = "amlogic,meson-gx-uart" }, 83 DEVICE_COMPAT_EOL 84 }; 85 86 struct meson_uart_softc { 87 device_t sc_dev; 88 bus_space_tag_t sc_bst; 89 bus_space_handle_t sc_bsh; 90 kmutex_t sc_intr_lock; 91 void *sc_ih; 92 void *sc_sih; 93 94 struct tty *sc_tty; 95 96 int sc_ospeed; 97 unsigned int sc_rbuf_w; /* write ptr of sc_rbuf[] */ 98 unsigned int sc_rbuf_r; /* read ptr of sc_rbuf[] */ 99 tcflag_t sc_cflag; 100 101 u_char sc_buf[1024]; 102 #define MESON_RBUFSZ 128 /* must be 2^n */ 103 u_char sc_rbuf[MESON_RBUFSZ]; /* good enough for sizeof RXFIFO */ 104 }; 105 106 static int meson_uart_console_phandle = -1; 107 108 static struct meson_uart_softc meson_uart_cnsc; 109 110 static struct cnm_state meson_uart_cnm_state; 111 112 struct consdev meson_uart_consdev = { 113 .cn_getc = meson_uart_cngetc, 114 .cn_putc = meson_uart_cnputc, 115 .cn_pollc = meson_uart_cnpollc, 116 .cn_dev = NODEV, 117 .cn_pri = CN_NORMAL, 118 }; 119 120 static dev_type_open(meson_uart_open); 121 static dev_type_open(meson_uart_close); 122 static dev_type_read(meson_uart_read); 123 static dev_type_write(meson_uart_write); 124 static dev_type_ioctl(meson_uart_ioctl); 125 static dev_type_tty(meson_uart_tty); 126 static dev_type_poll(meson_uart_poll); 127 static dev_type_stop(meson_uart_stop); 128 129 const struct cdevsw mesonuart_cdevsw = { 130 .d_open = meson_uart_open, 131 .d_close = meson_uart_close, 132 .d_read = meson_uart_read, 133 .d_write = meson_uart_write, 134 .d_ioctl = meson_uart_ioctl, 135 .d_stop = meson_uart_stop, 136 .d_tty = meson_uart_tty, 137 .d_poll = meson_uart_poll, 138 .d_mmap = nommap, 139 .d_kqfilter = ttykqfilter, 140 .d_discard = nodiscard, 141 .d_flag = D_TTY 142 }; 143 144 static int meson_uart_cmajor = -1; 145 146 CFATTACH_DECL_NEW(meson_uart, sizeof(struct meson_uart_softc), 147 meson_uart_match, meson_uart_attach, NULL, NULL); 148 149 static int 150 meson_uart_match(device_t parent, cfdata_t cf, void *aux) 151 { 152 struct fdt_attach_args * const faa = aux; 153 154 return of_compatible_match(faa->faa_phandle, compat_data); 155 } 156 157 static void 158 meson_uart_attach(device_t parent, device_t self, void *aux) 159 { 160 struct meson_uart_softc * const sc = device_private(self); 161 struct fdt_attach_args * const faa = aux; 162 const int phandle = faa->faa_phandle; 163 char intrstr[128]; 164 bus_addr_t addr; 165 bus_size_t size; 166 struct tty *tp; 167 int major, minor, error; 168 uint32_t misc, control; 169 170 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 171 aprint_error(": couldn't get registers\n"); 172 return; 173 } 174 175 sc->sc_dev = self; 176 sc->sc_bst = faa->faa_bst; 177 178 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 179 if (error != 0) { 180 aprint_error(": couldn't map registers\n"); 181 return; 182 } 183 184 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 185 aprint_error(": failed to decode interrupt\n"); 186 return; 187 } 188 189 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SERIAL); 190 sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SERIAL, 191 FDT_INTR_MPSAFE, meson_uart_intr, sc, device_xname(self)); 192 if (sc->sc_ih == NULL) { 193 aprint_error(": failed to establish interrupt on %s\n", 194 intrstr); 195 return; 196 } 197 198 sc->sc_sih = softint_establish(SOFTINT_SERIAL, meson_uart_rxsoft, sc); 199 if (sc->sc_sih == NULL) { 200 aprint_error(": failed to establish softint\n"); 201 return; 202 } 203 204 if (meson_uart_cmajor == -1) { 205 /* allocate a major number */ 206 int bmajor = -1, cmajor = -1; 207 error = devsw_attach("mesonuart", NULL, &bmajor, 208 &mesonuart_cdevsw, &cmajor); 209 if (error) { 210 aprint_error(": couldn't allocate major number\n"); 211 return; 212 } 213 meson_uart_cmajor = cmajor; 214 } 215 216 major = cdevsw_lookup_major(&mesonuart_cdevsw); 217 minor = device_unit(self); 218 219 tp = sc->sc_tty = tty_alloc(); 220 tp->t_oproc = meson_uart_start; 221 tp->t_param = meson_uart_param; 222 tp->t_dev = makedev(major, minor); 223 tp->t_sc = sc; 224 tty_attach(tp); 225 226 aprint_naive("\n"); 227 if (meson_uart_console_phandle == phandle) { 228 cn_tab->cn_dev = tp->t_dev; 229 aprint_normal(": console"); 230 } 231 aprint_normal("\n"); 232 233 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 234 235 misc = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG); 236 misc &= ~UART_MISC_TX_IRQ_CNT; 237 misc |= __SHIFTIN(0, UART_MISC_TX_IRQ_CNT); 238 misc &= ~UART_MISC_RX_IRQ_CNT; 239 misc |= __SHIFTIN(1, UART_MISC_RX_IRQ_CNT); 240 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG, misc); 241 242 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG); 243 control &= ~(UART_CONTROL_TX_INT_EN|UART_CONTROL_RX_INT_EN); 244 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control); 245 } 246 247 static int 248 meson_uart_cngetc(dev_t dev) 249 { 250 bus_space_tag_t bst = meson_uart_cnsc.sc_bst; 251 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh; 252 uint32_t status; 253 int s, c; 254 255 s = splserial(); 256 257 status = bus_space_read_4(bst, bsh, UART_STATUS_REG); 258 if (status & UART_STATUS_RX_EMPTY) { 259 splx(s); 260 return -1; 261 } 262 263 c = bus_space_read_4(bst, bsh, UART_RFIFO_REG); 264 #if defined(DDB) 265 extern int db_active; 266 if (!db_active) 267 #endif 268 { 269 int cn_trapped __unused = 0; 270 cn_check_magic(dev, c, meson_uart_cnm_state); 271 } 272 273 splx(s); 274 275 return c & 0xff; 276 } 277 278 static void 279 meson_uart_cnputc(dev_t dev, int c) 280 { 281 bus_space_tag_t bst = meson_uart_cnsc.sc_bst; 282 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh; 283 int s; 284 285 s = splserial(); 286 287 while ((bus_space_read_4(bst, bsh, UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0) 288 ; 289 290 bus_space_write_4(bst, bsh, UART_WFIFO_REG, c); 291 292 splx(s); 293 } 294 295 296 static void 297 meson_uart_cnpollc(dev_t dev, int on) 298 { 299 } 300 301 static int 302 meson_uart_open(dev_t dev, int flag, int mode, lwp_t *l) 303 { 304 struct meson_uart_softc *sc = 305 device_lookup_private(&mesonuart_cd, minor(dev)); 306 struct tty *tp = sc->sc_tty; 307 uint32_t control; 308 309 if (kauth_authorize_device_tty(l->l_cred, 310 KAUTH_DEVICE_TTY_OPEN, tp) != 0) { 311 return EBUSY; 312 } 313 314 if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) { 315 tp->t_dev = dev; 316 ttychars(tp); 317 tp->t_iflag = TTYDEF_IFLAG; 318 tp->t_oflag = TTYDEF_OFLAG; 319 tp->t_cflag = TTYDEF_CFLAG; 320 tp->t_lflag = TTYDEF_LFLAG; 321 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 322 ttsetwater(tp); 323 324 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG); 325 control |= UART_CONTROL_RX_INT_EN; 326 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control); 327 } 328 tp->t_state |= TS_CARR_ON; 329 330 return tp->t_linesw->l_open(dev, tp); 331 } 332 333 static int 334 meson_uart_close(dev_t dev, int flag, int mode, lwp_t *l) 335 { 336 struct meson_uart_softc *sc = 337 device_lookup_private(&mesonuart_cd, minor(dev)); 338 struct tty *tp = sc->sc_tty; 339 uint32_t control; 340 341 tp->t_linesw->l_close(tp, flag); 342 ttyclose(tp); 343 344 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 345 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG); 346 control &= ~UART_CONTROL_RX_INT_EN; 347 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control); 348 } 349 350 return 0; 351 } 352 353 static int 354 meson_uart_read(dev_t dev, struct uio *uio, int flag) 355 { 356 struct meson_uart_softc *sc = 357 device_lookup_private(&mesonuart_cd, minor(dev)); 358 struct tty *tp = sc->sc_tty; 359 360 return tp->t_linesw->l_read(tp, uio, flag); 361 } 362 363 static int 364 meson_uart_write(dev_t dev, struct uio *uio, int flag) 365 { 366 struct meson_uart_softc *sc = 367 device_lookup_private(&mesonuart_cd, minor(dev)); 368 struct tty *tp = sc->sc_tty; 369 370 return tp->t_linesw->l_write(tp, uio, flag); 371 } 372 373 static int 374 meson_uart_poll(dev_t dev, int events, lwp_t *l) 375 { 376 struct meson_uart_softc *sc = 377 device_lookup_private(&mesonuart_cd, minor(dev)); 378 struct tty *tp = sc->sc_tty; 379 380 return tp->t_linesw->l_poll(tp, events, l); 381 } 382 383 static int 384 meson_uart_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 385 { 386 struct meson_uart_softc *sc = 387 device_lookup_private(&mesonuart_cd, minor(dev)); 388 struct tty *tp = sc->sc_tty; 389 int error; 390 391 error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l); 392 if (error != EPASSTHROUGH) 393 return error; 394 395 return ttioctl(tp, cmd, data, flag, l); 396 } 397 398 static struct tty * 399 meson_uart_tty(dev_t dev) 400 { 401 struct meson_uart_softc *sc = 402 device_lookup_private(&mesonuart_cd, minor(dev)); 403 404 return sc->sc_tty; 405 } 406 407 static void 408 meson_uart_stop(struct tty *tp, int flag) 409 { 410 } 411 412 static void 413 meson_uart_start(struct tty *tp) 414 { 415 struct meson_uart_softc *sc = tp->t_sc; 416 u_char *p = sc->sc_buf; 417 int s, brem; 418 419 s = spltty(); 420 421 if (tp->t_state & (TS_TTSTOP | TS_BUSY | TS_TIMEOUT)) { 422 splx(s); 423 return; 424 } 425 tp->t_state |= TS_BUSY; 426 427 splx(s); 428 429 for (brem = q_to_b(&tp->t_outq, sc->sc_buf, sizeof(sc->sc_buf)); 430 brem > 0; 431 brem--, p++) { 432 while ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, 433 UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0) 434 ; 435 436 bus_space_write_4(sc->sc_bst, sc->sc_bsh, 437 UART_WFIFO_REG, *p); 438 } 439 440 s = spltty(); 441 tp->t_state &= ~TS_BUSY; 442 if (ttypull(tp)) { 443 tp->t_state |= TS_TIMEOUT; 444 callout_schedule(&tp->t_rstrt_ch, 1); 445 } 446 splx(s); 447 } 448 449 static int 450 meson_uart_param(struct tty *tp, struct termios *t) 451 { 452 453 tp->t_ispeed = t->c_ispeed; 454 tp->t_ospeed = t->c_ospeed; 455 tp->t_cflag = t->c_cflag; 456 457 return 0; 458 } 459 460 static int 461 meson_uart_intr(void *priv) 462 { 463 struct meson_uart_softc *sc = priv; 464 struct tty *tp = sc->sc_tty; 465 uint32_t status, c; 466 467 mutex_spin_enter(&sc->sc_intr_lock); 468 for (;;) { 469 int cn_trapped = 0; 470 status = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 471 UART_STATUS_REG); 472 if (status & UART_STATUS_RX_EMPTY) { 473 break; 474 } 475 if (status & UART_STATUS_BREAK) { 476 cn_check_magic(tp->t_dev, CNC_BREAK, 477 meson_uart_cnm_state); 478 if (cn_trapped) 479 continue; 480 } 481 482 c = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_RFIFO_REG); 483 cn_check_magic(tp->t_dev, c & 0xff, meson_uart_cnm_state); 484 if (cn_trapped) 485 continue; 486 487 if ((sc->sc_rbuf_w - sc->sc_rbuf_r) >= (MESON_RBUFSZ - 1)) 488 continue; 489 sc->sc_rbuf[sc->sc_rbuf_w++ & (MESON_RBUFSZ - 1)] = c; 490 } 491 mutex_spin_exit(&sc->sc_intr_lock); 492 493 if (sc->sc_rbuf_w != sc->sc_rbuf_r) 494 softint_schedule(sc->sc_sih); 495 496 return 0; 497 } 498 499 static void 500 meson_uart_rxsoft(void *priv) 501 { 502 struct meson_uart_softc *sc = priv; 503 struct tty *tp = sc->sc_tty; 504 int c; 505 506 while (sc->sc_rbuf_w != sc->sc_rbuf_r) { 507 c = sc->sc_rbuf[sc->sc_rbuf_r++ & (MESON_RBUFSZ - 1)]; 508 if (tp->t_linesw->l_rint(c & 0xff, tp) == -1) 509 break; 510 } 511 } 512 513 static int 514 meson_uart_console_match(int phandle) 515 { 516 return of_compatible_match(phandle, compat_data); 517 } 518 519 static void 520 meson_uart_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) 521 { 522 struct meson_uart_softc *sc = &meson_uart_cnsc; 523 const int phandle = faa->faa_phandle; 524 bus_addr_t addr; 525 bus_size_t size; 526 int error; 527 528 fdtbus_get_reg(phandle, 0, &addr, &size); 529 530 sc->sc_bst = faa->faa_bst; 531 sc->sc_ospeed = fdtbus_get_stdout_speed(); 532 if (sc->sc_ospeed < 0) 533 sc->sc_ospeed = 115200; 534 sc->sc_cflag = fdtbus_get_stdout_flags(); 535 536 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 537 if (error != 0) 538 panic("failed to map console, error = %d", error); 539 540 cn_tab = &meson_uart_consdev; 541 cn_init_magic(&meson_uart_cnm_state); 542 cn_set_magic("\047\001"); 543 544 meson_uart_console_phandle = phandle; 545 } 546 547 static const struct fdt_console meson_uart_console = { 548 .match = meson_uart_console_match, 549 .consinit = meson_uart_console_consinit, 550 }; 551 552 FDT_CONSOLE(meson_uart, &meson_uart_console); 553