diff target-utils/libcommon/cmd_baud_switch.c @ 0:e7502631a0f9

initial import from freecalypso-sw rev 1033:5ab737ac3ad7
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 11 Jun 2016 00:13:35 +0000
parents
children 2b041d57de1f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_baud_switch.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,112 @@
+/*
+ * Baud rate switching command
+ */
+
+#include <stdlib.h>
+#include "types.h"
+#include "ns16550.h"
+
+extern struct ns16550_regs *uart_base;
+extern int serial_in_poll();
+
+static const struct tab {
+	int baud;
+	int divisor;
+} rate_table[] = {
+	/*
+	 * First support the rates and divisors implemented by the
+	 * Calypso boot ROM.  Dividing 13 MHz by 7 gives an approximation
+	 * of 115200 (x16); the divisors used by the boot ROM code for
+	 * the slower baud rates are all 7x the usual PC value.
+	 */
+	{115200, 7},
+	{57600, 7 * 2},
+	{38400, 7 * 3},
+	{28800, 7 * 4},
+	{19200, 7 * 6},
+	/*
+	 * Going faster than ~115200 baud means using a divisor
+	 * less than 7, resulting in a non-standard baud rate.
+	 * The /1, /2 and /4 seem like reasonable choices.
+	 */
+	{812500, 1},
+	{406250, 2},
+	{203125, 4},
+	/* that's all we really need to support */
+	{0, 0}
+};
+
+/*
+ * The following helper function actually switches the UART
+ * baud rate divisor.  Call serial_flush() first.  It returns the
+ * old divisor value.
+ *
+ * Note the u8 type for both the new and old divisor values.
+ * All supported divisors are well below 255, so we don't bother
+ * with the upper byte.
+ */
+static u8
+actually_switch_baud(newdiv)
+	u8 newdiv;
+{
+	volatile struct ns16550_regs *regs;
+	u8 save_lcr, save_old_baud;
+
+	regs = uart_base;
+	save_lcr = regs->lcr;
+	regs->lcr = save_lcr | NS16550_LCR_DLAB;
+	save_old_baud = regs->datareg;
+	regs->datareg = newdiv;
+	regs->lcr = save_lcr;
+	return(save_old_baud);
+}
+
+void
+cmd_baud_switch(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	int baudarg;
+	struct tab *tp;
+	u8 save_old_baud;
+	int c;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	baudarg = atoi(argv[0]);
+	for (tp = rate_table; tp->baud; tp++)
+		if (tp->baud == baudarg)
+			break;
+	if (!tp->baud) {
+		printf("ERROR: invalid/unimplemented baud rate argument\n");
+		return;
+	}
+
+	/* do it */
+	serial_flush();
+	save_old_baud = actually_switch_baud(tp->divisor);
+
+	/*
+	 * After getting the echo of this command at the old baud rate
+	 * (see the serial flush call just before switching the divisor),
+	 * the line will go silent from the user's perspective.
+	 * The user should wait just a little bit, then send us a 0x55 ('U')
+	 * at the new baud rate - we should be in the below loop waiting
+	 * for this character by then.  Receiving that character
+	 * correctly (0x55 was chosen for the bit pattern - unlikely to
+	 * be received if the sender is sending at a wrong baud rate)
+	 * will cause us to conclude this command and return a new '='
+	 * prompt at the new baud rate.
+	 *
+	 * If we get something else, we assume that someone messed up,
+	 * switch back to the old baud rate, scribble an error message
+	 * and return.
+	 */
+	do
+		c = serial_in_poll();
+	while (c < 0);
+	if (c != 0x55) {
+	  actually_switch_baud(save_old_baud);
+	  printf("ERROR: no \'U\' received, switched back to old baud rate\n");
+	}
+}