USB: serial: FTDI: add high speed device support
authorMark Adamson <mark.adamson@ftdichip.com>
Thu, 9 Apr 2009 14:03:09 +0000 (15:03 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 16 Jun 2009 04:44:41 +0000 (21:44 -0700)
Added support for FTDI's USB 2.0 hi-speed devices - FT2232H (2
interfaces) and FT4232H (4 interfaces), including a new baud rate
calculation for these devices which can now achieve up to 12Mbaud by
turning off a divide by 2.5 in the baud rate generator of the chips.  In
order to achieve baud rates of <1200 baud, the divide by 2.5 must be
active.  The default product ID of the FT2232H is 0x6010 (same as the
FT2232C IC).  The default PID of the FT4232H is 0x6011.

Signed-off-by: Mark J. Adamson <mark.adamson@ftdichip.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio.h

index 59c7501840cb944520716b14f073f20d914c1f78..80ccfa13e0b3de879cd227e5164d8985f186072b 100644 (file)
@@ -47,7 +47,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.4.3"
+#define DRIVER_VERSION "v1.5.0"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>"
 #define DRIVER_DESC "USB FTDI Serial Converters Driver"
 
@@ -82,7 +82,8 @@ struct ftdi_private {
        int rx_processed;
        unsigned long rx_bytes;
 
-       __u16 interface;        /* FT2232C port interface (0 for FT232/245) */
+       __u16 interface;        /* FT2232C, FT2232H or FT4232H port interface
+                                  (0 for FT232/245) */
 
        speed_t force_baud;     /* if non-zero, force the baud rate to
                                   this value */
@@ -164,6 +165,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
@@ -694,6 +696,8 @@ static const char *ftdi_chip_name[] = {
        [FT232BM] = "FT232BM",
        [FT2232C] = "FT2232C",
        [FT232RL] = "FT232RL",
+       [FT2232H] = "FT2232H",
+       [FT4232H] = "FT4232H"
 };
 
 
@@ -745,6 +749,8 @@ static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
 static unsigned short int ftdi_232am_baud_to_divisor(int baud);
 static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
 static __u32 ftdi_232bm_baud_to_divisor(int baud);
+static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
+static __u32 ftdi_2232h_baud_to_divisor(int baud);
 
 static struct usb_serial_driver ftdi_sio_device = {
        .driver = {
@@ -839,6 +845,36 @@ static __u32 ftdi_232bm_baud_to_divisor(int baud)
         return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
 }
 
+static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
+{
+       static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+       __u32 divisor;
+       int divisor3;
+
+       /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
+       divisor3 = (base / 10 / baud) * 8;
+
+       divisor = divisor3 >> 3;
+       divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
+       /* Deal with special cases for highest baud rates. */
+       if (divisor == 1)
+               divisor = 0;
+       else if (divisor == 0x4001)
+               divisor = 1;
+       /*
+        * Set this bit to turn off a divide by 2.5 on baud rate generator
+        * This enables baud rates up to 12Mbaud but cannot reach below 1200
+        * baud with this bit set
+        */
+       divisor |= 0x00020000;
+       return divisor;
+}
+
+static __u32 ftdi_2232h_baud_to_divisor(int baud)
+{
+        return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
+}
+
 #define set_mctrl(port, set)           update_mctrl((port), (set), 0)
 #define clear_mctrl(port, clear)       update_mctrl((port), 0, (clear))
 
@@ -997,6 +1033,19 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
                        baud = 9600;
                }
                break;
+       case FT2232H: /* FT2232H chip */
+       case FT4232H: /* FT4232H chip */
+               if ((baud <= 12000000) & (baud >= 1200)) {
+                       div_value = ftdi_2232h_baud_to_divisor(baud);
+               } else if (baud < 1200) {
+                       div_value = ftdi_232bm_baud_to_divisor(baud);
+               } else {
+                       dbg("%s - Baud rate too high!", __func__);
+                       div_value = ftdi_232bm_baud_to_divisor(9600);
+                       div_okay = 0;
+                       baud = 9600;
+               }
+               break;
        } /* priv->chip_type */
 
        if (div_okay) {
@@ -1197,14 +1246,29 @@ static void ftdi_determine_type(struct usb_serial_port *port)
        if (interfaces > 1) {
                int inter;
 
-               /* Multiple interfaces.  Assume FT2232C. */
-               priv->chip_type = FT2232C;
+               /* Multiple interfaces.*/
+               if (version == 0x0800) {
+                       priv->chip_type = FT4232H;
+                       /* Hi-speed - baud clock runs at 120MHz */
+                       priv->baud_base = 120000000 / 2;
+               } else if (version == 0x0700) {
+                       priv->chip_type = FT2232H;
+                       /* Hi-speed - baud clock runs at 120MHz */
+                       priv->baud_base = 120000000 / 2;
+               } else
+                       priv->chip_type = FT2232C;
+
                /* Determine interface code. */
                inter = serial->interface->altsetting->desc.bInterfaceNumber;
-               if (inter == 0)
-                       priv->interface = PIT_SIOA;
-               else
-                       priv->interface = PIT_SIOB;
+               if (inter == 0) {
+                       priv->interface = INTERFACE_A;
+               } else  if (inter == 1) {
+                       priv->interface = INTERFACE_B;
+               } else  if (inter == 2) {
+                       priv->interface = INTERFACE_C;
+               } else  if (inter == 3) {
+                       priv->interface = INTERFACE_D;
+               }
                /* BM-type devices have a bug where bcdDevice gets set
                 * to 0x200 when iSerialNumber is 0.  */
                if (version < 0x500) {
@@ -1315,7 +1379,9 @@ static int create_sysfs_attrs(struct usb_serial_port *port)
                if ((!retval) &&
                    (priv->chip_type == FT232BM ||
                     priv->chip_type == FT2232C ||
-                    priv->chip_type == FT232RL)) {
+                    priv->chip_type == FT232RL ||
+                    priv->chip_type == FT2232H ||
+                    priv->chip_type == FT4232H)) {
                        retval = device_create_file(&port->dev,
                                                    &dev_attr_latency_timer);
                }
@@ -1334,7 +1400,9 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
                device_remove_file(&port->dev, &dev_attr_event_char);
                if (priv->chip_type == FT232BM ||
                    priv->chip_type == FT2232C ||
-                   priv->chip_type == FT232RL) {
+                   priv->chip_type == FT232RL ||
+                   priv->chip_type == FT2232H ||
+                   priv->chip_type == FT4232H) {
                        device_remove_file(&port->dev, &dev_attr_latency_timer);
                }
        }
@@ -2333,6 +2401,8 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)
        case FT232BM:
        case FT2232C:
        case FT232RL:
+       case FT2232H:
+       case FT4232H:
                /* the 8U232AM returns a two byte value (the sio is a 1 byte
                   value) - in the same format as the data returned from the in
                   point */
index 0a09118f315e5c39ade0ef6df348ace1640dd8fc..f1d440a728a376770617ad7776a52eba590cc282 100644 (file)
@@ -10,7 +10,7 @@
  * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
  * USB on the other.
  *
- * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details
+ * Thanx to FTDI (http://www.ftdichip.com) for so kindly providing details
  * of the protocol required to talk to the device and ongoing assistence
  * during development.
  *
@@ -28,6 +28,7 @@
 #define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */
 #define FTDI_8U2232C_PID 0x6010 /* Dual channel device */
 #define FTDI_232RL_PID  0xFBFA  /* Product ID for FT232RL */
+#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */
 #define FTDI_RELAIS_PID        0xFA10  /* Relais device from Rudolf Gugler */
 #define FTDI_NF_RIC_VID        0x0DCD  /* Vendor Id */
 #define FTDI_NF_RIC_PID        0x0001  /* Product Id */
 #define FTDI_SIO_SET_LATENCY_TIMER     9 /* Set the latency timer */
 #define FTDI_SIO_GET_LATENCY_TIMER     10 /* Get the latency timer */
 
+/* Interface indicies for FT2232, FT2232H and FT4232H devices*/
+#define INTERFACE_A            1
+#define INTERFACE_B            2
+#define INTERFACE_C            3
+#define INTERFACE_D            4
 
 /*
  * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3
@@ -1039,6 +1045,8 @@ typedef enum {
        FT232BM = 3,
        FT2232C = 4,
        FT232RL = 5,
+       FT2232H = 6,
+       FT4232H = 7
 } ftdi_chip_type_t;
 
 typedef enum {