jhb 025e3d0a95 MFC: Split struct ithd into struct intr_thread and intr_event and
associated changes.  More details below:

  Remove public declarations of variables that were forgotten when they were
  made static.

  Revision  Changes    Path
  1.31      +0 -1      src/sys/sys/interrupt.h

  Make sure the interrupt is masked before processing it, or bad things
  can happen.

  Revision  Changes    Path
  1.10      +3 -3      src/sys/arm/arm/intr.c

  Reorganize the interrupt handling code a bit to make a few things cleaner
  and increase flexibility to allow various different approaches to be tried
  in the future.
  - Split struct ithd up into two pieces.  struct intr_event holds the list
    of interrupt handlers associated with interrupt sources.
    struct intr_thread contains the data relative to an interrupt thread.
    Currently we still provide a 1:1 relationship of events to threads
    with the exception that events only have an associated thread if there
    is at least one threaded interrupt handler attached to the event.  This
    means that on x86 we no longer have 4 bazillion interrupt threads with
    no handlers.  It also means that interrupt events with only INTR_FAST
    handlers no longer have an associated thread either.
  - Renamed struct intrhand to struct intr_handler to follow the struct
    intr_foo naming convention.  This did require renaming the powerpc
    MD struct intr_handler to struct ppc_intr_handler.
  - INTR_FAST no longer implies INTR_EXCL on all architectures except for
    powerpc.  This means that multiple INTR_FAST handlers can attach to the
    same interrupt and that INTR_FAST and non-INTR_FAST handlers can attach
    to the same interrupt.  Sharing INTR_FAST handlers may not always be
    desirable, but having sio(4) and uhci(4) fight over an IRQ isn't fun
    either.  Drivers can always still use INTR_EXCL to ask for an interrupt
    exclusively.  The way this sharing works is that when an interrupt
    comes in, all the INTR_FAST handlers are executed first, and if any
    threaded handlers exist, the interrupt thread is scheduled afterwards.
    This type of layout also makes it possible to investigate using interrupt
    filters ala OS X where the filter determines whether or not its companion
    threaded handler should run.
  - Aside from the INTR_FAST changes above, the impact on MD interrupt code
    is mostly just 's/ithread/intr_event/'.
  - A new MI ddb command 'show intrs' walks the list of interrupt events
    dumping their state.  It also has a '/v' verbose switch which dumps
    info about all of the handlers attached to each event.
  - We currently don't destroy an interrupt thread when the last threaded
    handler is removed because it would suck for things like ppbus(8)'s
    braindead behavior.  The code is present, though, it is just under
    #if 0 for now.
  - Move the code to actually execute the threaded handlers for an interrrupt
    event into a separate function so that ithread_loop() becomes more
    readable.  Previously this code was all in the middle of ithread_loop()
    and indented halfway across the screen.
  - Made struct intr_thread private to kern_intr.c and replaced td_ithd
    with a thread private flag TDP_ITHREAD.
  - In statclock, check curthread against idlethread directly rather than
    curthread's proc against idlethread's proc. (Not really related to intr
    changes)

  Tested on:      alpha, amd64, i386, sparc64
  Tested on:      arm, ia64 (older version of patch by cognet and marcel)

  Revision  Changes    Path
  1.88      +43 -29    src/sys/alpha/alpha/interrupt.c
  1.38      +5 -5      src/sys/alpha/isa/isa.c
  1.16      +58 -52    src/sys/amd64/amd64/intr_machdep.c
  1.6       +1 -1      src/sys/amd64/include/intr_machdep.h
  1.16      +2 -2      src/sys/amd64/isa/atpic.c
  1.11      +28 -22    src/sys/arm/arm/intr.c
  1.462     +2 -2      src/sys/dev/sio/sio.c
  1.6       +1 -1      src/sys/dev/uart/uart_kbd_sun.c
  1.24      +2 -2      src/sys/dev/uart/uart_tty.c
  1.15      +58 -52    src/sys/i386/i386/intr_machdep.c
  1.8       +1 -1      src/sys/i386/include/intr_machdep.h
  1.21      +2 -2      src/sys/i386/isa/atpic.c
  1.52      +32 -25    src/sys/ia64/ia64/interrupt.c
  1.180     +3 -2      src/sys/kern/kern_clock.c
  1.127     +437 -270  src/sys/kern/kern_intr.c
  1.206     +0 -1      src/sys/kern/subr_witness.c
  1.6       +3 -3      src/sys/powerpc/include/intr_machdep.h
  1.7       +35 -32    src/sys/powerpc/powerpc/intr_machdep.c
  1.14      +1 -1      src/sys/sparc64/include/intr_machdep.h
  1.24      +43 -36    src/sys/sparc64/sparc64/intr_machdep.c
  1.32      +36 -36    src/sys/sys/interrupt.h
  1.440     +1 -3      src/sys/sys/proc.h

  Catch up with interrupt-thread changes.

  Revision  Changes    Path
  1.32      +1 -1      src/sys/dev/zs/zs.c

  Catch up with new interrupt handling code.

  Revision  Changes    Path
  1.16      +3 -3      src/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c

  Catch up with new interrupt handling code.

  Revision  Changes    Path
  1.162     +2 -2      src/sys/dev/cy/cy.c
  1.101     +2 -2      src/sys/dev/rc/rc.c

  Catch up with new interrupt handling code.

  Revision  Changes    Path
  1.50      +2 -2      src/sys/dev/cx/if_cx.c
  1.41      +1 -1      src/sys/dev/sab/sab.c
  1.238     +2 -2      src/sys/pc98/cbus/sio.c

  Add a swi_remove() function to teardown software interrupt handlers.  For
  now it just calls intr_event_remove_handler(), but at some point it might
  also be responsible for tearing down interrupt events created via swi_add.

  Revision  Changes    Path
  1.128     +17 -0     src/sys/kern/kern_intr.c
  1.33      +1 -0      src/sys/sys/interrupt.h

  - Use swi_remove() to teardown swi handlers rather than
    intr_event_remove_handler().
  - Remove tty: prefix from a couple of swi handler names.

  Revision  Changes    Path
  1.51      +1 -1      src/sys/dev/cx/if_cx.c
  1.102     +2 -2      src/sys/dev/rc/rc.c
  1.42      +1 -1      src/sys/dev/sab/sab.c
  1.25      +1 -1      src/sys/dev/uart/uart_tty.c
  1.33      +1 -1      src/sys/dev/zs/zs.c
  1.17      +2 -2      src/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c

  Remove a stray return statement in the interrupt dispatch function
  that caused a premature exit after calling a fast interrupt handler
  and bypassing a much needed critical_exit() and the scheduling of
  the interrupt thread for non-fast handlers. In short: unbreak :-)

  Revision  Changes    Path
  1.53      +0 -1      src/sys/ia64/ia64/interrupt.c

  If we get a stray interrupt, return after logging it.  In the extremely
  rare case of a stray interrupt to an unregistered source (such as a stray
  interrupt from the 8259As when using APIC), this could result in a page
  fault when it tried to walk the list of interrupt handlers to execute
  INTR_FAST handlers.  This bug was introduced with the intr_event changes,
  so it's not present in 5.x or 6.x.

  Submitted by:   Mark Tinguely tinguely at casselton dot net

  Revision  Changes    Path
  1.17      +1 -0      src/sys/amd64/amd64/intr_machdep.c
  1.16      +1 -0      src/sys/i386/i386/intr_machdep.c

Approved by:	re (scottl)
2006-03-10 19:37:35 +00:00

1159 lines
25 KiB
C

/* $OpenBSD: sab.c,v 1.7 2002/04/08 17:49:42 jason Exp $ */
/*-
* Copyright (c) 2001 Jason L. Wright (jason@thought.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Jason L. Wright
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Effort sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F30602-01-2-0537.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* SAB82532 Dual UART driver
*/
#include "opt_comconsole.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/cons.h>
#include <sys/fcntl.h>
#include <sys/interrupt.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/mutex.h>
#include <sys/module.h>
#include <sys/proc.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/openfirm.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <sys/serial.h>
#include <sys/syslog.h>
#include <sys/tty.h>
#include <dev/sab/sab82532reg.h>
#define SAB_READ(sc, r) \
bus_space_read_1((sc)->sc_bt, (sc)->sc_bh, (r))
#define SAB_WRITE(sc, r, v) \
bus_space_write_1((sc)->sc_bt, (sc)->sc_bh, (r), (v))
#define SABTTY_LOCK(sc) mtx_lock_spin(&sc->sc_mtx)
#define SABTTY_UNLOCK(sc) mtx_unlock_spin(&sc->sc_mtx)
struct sabtty_softc {
device_t sc_dev;
struct sab_softc *sc_parent;
bus_space_tag_t sc_bt;
bus_space_handle_t sc_bh;
struct tty *sc_tty;
int sc_channel;
int sc_icnt;
uint8_t *sc_iput;
uint8_t *sc_iget;
int sc_ocnt;
uint8_t *sc_oget;
int sc_alt_break_state;
struct mtx sc_mtx;
uint8_t sc_console;
uint8_t sc_tx_done;
uint8_t sc_pvr_dtr;
uint8_t sc_pvr_dsr;
uint8_t sc_imr0;
uint8_t sc_imr1;
uint8_t sc_ibuf[CBLOCK];
uint8_t sc_obuf[CBLOCK];
};
struct sab_softc {
device_t sc_dev;
bus_space_tag_t sc_bt;
bus_space_handle_t sc_bh;
struct sabtty_softc *sc_child[SAB_NCHAN];
void *sc_ih;
void *sc_softih;
struct resource *sc_irqres;
int sc_irqrid;
struct resource *sc_iores;
int sc_iorid;
uint8_t sc_ipc;
};
static int sab_probe(device_t dev);
static int sab_attach(device_t dev);
static int sab_detach(device_t dev);
static void sab_intr(void *vsc);
static void sab_softintr(void *vsc);
static void sab_shutdown(void *vsc);
static int sabtty_probe(device_t dev);
static int sabtty_attach(device_t dev);
static int sabtty_detach(device_t dev);
static int sabtty_intr(struct sabtty_softc *sc);
static void sabtty_softintr(struct sabtty_softc *sc);
static void sabttybreak(struct tty *tp, int brk);
static int sabttymodem(struct tty *tp, int biton, int bitoff);
static int sabtty_param(struct sabtty_softc *sc, struct tty *tp,
struct termios *t);
static void sabtty_cec_wait(struct sabtty_softc *sc);
static void sabtty_tec_wait(struct sabtty_softc *sc);
static void sabtty_reset(struct sabtty_softc *sc);
static void sabtty_flush(struct sabtty_softc *sc);
static int sabtty_speed(int);
static int sabtty_console(device_t dev, char *mode, int len);
static cn_probe_t sab_cnprobe;
static cn_init_t sab_cninit;
static cn_term_t sab_cnterm;
static cn_getc_t sab_cngetc;
static cn_checkc_t sab_cncheckc;
static cn_putc_t sab_cnputc;
static cn_dbctl_t sab_cndbctl;
static int sabtty_cngetc(struct sabtty_softc *sc);
static int sabtty_cncheckc(struct sabtty_softc *sc);
static void sabtty_cnputc(struct sabtty_softc *sc, int c);
static t_open_t sabttyopen;
static void sabttystart(struct tty *tp);
static void sabttystop(struct tty *tp, int rw);
static int sabttyparam(struct tty *tp, struct termios *t);
static device_method_t sab_methods[] = {
DEVMETHOD(device_probe, sab_probe),
DEVMETHOD(device_attach, sab_attach),
DEVMETHOD(device_detach, sab_detach),
DEVMETHOD(bus_print_child, bus_generic_print_child),
{ 0, 0 }
};
static device_method_t sabtty_methods[] = {
DEVMETHOD(device_probe, sabtty_probe),
DEVMETHOD(device_attach, sabtty_attach),
DEVMETHOD(device_detach, sabtty_detach),
{ 0, 0 }
};
static driver_t sab_driver = {
"sab",
sab_methods,
sizeof(struct sab_softc),
};
static driver_t sabtty_driver = {
"sabtty",
sabtty_methods,
sizeof(struct sabtty_softc),
};
static devclass_t sab_devclass;
static devclass_t sabtty_devclass;
static struct sabtty_softc *sabtty_cons;
DRIVER_MODULE(sab, ebus, sab_driver, sab_devclass, 0, 0);
DRIVER_MODULE(sabtty, sab, sabtty_driver, sabtty_devclass, 0, 0);
CONS_DRIVER(sab, sab_cnprobe, sab_cninit, sab_cnterm, sab_cngetc,
sab_cncheckc, sab_cnputc, sab_cndbctl);
struct sabtty_rate {
int baud;
int n, m;
};
struct sabtty_rate sabtty_baudtable[] = {
{ 50, 35, 10 },
{ 75, 47, 9 },
{ 110, 32, 9 },
{ 134, 53, 8 },
{ 150, 47, 8 },
{ 200, 35, 8 },
{ 300, 47, 7 },
{ 600, 47, 6 },
{ 1200, 47, 5 },
{ 1800, 31, 5 },
{ 2400, 47, 4 },
{ 4800, 47, 3 },
{ 9600, 47, 2 },
{ 19200, 47, 1 },
{ 38400, 23, 1 },
{ 57600, 15, 1 },
{ 115200, 7, 1 },
{ 230400, 3, 1 },
{ 460800, 1, 1 },
{ 76800, 11, 1 },
{ 153600, 5, 1 },
{ 307200, 3, 1 },
{ 614400, 3, 0 },
{ 921600, 0, 1 },
};
static int
sab_probe(device_t dev)
{
struct resource *res;
bus_space_handle_t handle;
bus_space_tag_t tag;
uint8_t r;
int rid;
const char *name;
name = ofw_bus_get_name(dev);
if (strcmp(name, "se") != 0 && strcmp(name, "serial") != 0)
return (ENXIO);
rid = 0;
res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (res == NULL)
return (ENXIO);
tag = rman_get_bustag(res);
handle = rman_get_bushandle(res);
r = bus_space_read_1(tag, handle, SAB_VSTR) & SAB_VSTR_VMASK;
switch (r) {
case SAB_VSTR_V_1:
device_set_desc(dev, "Siemens SAB 82532 v1");
break;
case SAB_VSTR_V_2:
device_set_desc(dev, "Siemens SAB 82532 v2");
break;
case SAB_VSTR_V_32:
device_set_desc(dev, "Siemens SAB 82532 v3.2");
break;
default:
device_set_desc(dev, "Siemens SAB 82532 ???");
break;
}
bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
return (0);
}
static int
sab_attach(device_t dev)
{
struct device *child[SAB_NCHAN];
struct resource *irqres;
struct resource *iores;
struct sab_softc *sc;
int irqrid;
int iorid;
int i;
iorid = 0;
irqrid = 0;
irqres = NULL;
sc = device_get_softc(dev);
iores = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &iorid,
RF_ACTIVE);
if (iores == NULL)
goto error;
irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqrid,
RF_ACTIVE);
if (irqres == NULL)
goto error;
if (bus_setup_intr(dev, irqres, INTR_TYPE_TTY | INTR_FAST, sab_intr,
sc, &sc->sc_ih) != 0)
goto error;
sc->sc_dev = dev;
sc->sc_irqres = irqres;
sc->sc_iores = iores;
sc->sc_iorid = iorid;
sc->sc_irqres = irqres;
sc->sc_irqrid = irqrid;
sc->sc_bt = rman_get_bustag(iores);
sc->sc_bh = rman_get_bushandle(iores);
/* Set all pins, except DTR pins to be inputs */
SAB_WRITE(sc, SAB_PCR, ~(SAB_PVR_DTR_A | SAB_PVR_DTR_B));
/* Disable port interrupts */
SAB_WRITE(sc, SAB_PIM, 0xff);
SAB_WRITE(sc, SAB_PVR, SAB_PVR_DTR_A | SAB_PVR_DTR_B | SAB_PVR_MAGIC);
sc->sc_ipc = SAB_IPC_ICPL | SAB_IPC_VIS;
SAB_WRITE(sc, SAB_IPC, sc->sc_ipc);
for (i = 0; i < SAB_NCHAN; i++)
child[i] = device_add_child(dev, "sabtty", -1);
bus_generic_attach(dev);
for (i = 0; i < SAB_NCHAN; i++)
sc->sc_child[i] = device_get_softc(child[i]);
swi_add(&tty_intr_event, "sab", sab_softintr, sc, SWI_TTY,
INTR_TYPE_TTY, &sc->sc_softih);
if (sabtty_cons != NULL) {
DELAY(50000);
cninit();
}
EVENTHANDLER_REGISTER(shutdown_final, sab_shutdown, sc,
SHUTDOWN_PRI_DEFAULT);
return (0);
error:
if (irqres != NULL)
bus_release_resource(dev, SYS_RES_IRQ, irqrid, irqres);
if (iores != NULL)
bus_release_resource(dev, SYS_RES_IRQ, iorid, iores);
return (ENXIO);
}
static int
sab_detach(device_t dev)
{
struct sab_softc *sc;
sc = device_get_softc(dev);
bus_generic_detach(dev);
if (sc->sc_iores != NULL)
bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_iorid,
sc->sc_iores);
return (0);
}
static void
sab_intr(void *v)
{
struct sab_softc *sc = v;
int needsoft = 0;
uint8_t gis;
gis = SAB_READ(sc, SAB_GIS);
/* channel A */
if ((gis & (SAB_GIS_ISA1 | SAB_GIS_ISA0)) != 0)
needsoft |= sabtty_intr(sc->sc_child[0]);
/* channel B */
if ((gis & (SAB_GIS_ISB1 | SAB_GIS_ISB0)) != 0)
needsoft |= sabtty_intr(sc->sc_child[1]);
if (needsoft)
swi_sched(sc->sc_softih, 0);
}
static void
sab_softintr(void *v)
{
struct sab_softc *sc = v;
sabtty_softintr(sc->sc_child[0]);
sabtty_softintr(sc->sc_child[1]);
}
static void
sab_shutdown(void *v)
{
struct sab_softc *sc = v;
SAB_WRITE(sc, SAB_IPC, SAB_IPC_ICPL | SAB_IPC_VIS);
SAB_WRITE(sc, SAB_RFC, SAB_READ(sc, SAB_RFC) & ~SAB_RFC_RFDF);
}
static int
sabtty_probe(device_t dev)
{
char desc[32];
snprintf(desc, sizeof(desc), "ttyz%d", device_get_unit(dev));
device_set_desc_copy(dev, desc);
return (0);
}
static int
sabtty_attach(device_t dev)
{
struct sabtty_softc *sc;
struct tty *tp;
char mode[32];
int baud;
int clen;
char parity;
int stop;
char c;
sc = device_get_softc(dev);
mtx_init(&sc->sc_mtx, "sabtty", NULL, MTX_SPIN);
sc->sc_dev = dev;
sc->sc_parent = device_get_softc(device_get_parent(dev));
sc->sc_bt = sc->sc_parent->sc_bt;
sc->sc_channel = device_get_unit(dev) & 1;
sc->sc_iput = sc->sc_iget = sc->sc_ibuf;
sc->sc_oget = sc->sc_obuf;
switch (sc->sc_channel) {
case 0: /* port A */
sc->sc_pvr_dtr = SAB_PVR_DTR_A;
sc->sc_pvr_dsr = SAB_PVR_DSR_A;
bus_space_subregion(sc->sc_bt, sc->sc_parent->sc_bh,
SAB_CHAN_A, SAB_CHANLEN, &sc->sc_bh);
break;
case 1: /* port B */
sc->sc_pvr_dtr = SAB_PVR_DTR_B;
sc->sc_pvr_dsr = SAB_PVR_DSR_B;
bus_space_subregion(sc->sc_bt, sc->sc_parent->sc_bh,
SAB_CHAN_B, SAB_CHANLEN, &sc->sc_bh);
break;
default:
return (ENXIO);
}
tp = ttyalloc();
tp->t_sc = sc;
sc->sc_tty = tp;
tp->t_oproc = sabttystart;
tp->t_param = sabttyparam;
tp->t_modem = sabttymodem;
tp->t_break = sabttybreak;
tp->t_stop = sabttystop;
tp->t_open = sabttyopen;
ttycreate(tp, NULL, 0, 0, "z%r", device_get_unit(dev));
if (sabtty_console(dev, mode, sizeof(mode))) {
if (sscanf(mode, "%d,%d,%c,%d,%c", &baud, &clen, &parity,
&stop, &c) == 5) {
ttyconsolemode(tp, baud);
tp->t_cflag = CREAD | CLOCAL;
switch (clen) {
case 5:
tp->t_cflag |= CS5;
break;
case 6:
tp->t_cflag |= CS6;
break;
case 7:
tp->t_cflag |= CS7;
break;
case 8:
default:
tp->t_cflag |= CS8;
break;
}
if (parity == 'e')
tp->t_cflag |= PARENB;
else if (parity == 'o')
tp->t_cflag |= PARENB | PARODD;
if (stop == 2)
tp->t_cflag |= CSTOPB;
}
device_printf(dev, "console %s\n", mode);
sc->sc_console = 1;
sabtty_cons = sc;
}
return (0);
}
static int
sabtty_detach(device_t dev)
{
return (bus_generic_detach(dev));
}
static int
sabtty_intr(struct sabtty_softc *sc)
{
int clearfifo;
int needsoft;
uint8_t isr0;
uint8_t isr1;
uint8_t c;
int brk;
int len;
int i;
brk = 0;
len = 0;
clearfifo = 0;
needsoft = 0;
SABTTY_LOCK(sc);
isr0 = SAB_READ(sc, SAB_ISR0);
isr1 = SAB_READ(sc, SAB_ISR1);
if (isr0 & SAB_ISR0_RPF) {
len = 32;
clearfifo = 1;
}
if (isr0 & SAB_ISR0_TCD) {
len = (32 - 1) & SAB_READ(sc, SAB_RBCL);
clearfifo = 1;
}
if (isr0 & SAB_ISR0_TIME) {
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RFRD);
}
if (isr0 & SAB_ISR0_RFO)
clearfifo = 1;
if (len != 0) {
for (i = 0; i < len; i++) {
c = SAB_READ(sc, SAB_RFIFO);
#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
if (sc->sc_console != 0 && (i & 1) == 0)
brk = kdb_alt_break(c,
&sc->sc_alt_break_state);
#endif
*sc->sc_iput++ = c;
if (sc->sc_iput == sc->sc_ibuf + sizeof(sc->sc_ibuf))
sc->sc_iput = sc->sc_ibuf;
}
needsoft = 1;
}
if (clearfifo) {
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RMC);
}
if (isr1 & SAB_ISR1_ALLS) {
sc->sc_tx_done = 1;
sc->sc_imr1 |= SAB_IMR1_ALLS;
SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);
needsoft = 1;
}
#if defined(DDB) && defined(BREAK_TO_DEBUGGER)
if (sc->sc_console != 0 && (isr1 & SAB_ISR1_BRK) != 0) {
brk = 1;
}
#endif
if (isr1 & SAB_ISR1_XPR) {
if (sc->sc_ocnt > 0) {
len = min(sc->sc_ocnt, 32);
for (i = 0; i < len; i++)
SAB_WRITE(sc, SAB_XFIFO + i, *sc->sc_oget++);
sc->sc_ocnt -= len;
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_XF);
}
if (sc->sc_ocnt == 0) {
sc->sc_imr1 |= SAB_IMR1_XPR;
sc->sc_imr1 &= ~SAB_IMR1_ALLS;
SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);
}
}
SABTTY_UNLOCK(sc);
if (brk != 0)
breakpoint();
return (needsoft);
}
static void
sabtty_softintr(struct sabtty_softc *sc)
{
struct tty *tp = sc->sc_tty;
int data;
int stat;
if ((tp->t_state & TS_ISOPEN) == 0)
return;
while (sc->sc_iget != sc->sc_iput) {
data = *sc->sc_iget++;
stat = *sc->sc_iget++;
if (stat & SAB_RSTAT_PE)
data |= TTY_PE;
if (stat & SAB_RSTAT_FE)
data |= TTY_FE;
if (sc->sc_iget == sc->sc_ibuf + sizeof(sc->sc_ibuf))
sc->sc_iget = sc->sc_ibuf;
ttyld_rint(tp, data);
}
if (sc->sc_tx_done != 0) {
sc->sc_tx_done = 0;
tp->t_state &= ~TS_BUSY;
ttyld_start(tp);
}
}
static int
sabttyopen(struct tty *tp, struct cdev *dev __unused)
{
struct sabtty_softc *sc;
sc = tp->t_sc;
sc->sc_iput = sc->sc_iget = sc->sc_ibuf;
SABTTY_LOCK(sc);
sabtty_reset(sc);
sc->sc_parent->sc_ipc = SAB_IPC_ICPL;
SAB_WRITE(sc->sc_parent, SAB_IPC, sc->sc_parent->sc_ipc);
sc->sc_imr0 = SAB_IMR0_PERR | SAB_IMR0_FERR | SAB_IMR0_PLLA;
SAB_WRITE(sc, SAB_IMR0, sc->sc_imr0);
#if defined(DDB) && defined(BREAK_TO_DEBUGGER)
sc->sc_imr1 = SAB_IMR1_ALLS | SAB_IMR1_XDU |
SAB_IMR1_TIN | SAB_IMR1_CSC | SAB_IMR1_XMR | SAB_IMR1_XPR;
#else
sc->sc_imr1 = SAB_IMR1_BRK | SAB_IMR1_ALLS | SAB_IMR1_XDU |
SAB_IMR1_TIN | SAB_IMR1_CSC | SAB_IMR1_XMR | SAB_IMR1_XPR;
#endif
SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);
SAB_WRITE(sc, SAB_CCR0, SAB_READ(sc, SAB_CCR0) | SAB_CCR0_PU);
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_XRES);
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RRES);
sabtty_cec_wait(sc);
sabtty_flush(sc);
SABTTY_UNLOCK(sc);
return (0);
}
static void
sabttybreak(struct tty *tp, int brk)
{
struct sabtty_softc *sc;
sc = tp->t_sc;
if (brk)
SAB_WRITE(sc, SAB_DAFO,
SAB_READ(sc, SAB_DAFO) | SAB_DAFO_XBRK);
else
SAB_WRITE(sc, SAB_DAFO,
SAB_READ(sc, SAB_DAFO) & ~SAB_DAFO_XBRK);
}
static void
sabttystart(struct tty *tp)
{
struct sabtty_softc *sc;
sc = tp->t_sc;
if ((tp->t_state & TS_TBLOCK) != 0)
/* XXX clear RTS */;
else
/* XXX set RTS */;
if ((tp->t_state & (TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) != 0) {
ttwwakeup(tp);
return;
}
if (tp->t_outq.c_cc <= tp->t_olowat) {
if ((tp->t_state & TS_SO_OLOWAT) != 0) {
tp->t_state &= ~TS_SO_OLOWAT;
wakeup(TSA_OLOWAT(tp));
}
selwakeuppri(&tp->t_wsel, TTOPRI);
if (tp->t_outq.c_cc == 0) {
if ((tp->t_state & (TS_BUSY | TS_SO_OCOMPLETE)) ==
TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
tp->t_state &= ~TS_SO_OCOMPLETE;
wakeup(TSA_OCOMPLETE(tp));
}
return;
}
}
sc->sc_ocnt = q_to_b(&tp->t_outq, sc->sc_obuf, sizeof(sc->sc_obuf));
if (sc->sc_ocnt == 0)
return;
sc->sc_oget = sc->sc_obuf;
tp->t_state |= TS_BUSY;
sc->sc_imr1 &= ~SAB_IMR1_XPR;
SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);
ttwwakeup(tp);
}
static void
sabttystop(struct tty *tp, int flag)
{
struct sabtty_softc *sc;
sc = tp->t_sc;
if ((flag & FREAD) != 0) {
/* XXX stop reading, anything to do? */;
}
if ((flag & FWRITE) != 0) {
if ((tp->t_state & TS_BUSY) != 0) {
/* XXX do what? */
if ((tp->t_state & TS_TTSTOP) == 0)
tp->t_state |= TS_FLUSH;
}
}
}
static int
sabttyparam(struct tty *tp, struct termios *t)
{
struct sabtty_softc *sc;
sc = tp->t_sc;
return (sabtty_param(sc, tp, t));
}
int
sabttymodem(struct tty *tp, int biton, int bitoff)
{
struct sabtty_softc *sc;
u_int8_t r;
sc = tp->t_sc;
if (biton == 0 && bitoff == 0) {
if (SAB_READ(sc, SAB_STAR) & SAB_STAR_CTS)
biton |= TIOCM_CTS;
if ((SAB_READ(sc, SAB_VSTR) & SAB_VSTR_CD) == 0)
biton |= TIOCM_CD;
r = SAB_READ(sc, SAB_PVR);
if ((r & sc->sc_pvr_dtr) == 0)
biton |= TIOCM_DTR;
if ((r & sc->sc_pvr_dsr) == 0)
biton |= TIOCM_DSR;
r = SAB_READ(sc, SAB_MODE);
if ((r & (SAB_MODE_RTS|SAB_MODE_FRTS)) == SAB_MODE_RTS)
biton |= TIOCM_RTS;
return (biton);
}
bitoff |= biton;
if (bitoff & SER_RTS) {
r = SAB_READ(sc, SAB_MODE);
r &= ~SAB_MODE_FRTS;
if (biton & SER_RTS)
r |= SAB_MODE_RTS;
SAB_WRITE(sc, SAB_MODE, r);
}
if (bitoff & SER_DTR) {
r = SAB_READ(sc, SAB_PVR);
r &= ~sc->sc_pvr_dtr;
if (biton & SER_DTR)
r |= sc->sc_pvr_dtr;
SAB_WRITE(sc, SAB_PVR, r);
}
return (0);
}
int
sabtty_param(struct sabtty_softc *sc, struct tty *tp, struct termios *t)
{
int ospeed;
tcflag_t cflag;
u_int8_t dafo, r;
ospeed = sabtty_speed(t->c_ospeed);
if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
return (EINVAL);
/*
* If there were no changes, don't do anything. This avoids dropping
* input and improves performance when all we did was frob things like
* VMIN and VTIME.
*/
if (tp->t_ospeed == t->c_ospeed &&
tp->t_cflag == t->c_cflag)
return (0);
/* hang up line if ospeed is zero, otherwise raise dtr */
if (t->c_ospeed != 0)
sabttymodem(tp, SER_DTR, 0);
else
sabttymodem(tp, 0, SER_DTR);
dafo = SAB_READ(sc, SAB_DAFO);
cflag = t->c_cflag;
if (sc->sc_console != 0) {
cflag |= CLOCAL;
cflag &= ~HUPCL;
}
if (cflag & CSTOPB)
dafo |= SAB_DAFO_STOP;
else
dafo &= ~SAB_DAFO_STOP;
dafo &= ~SAB_DAFO_CHL_CSIZE;
switch (cflag & CSIZE) {
case CS5:
dafo |= SAB_DAFO_CHL_CS5;
break;
case CS6:
dafo |= SAB_DAFO_CHL_CS6;
break;
case CS7:
dafo |= SAB_DAFO_CHL_CS7;
break;
default:
dafo |= SAB_DAFO_CHL_CS8;
break;
}
dafo &= ~SAB_DAFO_PARMASK;
if (cflag & PARENB) {
if (cflag & PARODD)
dafo |= SAB_DAFO_PAR_ODD;
else
dafo |= SAB_DAFO_PAR_EVEN;
} else
dafo |= SAB_DAFO_PAR_NONE;
tp->t_ispeed = 0;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = cflag;
ttsetwater(tp);
SABTTY_LOCK(sc);
if (ospeed != 0) {
SAB_WRITE(sc, SAB_BGR, ospeed & 0xff);
r = SAB_READ(sc, SAB_CCR2);
r &= ~(SAB_CCR2_BR9 | SAB_CCR2_BR8);
r |= (ospeed >> 2) & (SAB_CCR2_BR9 | SAB_CCR2_BR8);
SAB_WRITE(sc, SAB_CCR2, r);
}
r = SAB_READ(sc, SAB_MODE);
r |= SAB_MODE_RAC;
if (cflag & CRTSCTS) {
r &= ~(SAB_MODE_RTS | SAB_MODE_FCTS);
r |= SAB_MODE_FRTS;
sc->sc_imr1 &= ~SAB_IMR1_CSC;
} else {
r |= SAB_MODE_RTS | SAB_MODE_FCTS;
r &= ~SAB_MODE_FRTS;
sc->sc_imr1 |= SAB_IMR1_CSC;
}
SAB_WRITE(sc, SAB_MODE, r);
SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);
SABTTY_UNLOCK(sc);
return (0);
}
static void
sabtty_cec_wait(struct sabtty_softc *sc)
{
int i = 50000;
for (;;) {
if ((SAB_READ(sc, SAB_STAR) & SAB_STAR_CEC) == 0)
return;
if (--i == 0)
break;
DELAY(1);
}
}
static void
sabtty_tec_wait(struct sabtty_softc *sc)
{
int i = 200000;
for (;;) {
if ((SAB_READ(sc, SAB_STAR) & SAB_STAR_TEC) == 0)
return;
if (--i == 0)
break;
DELAY(1);
}
}
static void
sabtty_reset(struct sabtty_softc *sc)
{
/* power down */
SAB_WRITE(sc, SAB_CCR0, 0);
/* set basic configuration */
SAB_WRITE(sc, SAB_CCR0,
SAB_CCR0_MCE | SAB_CCR0_SC_NRZ | SAB_CCR0_SM_ASYNC);
SAB_WRITE(sc, SAB_CCR1, SAB_CCR1_ODS | SAB_CCR1_BCR | SAB_CCR1_CM_7);
SAB_WRITE(sc, SAB_CCR2, SAB_CCR2_BDF | SAB_CCR2_SSEL | SAB_CCR2_TOE);
SAB_WRITE(sc, SAB_CCR3, 0);
SAB_WRITE(sc, SAB_CCR4, SAB_CCR4_MCK4 | SAB_CCR4_EBRG);
SAB_WRITE(sc, SAB_MODE, SAB_MODE_RTS | SAB_MODE_FCTS | SAB_MODE_RAC);
SAB_WRITE(sc, SAB_RFC,
SAB_RFC_DPS | SAB_RFC_RFDF | SAB_RFC_RFTH_32CHAR);
/* clear interrupts */
sc->sc_imr0 = sc->sc_imr1 = 0xff;
SAB_WRITE(sc, SAB_IMR0, sc->sc_imr0);
SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);
SAB_READ(sc, SAB_ISR0);
SAB_READ(sc, SAB_ISR1);
}
static void
sabtty_flush(struct sabtty_softc *sc)
{
/* clear rx fifo */
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RRES);
/* clear tx fifo */
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_XRES);
}
static int
sabtty_speed(int rate)
{
int i, len, r;
if (rate == 0)
return (0);
len = sizeof(sabtty_baudtable)/sizeof(sabtty_baudtable[0]);
for (i = 0; i < len; i++) {
if (rate == sabtty_baudtable[i].baud) {
r = sabtty_baudtable[i].n |
(sabtty_baudtable[i].m << 6);
return (r);
}
}
return (-1);
}
static void
sab_cnprobe(struct consdev *cn)
{
struct sabtty_softc *sc = sabtty_cons;
if (sc == NULL)
cn->cn_pri = CN_DEAD;
else {
cn->cn_pri = CN_REMOTE;
strcpy(cn->cn_name, device_get_desc(sc->sc_dev));
cn->cn_tp = sc->sc_tty;
}
}
static void
sab_cninit(struct consdev *cn)
{
}
static void
sab_cnterm(struct consdev *cn)
{
}
static int
sab_cngetc(struct consdev *cn)
{
struct sabtty_softc *sc = sabtty_cons;
if (sc == NULL)
return (-1);
return (sabtty_cngetc(sc));
}
static int
sab_cncheckc(struct consdev *cn)
{
struct sabtty_softc *sc = sabtty_cons;
if (sc == NULL)
return (-1);
return (sabtty_cncheckc(sc));
}
static void
sab_cnputc(struct consdev *cn, int c)
{
struct sabtty_softc *sc = sabtty_cons;
if (sc == NULL)
return;
sabtty_cnputc(sc, c);
}
static void
sab_cndbctl(struct consdev *cn, int c)
{
}
static void
sabtty_cnopen(struct sabtty_softc *sc)
{
SAB_WRITE(sc, SAB_IMR0, 0xff);
SAB_WRITE(sc, SAB_IMR1, 0xff);
SAB_WRITE(sc->sc_parent, SAB_IPC, sc->sc_parent->sc_ipc | SAB_IPC_VIS);
}
static void
sabtty_cnclose(struct sabtty_softc *sc)
{
SAB_WRITE(sc, SAB_IMR0, sc->sc_imr0);
SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);
SAB_WRITE(sc->sc_parent, SAB_IPC, sc->sc_parent->sc_ipc);
}
static int
sabtty_cngetc(struct sabtty_softc *sc)
{
uint8_t len;
uint8_t r;
sabtty_cnopen(sc);
again:
do {
r = SAB_READ(sc, SAB_STAR);
} while ((r & SAB_STAR_RFNE) == 0);
/*
* Ok, at least one byte in RFIFO, ask for permission to access RFIFO
* (I hate this chip... hate hate hate).
*/
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RFRD);
/* Wait for RFIFO to come ready */
do {
r = SAB_READ(sc, SAB_ISR0);
} while ((r & SAB_ISR0_TCD) == 0);
len = SAB_READ(sc, SAB_RBCL) & (32 - 1);
if (len == 0)
goto again; /* Shouldn't happen... */
r = SAB_READ(sc, SAB_RFIFO);
/*
* Blow away everything left in the FIFO...
*/
sabtty_cec_wait(sc);
SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RMC);
sabtty_cnclose(sc);
return (r);
}
static int
sabtty_cncheckc(struct sabtty_softc *sc)
{
int8_t r;
r = -1;
sabtty_cnopen(sc);
if ((SAB_READ(sc, SAB_STAR) & SAB_STAR_RFNE) != 0)
r = sabtty_cngetc(sc);
sabtty_cnclose(sc);
return (r);
}
static void
sabtty_cnputc(struct sabtty_softc *sc, int c)
{
sabtty_cnopen(sc);
sabtty_tec_wait(sc);
SAB_WRITE(sc, SAB_TIC, c);
sabtty_tec_wait(sc);
sabtty_cnclose(sc);
}
static int
sabtty_console(device_t dev, char *mode, int len)
{
phandle_t chosen;
phandle_t options;
phandle_t parent;
ihandle_t stdin;
ihandle_t stdout;
char output[32];
char input[32];
char name[32];
int unit;
chosen = OF_finddevice("/chosen");
options = OF_finddevice("/options");
parent = ofw_bus_get_node(device_get_parent(dev));
if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1 ||
OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1 ||
OF_getprop(options, "input-device", input, sizeof(input)) == -1 ||
OF_getprop(options, "output-device", output, sizeof(output)) == -1)
return (0);
if (parent != OF_instance_to_package(stdin) ||
parent != OF_instance_to_package(stdout))
return (0);
unit = device_get_unit(dev);
switch (unit) {
case 0: strcpy(name, "ttya"); break;
case 1: strcpy(name, "ttyb"); break;
default: strcpy(name, "ttysomething"); break;
}
if ((strcmp(input, name) == 0 && strcmp(output, name) == 0) ||
(strcmp(input, "keyboard") == 0 && strcmp(output, "screen") == 0 &&
(unit & 1) == 0)) {
if (mode != NULL) {
strcat(name, "-mode");
return (OF_getprop(options, name, mode, len) != -1);
} else
return (1);
}
return (0);
}