PERFORCE change 30345 for review
Marcel Moolenaar
marcel at FreeBSD.org
Thu May 1 13:59:34 PDT 2003
http://perforce.freebsd.org/chv.cgi?CH=30345
Change 30345 by marcel at marcel_nfs on 2003/05/01 13:59:30
This got dropped from the previous change (30343) due
to merge conflicts. Files have been copied from the
ia64 brach after accepting the conflicting merge.
This should be ok...
Affected files ...
.. //depot/projects/sio/sys/dev/sio/sio.c#2 edit
.. //depot/projects/sio/sys/dev/sio/sio_pccard.c#2 edit
.. //depot/projects/sio/sys/dev/sio/sio_pci.c#2 edit
.. //depot/projects/sio/sys/dev/sio/sioreg.h#2 edit
Differences ...
==== //depot/projects/sio/sys/dev/sio/sio.c#2 (text+ko) ====
@@ -36,7 +36,6 @@
*/
#include "opt_comconsole.h"
-#include "opt_compat.h"
#include "opt_ddb.h"
#include "opt_sio.h"
@@ -66,24 +65,18 @@
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/tty.h>
-#include <machine/bus_pio.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/timepps.h>
#include <sys/uio.h>
-
-#include <isa/isavar.h>
+#include <sys/cons.h>
#include <machine/resource.h>
+#include <machine/stdarg.h>
#include <dev/sio/sioreg.h>
#include <dev/sio/siovar.h>
-#ifdef COM_ESP
-#include <dev/ic/esp.h>
-#endif
-#include <dev/ic/ns16550.h>
-
#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
#define CALLOUT_MASK 0x80
@@ -96,19 +89,6 @@
#define UNIT_TO_MINOR(unit) ((((unit) & ~0x1fU) << (8 + 3)) \
| ((unit) & 0x1f))
-#ifdef COM_MULTIPORT
-/* checks in flags for multiport and which is multiport "master chip"
- * for a given card
- */
-#define COM_ISMULTIPORT(flags) ((flags) & 0x01)
-#define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff)
-#define COM_NOTAST4(flags) ((flags) & 0x04)
-#else
-#define COM_ISMULTIPORT(flags) (0)
-#endif /* COM_MULTIPORT */
-
-#define COM_CONSOLE(flags) ((flags) & 0x10)
-#define COM_FORCECONSOLE(flags) ((flags) & 0x20)
#define COM_LLCONSOLE(flags) ((flags) & 0x40)
#define COM_DEBUGGER(flags) ((flags) & 0x80)
#define COM_LOSESOUTINTS(flags) ((flags) & 0x08)
@@ -123,11 +103,6 @@
#define COM_TI16754(flags) ((flags) & 0x200000)
#define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24)
-#define sio_getreg(com, off) \
- (bus_space_read_1((com)->bst, (com)->bsh, (off)))
-#define sio_setreg(com, off, value) \
- (bus_space_write_1((com)->bst, (com)->bsh, (off), (value)))
-
/*
* com state bits.
* (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
@@ -163,125 +138,9 @@
"tty-level buffer overflow",
};
-#define CE_NTYPES 3
#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
-/* types. XXX - should be elsewhere */
-typedef u_int Port_t; /* hardware port */
-typedef u_char bool_t; /* boolean */
-
-/* queue of linear buffers */
-struct lbq {
- u_char *l_head; /* next char to process */
- u_char *l_tail; /* one past the last char to process */
- struct lbq *l_next; /* next in queue */
- bool_t l_queued; /* nonzero if queued */
-};
-
-/* com device structure */
-struct com_s {
- u_int flags; /* Copy isa device flags */
- u_char state; /* miscellaneous flag bits */
- bool_t active_out; /* nonzero if the callout device is open */
- u_char cfcr_image; /* copy of value written to CFCR */
-#ifdef COM_ESP
- bool_t esp; /* is this unit a hayes esp board? */
-#endif
- u_char extra_state; /* more flag bits, separate for order trick */
- u_char fifo_image; /* copy of value written to FIFO */
- bool_t hasfifo; /* nonzero for 16550 UARTs */
- bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */
- bool_t loses_outints; /* nonzero if device loses output interrupts */
- u_char mcr_image; /* copy of value written to MCR */
-#ifdef COM_MULTIPORT
- bool_t multiport; /* is this unit part of a multiport device? */
-#endif /* COM_MULTIPORT */
- bool_t no_irq; /* nonzero if irq is not attached */
- bool_t gone; /* hardware disappeared */
- bool_t poll; /* nonzero if polling is required */
- bool_t poll_output; /* nonzero if polling for output is required */
- int unit; /* unit number */
- int dtr_wait; /* time to hold DTR down on close (* 1/hz) */
- u_int tx_fifo_size;
- u_int wopeners; /* # processes waiting for DCD in open() */
-
- /*
- * The high level of the driver never reads status registers directly
- * because there would be too many side effects to handle conveniently.
- * Instead, it reads copies of the registers stored here by the
- * interrupt handler.
- */
- u_char last_modem_status; /* last MSR read by intr handler */
- u_char prev_modem_status; /* last MSR handled by high level */
-
- u_char hotchar; /* ldisc-specific char to be handled ASAP */
- u_char *ibuf; /* start of input buffer */
- u_char *ibufend; /* end of input buffer */
- u_char *ibufold; /* old input buffer, to be freed */
- u_char *ihighwater; /* threshold in input buffer */
- u_char *iptr; /* next free spot in input buffer */
- int ibufsize; /* size of ibuf (not include error bytes) */
- int ierroff; /* offset of error bytes in ibuf */
-
- struct lbq obufq; /* head of queue of output buffers */
- struct lbq obufs[2]; /* output buffers */
-
- bus_space_tag_t bst;
- bus_space_handle_t bsh;
-
- Port_t data_port; /* i/o ports */
-#ifdef COM_ESP
- Port_t esp_port;
-#endif
- Port_t int_id_port;
- Port_t modem_ctl_port;
- Port_t line_status_port;
- Port_t modem_status_port;
- Port_t intr_ctl_port; /* Ports of IIR register */
-
- struct tty *tp; /* cross reference */
-
- /* Initial state. */
- struct termios it_in; /* should be in struct tty */
- struct termios it_out;
-
- /* Lock state. */
- struct termios lt_in; /* should be in struct tty */
- struct termios lt_out;
-
- bool_t do_timestamp;
- bool_t do_dcd_timestamp;
- struct timeval timestamp;
- struct timeval dcd_timestamp;
- struct pps_state pps;
- int pps_bit;
-
- u_long bytes_in; /* statistics */
- u_long bytes_out;
- u_int delta_error_counts[CE_NTYPES];
- u_long error_counts[CE_NTYPES];
-
- u_long rclk;
-
- struct resource *irqres;
- struct resource *ioportres;
- void *cookie;
- dev_t devs[6];
-
- /*
- * Data area for output buffers. Someday we should build the output
- * buffer queue without copying data.
- */
- u_char obuf1[256];
- u_char obuf2[256];
-};
-
-#ifdef COM_ESP
-static int espattach(struct com_s *com, Port_t esp_port);
-#endif
-
static timeout_t siobusycheck;
-static u_int siodivisor(u_long rclk, speed_t speed);
static timeout_t siodtrwakeup;
static void comhardclose(struct com_s *com);
static void sioinput(struct com_s *com);
@@ -327,18 +186,16 @@
.d_kqfilter = ttykqfilter,
};
-static int comconsole = -1;
-static volatile speed_t comdefaultrate = CONSPEED;
-static u_long comdefaultrclk = DEFAULT_RCLK;
+speed_t comdefaultrate = CONSPEED;
+
+u_long comdefaultrclk = DEFAULT_RCLK;
SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, "");
-static speed_t gdbdefaultrate = GDBSPEED;
-SYSCTL_UINT(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW,
- &gdbdefaultrate, GDBSPEED, "");
+
+static speed_t gdbdefaultrate = GDBSPEED;
+SYSCTL_ULONG(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW, &gdbdefaultrate,
+ GDBSPEED, "");
+
static u_int com_events; /* input chars + weighted output completions */
-static Port_t siocniobase;
-static int siocnunit = -1;
-static Port_t siogdbiobase;
-static int siogdbunit = -1;
static void *sio_slow_ih;
static void *sio_fast_ih;
static int sio_timeout;
@@ -347,13 +204,6 @@
= CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle);
static int sio_numunits;
-#ifdef COM_ESP
-/* XXX configure this properly. */
-/* XXX quite broken for new-bus. */
-static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
-static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 };
-#endif
-
/*
* handle sysctl read/write requests for console speed
*
@@ -380,13 +230,10 @@
comdefaultrate = newspeed;
- if (comconsole < 0) /* serial console not selected? */
+ com = &sio_console;
+ if (com->consdev == NULL)
return (0);
- com = com_addr(comconsole);
- if (com == NULL)
- return (ENXIO);
-
/*
* set the initial and lock rates for /dev/ttydXX and /dev/cuaXX
* (note, the lock rates really are boolean -- if non-zero, disallow
@@ -415,544 +262,498 @@
0, 0, sysctl_machdep_comdefaultrate, "I", "");
/* TUNABLE_INT("machdep.conspeed", &comdefaultrate); */
-#define SET_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) | (bit))
-#define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit))
+#ifdef SIO_DEBUG
+static void
+siodebug(struct com_s *com, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (com != NULL)
+ device_print_prettyname(com->dev);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+#else
+static __inline void
+siodebug(struct com_s *com, const char *fmt, ...)
+{
+}
+#endif
/*
- * Unload the driver and clear the table.
- * XXX this is mostly wrong.
- * XXX TODO:
- * This is usually called when the card is ejected, but
- * can be caused by a kldunload of a controller driver.
- * The idea is to reset the driver's view of the device
- * and ensure that any driver entry points such as
- * read and write do not hang.
+ * Flush the UART. Flush the transmitter FIFO and shift register first, then
+ * flush the receiver FIFO. In this order flushing works correctly even when
+ * the UART is in loopback mode. Bad timing may cause at most one character
+ * to remain in the receiver FIFO/buffer after we're done flushing. This
+ * would be the character that is (partly) in the receiver shift register.
*/
-int
-siodetach(dev)
- device_t dev;
+static int
+sioflush(struct com_s *com)
{
- struct com_s *com;
- int i;
+ int delay, limit;
+
+ /* 1/10th the time to transmit 1 character (estimate). */
+ delay = 16000000 * com->reg_dl / com->rclk;
- com = (struct com_s *) device_get_softc(dev);
- if (com == NULL) {
- device_printf(dev, "NULL com in siounload\n");
- return (0);
+ /* Flush the transmitter. */
+ limit = (com->fifosize) ? com->fifosize : 1;
+ limit = (limit + 2) * 10;
+ while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit)
+ DELAY(delay);
+ if (limit == 0) {
+ siodebug(NULL, "transmitter appears stuck... ");
+ return (EIO);
}
- com->gone = 1;
- for (i = 0 ; i < 6; i++)
- destroy_dev(com->devs[i]);
- if (com->irqres) {
- bus_teardown_intr(dev, com->irqres, com->cookie);
- bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres);
+
+ DELAY(10*delay);
+
+ /*
+ * Flush the receiver. Pick an arbitrary high limit to avoid
+ * getting stuck in an infinite loop when the hardware is
+ * broken.
+ */
+ limit=32768;
+ while ((sio_getreg(com, com_lsr) & LSR_RXRDY) && --limit) {
+ (void)sio_getreg(com, com_data);
+ /* XXX barrier */
+ DELAY(5*delay);
}
- if (com->ioportres)
- bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres);
- if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
- device_printf(dev, "still open, forcing close\n");
- (*linesw[com->tp->t_line].l_close)(com->tp, 0);
- com->tp->t_gen++;
- ttyclose(com->tp);
- ttwakeup(com->tp);
- ttwwakeup(com->tp);
- } else {
- if (com->ibuf != NULL)
- free(com->ibuf, M_DEVBUF);
- device_set_softc(dev, NULL);
- free(com, M_DEVBUF);
+ if (limit == 0) {
+ siodebug(NULL, "receiver appears broken... ");
+ return (EIO);
}
+
return (0);
}
-int
-sioprobe(dev, xrid, rclk, noprobe)
- device_t dev;
- int xrid;
- u_long rclk;
- int noprobe;
+/*
+ * Initialize the FIFOs and determine the size of the receiver FIFO. We
+ * assume that the transmitter FIFO has the same size. First we set DMA
+ * mode (mode 1) with the highest trigger level. In combination with
+ * loopback this allows us to determine the size of the receiver FIFO.
+ * After that we re-initialize with a medium high trigger level. This
+ * function is expected to be called with FIFOs disabled.
+ */
+static int
+sioinitfifo(struct com_s *com)
{
-#if 0
- static bool_t already_init;
- device_t xdev;
-#endif
- struct com_s *com;
- u_int divisor;
- bool_t failures[10];
- int fn;
- device_t idev;
- Port_t iobase;
- intrmask_t irqmap[4];
- intrmask_t irqs;
- u_char mcr_image;
- int result;
- u_long xirq;
- u_int flags = device_get_flags(dev);
- int rid;
- struct resource *port;
+ int count, delay, limit;
+
+ com->hasfifo = 0;
+ com->fifosize = 0;
- rid = xrid;
- port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
- 0, ~0, IO_COMSIZE, RF_ACTIVE);
- if (!port)
- return (ENXIO);
+ /* 1/10th the time to transmit 1 character (estimate). */
+ delay = 16000000 * com->reg_dl / com->rclk;
- com = malloc(sizeof(*com), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (com == NULL)
- return (ENOMEM);
- device_set_softc(dev, com);
- com->bst = rman_get_bustag(port);
- com->bsh = rman_get_bushandle(port);
- if (rclk == 0)
- rclk = DEFAULT_RCLK;
- com->rclk = rclk;
+ /*
+ * Make sure the transmitter is idle before we play with the
+ * FIFOs. We don't wait more than roughly three times as long
+ * as needed to send a single character. Given that we may need
+ * to wait for both the THR and the TSR to clear, this leaves
+ * us roughly 1 character time slack.
+ */
+ limit = 30;
+ while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
+ if (delay)
+ DELAY(delay);
+ }
+ if (limit == 0) {
+ siodebug(NULL, "transmitter appears stuck");
+ return (EIO);
+ }
- while (sio_inited != 2)
- if (atomic_cmpset_int(&sio_inited, 0, 1)) {
- mtx_init(&sio_lock, sio_driver_name, NULL,
- (comconsole != -1) ?
- MTX_SPIN | MTX_QUIET : MTX_SPIN);
- atomic_store_rel_int(&sio_inited, 2);
- }
+ /*
+ * Set loopback mode. This avoids having garbage on the wire and
+ * also allows us send and receive data. We set DTR and RTS to
+ * avoid the possibility that automatic flow-control prevents
+ * any data from being sent.
+ */
+ sio_setreg(com, com_mcr, com->reg_mcr | MCR_LOOPBACK|MCR_DTR|MCR_RTS);
+ /* XXX barrier */
-#if 0
/*
- * XXX this is broken - when we are first called, there are no
- * previously configured IO ports. We could hard code
- * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse.
- * This code has been doing nothing since the conversion since
- * "count" is zero the first time around.
+ * Enable FIFOs. Set DMA mode with the highest trigger level so
+ * that we can determine the FIFO size. Since this is the first
+ * time we enable the FIFOs, reset them.
*/
- if (!already_init) {
- /*
- * Turn off MCR_IENABLE for all likely serial ports. An unused
- * port with its MCR_IENABLE gate open will inhibit interrupts
- * from any used port that shares the interrupt vector.
- * XXX the gate enable is elsewhere for some multiports.
- */
- device_t *devs;
- int count, i, xioport;
+ com->reg_fcr = FCR_ENABLE | FCR_DMA_MODE | FCR_RX_HIGH;
+ sio_setreg(com, com_fcr, com->reg_fcr | FCR_XMT_RST | FCR_RCV_RST);
+ /* XXX barrier */
- devclass_get_devices(sio_devclass, &devs, &count);
- for (i = 0; i < count; i++) {
- xdev = devs[i];
- if (device_is_enabled(xdev) &&
- bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport,
- NULL) == 0)
- outb(xioport + com_mcr, 0);
- }
- free(devs, M_TEMP);
- already_init = TRUE;
+ /* Check if the UART has FIFOs. If not, we're done. */
+ com->hasfifo = (sio_getreg(com, com_iir) & IIR_FIFO_MASK) ? 1 : 0;
+ if (!com->hasfifo) {
+ sio_setreg(com, com_mcr, com->reg_mcr);
+ /* XXX barrier */
+ siodebug(NULL, "no FIFOs... ");
+ return (0);
}
-#endif
- if (COM_LLCONSOLE(flags)) {
- printf("sio%d: reserved for low-level i/o\n",
- device_get_unit(dev));
- bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
- device_set_softc(dev, NULL);
- free(com, M_DEVBUF);
- return (ENXIO);
+ /* We have FIFOs. Flush the transmitter and receiver. */
+ if (sioflush(com)) {
+ sio_setreg(com, com_mcr, com->reg_mcr);
+ /* XXX barrier */
+ goto fallback;
}
+ sio_setreg(com, com_ier, IER_ERXRDY);
+ /* XXX barrier */
+
/*
- * If the device is on a multiport card and has an AST/4
- * compatible interrupt control register, initialize this
- * register and prepare to leave MCR_IENABLE clear in the mcr.
- * Otherwise, prepare to set MCR_IENABLE in the mcr.
- * Point idev to the device struct giving the correct id_irq.
- * This is the struct for the master device if there is one.
+ * We should have a sufficiently clean "pipe" to determine the
+ * size of the FIFOs. We send as much characters as is reasonable
+ * and wait for the the RX interrupt to be asserted, counting the
+ * characters as we send them. Based on that count we know the
+ * FIFO size.
*/
- idev = dev;
- mcr_image = MCR_IENABLE;
-#ifdef COM_MULTIPORT
- if (COM_ISMULTIPORT(flags)) {
- Port_t xiobase;
- u_long io;
+ count = 0;
+ while ((sio_getreg(com, com_iir) & IIR_RXRDY) == 0 && count < 1030) {
+ sio_setreg(com, com_data, 0);
+ /* XXX barrier */
+ count++;
- idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags));
- if (idev == NULL) {
- printf("sio%d: master device %d not configured\n",
- device_get_unit(dev), COM_MPMASTER(flags));
- idev = dev;
+ limit = 30;
+ while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
+ if (delay)
+ DELAY(delay);
}
- if (!COM_NOTAST4(flags)) {
- if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io,
- NULL) == 0) {
- xiobase = io;
- if (bus_get_resource(idev, SYS_RES_IRQ, 0,
- NULL, NULL) == 0)
- outb(xiobase + com_scr, 0x80);
- else
- outb(xiobase + com_scr, 0);
- }
- mcr_image = 0;
+ if (limit == 0) {
+ sio_setreg(com, com_ier, 0);
+ /* XXX barrier */
+ sio_setreg(com, com_mcr, com->reg_mcr);
+ /* XXX barrier */
+ siodebug(NULL, "can't determine FIFO size... ");
+ goto fallback;
}
}
-#endif /* COM_MULTIPORT */
- if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0)
- mcr_image = 0;
+
+ sio_setreg(com, com_ier, 0);
+ /* XXX barrier */
+
+ /* Reset FIFOs. */
+ com->reg_fcr = FCR_ENABLE;
+ sio_setreg(com, com_fcr, com->reg_fcr | FCR_XMT_RST | FCR_RCV_RST);
+ /* XXX barrier */
+
+ sio_setreg(com, com_mcr, com->reg_mcr);
+ /* XXX barrier */
+
+ if (count >= 14 && count < 16)
+ com->fifosize = 16; /* 16550 */
+ else if (count >= 28 && count < 32)
+ com->fifosize = 32; /* 16650 */
+ else if (count >= 56 && count < 64)
+ com->fifosize = 64; /* 16750 */
+ else if (count >= 112 && count < 128)
+ com->fifosize = 128; /* 16950 */
+ else
+ com->fifosize = 1; /* XXX */
- bzero(failures, sizeof failures);
- iobase = rman_get_start(port);
+ siodebug(NULL, "count=%d; FIFO=%d... ", count, com->fifosize);
+ return (0);
- /*
- * We don't want to get actual interrupts, just masked ones.
- * Interrupts from this line should already be masked in the ICU,
- * but mask them in the processor as well in case there are some
- * (misconfigured) shared interrupts.
- */
- mtx_lock_spin(&sio_lock);
-/* EXTRA DELAY? */
+fallback:
+ siodebug(NULL, "disabling FIFOs... ");
+ com->hasfifo = 0;
+ return (0);
+}
- /*
- * For the TI16754 chips, set prescaler to 1 (4 is often the
- * default after-reset value) as otherwise it's impossible to
- * get highest baudrates.
- */
- if (COM_TI16754(flags)) {
- u_char cfcr, efr;
+static void
+siodescribe(struct com_s *com)
+{
+ int has_spr;
+ u_char spr;
- cfcr = sio_getreg(com, com_cfcr);
- sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE);
- efr = sio_getreg(com, com_efr);
- /* Unlock extended features to turn off prescaler. */
- sio_setreg(com, com_efr, efr | EFR_EFE);
- /* Disable EFR. */
- sio_setreg(com, com_cfcr, (cfcr != CFCR_EFR_ENABLE) ? cfcr : 0);
- /* Turn off prescaler. */
- sio_setreg(com, com_mcr,
- sio_getreg(com, com_mcr) & ~MCR_PRESCALE);
- sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE);
- sio_setreg(com, com_efr, efr);
- sio_setreg(com, com_cfcr, cfcr);
+ if (com->hasfifo) {
+ /*
+ * NS16550 or higher. The minimum FIFO size is 16 bytes for
+ * the NS16550. The ST16C650 has 32 bytes FIFOs and the
+ * NS16750 has 64 bytes FIFOs.
+ */
+ switch (com->fifosize) {
+ case 16:
+ /*
+ * XXX Should we try to see if we have a broken
+ * 16550 or a fixed 16550A?
+ */
+ device_set_desc(com->dev, "16550 or compatible");
+ break;
+ case 32:
+ /* XXX Should we check features? */
+ device_set_desc(com->dev, "16650 or compatible");
+ break;
+ case 64:
+ /* XXX Should we check features? */
+ device_set_desc(com->dev, "16750 or compatible");
+ break;
+ case 128:
+ /* XXX Should we check features? */
+ device_set_desc(com->dev, "16950 or compatible");
+ break;
+ default:
+ /* XXX Probably not right. */
+ device_set_desc(com->dev,
+ "Non-standard or broken UART with FIFO");
+ break;
+ }
+ } else {
+ /*
+ * NS16450 or lower. Use the Scratch Pad Register (SPR) to
+ * differentiate the NS16450 from the INS8250.
+ */
+ spr = sio_getreg(com, com_scr);
+ sio_setreg(com, com_scr, ~spr);
+ /* XXX barrier */
+ has_spr = (sio_getreg(com, com_scr) == ~spr) ? 1 : 0;
+ /* XXX barrier */
+ sio_setreg(com, com_scr, spr);
+ /* XXX barrier */
+ if (!has_spr)
+ device_set_desc(com->dev, "8250 or compatible");
+ else
+ device_set_desc(com->dev, "16450 or compatible");
}
+}
+
+u_int
+siodivisor(u_long rclk, u_long speed)
+{
+ long actual_speed;
+ u_int divisor;
+ int error;
+
+ if (speed == 0)
+ return (0);
+#if UINT_MAX > (ULONG_MAX - 1) / 8
+ if (speed > (ULONG_MAX - 1) / 8)
+ return (0);
+#endif
+ divisor = (rclk / (8UL * speed) + 1) / 2;
+ if (divisor == 0 || divisor >= 65536)
+ return (0);
+ actual_speed = rclk / (16UL * divisor);
- /*
- * Initialize the speed and the word size and wait long enough to
- * drain the maximum of 16 bytes of junk in device output queues.
- * The speed is undefined after a master reset and must be set
- * before relying on anything related to output. There may be
- * junk after a (very fast) soft reboot and (apparently) after
- * master reset.
- * XXX what about the UART bug avoided by waiting in comparam()?
- * We don't want to to wait long enough to drain at 2 bps.
- */
- if (iobase == siocniobase)
- DELAY((16 + 1) * 1000000 / (comdefaultrate / 10));
- else {
- sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS);
- divisor = siodivisor(rclk, SIO_TEST_SPEED);
- sio_setreg(com, com_dlbl, divisor & 0xff);
- sio_setreg(com, com_dlbh, divisor >> 8);
- sio_setreg(com, com_cfcr, CFCR_8BITS);
- DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10));
- }
+ /* 10 times error in percent: */
+ error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2;
- /*
- * Enable the interrupt gate and disable device interupts. This
- * should leave the device driving the interrupt line low and
- * guarantee an edge trigger if an interrupt can be generated.
- */
-/* EXTRA DELAY? */
- sio_setreg(com, com_mcr, mcr_image);
- sio_setreg(com, com_ier, 0);
- DELAY(1000); /* XXX */
- irqmap[0] = isa_irq_pending();
+ /* 3.0% maximum error tolerance: */
+ if (error < -30 || error > 30)
+ return (0);
- /*
- * Attempt to set loopback mode so that we can send a null byte
- * without annoying any external device.
- */
-/* EXTRA DELAY? */
- sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK);
+ return (divisor);
+}
- /*
- * Attempt to generate an output interrupt. On 8250's, setting
- * IER_ETXRDY generates an interrupt independent of the current
- * setting and independent of whether the THR is empty. On 16450's,
- * setting IER_ETXRDY generates an interrupt independent of the
- * current setting. On 16550A's, setting IER_ETXRDY only
- * generates an interrupt when IER_ETXRDY is not already set.
- */
- sio_setreg(com, com_ier, IER_ETXRDY);
+/*
+ * Do some non-destructive (for this device that is) tests
+ * to make sure we have something that looks like an UART.
+ */
+int sioprobe1(struct com_s *com)
+{
+ u_char lcr, val;
- /*
- * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
- * an interrupt. They'd better generate one for actually doing
- * output. Loopback may be broken on the same incompatibles but
- * it's unlikely to do more than allow the null byte out.
- */
- sio_setreg(com, com_data, 0);
- DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10));
+ lcr = sio_getreg(com, com_lcr);
+ sio_setreg(com, com_lcr, lcr & ~LCR_DLAB);
- /*
- * Turn off loopback mode so that the interrupt gate works again
- * (MCR_IENABLE was hidden). This should leave the device driving
- * an interrupt line high. It doesn't matter if the interrupt
- * line oscillates while we are not looking at it, since interrupts
- * are disabled.
- */
-/* EXTRA DELAY? */
- sio_setreg(com, com_mcr, mcr_image);
-
- /*
- * It seems my Xircom CBEM56G Cardbus modem wants to be reset
- * to 8 bits *again*, or else probe test 0 will fail.
- * gwk at sgi.com, 4/19/2001
- */
- sio_setreg(com, com_cfcr, CFCR_8BITS);
+ /* Check known 0 bits that don't depend on DLAB. */
+ val = sio_getreg(com, com_iir);
+ if (val & 0x30)
+ goto fail;
+ val = sio_getreg(com, com_mcr);
+ if (val & 0xe0)
+ goto fail;
- /*
- * Some pcmcia cards have the "TXRDY bug", so we check everyone
- * for IIR_TXRDY implementation ( Palido 321s, DC-1S... )
- */
- if (noprobe) {
- /* Reading IIR register twice */
- for (fn = 0; fn < 2; fn ++) {
- DELAY(10000);
- failures[6] = sio_getreg(com, com_iir);
- }
- /* Check IIR_TXRDY clear ? */
- result = 0;
- if (failures[6] & IIR_TXRDY) {
- /* No, Double check with clearing IER */
- sio_setreg(com, com_ier, 0);
- if (sio_getreg(com, com_iir) & IIR_NOPEND) {
- /* Ok. We discovered TXRDY bug! */
- SET_FLAG(dev, COM_C_IIR_TXRDYBUG);
- } else {
- /* Unknown, Just omit this chip.. XXX */
- result = ENXIO;
- sio_setreg(com, com_mcr, 0);
- }
- } else {
- /* OK. this is well-known guys */
- CLR_FLAG(dev, COM_C_IIR_TXRDYBUG);
- }
- sio_setreg(com, com_ier, 0);
- sio_setreg(com, com_cfcr, CFCR_8BITS);
- mtx_unlock_spin(&sio_lock);
- bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
- if (iobase == siocniobase)
- result = 0;
- if (result != 0) {
- device_set_softc(dev, NULL);
- free(com, M_DEVBUF);
- }
- return (result);
- }
+ /* Check known 0 bits that depend on !DLAB. */
+ val = sio_getreg(com, com_ier);
+ if (val & 0xf0)
+ goto fail;
- /*
- * Check that
- * o the CFCR, IER and MCR in UART hold the values written to them
- * (the values happen to be all distinct - this is good for
- * avoiding false positive tests from bus echoes).
- * o an output interrupt is generated and its vector is correct.
- * o the interrupt goes away when the IIR in the UART is read.
- */
-/* EXTRA DELAY? */
- failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS;
- failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY;
- failures[2] = sio_getreg(com, com_mcr) - mcr_image;
- DELAY(10000); /* Some internal modems need this time */
- irqmap[1] = isa_irq_pending();
- failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY;
- DELAY(1000); /* XXX */
- irqmap[2] = isa_irq_pending();
- failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND;
+ /* XXX we can do more. */
- /*
- * Turn off all device interrupts and check that they go off properly.
- * Leave MCR_IENABLE alone. For ports without a master port, it gates
- * the OUT2 output of the UART to
- * the ICU input. Closing the gate would give a floating ICU input
- * (unless there is another device driving it) and spurious interrupts.
- * (On the system that this was first tested on, the input floats high
- * and gives a (masked) interrupt as soon as the gate is closed.)
- */
sio_setreg(com, com_ier, 0);
- sio_setreg(com, com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
- failures[7] = sio_getreg(com, com_ier);
- DELAY(1000); /* XXX */
- irqmap[3] = isa_irq_pending();
- failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND;
+ /* XXX barrier */
+ sio_setreg(com, com_lcr, lcr);
+ /* XXX barrier */
+ return (0);
+
+ fail:
+ sio_setreg(com, com_lcr, lcr);
+ /* XXX barrier */
+ return (ENXIO);
+}
+
+/*
+ * Unload the driver and clear the table.
+ * XXX this is mostly wrong.
+ * XXX TODO:
+ * This is usually called when the card is ejected, but
+ * can be caused by a kldunload of a controller driver.
+ * The idea is to reset the driver's view of the device
+ * and ensure that any driver entry points such as
+ * read and write do not hang.
+ */
+int
+siodetach(device_t dev)
+{
+ struct com_s *com;
+ int i;
- mtx_unlock_spin(&sio_lock);
+ com = (struct com_s *)device_get_softc(dev);
+ com->gone = 1;
- irqs = irqmap[1] & ~irqmap[0];
- if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 &&
- ((1 << xirq) & irqs) == 0) {
- printf(
- "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n",
- device_get_unit(dev), xirq, irqs);
- printf(
- "sio%d: port may not be enabled\n",
- device_get_unit(dev));
+ if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
+ device_printf(dev, "still open, forcing close\n");
+ (*linesw[com->tp->t_line].l_close)(com->tp, 0);
+ com->tp->t_gen++;
+ ttyclose(com->tp);
+ ttwakeup(com->tp);
+ ttwwakeup(com->tp);
}
- if (bootverbose)
- printf("sio%d: irq maps: %#x %#x %#x %#x\n",
- device_get_unit(dev),
- irqmap[0], irqmap[1], irqmap[2], irqmap[3]);
-
- result = 0;
- for (fn = 0; fn < sizeof failures; ++fn)
- if (failures[fn]) {
- sio_setreg(com, com_mcr, 0);
- result = ENXIO;
- if (bootverbose) {
- printf("sio%d: probe failed test(s):",
- device_get_unit(dev));
- for (fn = 0; fn < sizeof failures; ++fn)
- if (failures[fn])
- printf(" %d", fn);
- printf("\n");
- }
- break;
- }
- bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
- if (iobase == siocniobase)
- result = 0;
- if (result != 0) {
- device_set_softc(dev, NULL);
- free(com, M_DEVBUF);
+ if (com->irq_res) {
+ bus_teardown_intr(dev, com->irq_res, com->irq_cookie);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, com->irq_res);
}
- return (result);
+ bus_release_resource(dev, com->addr_type, com->addr_rid, com->addr_res);
+ for (i = 0 ; i < 6; i++)
+ destroy_dev(com->devs[i]);
+ if (com->ibuf != NULL)
+ free(com->ibuf, M_DEVBUF);
+ return (0);
}
-#ifdef COM_ESP
-static int
-espattach(com, esp_port)
- struct com_s *com;
- Port_t esp_port;
+int
+sioprobe(device_t dev)
{
- u_char dips;
- u_char val;
+ struct com_s *com;
+ int error;
+ u_int flags;
+
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list