Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/usb Support serial speeds up to 12mbaud with newer F...



details:   https://anonhg.NetBSD.org/src/rev/82194b50a176
branches:  trunk
changeset: 463868:82194b50a176
user:      ryo <ryo%NetBSD.org@localhost>
date:      Fri Sep 13 20:50:45 2019 +0000

description:
Support serial speeds up to 12mbaud with newer FTDI chips.

baudrate calculation code uftdi_encode_baudrate() from FreeBSD uftdi.c@r330385

diffstat:

 sys/dev/usb/uftdi.c    |  193 +++++++++++++++++++++++++++++++++++++++++-------
 sys/dev/usb/uftdireg.h |   18 +----
 2 files changed, 165 insertions(+), 46 deletions(-)

diffs (truncated from 301 to 300 lines):

diff -r 01a5998d1acc -r 82194b50a176 sys/dev/usb/uftdi.c
--- a/sys/dev/usb/uftdi.c       Fri Sep 13 18:07:30 2019 +0000
+++ b/sys/dev/usb/uftdi.c       Fri Sep 13 20:50:45 2019 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uftdi.c,v 1.70 2019/05/09 02:43:35 mrg Exp $   */
+/*     $NetBSD: uftdi.c,v 1.71 2019/09/13 20:50:45 ryo Exp $   */
 
 /*
  * Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uftdi.c,v 1.70 2019/05/09 02:43:35 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uftdi.c,v 1.71 2019/09/13 20:50:45 ryo Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -85,6 +85,10 @@
        int                     sc_iface_no;
 
        enum uftdi_type         sc_type;
+       u_int                   sc_flags;
+#define FLAGS_BAUDCLK_12M      0x00000001
+#define FLAGS_ROUNDOFF_232A    0x00000002
+#define FLAGS_BAUDBITS_HINDEX  0x00000004
        u_int                   sc_hdrlen;
        u_int                   sc_chiptype;
 
@@ -96,7 +100,6 @@
        bool                    sc_dying;
 
        u_int                   last_lcr;
-
 };
 
 static void    uftdi_get_status(void *, int, u_char *, u_char *);
@@ -225,15 +228,46 @@
        sc->sc_iface_no = uiaa->uiaa_ifaceno;
        sc->sc_type = UFTDI_TYPE_8U232AM; /* most devices are post-8U232AM */
        sc->sc_hdrlen = 0;
-       if (uiaa->uiaa_vendor == USB_VENDOR_FTDI
-           && uiaa->uiaa_product == USB_PRODUCT_FTDI_SERIAL_8U100AX) {
-               sc->sc_type = UFTDI_TYPE_SIO;
-               sc->sc_hdrlen = 1;
-       }
 
        ddesc = usbd_get_device_descriptor(dev);
        sc->sc_chiptype = UGETW(ddesc->bcdDevice);
 
+       switch (sc->sc_chiptype) {
+       case 0x0200:
+               if (ddesc->iSerialNumber != 0)
+                       sc->sc_flags |= FLAGS_ROUNDOFF_232A;
+               ucaa.ucaa_portno = 0;
+               break;
+       case 0x0400:
+               ucaa.ucaa_portno = 0;
+               break;
+       case 0x0500:
+               sc->sc_flags |= FLAGS_BAUDBITS_HINDEX;
+               ucaa.ucaa_portno = FTDI_PIT_SIOA + sc->sc_iface_no;
+               break;
+       case 0x0600:
+               ucaa.ucaa_portno = 0;
+               break;
+       case 0x0700:
+       case 0x0800:
+       case 0x0900:
+               sc->sc_flags |= FLAGS_BAUDCLK_12M;
+               sc->sc_flags |= FLAGS_BAUDBITS_HINDEX;
+               ucaa.ucaa_portno = FTDI_PIT_SIOA + sc->sc_iface_no;
+               break;
+       case 0x1000:
+               sc->sc_flags |= FLAGS_BAUDBITS_HINDEX;
+               ucaa.ucaa_portno = FTDI_PIT_SIOA + sc->sc_iface_no;
+               break;
+       default:
+               if (sc->sc_chiptype < 0x0200) {
+                       sc->sc_type = UFTDI_TYPE_SIO;
+                       sc->sc_hdrlen = 1;
+               }
+               ucaa.ucaa_portno = 0;
+               break;
+       }
+
        id = usbd_get_interface_descriptor(iface);
 
        sc->sc_iface = iface;
@@ -281,7 +315,6 @@
                goto bad;
        }
 
-       ucaa.ucaa_portno = FTDI_PIT_SIOA + sc->sc_iface_no;
        /* ucaa_bulkin, ucaa_bulkout set above */
        if (ucaa.ucaa_ibufsize == 0)
                ucaa.ucaa_ibufsize = UFTDIIBUFSIZE;
@@ -469,13 +502,130 @@
        (void)usbd_do_request(sc->sc_udev, &req, NULL);
 }
 
+/*
+ * Return true if the given speed is within operational tolerance of the target
+ * speed.  FTDI recommends that the hardware speed be within 3% of nominal.
+ */
+static inline bool
+uftdi_baud_within_tolerance(uint64_t speed, uint64_t target)
+{
+       return ((speed >= (target * 100) / 103) &&
+           (speed <= (target * 100) / 97));
+}
+
+static int
+uftdi_encode_baudrate(struct uftdi_softc *sc, int speed, int *rate, int *ratehi)
+{
+       static const uint8_t encoded_fraction[8] = {
+           0, 3, 2, 4, 1, 5, 6, 7
+       };
+       static const uint8_t roundoff_232a[16] = {
+           0,  1,  0,  1,  0, -1,  2,  1,
+           0, -1, -2, -3,  4,  3,  2,  1,
+       };
+       uint32_t clk, divisor, fastclk_flag, frac, hwspeed;
+
+       /*
+        * If this chip has the fast clock capability and the speed is within
+        * range, use the 12MHz clock, otherwise the standard clock is 3MHz.
+        */
+       if ((sc->sc_flags & FLAGS_BAUDCLK_12M) && speed >= 1200) {
+               clk = 12000000;
+               fastclk_flag = (1 << 17);
+       } else {
+               clk = 3000000;
+               fastclk_flag = 0;
+       }
+
+       /*
+        * Make sure the requested speed is reachable with the available clock
+        * and a 14-bit divisor.
+        */
+       if (speed < (clk >> 14) || speed > clk)
+               return -1;
+
+       /*
+        * Calculate the divisor, initially yielding a fixed point number with a
+        * 4-bit (1/16ths) fraction, then round it to the nearest fraction the
+        * hardware can handle.  When the integral part of the divisor is
+        * greater than one, the fractional part is in 1/8ths of the base clock.
+        * The FT8U232AM chips can handle only 0.125, 0.250, and 0.5 fractions.
+        * Later chips can handle all 1/8th fractions.
+        *
+        * If the integral part of the divisor is 1, a special rule applies: the
+        * fractional part can only be .0 or .5 (this is a limitation of the
+        * hardware).  We handle this by truncating the fraction rather than
+        * rounding, because this only applies to the two fastest speeds the
+        * chip can achieve and rounding doesn't matter, either you've asked for
+        * that exact speed or you've asked for something the chip can't do.
+        *
+        * For the FT8U232AM chips, use a roundoff table to adjust the result
+        * to the nearest 1/8th fraction that is supported by the hardware,
+        * leaving a fixed-point number with a 3-bit fraction which exactly
+        * represents the math the hardware divider will do.  For later-series
+        * chips that support all 8 fractional divisors, just round 16ths to
+        * 8ths by adding 1 and dividing by 2.
+        */
+       divisor = (clk << 4) / speed;
+       if ((divisor & 0xf) == 1)
+               divisor &= 0xfffffff8;
+       else if (sc->sc_flags & FLAGS_ROUNDOFF_232A)
+               divisor += roundoff_232a[divisor & 0x0f];
+       else
+               divisor += 1;  /* Rounds odd 16ths up to next 8th. */
+       divisor >>= 1;
+
+       /*
+        * Ensure the resulting hardware speed will be within operational
+        * tolerance (within 3% of nominal).
+        */
+       hwspeed = (clk << 3) / divisor;
+       if (!uftdi_baud_within_tolerance(hwspeed, speed))
+               return -1;
+
+       /*
+        * Re-pack the divisor into hardware format. The lower 14-bits hold the
+        * integral part, while the upper bits specify the fraction by indexing
+        * a table of fractions within the hardware which is laid out as:
+        *    {0.0, 0.5, 0.25, 0.125, 0.325, 0.625, 0.725, 0.875}
+        * The A-series chips only have the first four table entries; the
+        * roundoff table logic above ensures that the fractional part for those
+        * chips will be one of the first four values.
+        *
+        * When the divisor is 1 a special encoding applies:  1.0 is encoded as
+        * 0.0, and 1.5 is encoded as 1.0.  The rounding logic above has already
+        * ensured that the fraction is either .0 or .5 if the integral is 1.
+        */
+       frac = divisor & 0x07;
+       divisor >>= 3;
+       if (divisor == 1) {
+               if (frac == 0)
+                       divisor = 0;    /* 1.0 becomes 0.0 */
+               else
+                       frac = 0;       /* 1.5 becomes 1.0 */
+       }
+       divisor |= (encoded_fraction[frac] << 14) | fastclk_flag;
+
+       *rate = (uint16_t)divisor;
+       *ratehi = (uint16_t)(divisor >> 16);
+
+       /*
+        * If this chip requires the baud bits to be in the high byte of the
+        * index word, move the bits up to that location.
+        */
+       if (sc->sc_flags & FLAGS_BAUDBITS_HINDEX)
+               *ratehi <<= 8;
+
+       return 0;
+}
+
 static int
 uftdi_param(void *vsc, int portno, struct termios *t)
 {
        struct uftdi_softc *sc = vsc;
        usb_device_request_t req;
        usbd_status err;
-       int rate, data, flow;
+       int rate, ratehi, rerr, data, flow;
 
        DPRINTF(("uftdi_param: sc=%p\n", sc));
 
@@ -507,35 +657,20 @@
                default:
                        return EINVAL;
                }
+               ratehi = 0;
                break;
-
        case UFTDI_TYPE_8U232AM:
-               switch(t->c_ospeed) {
-               case 300: rate = ftdi_8u232am_b300; break;
-               case 600: rate = ftdi_8u232am_b600; break;
-               case 1200: rate = ftdi_8u232am_b1200; break;
-               case 2400: rate = ftdi_8u232am_b2400; break;
-               case 4800: rate = ftdi_8u232am_b4800; break;
-               case 9600: rate = ftdi_8u232am_b9600; break;
-               case 19200: rate = ftdi_8u232am_b19200; break;
-               case 38400: rate = ftdi_8u232am_b38400; break;
-               case 57600: rate = ftdi_8u232am_b57600; break;
-               case 115200: rate = ftdi_8u232am_b115200; break;
-               case 230400: rate = ftdi_8u232am_b230400; break;
-               case 460800: rate = ftdi_8u232am_b460800; break;
-               case 921600: rate = ftdi_8u232am_b921600; break;
-               default:
+               rerr = uftdi_encode_baudrate(sc, t->c_ospeed, &rate, &ratehi);
+               if (rerr != 0)
                        return EINVAL;
-               }
                break;
-
        default:
                return EINVAL;
        }
        req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
        req.bRequest = FTDI_SIO_SET_BAUD_RATE;
        USETW(req.wValue, rate);
-       USETW(req.wIndex, portno);
+       USETW(req.wIndex, portno | ratehi);
        USETW(req.wLength, 0);
        DPRINTFN(2,("uftdi_param: reqtype=0x%02x req=0x%02x value=0x%04x "
                    "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest,
diff -r 01a5998d1acc -r 82194b50a176 sys/dev/usb/uftdireg.h
--- a/sys/dev/usb/uftdireg.h    Fri Sep 13 18:07:30 2019 +0000
+++ b/sys/dev/usb/uftdireg.h    Fri Sep 13 20:50:45 2019 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uftdireg.h,v 1.10 2016/04/23 10:15:32 skrll Exp $ */
+/*     $NetBSD: uftdireg.h,v 1.11 2019/09/13 20:50:45 ryo Exp $ */
 
 /*
  * Definitions for the FTDI USB Single Port Serial Converter -
@@ -91,22 +91,6 @@
        ftdi_sio_b115200 = 9
 };
 
-enum {
-       ftdi_8u232am_b300 = 0x2710,
-       ftdi_8u232am_b600 = 0x1388,
-       ftdi_8u232am_b1200 = 0x09c4,
-       ftdi_8u232am_b2400 = 0x04e2,
-       ftdi_8u232am_b4800 = 0x0271,
-       ftdi_8u232am_b9600 = 0x4138,
-       ftdi_8u232am_b19200 = 0x809c,
-       ftdi_8u232am_b38400 = 0xc04e,
-       ftdi_8u232am_b57600 = 0x0034,
-       ftdi_8u232am_b115200 = 0x001a,
-       ftdi_8u232am_b230400 = 0x000d,
-       ftdi_8u232am_b460800 = 0x4006,
-       ftdi_8u232am_b921600 = 0x8003
-};
-
 /*
  * BmRequestType:  0100 0000B



Home | Main Index | Thread Index | Old Index