PERFORCE change 37696 for review
Marcel Moolenaar
marcel at FreeBSD.org
Sat Sep 6 18:25:31 PDT 2003
http://perforce.freebsd.org/chv.cgi?CH=37696
Change 37696 by marcel at marcel_nfs on 2003/09/06 18:25:18
Revert the SIO/ng from this branch (with extreme prejudice).
Affected files ...
.. //depot/projects/ia64/sys/conf/files#98 edit
.. //depot/projects/ia64/sys/conf/files.ia64#47 edit
.. //depot/projects/ia64/sys/dev/sio/sio.c#50 edit
.. //depot/projects/ia64/sys/dev/sio/sio_cons.c#14 delete
.. //depot/projects/ia64/sys/dev/sio/sio_ebus.c#8 edit
.. //depot/projects/ia64/sys/dev/sio/sio_isa.c#18 edit
.. //depot/projects/ia64/sys/dev/sio/sio_pccard.c#12 edit
.. //depot/projects/ia64/sys/dev/sio/sio_pci.c#19 edit
.. //depot/projects/ia64/sys/dev/sio/sio_puc.c#10 edit
.. //depot/projects/ia64/sys/dev/sio/sioreg.h#8 edit
.. //depot/projects/ia64/sys/dev/sio/siovar.h#16 edit
.. //depot/projects/ia64/sys/ia64/ia64/sio_machdep.c#10 delete
Differences ...
==== //depot/projects/ia64/sys/conf/files#98 (text+ko) ====
@@ -677,8 +677,6 @@
dev/si/si_eisa.c optional si eisa
dev/si/si_isa.c optional si isa
dev/si/si_pci.c optional si pci
-dev/sio/sio.c optional sio
-dev/sio/sio_cons.c optional sio
dev/sio/sio_ebus.c optional sio ebus
dev/sio/sio_pccard.c optional sio card
dev/sio/sio_pccard.c optional sio pccard
==== //depot/projects/ia64/sys/conf/files.ia64#47 (text+ko) ====
@@ -57,7 +57,7 @@
dev/kbd/kbd.c optional sc
dev/kbd/kbd.c optional ukbd
dev/ppc/ppc.c optional ppc isa
-dev/sio/sio_isa.c optional sio acpi
+dev/sio/sio.c optional sio
dev/sio/sio_isa.c optional sio isa
dev/syscons/schistory.c optional sc
dev/syscons/scmouse.c optional sc
@@ -115,7 +115,6 @@
ia64/ia64/sal.c standard
ia64/ia64/sapic.c standard
ia64/ia64/setjmp.S standard
-ia64/ia64/sio_machdep.c optional sio
ia64/ia64/ssc.c optional ski
ia64/ia64/sscdisk.c optional ski
ia64/ia64/support.S standard
@@ -129,8 +128,8 @@
ia64/isa/isa.c optional isa
ia64/isa/isa_dma.c optional isa
ia64/pci/pci_cfgreg.c optional pci
-isa/atkbd_isa.c optional isa atkbd
-isa/atkbdc_isa.c optional isa atkbdc
+isa/atkbd_isa.c optional atkbd isa
+isa/atkbdc_isa.c optional atkbdc isa
isa/fd.c optional fdc isa
isa/psm.c optional psm isa
isa/syscons_isa.c optional sc
==== //depot/projects/ia64/sys/dev/sio/sio.c#50 (text+ko) ====
@@ -38,6 +38,7 @@
__FBSDID("$FreeBSD: src/sys/dev/sio/sio.c,v 1.404 2003/08/28 03:54:49 njl Exp $");
#include "opt_comconsole.h"
+#include "opt_compat.h"
#include "opt_ddb.h"
#include "opt_sio.h"
@@ -67,6 +68,7 @@
#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>
@@ -76,12 +78,18 @@
#include <ddb/ddb.h>
#endif
+#include <isa/isavar.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
@@ -94,6 +102,19 @@
#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)
@@ -108,6 +129,11 @@
#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
@@ -143,9 +169,129 @@
"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;
+#ifdef ALT_BREAK_TO_DEBUGGER
+ int alt_brk_state;
+#endif
+
+ 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;
+ int ioportrid;
+ 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);
@@ -191,16 +337,18 @@
.d_kqfilter = ttykqfilter,
};
-speed_t comdefaultrate = CONSPEED;
-
-u_long comdefaultrclk = DEFAULT_RCLK;
+int comconsole = -1;
+static volatile speed_t comdefaultrate = CONSPEED;
+static u_long comdefaultrclk = DEFAULT_RCLK;
SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, "");
-
-static speed_t gdbdefaultrate = GDBSPEED;
-SYSCTL_ULONG(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW, &gdbdefaultrate,
- GDBSPEED, "");
-
+static speed_t gdbdefaultrate = GDBSPEED;
+SYSCTL_UINT(_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;
@@ -209,6 +357,13 @@
= 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
*
@@ -235,10 +390,13 @@
comdefaultrate = newspeed;
- com = &sio_console;
- if (com->consdev == NULL)
+ if (comconsole < 0) /* serial console not selected? */
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
@@ -267,497 +425,545 @@
0, 0, sysctl_machdep_comdefaultrate, "I", "");
/* TUNABLE_INT("machdep.conspeed", &comdefaultrate); */
-#ifdef SIO_DEBUG
-static void
-siodebug(struct com_s *com, const char *fmt, ...)
-{
- va_list ap;
+#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))
- 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
-
/*
- * 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.
+ * 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.
*/
-static int
-sioflush(struct com_s *com)
+int
+siodetach(dev)
+ device_t dev;
{
- int delay, limit;
+ struct com_s *com;
+ int i;
- /* 1/10th the time to transmit 1 character (estimate). */
- delay = 16000000 * com->reg_dl / com->rclk;
-
- /* 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 = (struct com_s *) device_get_softc(dev);
+ if (com == NULL) {
+ device_printf(dev, "NULL com in siounload\n");
+ return (0);
}
-
- 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);
+ 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);
}
- if (limit == 0) {
- siodebug(NULL, "receiver appears broken... ");
- return (EIO);
+ if (com->ioportres)
+ bus_release_resource(dev, SYS_RES_IOPORT, com->ioportrid,
+ 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);
}
-
return (0);
}
-/*
- * 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)
+int
+sioprobe(dev, xrid, rclk, noprobe)
+ device_t dev;
+ int xrid;
+ u_long rclk;
+ int noprobe;
{
- int count, delay, limit;
+#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;
+
+ rid = xrid;
+ port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0, ~0, IO_COMSIZE, RF_ACTIVE);
+ if (!port)
+ return (ENXIO);
- com->hasfifo = 0;
- com->fifosize = 0;
+ 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;
- /* 1/10th the time to transmit 1 character (estimate). */
- delay = 16000000 * com->reg_dl / com->rclk;
+ 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);
+ }
+#if 0
/*
- * 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.
+ * 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.
*/
- limit = 30;
- while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
- if (delay)
- DELAY(delay);
+ 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;
+
+ 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;
}
- if (limit == 0) {
- siodebug(NULL, "transmitter appears stuck");
- return (EIO);
+#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);
}
/*
- * 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.
+ * 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.
*/
- sio_setreg(com, com_mcr, com->reg_mcr | MCR_LOOPBACK|MCR_DTR|MCR_RTS);
- /* XXX barrier */
+ idev = dev;
+ mcr_image = MCR_IENABLE;
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(flags)) {
+ Port_t xiobase;
+ u_long io;
+
+ 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;
+ }
+ 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;
+ }
+ }
+#endif /* COM_MULTIPORT */
+ if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0)
+ mcr_image = 0;
+
+ bzero(failures, sizeof failures);
+ iobase = rman_get_start(port);
/*
- * 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.
+ * 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.
*/
- 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 */
+ mtx_lock_spin(&sio_lock);
+/* EXTRA DELAY? */
- /* 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);
- }
+ /*
+ * 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;
- /* We have FIFOs. Flush the transmitter and receiver. */
- if (sioflush(com)) {
- sio_setreg(com, com_mcr, com->reg_mcr);
- /* XXX barrier */
- goto fallback;
+ 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);
}
- sio_setreg(com, com_ier, IER_ERXRDY);
- /* XXX barrier */
-
/*
- * 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.
+ * 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.
*/
- count = 0;
- while ((sio_getreg(com, com_iir) & IIR_RXRDY) == 0 && count < 1030) {
- sio_setreg(com, com_data, 0);
- /* XXX barrier */
- count++;
-
- limit = 30;
- while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
- if (delay)
- DELAY(delay);
- }
- 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;
- }
+ 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));
}
+ /*
+ * 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);
- /* XXX barrier */
+ DELAY(1000); /* XXX */
+ irqmap[0] = isa_irq_pending();
- /* Reset FIFOs. */
- com->reg_fcr = FCR_ENABLE;
- sio_setreg(com, com_fcr, com->reg_fcr | FCR_XMT_RST | FCR_RCV_RST);
- /* XXX barrier */
+ /*
+ * 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);
- sio_setreg(com, com_mcr, com->reg_mcr);
- /* XXX barrier */
+ /*
+ * 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);
- 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 */
+ /*
+ * 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));
- siodebug(NULL, "count=%d; FIFO=%d... ", count, com->fifosize);
- return (0);
+ /*
+ * 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);
-fallback:
- siodebug(NULL, "disabling FIFOs... ");
- com->hasfifo = 0;
- return (0);
-}
-
-static void
-siodescribe(struct com_s *com)
-{
- int has_spr;
- u_char spr;
-
- 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;
+ /*
+ * 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);
}
- } 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");
+ return (result);
}
-}
-u_int
-siodivisor(u_long rclk, u_long speed)
-{
- long actual_speed;
- u_int divisor;
- int error;
+ /*
+ * 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;
- 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);
-
- /* 10 times error in percent: */
- error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2;
-
- /* 3.0% maximum error tolerance: */
- if (error < -30 || error > 30)
- return (0);
-
- return (divisor);
-}
-
-/*
- * 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;
-
- lcr = sio_getreg(com, com_lcr);
- sio_setreg(com, com_lcr, lcr & ~LCR_DLAB);
-
- /* 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;
-
- /* Check known 0 bits that depend on !DLAB. */
- val = sio_getreg(com, com_ier);
- if (val & 0xf0)
- goto fail;
-
- /* 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);
- /* XXX barrier */
- sio_setreg(com, com_lcr, lcr);
- /* XXX barrier */
- return (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;
- fail:
- sio_setreg(com, com_lcr, lcr);
- /* XXX barrier */
- return (ENXIO);
-}
+ mtx_unlock_spin(&sio_lock);
-/*
- * 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;
+ 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 (bootverbose)
+ printf("sio%d: irq maps: %#x %#x %#x %#x\n",
+ device_get_unit(dev),
+ irqmap[0], irqmap[1], irqmap[2], irqmap[3]);
- com = (struct com_s *)device_get_softc(dev);
- com->gone = 1;
-
- if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
- device_printf(dev, "still open, forcing close\n");
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list