[RFC PATCH] RFC: Replace CONFIG_SYS_BAUDRATE_TABLE by board and UART driver rounding functions

Pali Rohár pali at kernel.org
Sat Sep 25 14:19:58 CEST 2021


Add new functions which returns the nearest baudrate and use them instead
of hardcoded and incomplete CONFIG_SYS_BAUDRATE_TABLE compile time option.

Add implementation of rounding function for serial_mvebu_a3700 driver and
also for A3720 Espressobin board which has integrated pl2303 USB<->UART
converter, which basically limits baudrates which can user set.

Completely remove CONFIG_SYS_BAUDRATE_TABLE defines from all A3720 boards
as now with rounding functions it is not used anymore.

NOTE: This is just an example how to kill CONFIG_SYS_BAUDRATE_TABLE compile
time definitions. I tested it that it works on A3720 Turris Mox board. I
have not tested A3720 Espressobin board yet.

More discussion on this approach is required, so take this just as RFC
change.

Signed-off-by: Pali Rohár <pali at kernel.org>
---
 board/Marvell/mvebu_armada-37xx/board.c | 28 ++++++++++
 drivers/serial/serial-uclass.c          | 71 +++++++++++++++++++++----
 drivers/serial/serial_mvebu_a3700.c     | 39 ++++++++++++++
 include/configs/mvebu_armada-37xx.h     |  8 ---
 include/configs/turris_mox.h            |  8 ---
 include/serial.h                        | 24 +++++++++
 6 files changed, 152 insertions(+), 26 deletions(-)

diff --git a/board/Marvell/mvebu_armada-37xx/board.c b/board/Marvell/mvebu_armada-37xx/board.c
index fdc873b1952f..0cd9ea6a1515 100644
--- a/board/Marvell/mvebu_armada-37xx/board.c
+++ b/board/Marvell/mvebu_armada-37xx/board.c
@@ -435,3 +435,31 @@ int ft_board_setup(void *blob, struct bd_info *bd)
 	return 0;
 }
 #endif
+
+int board_round_baudrate(int baudrate)
+{
+	/*
+	 * Espressobin has on-board pl2303 connected to A3720 UART.
+	 * So calculate the final real baudrate supported by pl2303.
+	 * Code from linux kernel function pl2303_encode_baud_rate_divisor()
+	 * Exact formula is: baudrate = 12M * 32 / (mantissa * 4^exponent)
+	 */
+	unsigned int baseline, mantissa, exponent;
+
+	baseline = 12000000 * 32;
+	mantissa = baseline / baudrate;
+	if (mantissa == 0)
+		mantissa = 1;
+	exponent = 0;
+	while (mantissa >= 512) {
+		if (exponent < 7) {
+			mantissa >>= 2;
+			exponent++;
+		} else {
+			mantissa = 511;
+			break;
+		}
+	}
+
+	return (baseline / mantissa) >> (exponent << 1);
+}
diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index 8171b17faf88..0d725ed764e6 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -299,6 +299,20 @@ int serial_tstc(void)
 	return _serial_tstc(gd->cur_serial_dev);
 }
 
+int serial_round_baudrate(int baudrate)
+{
+	struct dm_serial_ops *ops;
+
+	if (!gd->cur_serial_dev)
+		return 0;
+
+	ops = serial_get_ops(gd->cur_serial_dev);
+	if (!ops->round_baudrate)
+		return 0;
+
+	return ops->round_baudrate(gd->cur_serial_dev, baudrate);
+}
+
 void serial_setbrg(void)
 {
 	struct dm_serial_ops *ops;
@@ -378,6 +392,11 @@ static int serial_stub_tstc(struct stdio_dev *sdev)
 }
 #endif
 
+int __weak board_round_baudrate(int baudrate)
+{
+	return 0;
+}
+
 /**
  * on_baudrate() - Update the actual baudrate when the env var changes
  *
@@ -388,6 +407,8 @@ static int on_baudrate(const char *name, const char *value, enum env_op op,
 {
 	int i;
 	int baudrate;
+	int real_baudrate;
+	int board_baudrate;
 
 	switch (op) {
 	case env_op_create:
@@ -397,20 +418,50 @@ static int on_baudrate(const char *name, const char *value, enum env_op op,
 		 */
 		baudrate = dectoul(value, NULL);
 
-		/* Not actually changing */
-		if (gd->baudrate == baudrate)
-			return 0;
+		real_baudrate = serial_round_baudrate(baudrate);
 
-		for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) {
-			if (baudrate == baudrate_table[i])
-				break;
-		}
-		if (i == ARRAY_SIZE(baudrate_table)) {
-			if ((flags & H_FORCE) == 0)
+		if (real_baudrate) {
+			/* Baudrate is supported if is within 3% tolerance */
+			if (100 * real_baudrate < baudrate * (100 - 3) ||
+			    100 * real_baudrate > baudrate * (100 + 3)) {
 				printf("## Baudrate %d bps not supported\n",
 				       baudrate);
-			return 1;
+				return 1;
+			}
+			baudrate = real_baudrate;
+		} else {
+			/*
+			 * If round_baudrate() callback is unsupported then
+			 * check supported baudrate against baudrate_table[]
+			 */
+			for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) {
+				if (baudrate == baudrate_table[i])
+					break;
+			}
+			if (i == ARRAY_SIZE(baudrate_table)) {
+				if ((flags & H_FORCE) == 0)
+					printf("## Baudrate %d bps not supported\n",
+					       baudrate);
+				return 1;
+			}
+		}
+
+		board_baudrate = board_round_baudrate(baudrate);
+
+		if (board_baudrate) {
+			/* Baudrate is supported if is within 3% tolerance */
+			if (100 * board_baudrate < baudrate * (100 - 3) ||
+			    100 * board_baudrate > baudrate * (100 + 3)) {
+				printf("## Baudrate %d bps not supported by the board\n",
+				       baudrate);
+				return 1;
+			}
 		}
+
+		/* Not actually changing */
+		if (gd->baudrate == baudrate)
+			return 0;
+
 		if ((flags & H_INTERACTIVE) != 0) {
 			printf("## Switch baudrate to %d bps and press ENTER ...\n",
 			       baudrate);
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c
index 8dc4e5880e6b..d8bf3e97c486 100644
--- a/drivers/serial/serial_mvebu_a3700.c
+++ b/drivers/serial/serial_mvebu_a3700.c
@@ -147,6 +147,44 @@ static int mvebu_serial_setbrg(struct udevice *dev, int baudrate)
 	return 0;
 }
 
+static int mvebu_serial_round_baudrate(struct udevice *dev, int baudrate)
+{
+	struct mvebu_plat *plat = dev_get_plat(dev);
+	u32 divider, d1, d2, m;
+
+	m = 16;
+	d1 = d2 = 1;
+	divider = DIV_ROUND_CLOSEST(plat->tbg_rate, baudrate * d1 * d2 * m);
+
+	if (divider < 1)
+		divider = 1;
+	else if (divider > 1023) {
+		d1 = 6;
+		divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
+					    baudrate * d1 * d2 * m);
+		if (divider < 1)
+			divider = 1;
+		else if (divider > 1023) {
+			d2 = 6;
+			divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
+						    baudrate * d1 * d2 * m);
+			if (divider < 1)
+				divider = 1;
+			else if (divider > 1023) {
+				m = 63;
+				divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
+							    baudrate * d1 * d2 * m);
+				if (divider < 1)
+					divider = 1;
+				else if (divider > 1023)
+					divider = 1023;
+			}
+		}
+	}
+
+	return DIV_ROUND_CLOSEST(plat->tbg_rate, d1 * d2 * m * divider);
+}
+
 static int mvebu_serial_probe(struct udevice *dev)
 {
 	struct mvebu_plat *plat = dev_get_plat(dev);
@@ -335,6 +373,7 @@ static const struct dm_serial_ops mvebu_serial_ops = {
 	.pending = mvebu_serial_pending,
 	.getc = mvebu_serial_getc,
 	.setbrg = mvebu_serial_setbrg,
+	.round_baudrate = mvebu_serial_round_baudrate,
 };
 
 static const struct udevice_id mvebu_serial_ids[] = {
diff --git a/include/configs/mvebu_armada-37xx.h b/include/configs/mvebu_armada-37xx.h
index c8c34d7d92dd..6f7279f2ae4a 100644
--- a/include/configs/mvebu_armada-37xx.h
+++ b/include/configs/mvebu_armada-37xx.h
@@ -17,14 +17,6 @@
 
 #define CONFIG_SYS_BOOTM_LEN	SZ_64M /* Increase max gunzip size */
 
-#define CONFIG_SYS_BAUDRATE_TABLE	{ 300, 600, 1200, 1800, 2400, 4800, \
-					  9600, 19200, 38400, 57600, 115200, \
-					  230400, 460800, 500000, 576000, \
-					  921600, 1000000, 1152000, 1500000, \
-					  2000000, 2500000, 3000000, 3500000, \
-					  4000000, 4500000, 5000000, 5500000, \
-					  6000000 }
-
 /*
  * For booting Linux, the board info and command line data
  * have to be in the first 8 MB of memory, since this is
diff --git a/include/configs/turris_mox.h b/include/configs/turris_mox.h
index 671283982356..03813613e193 100644
--- a/include/configs/turris_mox.h
+++ b/include/configs/turris_mox.h
@@ -22,14 +22,6 @@
 
 /* auto boot */
 
-#define CONFIG_SYS_BAUDRATE_TABLE	{ 300, 600, 1200, 1800, 2400, 4800, \
-					  9600, 19200, 38400, 57600, 115200, \
-					  230400, 460800, 500000, 576000, \
-					  921600, 1000000, 1152000, 1500000, \
-					  2000000, 2500000, 3000000, 3500000, \
-					  4000000, 4500000, 5000000, 5500000, \
-					  6000000 }
-
 /*
  * For booting Linux, the board info and command line data
  * have to be in the first 8 MB of memory, since this is
diff --git a/include/serial.h b/include/serial.h
index 6d1e62c6770c..5e8a7501b9a6 100644
--- a/include/serial.h
+++ b/include/serial.h
@@ -176,6 +176,30 @@ struct dm_serial_ops {
 	 * @return 0 if OK, -ve on error
 	 */
 	int (*setbrg)(struct udevice *dev, int baudrate);
+	/**
+	 * round_baudrate() - Return the nearest available baudrate
+	 *
+	 * Return the exact baudrate value which would be set by setbrg()
+	 * if is called with this @baudrate argument. So this function
+	 * should return the nearest available baudrate.
+	 *
+	 * This function must not change baudrate generator. Its purpose
+	 * is just to test if particular baudrate value is supported
+	 * and to calculate baudrate tolerance.
+	 *
+	 * Caller may specify number 0 to retrieve the smallest possible
+	 * supported baudrate and INT_MAX number to retrive the highest
+	 * possible supported baudrate.
+	 *
+	 * Returning zero value is same as if this function is not
+	 * implemented at all. Meaning that driver cannot predict what is
+	 * the real final baudrate value.
+	 *
+	 * @dev: Device pointer
+	 * @baudrate: Baudrate to round
+	 * @return real baudrate value (see above)
+	 */
+	int (*round_baudrate)(struct udevice *dev, int baudrate);
 	/**
 	 * getc() - Read a character and return it
 	 *
-- 
2.20.1



More information about the U-Boot mailing list