altera_jtag_uart_tty.c revision 275429
1/*- 2 * Copyright (c) 2011-2012 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * This software was developed by SRI International and the University of 6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7 * ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: stable/10/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c 275429 2014-12-02 22:04:27Z brooks $"); 33 34#include <sys/param.h> 35#include <sys/bus.h> 36#include <sys/cons.h> 37#include <sys/endian.h> 38#include <sys/kdb.h> 39#include <sys/rman.h> 40#include <sys/systm.h> 41#include <sys/kernel.h> 42#include <sys/reboot.h> 43#include <sys/tty.h> 44 45#include <ddb/ddb.h> 46 47#include <machine/bus.h> 48 49#include <dev/altera/jtag_uart/altera_jtag_uart.h> 50 51/* 52 * If one of the Altera JTAG UARTs is currently the system console, register 53 * it here. 54 */ 55static struct altera_jtag_uart_softc *aju_cons_sc; 56 57static tsw_outwakeup_t aju_outwakeup; 58static void aju_ac_callout(void *); 59static void aju_io_callout(void *); 60 61static struct ttydevsw aju_ttydevsw = { 62 .tsw_flags = TF_NOPREFIX, 63 .tsw_outwakeup = aju_outwakeup, 64}; 65 66/* 67 * When polling for the AC bit, the number of times we have to not see it 68 * before assuming JTAG has disappeared on us. By default, two seconds. 69 */ 70#define AJU_JTAG_MAXMISS 10 71 72/* 73 * Polling intervals for input/output and JTAG connection events. 74 */ 75#define AJU_IO_POLLINTERVAL (hz/100) 76#define AJU_AC_POLLINTERVAL (hz/5) 77 78/* 79 * Low-level read and write register routines; the Altera UART is little 80 * endian, so we byte swap 32-bit reads and writes. 81 */ 82static inline uint32_t 83aju_data_read(struct altera_jtag_uart_softc *sc) 84{ 85 86 return (le32toh(bus_read_4(sc->ajus_mem_res, 87 ALTERA_JTAG_UART_DATA_OFF))); 88} 89 90static inline void 91aju_data_write(struct altera_jtag_uart_softc *sc, uint32_t v) 92{ 93 94 bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_DATA_OFF, htole32(v)); 95} 96 97static inline uint32_t 98aju_control_read(struct altera_jtag_uart_softc *sc) 99{ 100 101 return (le32toh(bus_read_4(sc->ajus_mem_res, 102 ALTERA_JTAG_UART_CONTROL_OFF))); 103} 104 105static inline void 106aju_control_write(struct altera_jtag_uart_softc *sc, uint32_t v) 107{ 108 109 bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_CONTROL_OFF, 110 htole32(v)); 111} 112 113/* 114 * Slightly higher-level routines aware of buffering and flow control. 115 */ 116static inline int 117aju_writable(struct altera_jtag_uart_softc *sc) 118{ 119 120 return ((aju_control_read(sc) & 121 ALTERA_JTAG_UART_CONTROL_WSPACE) != 0); 122} 123 124static inline int 125aju_readable(struct altera_jtag_uart_softc *sc) 126{ 127 uint32_t v; 128 129 AJU_LOCK_ASSERT(sc); 130 131 if (*sc->ajus_buffer_validp) 132 return (1); 133 v = aju_data_read(sc); 134 if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) { 135 *sc->ajus_buffer_validp = 1; 136 *sc->ajus_buffer_datap = (v & ALTERA_JTAG_UART_DATA_DATA); 137 return (1); 138 } 139 return (0); 140} 141 142static char 143aju_read(struct altera_jtag_uart_softc *sc) 144{ 145 146 AJU_LOCK_ASSERT(sc); 147 148 while (!aju_readable(sc)); 149 *sc->ajus_buffer_validp = 0; 150 return (*sc->ajus_buffer_datap); 151} 152 153/* 154 * Routines for enabling and disabling interrupts for read and write. 155 */ 156static void 157aju_intr_readable_enable(struct altera_jtag_uart_softc *sc) 158{ 159 uint32_t v; 160 161 AJU_LOCK_ASSERT(sc); 162 163 v = aju_control_read(sc); 164 v |= ALTERA_JTAG_UART_CONTROL_RE; 165 aju_control_write(sc, v); 166} 167 168static void 169aju_intr_writable_enable(struct altera_jtag_uart_softc *sc) 170{ 171 uint32_t v; 172 173 AJU_LOCK_ASSERT(sc); 174 175 v = aju_control_read(sc); 176 v |= ALTERA_JTAG_UART_CONTROL_WE; 177 aju_control_write(sc, v); 178} 179 180static void 181aju_intr_writable_disable(struct altera_jtag_uart_softc *sc) 182{ 183 uint32_t v; 184 185 AJU_LOCK_ASSERT(sc); 186 187 v = aju_control_read(sc); 188 v &= ~ALTERA_JTAG_UART_CONTROL_WE; 189 aju_control_write(sc, v); 190} 191 192static void 193aju_intr_disable(struct altera_jtag_uart_softc *sc) 194{ 195 uint32_t v; 196 197 AJU_LOCK_ASSERT(sc); 198 199 v = aju_control_read(sc); 200 v &= ~(ALTERA_JTAG_UART_CONTROL_RE | ALTERA_JTAG_UART_CONTROL_WE); 201 aju_control_write(sc, v); 202} 203 204/* 205 * The actual work of checking for, and handling, available reads. This is 206 * used in both polled and interrupt-driven modes, as JTAG UARTs may be hooked 207 * up with, or without, IRQs allocated. 208 */ 209static void 210aju_handle_input(struct altera_jtag_uart_softc *sc, struct tty *tp) 211{ 212 int c; 213 214 tty_lock_assert(tp, MA_OWNED); 215 AJU_LOCK_ASSERT(sc); 216 217 while (aju_readable(sc)) { 218 c = aju_read(sc); 219 AJU_UNLOCK(sc); 220#ifdef KDB 221 if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) 222 kdb_alt_break(c, &sc->ajus_alt_break_state); 223#endif 224 ttydisc_rint(tp, c, 0); 225 AJU_LOCK(sc); 226 } 227 AJU_UNLOCK(sc); 228 ttydisc_rint_done(tp); 229 AJU_LOCK(sc); 230} 231 232/* 233 * Send output to the UART until either there's none left to send, or we run 234 * out of room and need to await an interrupt so that we can start sending 235 * again. 236 * 237 * XXXRW: It would be nice to query WSPACE at the beginning and write to the 238 * FIFO in bugger chunks. 239 */ 240static void 241aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp) 242{ 243 uint32_t v; 244 uint8_t ch; 245 246 tty_lock_assert(tp, MA_OWNED); 247 AJU_LOCK_ASSERT(sc); 248 249 AJU_UNLOCK(sc); 250 while (ttydisc_getc_poll(tp) != 0) { 251 AJU_LOCK(sc); 252 v = aju_control_read(sc); 253 if ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) != 0) { 254 AJU_UNLOCK(sc); 255 if (ttydisc_getc(tp, &ch, sizeof(ch)) != sizeof(ch)) 256 panic("%s: ttydisc_getc", __func__); 257 AJU_LOCK(sc); 258 259 /* 260 * XXXRW: There is a slight race here in which we test 261 * for writability, drop the lock, get the character 262 * from the tty layer, re-acquire the lock, and then 263 * write. It's possible for other code -- 264 * specifically, the low-level console -- to have 265 * written in the mean time, which might mean that 266 * there is no longer space. The BERI memory bus will 267 * cause this write to block, wedging the processor 268 * until space is available -- which could be a while 269 * if JTAG is not attached! 270 * 271 * The 'easy' fix is to drop the character if WSPACE 272 * has become unset. Not sure what the 'hard' fix is. 273 */ 274 aju_data_write(sc, ch); 275 } else { 276 /* 277 * If JTAG is not present, then we will drop this 278 * character instead of perhaps scheduling an 279 * interrupt to let us know when there is buffer 280 * space. Otherwise we might get a write interrupt 281 * later even though we aren't interested in sending 282 * anymore. Loop to drain TTY-layer buffer. 283 */ 284 if (*sc->ajus_jtag_presentp == 0) { 285 if (ttydisc_getc(tp, &ch, sizeof(ch)) != 286 sizeof(ch)) 287 panic("%s: ttydisc_getc 2", __func__); 288 AJU_UNLOCK(sc); 289 continue; 290 } 291 if (sc->ajus_irq_res != NULL) 292 aju_intr_writable_enable(sc); 293 return; 294 } 295 AJU_UNLOCK(sc); 296 } 297 AJU_LOCK(sc); 298 aju_intr_writable_disable(sc); 299} 300 301static void 302aju_outwakeup(struct tty *tp) 303{ 304 struct altera_jtag_uart_softc *sc = tty_softc(tp); 305 306 tty_lock_assert(tp, MA_OWNED); 307 308 AJU_LOCK(sc); 309 aju_handle_output(sc, tp); 310 AJU_UNLOCK(sc); 311} 312 313static void 314aju_io_callout(void *arg) 315{ 316 struct altera_jtag_uart_softc *sc = arg; 317 struct tty *tp = sc->ajus_ttyp; 318 319 tty_lock(tp); 320 AJU_LOCK(sc); 321 322 /* 323 * It would be convenient if we could share code with aju_intr() here 324 * by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and 325 * ALTERA_JTAG_UART_CONTROL_WI. Unfortunately, it's not clear that 326 * this is supported, so do all the work to poll for both input and 327 * output. 328 */ 329 aju_handle_input(sc, tp); 330 aju_handle_output(sc, tp); 331 332 /* 333 * Reschedule next poll attempt. There's some argument that we should 334 * do adaptive polling based on the expectation of I/O: is something 335 * pending in the output buffer, or have we recently had input, but we 336 * don't. 337 */ 338 callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, 339 aju_io_callout, sc); 340 AJU_UNLOCK(sc); 341 tty_unlock(tp); 342} 343 344static void 345aju_ac_callout(void *arg) 346{ 347 struct altera_jtag_uart_softc *sc = arg; 348 struct tty *tp = sc->ajus_ttyp; 349 uint32_t v; 350 351 tty_lock(tp); 352 AJU_LOCK(sc); 353 v = aju_control_read(sc); 354 if (v & ALTERA_JTAG_UART_CONTROL_AC) { 355 v &= ~ALTERA_JTAG_UART_CONTROL_AC; 356 aju_control_write(sc, v); 357 if (*sc->ajus_jtag_presentp == 0) { 358 *sc->ajus_jtag_missedp = 0; 359 *sc->ajus_jtag_presentp = 1; 360 aju_handle_output(sc, tp); 361 } 362 } else if (*sc->ajus_jtag_presentp != 0) { 363 (*sc->ajus_jtag_missedp)++; 364 if (*sc->ajus_jtag_missedp >= AJU_JTAG_MAXMISS) { 365 *sc->ajus_jtag_presentp = 0; 366 aju_handle_output(sc, tp); 367 } 368 } 369 callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, 370 aju_ac_callout, sc); 371 AJU_UNLOCK(sc); 372 tty_unlock(tp); 373} 374 375static void 376aju_intr(void *arg) 377{ 378 struct altera_jtag_uart_softc *sc = arg; 379 struct tty *tp = sc->ajus_ttyp; 380 uint32_t v; 381 382 tty_lock(tp); 383 AJU_LOCK(sc); 384 v = aju_control_read(sc); 385 if (v & ALTERA_JTAG_UART_CONTROL_RI) 386 aju_handle_input(sc, tp); 387 if (v & ALTERA_JTAG_UART_CONTROL_WI) 388 aju_handle_output(sc, tp); 389 AJU_UNLOCK(sc); 390 tty_unlock(tp); 391} 392 393int 394altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc) 395{ 396 struct tty *tp; 397 int error; 398 399 AJU_LOCK_INIT(sc); 400 401 /* 402 * XXXRW: Currently, we detect the console solely based on it using a 403 * reserved address, and borrow console-level locks and buffer if so. 404 * Is there a better way? 405 */ 406 if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) { 407 sc->ajus_lockp = &aju_cons_lock; 408 sc->ajus_buffer_validp = &aju_cons_buffer_valid; 409 sc->ajus_buffer_datap = &aju_cons_buffer_data; 410 sc->ajus_jtag_presentp = &aju_cons_jtag_present; 411 sc->ajus_jtag_missedp = &aju_cons_jtag_missed; 412 sc->ajus_flags |= ALTERA_JTAG_UART_FLAG_CONSOLE; 413 } else { 414 sc->ajus_lockp = &sc->ajus_lock; 415 sc->ajus_buffer_validp = &sc->ajus_buffer_valid; 416 sc->ajus_buffer_datap = &sc->ajus_buffer_data; 417 sc->ajus_jtag_presentp = &sc->ajus_jtag_present; 418 sc->ajus_jtag_missedp = &sc->ajus_jtag_missed; 419 } 420 421 /* 422 * Disable interrupts regardless of whether or not we plan to use 423 * them. We will register an interrupt handler now if they will be 424 * used, but not re-enable intil later once the remainder of the tty 425 * layer is properly initialised, as we're not ready for input yet. 426 */ 427 AJU_LOCK(sc); 428 aju_intr_disable(sc); 429 AJU_UNLOCK(sc); 430 if (sc->ajus_irq_res != NULL) { 431 error = bus_setup_intr(sc->ajus_dev, sc->ajus_irq_res, 432 INTR_ENTROPY | INTR_TYPE_TTY | INTR_MPSAFE, NULL, 433 aju_intr, sc, &sc->ajus_irq_cookie); 434 if (error) { 435 device_printf(sc->ajus_dev, 436 "could not activate interrupt\n"); 437 AJU_LOCK_DESTROY(sc); 438 return (error); 439 } 440 } 441 tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc); 442 if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) { 443 aju_cons_sc = sc; 444 tty_init_console(tp, 0); 445 } 446 tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit); 447 448 /* 449 * If we will be using interrupts, enable them now; otherwise, start 450 * polling. From this point onwards, input can arrive. 451 */ 452 if (sc->ajus_irq_res != NULL) { 453 AJU_LOCK(sc); 454 aju_intr_readable_enable(sc); 455 AJU_UNLOCK(sc); 456 } else { 457 callout_init(&sc->ajus_io_callout, CALLOUT_MPSAFE); 458 callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, 459 aju_io_callout, sc); 460 } 461 callout_init(&sc->ajus_ac_callout, CALLOUT_MPSAFE); 462 callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, 463 aju_ac_callout, sc); 464 return (0); 465} 466 467void 468altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc) 469{ 470 struct tty *tp = sc->ajus_ttyp; 471 472 /* 473 * If we're using interrupts, disable and release the interrupt 474 * handler now. Otherwise drain the polling timeout. 475 */ 476 if (sc->ajus_irq_res != NULL) { 477 AJU_LOCK(sc); 478 aju_intr_disable(sc); 479 AJU_UNLOCK(sc); 480 bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res, 481 sc->ajus_irq_cookie); 482 } else 483 callout_drain(&sc->ajus_io_callout); 484 callout_drain(&sc->ajus_ac_callout); 485 if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) 486 aju_cons_sc = NULL; 487 tty_lock(tp); 488 tty_rel_gone(tp); 489 AJU_LOCK_DESTROY(sc); 490} 491