diff --git a/src/ftdi.c b/src/ftdi.c
index ac56d08..3909559 100644
--- a/src/ftdi.c
+++ b/src/ftdi.c
@@ -51,6 +51,9 @@
    } while(0);
 
 
+#define FTDI_BAUDRATE_REF_CLOCK 3000000 /* 3 MHz */
+#define FTDI_BAUDRATE_TOLERANCE       5 /* acceptable clock drift, in % */
+
 /**
     Internal function to close usb device pointer.
     Sets ftdi->usb_dev to NULL.
@@ -969,14 +972,28 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
     int divisor, best_divisor, best_baud, best_baud_diff;
     unsigned long encoded_divisor;
     int i;
-
+    unsigned int ref_clock = FTDI_BAUDRATE_REF_CLOCK;
+    int hispeed = 0;
+    
     if (baudrate <= 0)
     {
         // Return error
         return -1;
     }
 
-    divisor = 24000000 / baudrate;
+    if ( (ftdi->type == TYPE_4232H) || (ftdi->type == TYPE_2232H) )
+    {
+        // these chips can support a 12MHz clock in addition to the original
+        // 3MHz clock. This allows higher baudrate (up to 12Mbps) and more
+        // precise baudrates for baudrate > 3Mbps/2
+        if ( baudrate > (FTDI_BAUDRATE_REF_CLOCK>>1) )
+        {
+            ref_clock *= 4; // 12 MHz
+            hispeed = 1;
+        }
+    }
+    
+    divisor = (ref_clock<<3) / baudrate;
 
     if (ftdi->type == TYPE_AM)
     {
@@ -994,45 +1011,49 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
         int baud_estimate;
         int baud_diff;
 
-        // Round up to supported divisor value
-        if (try_divisor <= 8)
-        {
-            // Round up to minimum supported divisor
-            try_divisor = 8;
-        }
-        else if (ftdi->type != TYPE_AM && try_divisor < 12)
+        if ( ! hispeed )
         {
-            // BM doesn't support divisors 9 through 11 inclusive
-            try_divisor = 12;
-        }
-        else if (divisor < 16)
-        {
-            // AM doesn't support divisors 9 through 15 inclusive
-            try_divisor = 16;
-        }
-        else
-        {
-            if (ftdi->type == TYPE_AM)
+            // Round up to supported divisor value
+            if (try_divisor <= 8)
             {
-                // Round up to supported fraction (AM only)
-                try_divisor += am_adjust_up[try_divisor & 7];
-                if (try_divisor > 0x1FFF8)
-                {
-                    // Round down to maximum supported divisor value (for AM)
-                    try_divisor = 0x1FFF8;
-                }
+                // Round up to minimum supported divisor
+                try_divisor = 8;
+            }
+            else if (ftdi->type != TYPE_AM && try_divisor < 12)
+            {
+                // BM doesn't support divisors 9 through 11 inclusive
+                try_divisor = 12;
+            }
+            else if ( divisor < 16 )
+            {
+                // AM doesn't support divisors 9 through 15 inclusive
+                try_divisor = 16;
             }
             else
             {
-                if (try_divisor > 0x1FFFF)
+                if (ftdi->type == TYPE_AM)
                 {
-                    // Round down to maximum supported divisor value (for BM)
-                    try_divisor = 0x1FFFF;
+                    // Round up to supported fraction (AM only)
+                    try_divisor += am_adjust_up[try_divisor & 7];
+                    if (try_divisor > 0x1FFF8)
+                    {
+                        // Round down to maximum supported divisor value (for AM)
+                        try_divisor = 0x1FFF8;
+                    }
+                }
+                else
+                {
+                    if (try_divisor > 0x1FFFF)
+                    {
+                        // Round down to maximum supported divisor value (for BM)
+                        try_divisor = 0x1FFFF;
+                    }
                 }
             }
         }
+
         // Get estimated baud rate (to nearest integer)
-        baud_estimate = (24000000 + (try_divisor / 2)) / try_divisor;
+        baud_estimate = ((ref_clock<<3) + (try_divisor / 2)) / try_divisor;
         // Get absolute difference from requested baud rate
         if (baud_estimate < baudrate)
         {
@@ -1075,7 +1096,13 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
         *index |= ftdi->index;
     }
     else
+    {
         *index = (unsigned short)(encoded_divisor >> 16);
+    }
+    if ( hispeed )
+    {
+        *index |= 1<<9; // use hispeed mode
+    }
 
     // Return the nearest baud rate
     return best_baud;
