From owner-p4-projects@FreeBSD.ORG Sat Sep 6 18:25:31 2003 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id B274D16A4C1; Sat, 6 Sep 2003 18:25:30 -0700 (PDT) Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 6E86B16A4BF for ; Sat, 6 Sep 2003 18:25:30 -0700 (PDT) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id 32E6C43FD7 for ; Sat, 6 Sep 2003 18:25:29 -0700 (PDT) (envelope-from marcel@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.12.6/8.12.6) with ESMTP id h871PT0U095621 for ; Sat, 6 Sep 2003 18:25:29 -0700 (PDT) (envelope-from marcel@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.12.6/8.12.6/Submit) id h871PSTR095618 for perforce@freebsd.org; Sat, 6 Sep 2003 18:25:28 -0700 (PDT) Date: Sat, 6 Sep 2003 18:25:28 -0700 (PDT) Message-Id: <200309070125.h871PSTR095618@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to marcel@freebsd.org using -f From: Marcel Moolenaar To: Perforce Change Reviews Subject: PERFORCE change 37696 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 07 Sep 2003 01:25:31 -0000 http://perforce.freebsd.org/chv.cgi?CH=37696 Change 37696 by marcel@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 #include #include +#include #include #include #include @@ -76,12 +78,18 @@ #include #endif +#include + #include -#include #include #include +#ifdef COM_ESP +#include +#endif +#include + #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@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) <<<