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
#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[] =
{
{"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}
};
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));
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)