1 /* $NetBSD: meson_uart.c,v 1.7 2022/10/26 23:38:06 riastradh 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.7 2022/10/26 23:38:06 riastradh 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 <ddb/db_active.h> 58 59 #include <dev/cons.h> 60 61 #include <dev/fdt/fdtvar.h> 62 63 #include <arm/amlogic/meson_uart.h> 64 65 static int meson_uart_match(device_t, cfdata_t, void *); 66 static void meson_uart_attach(device_t, device_t, void *); 67 68 static int meson_uart_intr(void *); 69 static void meson_uart_rxsoft(void *); 70 71 static int meson_uart_cngetc(dev_t); 72 static void meson_uart_cnputc(dev_t, int); 73 static void meson_uart_cnpollc(dev_t, int); 74 75 static void meson_uart_start(struct tty *); 76 static int meson_uart_param(struct tty *, struct termios *); 77 78 extern struct cfdriver mesonuart_cd; 79 80 static const struct device_compatible_entry compat_data[] = { 81 { .compat = "amlogic,meson6-uart" }, 82 { .compat = "amlogic,meson8-uart" }, 83 { .compat = "amlogic,meson8b-uart" }, 84 { .compat = "amlogic,meson-gx-uart" }, 85 DEVICE_COMPAT_EOL 86 }; 87 88 struct meson_uart_softc { 89 device_t sc_dev; 90 bus_space_tag_t sc_bst; 91 bus_space_handle_t sc_bsh; 92 kmutex_t sc_intr_lock; 93 void *sc_ih; 94 void *sc_sih; 95 96 struct tty *sc_tty; 97 98 int sc_ospeed; 99 unsigned int sc_rbuf_w; /* write ptr of sc_rbuf[] */ 100 unsigned int sc_rbuf_r; /* read ptr of sc_rbuf[] */ 101 tcflag_t sc_cflag; 102 103 u_char sc_buf[1024]; 104 #define MESON_RBUFSZ 128 /* must be 2^n */ 105 u_char sc_rbuf[MESON_RBUFSZ]; /* good enough for sizeof RXFIFO */ 106 }; 107 108 static int meson_uart_console_phandle = -1; 109 110 static struct meson_uart_softc meson_uart_cnsc; 111 112 static struct cnm_state meson_uart_cnm_state; 113 114 struct consdev meson_uart_consdev = { 115 .cn_getc = meson_uart_cngetc, 116 .cn_putc = meson_uart_cnputc, 117 .cn_pollc = meson_uart_cnpollc, 118 .cn_dev = NODEV, 119 .cn_pri = CN_NORMAL, 120 }; 121 122 static dev_type_open(meson_uart_open); 123 static dev_type_open(meson_uart_close); 124 static dev_type_read(meson_uart_read); 125 static dev_type_write(meson_uart_write); 126 static dev_type_ioctl(meson_uart_ioctl); 127 static dev_type_tty(meson_uart_tty); 128 static dev_type_poll(meson_uart_poll); 129 static dev_type_stop(meson_uart_stop); 130 131 const struct cdevsw mesonuart_cdevsw = { 132 .d_open = meson_uart_open, 133 .d_close = meson_uart_close, 134 .d_read = meson_uart_read, 135 .d_write = meson_uart_write, 136 .d_ioctl = meson_uart_ioctl, 137 .d_stop = meson_uart_stop, 138 .d_tty = meson_uart_tty, 139 .d_poll = meson_uart_poll, 140 .d_mmap = nommap, 141 .d_kqfilter = ttykqfilter, 142 .d_discard = nodiscard, 143 .d_flag = D_TTY 144 }; 145 146 static int meson_uart_cmajor = -1; 147 148 CFATTACH_DECL_NEW(meson_uart, sizeof(struct meson_uart_softc), 149 meson_uart_match, meson_uart_attach, NULL, NULL); 150 151 static int 152 meson_uart_match(device_t parent, cfdata_t cf, void *aux) 153 { 154 struct fdt_attach_args * const faa = aux; 155 156 return of_compatible_match(faa->faa_phandle, compat_data); 157 } 158 159 static void 160 meson_uart_attach(device_t parent, device_t self, void *aux) 161 { 162 struct meson_uart_softc * const sc = device_private(self); 163 struct fdt_attach_args * const faa = aux; 164 const int phandle = faa->faa_phandle; 165 char intrstr[128]; 166 bus_addr_t addr; 167 bus_size_t size; 168 struct tty *tp; 169 int major, minor, error; 170 uint32_t misc, control; 171 172 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 173 aprint_error(": couldn't get registers\n"); 174 return; 175 } 176 177 sc->sc_dev = self; 178 sc->sc_bst = faa->faa_bst; 179 180 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 181 if (error != 0) { 182 aprint_error(": couldn't map registers\n"); 183 return; 184 } 185 186 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 187 aprint_error(": failed to decode interrupt\n"); 188 return; 189 } 190 191 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SERIAL); 192 sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SERIAL, 193 FDT_INTR_MPSAFE, meson_uart_intr, sc, device_xname(self)); 194 if (sc->sc_ih == NULL) { 195 aprint_error(": failed to establish interrupt on %s\n", 196 intrstr); 197 return; 198 } 199 200 sc->sc_sih = softint_establish(SOFTINT_SERIAL, meson_uart_rxsoft, sc); 201 if (sc->sc_sih == NULL) { 202 aprint_error(": failed to establish softint\n"); 203 return; 204 } 205 206 if (meson_uart_cmajor == -1) { 207 /* allocate a major number */ 208 int bmajor = -1, cmajor = -1; 209 error = devsw_attach("mesonuart", NULL, &bmajor, 210 &mesonuart_cdevsw, &cmajor); 211 if (error) { 212 aprint_error(": couldn't allocate major number\n"); 213 return; 214 } 215 meson_uart_cmajor = cmajor; 216 } 217 218 major = cdevsw_lookup_major(&mesonuart_cdevsw); 219 minor = device_unit(self); 220 221 tp = sc->sc_tty = tty_alloc(); 222 tp->t_oproc = meson_uart_start; 223 tp->t_param = meson_uart_param; 224 tp->t_dev = makedev(major, minor); 225 tp->t_sc = sc; 226 tty_attach(tp); 227 228 aprint_naive("\n"); 229 if (meson_uart_console_phandle == phandle) { 230 cn_tab->cn_dev = tp->t_dev; 231 aprint_normal(": console"); 232 } 233 aprint_normal("\n"); 234 235 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 236 237 misc = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG); 238 misc &= ~UART_MISC_TX_IRQ_CNT; 239 misc |= __SHIFTIN(0, UART_MISC_TX_IRQ_CNT); 240 misc &= ~UART_MISC_RX_IRQ_CNT; 241 misc |= __SHIFTIN(1, UART_MISC_RX_IRQ_CNT); 242 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG, misc); 243 244 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG); 245 control &= ~(UART_CONTROL_TX_INT_EN|UART_CONTROL_RX_INT_EN); 246 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control); 247 } 248 249 static int 250 meson_uart_cngetc(dev_t dev) 251 { 252 bus_space_tag_t bst = meson_uart_cnsc.sc_bst; 253 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh; 254 uint32_t status; 255 int s, c; 256 257 s = splserial(); 258 259 status = bus_space_read_4(bst, bsh, UART_STATUS_REG); 260 if (status & UART_STATUS_RX_EMPTY) { 261 splx(s); 262 return -1; 263 } 264 265 c = bus_space_read_4(bst, bsh, UART_RFIFO_REG) & 0xff; 266 if (!db_active) { 267 int cn_trapped __unused = 0; 268 cn_check_magic(dev, c, meson_uart_cnm_state); 269 } 270 271 splx(s); 272 273 return c; 274 } 275 276 static void 277 meson_uart_cnputc(dev_t dev, int c) 278 { 279 bus_space_tag_t bst = meson_uart_cnsc.sc_bst; 280 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh; 281 int s; 282 283 s = splserial(); 284 285 while ((bus_space_read_4(bst, bsh, UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0) 286 ; 287 288 bus_space_write_4(bst, bsh, UART_WFIFO_REG, c); 289 290 splx(s); 291 } 292 293 294 static void 295 meson_uart_cnpollc(dev_t dev, int on) 296 { 297 } 298 299 static int 300 meson_uart_open(dev_t dev, int flag, int mode, lwp_t *l) 301 { 302 struct meson_uart_softc *sc = 303 device_lookup_private(&mesonuart_cd, minor(dev)); 304 struct tty *tp = sc->sc_tty; 305 uint32_t control; 306 307 if (kauth_authorize_device_tty(l->l_cred, 308 KAUTH_DEVICE_TTY_OPEN, tp) != 0) { 309 return EBUSY; 310 } 311 312 if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) { 313 tp->t_dev = dev; 314 ttychars(tp); 315 tp->t_iflag = TTYDEF_IFLAG; 316 tp->t_oflag = TTYDEF_OFLAG; 317 tp->t_cflag = TTYDEF_CFLAG; 318 tp->t_lflag = TTYDEF_LFLAG; 319 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 320 ttsetwater(tp); 321 322 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG); 323 control |= UART_CONTROL_RX_INT_EN; 324 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control); 325 } 326 tp->t_state |= TS_CARR_ON; 327 328 return tp->t_linesw->l_open(dev, tp); 329 } 330 331 static int 332 meson_uart_close(dev_t dev, int flag, int mode, lwp_t *l) 333 { 334 struct meson_uart_softc *sc = 335 device_lookup_private(&mesonuart_cd, minor(dev)); 336 struct tty *tp = sc->sc_tty; 337 uint32_t control; 338 339 tp->t_linesw->l_close(tp, flag); 340 ttyclose(tp); 341 342 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 343 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG); 344 control &= ~UART_CONTROL_RX_INT_EN; 345 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control); 346 } 347 348 return 0; 349 } 350 351 static int 352 meson_uart_read(dev_t dev, struct uio *uio, int flag) 353 { 354 struct meson_uart_softc *sc = 355 device_lookup_private(&mesonuart_cd, minor(dev)); 356 struct tty *tp = sc->sc_tty; 357 358 return tp->t_linesw->l_read(tp, uio, flag); 359 } 360 361 static int 362 meson_uart_write(dev_t dev, struct uio *uio, int flag) 363 { 364 struct meson_uart_softc *sc = 365 device_lookup_private(&mesonuart_cd, minor(dev)); 366 struct tty *tp = sc->sc_tty; 367 368 return tp->t_linesw->l_write(tp, uio, flag); 369 } 370 371 static int 372 meson_uart_poll(dev_t dev, int events, lwp_t *l) 373 { 374 struct meson_uart_softc *sc = 375 device_lookup_private(&mesonuart_cd, minor(dev)); 376 struct tty *tp = sc->sc_tty; 377 378 return tp->t_linesw->l_poll(tp, events, l); 379 } 380 381 static int 382 meson_uart_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 383 { 384 struct meson_uart_softc *sc = 385 device_lookup_private(&mesonuart_cd, minor(dev)); 386 struct tty *tp = sc->sc_tty; 387 int error; 388 389 error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l); 390 if (error != EPASSTHROUGH) 391 return error; 392 393 return ttioctl(tp, cmd, data, flag, l); 394 } 395 396 static struct tty * 397 meson_uart_tty(dev_t dev) 398 { 399 struct meson_uart_softc *sc = 400 device_lookup_private(&mesonuart_cd, minor(dev)); 401 402 return sc->sc_tty; 403 } 404 405 static void 406 meson_uart_stop(struct tty *tp, int flag) 407 { 408 } 409 410 static void 411 meson_uart_start(struct tty *tp) 412 { 413 struct meson_uart_softc *sc = tp->t_sc; 414 u_char *p = sc->sc_buf; 415 int s, brem; 416 417 s = spltty(); 418 419 if (tp->t_state & (TS_TTSTOP | TS_BUSY | TS_TIMEOUT)) { 420 splx(s); 421 return; 422 } 423 tp->t_state |= TS_BUSY; 424 425 splx(s); 426 427 for (brem = q_to_b(&tp->t_outq, sc->sc_buf, sizeof(sc->sc_buf)); 428 brem > 0; 429 brem--, p++) { 430 while ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, 431 UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0) 432 ; 433 434 bus_space_write_4(sc->sc_bst, sc->sc_bsh, 435 UART_WFIFO_REG, *p); 436 } 437 438 s = spltty(); 439 tp->t_state &= ~TS_BUSY; 440 if (ttypull(tp)) { 441 tp->t_state |= TS_TIMEOUT; 442 callout_schedule(&tp->t_rstrt_ch, 1); 443 } 444 splx(s); 445 } 446 447 static int 448 meson_uart_param(struct tty *tp, struct termios *t) 449 { 450 451 tp->t_ispeed = t->c_ispeed; 452 tp->t_ospeed = t->c_ospeed; 453 tp->t_cflag = t->c_cflag; 454 455 return 0; 456 } 457 458 static int 459 meson_uart_intr(void *priv) 460 { 461 struct meson_uart_softc *sc = priv; 462 struct tty *tp = sc->sc_tty; 463 uint32_t status, c; 464 465 mutex_spin_enter(&sc->sc_intr_lock); 466 for (;;) { 467 int cn_trapped = 0; 468 status = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 469 UART_STATUS_REG); 470 if (status & UART_STATUS_RX_EMPTY) { 471 break; 472 } 473 if (status & UART_STATUS_BREAK) { 474 cn_check_magic(tp->t_dev, CNC_BREAK, 475 meson_uart_cnm_state); 476 if (cn_trapped) 477 continue; 478 } 479 480 c = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_RFIFO_REG); 481 cn_check_magic(tp->t_dev, c & 0xff, meson_uart_cnm_state); 482 if (cn_trapped) 483 continue; 484 485 if ((sc->sc_rbuf_w - sc->sc_rbuf_r) >= (MESON_RBUFSZ - 1)) 486 continue; 487 sc->sc_rbuf[sc->sc_rbuf_w++ & (MESON_RBUFSZ - 1)] = c; 488 } 489 mutex_spin_exit(&sc->sc_intr_lock); 490 491 if (sc->sc_rbuf_w != sc->sc_rbuf_r) 492 softint_schedule(sc->sc_sih); 493 494 return 0; 495 } 496 497 static void 498 meson_uart_rxsoft(void *priv) 499 { 500 struct meson_uart_softc *sc = priv; 501 struct tty *tp = sc->sc_tty; 502 int c; 503 504 while (sc->sc_rbuf_w != sc->sc_rbuf_r) { 505 c = sc->sc_rbuf[sc->sc_rbuf_r++ & (MESON_RBUFSZ - 1)]; 506 if (tp->t_linesw->l_rint(c & 0xff, tp) == -1) 507 break; 508 } 509 } 510 511 static int 512 meson_uart_console_match(int phandle) 513 { 514 return of_compatible_match(phandle, compat_data); 515 } 516 517 static void 518 meson_uart_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) 519 { 520 struct meson_uart_softc *sc = &meson_uart_cnsc; 521 const int phandle = faa->faa_phandle; 522 bus_addr_t addr; 523 bus_size_t size; 524 int error; 525 526 fdtbus_get_reg(phandle, 0, &addr, &size); 527 528 sc->sc_bst = faa->faa_bst; 529 sc->sc_ospeed = fdtbus_get_stdout_speed(); 530 if (sc->sc_ospeed < 0) 531 sc->sc_ospeed = 115200; 532 sc->sc_cflag = fdtbus_get_stdout_flags(); 533 534 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 535 if (error != 0) 536 panic("failed to map console, error = %d", error); 537 538 cn_tab = &meson_uart_consdev; 539 cn_init_magic(&meson_uart_cnm_state); 540 cn_set_magic("\047\001"); 541 542 meson_uart_console_phandle = phandle; 543 } 544 545 static const struct fdt_console meson_uart_console = { 546 .match = meson_uart_console_match, 547 .consinit = meson_uart_console_consinit, 548 }; 549 550 FDT_CONSOLE(meson_uart, &meson_uart_console); 551