altera_jtag_uart_tty.c revision 256281
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 239676 2012-08-25 11:30:36Z rwatson $"); 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, one second. 69 */ 70#define AJU_JTAG_MAXMISS 5 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 aju_data_write(sc, ch); 259 } else { 260 /* 261 * If JTAG is not present, then we will drop this 262 * character instead of perhaps scheduling an 263 * interrupt to let us know when there is buffer 264 * space. Otherwise we might get a write interrupt 265 * later even though we aren't interested in sending 266 * anymore. Loop to drain TTY-layer buffer. 267 */ 268 if (*sc->ajus_jtag_presentp == 0) { 269 if (ttydisc_getc(tp, &ch, sizeof(ch)) != 270 sizeof(ch)) 271 panic("%s: ttydisc_getc 2", __func__); 272 AJU_UNLOCK(sc); 273 continue; 274 } 275 if (sc->ajus_irq_res != NULL) 276 aju_intr_writable_enable(sc); 277 return; 278 } 279 AJU_UNLOCK(sc); 280 } 281 AJU_LOCK(sc); 282 aju_intr_writable_disable(sc); 283} 284 285static void 286aju_outwakeup(struct tty *tp) 287{ 288 struct altera_jtag_uart_softc *sc = tty_softc(tp); 289 290 tty_lock_assert(tp, MA_OWNED); 291 292 AJU_LOCK(sc); 293 aju_handle_output(sc, tp); 294 AJU_UNLOCK(sc); 295} 296 297static void 298aju_io_callout(void *arg) 299{ 300 struct altera_jtag_uart_softc *sc = arg; 301 struct tty *tp = sc->ajus_ttyp; 302 303 tty_lock(tp); 304 AJU_LOCK(sc); 305 306 /* 307 * It would be convenient if we could share code with aju_intr() here 308 * by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and 309 * ALTERA_JTAG_UART_CONTROL_WI. Unfortunately, it's not clear that 310 * this is supported, so do all the work to poll for both input and 311 * output. 312 */ 313 aju_handle_input(sc, tp); 314 aju_handle_output(sc, tp); 315 316 /* 317 * Reschedule next poll attempt. There's some argument that we should 318 * do adaptive polling based on the expectation of I/O: is something 319 * pending in the output buffer, or have we recently had input, but we 320 * don't. 321 */ 322 callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, 323 aju_io_callout, sc); 324 AJU_UNLOCK(sc); 325 tty_unlock(tp); 326} 327 328static void 329aju_ac_callout(void *arg) 330{ 331 struct altera_jtag_uart_softc *sc = arg; 332 struct tty *tp = sc->ajus_ttyp; 333 uint32_t v; 334 335 tty_lock(tp); 336 AJU_LOCK(sc); 337 v = aju_control_read(sc); 338 if (v & ALTERA_JTAG_UART_CONTROL_AC) { 339 v &= ~ALTERA_JTAG_UART_CONTROL_AC; 340 aju_control_write(sc, v); 341 if (*sc->ajus_jtag_presentp == 0) { 342 *sc->ajus_jtag_missedp = 0; 343 *sc->ajus_jtag_presentp = 1; 344 aju_handle_output(sc, tp); 345 } 346 } else if (*sc->ajus_jtag_presentp != 0) { 347 (*sc->ajus_jtag_missedp)++; 348 if (*sc->ajus_jtag_missedp >= AJU_JTAG_MAXMISS) { 349 *sc->ajus_jtag_presentp = 0; 350 aju_handle_output(sc, tp); 351 } 352 } 353 callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, 354 aju_ac_callout, sc); 355 AJU_UNLOCK(sc); 356 tty_unlock(tp); 357} 358 359static void 360aju_intr(void *arg) 361{ 362 struct altera_jtag_uart_softc *sc = arg; 363 struct tty *tp = sc->ajus_ttyp; 364 uint32_t v; 365 366 tty_lock(tp); 367 AJU_LOCK(sc); 368 v = aju_control_read(sc); 369 if (v & ALTERA_JTAG_UART_CONTROL_RI) 370 aju_handle_input(sc, tp); 371 if (v & ALTERA_JTAG_UART_CONTROL_WI) 372 aju_handle_output(sc, tp); 373 AJU_UNLOCK(sc); 374 tty_unlock(tp); 375} 376 377int 378altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc) 379{ 380 struct tty *tp; 381 int error; 382 383 AJU_LOCK_INIT(sc); 384 385 /* 386 * XXXRW: Currently, we detect the console solely based on it using a 387 * reserved address, and borrow console-level locks and buffer if so. 388 * Is there a better way? 389 */ 390 if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) { 391 sc->ajus_lockp = &aju_cons_lock; 392 sc->ajus_buffer_validp = &aju_cons_buffer_valid; 393 sc->ajus_buffer_datap = &aju_cons_buffer_data; 394 sc->ajus_jtag_presentp = &aju_cons_jtag_present; 395 sc->ajus_jtag_missedp = &aju_cons_jtag_missed; 396 sc->ajus_flags |= ALTERA_JTAG_UART_FLAG_CONSOLE; 397 } else { 398 sc->ajus_lockp = &sc->ajus_lock; 399 sc->ajus_buffer_validp = &sc->ajus_buffer_valid; 400 sc->ajus_buffer_datap = &sc->ajus_buffer_data; 401 sc->ajus_jtag_presentp = &sc->ajus_jtag_present; 402 sc->ajus_jtag_missedp = &sc->ajus_jtag_missed; 403 } 404 405 /* 406 * Disable interrupts regardless of whether or not we plan to use 407 * them. We will register an interrupt handler now if they will be 408 * used, but not re-enable intil later once the remainder of the tty 409 * layer is properly initialised, as we're not ready for input yet. 410 */ 411 AJU_LOCK(sc); 412 aju_intr_disable(sc); 413 AJU_UNLOCK(sc); 414 if (sc->ajus_irq_res != NULL) { 415 error = bus_setup_intr(sc->ajus_dev, sc->ajus_irq_res, 416 INTR_ENTROPY | INTR_TYPE_TTY | INTR_MPSAFE, NULL, 417 aju_intr, sc, &sc->ajus_irq_cookie); 418 if (error) { 419 device_printf(sc->ajus_dev, 420 "could not activate interrupt\n"); 421 AJU_LOCK_DESTROY(sc); 422 return (error); 423 } 424 } 425 tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc); 426 if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) { 427 aju_cons_sc = sc; 428 tty_init_console(tp, 0); 429 } 430 tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit); 431 432 /* 433 * If we will be using interrupts, enable them now; otherwise, start 434 * polling. From this point onwards, input can arrive. 435 */ 436 if (sc->ajus_irq_res != NULL) { 437 AJU_LOCK(sc); 438 aju_intr_readable_enable(sc); 439 AJU_UNLOCK(sc); 440 } else { 441 callout_init(&sc->ajus_io_callout, CALLOUT_MPSAFE); 442 callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, 443 aju_io_callout, sc); 444 } 445 callout_init(&sc->ajus_ac_callout, CALLOUT_MPSAFE); 446 callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, 447 aju_ac_callout, sc); 448 return (0); 449} 450 451void 452altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc) 453{ 454 struct tty *tp = sc->ajus_ttyp; 455 456 /* 457 * If we're using interrupts, disable and release the interrupt 458 * handler now. Otherwise drain the polling timeout. 459 */ 460 if (sc->ajus_irq_res != NULL) { 461 AJU_LOCK(sc); 462 aju_intr_disable(sc); 463 AJU_UNLOCK(sc); 464 bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res, 465 sc->ajus_irq_cookie); 466 } else 467 callout_drain(&sc->ajus_io_callout); 468 callout_drain(&sc->ajus_ac_callout); 469 if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) 470 aju_cons_sc = NULL; 471 tty_lock(tp); 472 tty_rel_gone(tp); 473 AJU_LOCK_DESTROY(sc); 474} 475