Date: Wed, 25 Dec 2002 16:32:07 -0800 (PST) From: Marcel Moolenaar <marcel@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 22746 for review Message-ID: <200212260032.gBQ0W7IL060957@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=22746 Change 22746 by marcel@marcel_nfs on 2002/12/25 16:31:35 Improve on the FIFO size determination. We don't really need a siosetfifo() called twice by sioinitfifo(). Having seperate functions only creates phase ordering problems. They are now merged. Tune the algorithm to better deal with large FIFOs. The HP Diva card apparently needs a lot of time before the receiver FIFO is cleared. Also, have more verbose debugging for now and fallback to non-FIFO operation if we get in trouble after we enabled the FIFOs. Fix the trigger level for 128 byte FIFOs. Properly scaled it is 112. This allows us to properly set the FIFO size for the HP Diva card. Affected files ... .. //depot/projects/ia64/sys/dev/sio/sio.c#31 edit Differences ... ==== //depot/projects/ia64/sys/dev/sio/sio.c#31 (text+ko) ==== @@ -264,24 +264,24 @@ /* TUNABLE_INT("machdep.conspeed", &comdefaultrate); */ /* - * Set a FIFO mode. We try not to be disruptive, but leave some of the - * responsibility for the caller to avoid unconditional high overhead. - * For best results, set loopback mode and clear all interrupt sources - * before calling this function. The loopback mode prevents garbage on - * the line and clearing IER avoids possible stray interrupts. + * 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 -siosetfifo(struct com_s *com, int fcr) +sioinitfifo(struct com_s *com) { - int delay, limit; + int count, delay, limit; + + com->hasfifo = 0; + com->fifosize = 0; /* 1/10th the time to transmit 1 character (estimate). */ delay = 16000000 * com->reg_dl / com->rclk; - com->reg_fcr = 0; - sio_setreg(com, com_fcr, 0); - /* XXX barrier */ - /* * Make sure the transmitter is idle before we play with the * FIFOs. We don't wait more than roughly three times as long @@ -294,80 +294,80 @@ if (delay) DELAY(delay); } - if (limit == 0) + if (limit == 0) { + printf("transmitter appears stuck"); return (EIO); + } - com->reg_fcr = fcr; - sio_setreg(com, com_fcr, fcr | FCR_XMT_RST | FCR_RCV_RST); + /* + * 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 */ /* - * Set the hasfifo field when the device has FIFOs. In that case - * we also flush any possible garbage from the transmitter and - * receiver. Flush the transmitter before flushing the receiver - * to allow having the UART in loopback mode. Since we always - * enable FIFO mode after disabling it first, we won't have more - * than two characters the flush. + * 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. */ + 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 */ + + /* 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) { - limit = 30; - while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) { - if (delay) - DELAY(delay); - } - if (limit == 0) - return (EIO); + if (!com->hasfifo) { + sio_setreg(com, com_mcr, com->reg_mcr); + /* XXX barrier */ + printf("no FIFOs... "); + return (0); + } - limit = 30; - while ((sio_getreg(com, com_lsr) & LSR_RXRDY) && --limit) { - (void)sio_getreg(com, com_data); - /* XXX barrier */ - } - if (limit == 0) - return (EIO); + /* + * We have FIFOs. Flush the transmitter FIFO and shift register. + * This could fill up the receiver FIFO, so we flush that too. + * Bad timing may cause at most one character to remain in the + * receiver FIFO after we're done flushing. This cannot do any + * harm, so we don't worry about it. + */ + limit = 30; + while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) { + if (delay) + DELAY(delay); + } + if (limit == 0) { + sio_setreg(com, com_mcr, com->reg_mcr); + /* XXX barrier */ + printf("transmitter appears stuck... "); + goto fallback; } - 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. - */ -static int -sioinitfifo(struct com_s *com) -{ - int count, delay, error, limit; - - com->fifosize = 0; - - sio_setreg(com, com_mcr, MCR_LOOPBACK | MCR_DTR | MCR_RTS); - /* XXX barrier */ - - error = siosetfifo(com, FCR_ENABLE | FCR_DMA_MODE | FCR_RX_HIGH); - if (error || !com->hasfifo) { + limit = 10300; + while ((sio_getreg(com, com_lsr) & LSR_RXRDY) && --limit) { + (void)sio_getreg(com, com_data); + /* XXX barrier */ + if (delay) + DELAY(delay); + } + if (limit == 0) { sio_setreg(com, com_mcr, com->reg_mcr); /* XXX barrier */ - return (error); + printf("receiver appears broken... "); + goto fallback; } - /* 1/10th the time to transmit 1 character (estimate). */ - delay = 16000000 * com->reg_dl / com->rclk; - sio_setreg(com, com_ier, IER_ERXRDY); /* XXX barrier */ /* - * We should have a sufficiently clean "pipe" to determine - * the size of the FIFOs. If there was a character lurking - * in the receiver, then it really cannot do any harm. We - * send as much characters as is reasonable and wait for the - * the TX interrupt to be asserted, counting the characters - * as we send them. Based on that count we know the FIFO size. + * 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. */ count = 0; while ((sio_getreg(com, com_iir) & IIR_RXRDY) == 0 && count < 1030) { @@ -385,19 +385,21 @@ /* XXX barrier */ sio_setreg(com, com_mcr, com->reg_mcr); /* XXX barrier */ - return (EIO); + printf("can't determine FIFO size... "); + goto fallback; } } sio_setreg(com, com_ier, 0); /* XXX barrier */ - error = siosetfifo(com, FCR_ENABLE | FCR_RX_MEDH); - if (error) { - sio_setreg(com, com_mcr, com->reg_mcr); - /* XXX barrier */ - return (error); - } + /* 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 */ @@ -405,16 +407,17 @@ com->fifosize = 32; /* 16650 */ else if (count >= 56 && count < 64) com->fifosize = 64; /* 16750 */ - else if (count >= 120 && count < 128) + else if (count >= 112 && count < 128) com->fifosize = 128; /* 16950 */ else com->fifosize = 1; /* XXX */ - sio_setreg(com, com_mcr, com->reg_mcr); - /* XXX barrier */ + printf("count=%d; FIFO=%d... ", count, com->fifosize); + return (0); - device_printf(com->dev, "count=%d, FIFO size=%d\n", count, - com->fifosize); +fallback: + printf("disabling FIFOs... "); + com->hasfifo = 0; return (0); } @@ -583,6 +586,8 @@ int error; u_int flags; + device_printf(dev, "probing... "); + while (sio_inited != 2) if (atomic_cmpset_int(&sio_inited, 0, 1)) { mtx_init(&sio_lock, sio_driver_name, NULL, @@ -621,6 +626,7 @@ if (com->consdev == NULL) { error = sioprobe1(com); if (error) { + printf("sanity check failed\n"); bus_release_resource(dev, com->addr_type, com->addr_rid, com->addr_res); return (error); @@ -644,11 +650,14 @@ /* Initialize the FIFOs. */ error = sioinitfifo(com); if (error) { + printf("failed\n"); bus_release_resource(dev, com->addr_type, com->addr_rid, com->addr_res); return (error); } + printf("ok\n"); + if (device_get_desc(dev) == NULL) siodescribe(com); To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe p4-projects" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200212260032.gBQ0W7IL060957>
