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