1 /* $NetBSD: meson_uart.c,v 1.1 2019/01/19 20:56:03 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.1 2019/01/19 20:56:03 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; 230 control |= UART_CONTROL_RX_INT_EN; 231 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control); 232 } 233 234 static int 235 meson_uart_cngetc(dev_t dev) 236 { 237 bus_space_tag_t bst = meson_uart_cnsc.sc_bst; 238 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh; 239 uint32_t status; 240 int s, c; 241 242 s = splserial(); 243 244 status = bus_space_read_4(bst, bsh, UART_STATUS_REG); 245 if (status & UART_STATUS_RX_EMPTY) { 246 splx(s); 247 return -1; 248 } 249 250 c = bus_space_read_4(bst, bsh, UART_RFIFO_REG); 251 #if defined(DDB) 252 extern int db_active; 253 if (!db_active) 254 #endif 255 { 256 int cn_trapped __unused = 0; 257 cn_check_magic(dev, c, meson_uart_cnm_state); 258 } 259 260 splx(s); 261 262 return c & 0xff; 263 } 264 265 static void 266 meson_uart_cnputc(dev_t dev, int c) 267 { 268 bus_space_tag_t bst = meson_uart_cnsc.sc_bst; 269 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh; 270 int s; 271 272 s = splserial(); 273 274 while ((bus_space_read_4(bst, bsh, UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0) 275 ; 276 277 bus_space_write_4(bst, bsh, UART_WFIFO_REG, c); 278 279 splx(s); 280 } 281 282 283 static void 284 meson_uart_cnpollc(dev_t dev, int on) 285 { 286 } 287 288 static int 289 meson_uart_open(dev_t dev, int flag, int mode, lwp_t *l) 290 { 291 struct meson_uart_softc *sc = 292 device_lookup_private(&mesonuart_cd, minor(dev)); 293 struct tty *tp = sc->sc_tty; 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 tp->t_state |= TS_CARR_ON; 311 312 return tp->t_linesw->l_open(dev, tp); 313 } 314 315 static int 316 meson_uart_close(dev_t dev, int flag, int mode, lwp_t *l) 317 { 318 struct meson_uart_softc *sc = 319 device_lookup_private(&mesonuart_cd, minor(dev)); 320 struct tty *tp = sc->sc_tty; 321 322 tp->t_linesw->l_close(tp, flag); 323 ttyclose(tp); 324 325 return 0; 326 } 327 328 static int 329 meson_uart_read(dev_t dev, struct uio *uio, int flag) 330 { 331 struct meson_uart_softc *sc = 332 device_lookup_private(&mesonuart_cd, minor(dev)); 333 struct tty *tp = sc->sc_tty; 334 335 return tp->t_linesw->l_read(tp, uio, flag); 336 } 337 338 static int 339 meson_uart_write(dev_t dev, struct uio *uio, int flag) 340 { 341 struct meson_uart_softc *sc = 342 device_lookup_private(&mesonuart_cd, minor(dev)); 343 struct tty *tp = sc->sc_tty; 344 345 return tp->t_linesw->l_write(tp, uio, flag); 346 } 347 348 static int 349 meson_uart_poll(dev_t dev, int events, lwp_t *l) 350 { 351 struct meson_uart_softc *sc = 352 device_lookup_private(&mesonuart_cd, minor(dev)); 353 struct tty *tp = sc->sc_tty; 354 355 return tp->t_linesw->l_poll(tp, events, l); 356 } 357 358 static int 359 meson_uart_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 360 { 361 struct meson_uart_softc *sc = 362 device_lookup_private(&mesonuart_cd, minor(dev)); 363 struct tty *tp = sc->sc_tty; 364 int error; 365 366 error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l); 367 if (error != EPASSTHROUGH) 368 return error; 369 370 return ttioctl(tp, cmd, data, flag, l); 371 } 372 373 static struct tty * 374 meson_uart_tty(dev_t dev) 375 { 376 struct meson_uart_softc *sc = 377 device_lookup_private(&mesonuart_cd, minor(dev)); 378 379 return sc->sc_tty; 380 } 381 382 static void 383 meson_uart_stop(struct tty *tp, int flag) 384 { 385 } 386 387 static void 388 meson_uart_start(struct tty *tp) 389 { 390 struct meson_uart_softc *sc = tp->t_sc; 391 u_char *p = sc->sc_buf; 392 int s, brem; 393 394 s = spltty(); 395 396 if (tp->t_state & (TS_TTSTOP | TS_BUSY | TS_TIMEOUT)) { 397 splx(s); 398 return; 399 } 400 tp->t_state |= TS_BUSY; 401 402 splx(s); 403 404 for (brem = q_to_b(&tp->t_outq, sc->sc_buf, sizeof(sc->sc_buf)); 405 brem > 0; 406 brem--, p++) { 407 while ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, 408 UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0) 409 ; 410 411 bus_space_write_4(sc->sc_bst, sc->sc_bsh, 412 UART_WFIFO_REG, *p); 413 } 414 415 s = spltty(); 416 tp->t_state &= ~TS_BUSY; 417 if (ttypull(tp)) { 418 tp->t_state |= TS_TIMEOUT; 419 callout_schedule(&tp->t_rstrt_ch, 1); 420 } 421 splx(s); 422 } 423 424 static int 425 meson_uart_param(struct tty *tp, struct termios *t) 426 { 427 428 tp->t_ispeed = t->c_ispeed; 429 tp->t_ospeed = t->c_ospeed; 430 tp->t_cflag = t->c_cflag; 431 432 return 0; 433 } 434 435 static int 436 meson_uart_intr(void *priv) 437 { 438 struct meson_uart_softc *sc = priv; 439 struct tty *tp = sc->sc_tty; 440 uint32_t status, c; 441 442 for (;;) { 443 int cn_trapped = 0; 444 status = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 445 UART_STATUS_REG); 446 if (status & UART_STATUS_RX_EMPTY) { 447 break; 448 } 449 if (status & UART_STATUS_BREAK) { 450 cn_check_magic(tp->t_dev, CNC_BREAK, 451 meson_uart_cnm_state); 452 if (cn_trapped) 453 continue; 454 } 455 456 c = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_RFIFO_REG); 457 cn_check_magic(tp->t_dev, c & 0xff, meson_uart_cnm_state); 458 if (cn_trapped) 459 continue; 460 tp->t_linesw->l_rint(c & 0xff, tp); 461 } 462 463 return 0; 464 } 465 466 static int 467 meson_uart_console_match(int phandle) 468 { 469 return of_match_compatible(phandle, compatible); 470 } 471 472 static void 473 meson_uart_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) 474 { 475 struct meson_uart_softc *sc = &meson_uart_cnsc; 476 const int phandle = faa->faa_phandle; 477 bus_addr_t addr; 478 bus_size_t size; 479 int error; 480 481 fdtbus_get_reg(phandle, 0, &addr, &size); 482 483 sc->sc_bst = faa->faa_bst; 484 sc->sc_ospeed = fdtbus_get_stdout_speed(); 485 if (sc->sc_ospeed < 0) 486 sc->sc_ospeed = 115200; 487 sc->sc_cflag = fdtbus_get_stdout_flags(); 488 489 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 490 if (error != 0) 491 panic("failed to map console, error = %d", error); 492 493 cn_tab = &meson_uart_consdev; 494 cn_init_magic(&meson_uart_cnm_state); 495 cn_set_magic("\047\001"); 496 497 meson_uart_console_phandle = phandle; 498 } 499 500 static const struct fdt_console meson_uart_console = { 501 .match = meson_uart_console_match, 502 .consinit = meson_uart_console_consinit, 503 }; 504 505 FDT_CONSOLE(meson_uart, &meson_uart_console); 506