]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Support --base-clock for serial command to handle weird cards with
authorVladimir Serbinenko <phcoder@gmail.com>
Fri, 1 Nov 2013 18:33:22 +0000 (19:33 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Fri, 1 Nov 2013 18:33:22 +0000 (19:33 +0100)
non-standard base clock.

ChangeLog
grub-core/term/ns8250.c
grub-core/term/serial.c
include/grub/serial.h

index 8b67a496aa1c3b3d927ac778d0f3b970c8c5bf61..0470480ae763598a9716b990e772a068ee54cc47 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-11-01  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Support --base-clock for serial command to handle weird cards with
+       non-standard base clock.
+
 2013-11-01  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * grub-core/fs/ext2.c (grub_ext2_read_symlink): Use memcpy rather
index 2064e0dc9b5f7e58aea1b66d9b445780b3f639ce..b428235cc8bf8480423b8103ea54698b5dbd65da 100644 (file)
@@ -38,46 +38,33 @@ static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS;
 
 static int dead_ports = 0;
 
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+#define DEFAULT_BASE_CLOCK (2 * 115200)
+#else
+#define DEFAULT_BASE_CLOCK 115200
+#endif
+
+
 /* Convert speed to divisor.  */
 static unsigned short
 serial_get_divisor (const struct grub_serial_port *port __attribute__ ((unused)),
                    const struct grub_serial_config *config)
 {
-  unsigned int i;
-
-  /* The structure for speed vs. divisor.  */
-  struct divisor
-  {
-    unsigned int speed;
-    unsigned short div;
-  };
+  grub_uint32_t base_clock;
+  grub_uint32_t divisor;
+  grub_uint32_t actual_speed, error;
 
-  /* The table which lists common configurations.  */
-  /* 1843200 / (speed * 16)  */
-  static struct divisor divisor_tab[] =
-    {
-      { 2400,   0x0030 },
-      { 4800,   0x0018 },
-      { 9600,   0x000C },
-      { 19200,  0x0006 },
-      { 38400,  0x0003 },
-      { 57600,  0x0002 },
-      { 115200, 0x0001 }
-    };
+  base_clock = config->base_clock ? (config->base_clock >> 4) : DEFAULT_BASE_CLOCK;
 
-  /* Set the baud rate.  */
-  for (i = 0; i < ARRAY_SIZE (divisor_tab); i++)
-    if (divisor_tab[i].speed == config->speed)
-      {
-       /* internal Loongson UART runs twice the usual rate.  */
-#ifdef GRUB_MACHINE_MIPS_LOONGSON
-       if (port->port == 0xbff003f8)
-         return 2 * divisor_tab[i].div;
-       else
-#endif
-         return divisor_tab[i].div;
-      }
-  return 0;
+  divisor = (base_clock + (config->speed / 2)) / config->speed;
+  if (divisor > 0xffff || divisor == 0)
+    return 0;
+  actual_speed = base_clock / divisor;
+  error = actual_speed > config->speed ? (actual_speed - config->speed)
+    : (config->speed - actual_speed);
+  if (error > (config->speed / 30 + 1))
+    return 0;
+  return divisor;
 }
 
 static void
index 69596404b74cecb110966c3bc7a7d6b5e0578f49..0298ec613c9054ed713f0b660d6dfa2da8b79b33 100644 (file)
@@ -39,6 +39,17 @@ GRUB_MOD_LICENSE ("GPLv3+");
 
 #define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
 
+enum
+  {
+    OPTION_UNIT,
+    OPTION_PORT,
+    OPTION_SPEED,
+    OPTION_WORD,
+    OPTION_PARITY,
+    OPTION_STOP,
+    OPTION_BASE_CLOCK
+  };
+
 /* Argument options.  */
 static const struct grub_arg_option options[] =
 {
@@ -48,6 +59,7 @@ static const struct grub_arg_option options[] =
   {"word",   'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT},
   {"parity", 'r', 0, N_("Set the serial port parity."),      0, ARG_TYPE_STRING},
   {"stop",   't', 0, N_("Set the serial port stop bits."),   0, ARG_TYPE_INT},
+  {"base-clock",   'b', 0, N_("Set the base clock."),   0, ARG_TYPE_INT},
   {0, 0, 0, 0, 0, 0}
 };
 
@@ -178,14 +190,14 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args)
   struct grub_serial_config config;
   grub_err_t err;
 
-  if (state[0].set)
+  if (state[OPTION_UNIT].set)
     {
       grub_snprintf (pname, sizeof (pname), "com%ld",
                     grub_strtoul (state[0].arg, 0, 0));
       name = pname;
     }
 
-  if (state[1].set)
+  if (state[OPTION_PORT].set)
     {
       grub_snprintf (pname, sizeof (pname), "port%lx",
                     grub_strtoul (state[1].arg, 0, 0));
@@ -206,38 +218,48 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args)
 
   config = port->config;
 
-  if (state[2].set)
-    config.speed = grub_strtoul (state[2].arg, 0, 0);
+  if (state[OPTION_SPEED].set)
+    config.speed = grub_strtoul (state[OPTION_SPEED].arg, 0, 0);
 
-  if (state[3].set)
-    config.word_len = grub_strtoul (state[3].arg, 0, 0);
+  if (state[OPTION_WORD].set)
+    config.word_len = grub_strtoul (state[OPTION_WORD].arg, 0, 0);
 
-  if (state[4].set)
+  if (state[OPTION_PARITY].set)
     {
-      if (! grub_strcmp (state[4].arg, "no"))
+      if (! grub_strcmp (state[OPTION_PARITY].arg, "no"))
        config.parity = GRUB_SERIAL_PARITY_NONE;
-      else if (! grub_strcmp (state[4].arg, "odd"))
+      else if (! grub_strcmp (state[OPTION_PARITY].arg, "odd"))
        config.parity = GRUB_SERIAL_PARITY_ODD;
-      else if (! grub_strcmp (state[4].arg, "even"))
+      else if (! grub_strcmp (state[OPTION_PARITY].arg, "even"))
        config.parity = GRUB_SERIAL_PARITY_EVEN;
       else
        return grub_error (GRUB_ERR_BAD_ARGUMENT,
                           N_("unsupported serial port parity"));
     }
 
-  if (state[5].set)
+  if (state[OPTION_STOP].set)
     {
-      if (! grub_strcmp (state[5].arg, "1"))
+      if (! grub_strcmp (state[OPTION_STOP].arg, "1"))
        config.stop_bits = GRUB_SERIAL_STOP_BITS_1;
-      else if (! grub_strcmp (state[5].arg, "2"))
+      else if (! grub_strcmp (state[OPTION_STOP].arg, "2"))
        config.stop_bits = GRUB_SERIAL_STOP_BITS_2;
-      else if (! grub_strcmp (state[5].arg, "1.5"))
+      else if (! grub_strcmp (state[OPTION_STOP].arg, "1.5"))
        config.stop_bits = GRUB_SERIAL_STOP_BITS_1_5;
       else
        return grub_error (GRUB_ERR_BAD_ARGUMENT,
                           N_("unsupported serial port stop bits number"));
     }
 
+  if (state[OPTION_BASE_CLOCK].set)
+    {
+      char *ptr;
+      config.base_clock = grub_strtoull (state[OPTION_BASE_CLOCK].arg, &ptr, 0);
+      if (ptr && *ptr == 'M')
+       config.base_clock *= 1000000;
+      if (ptr && (*ptr == 'k' || *ptr == 'K'))
+       config.base_clock *= 1000;
+    }
+
   /* Initialize with new settings.  */
   err = port->driver->configure (port, &config);
   if (err)
index 20840d04b382bb26409122f6a79a35108beb67be..7d981f927f2f3365065ffb5f683bd4cf1dc02062 100644 (file)
@@ -67,6 +67,7 @@ struct grub_serial_config
   int word_len;
   grub_serial_parity_t parity;
   grub_serial_stop_bits_t stop_bits;
+  grub_uint64_t base_clock;
 };
 
 struct grub_serial_port
@@ -163,7 +164,8 @@ grub_serial_config_defaults (struct grub_serial_port *port)
 #endif
       .word_len = 8,
       .parity = GRUB_SERIAL_PARITY_NONE,
-      .stop_bits = GRUB_SERIAL_STOP_BITS_1
+      .stop_bits = GRUB_SERIAL_STOP_BITS_1,
+      .base_clock = 0
     };
 
   return port->driver->configure (port, &config);