Skip to contentSkip to conversation

serial: 8250: Add driver for Broadcom UART

PATCH Superseded Al Cooper 5 participants 7 messages Feb 12, 2021 – Feb 19, 2021

Subsystems (4)

v3 of 7 Superseded Latest is v7 →

lore
Share & tools
lore

Al Cooper

author maintainer
#0 lore raw

v3 cover serial: 8250: Add driver for Broadcom UART

v3 - remove "disable_dma" module param because it can be done
by modifying the device tree node instead. Reduce size by removing some debug fuctionality that was no longer used.
- Fix error from yaml compiler in bindings

v2 - remove the patch that modified 8250_of.c to keep it from
registering before this driver when this driver was deferred as it was getting it's "clocks". This was fixed by changing the Device Tree entry to remove "clock-frequency". This results in both drivers getting "clocks" and getting same the deferral.

Al Cooper (2):
dt-bindings: Add support for the Broadcom UART driver
serial: 8250: Add new 8250-core based Broadcom STB driver

 .../bindings/serial/brcm,bcm7271-uart.yaml    |   96 ++
 MAINTAINERS                                   |    8 +
 drivers/tty/serial/8250/8250_bcm7271.c        | 1099 +++++++++++++++++
 drivers/tty/serial/8250/Kconfig               |   11 +
 drivers/tty/serial/8250/Makefile              |    1 +
 drivers/tty/serial/8250/bcm7271_uart.h        |  158 +++
 6 files changed, 1373 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml
 create mode 100644 drivers/tty/serial/8250/8250_bcm7271.c
 create mode 100644 drivers/tty/serial/8250/bcm7271_uart.h
Signature · 1 line
2.17.1
author maintainer
#1 lore raw

PATCH v3 1/2 dt-bindings: Add support for the Broadcom UART driver

Add DT bindings for the Broadcom 8250 based UART driver. This UART is based on an 8250 but adds additional functionality. The additional features include the ability to use DMA for transfers and a baud rate clock system that is more accurate at high baud rates. This UART is backward compatible with the standard 8250 UART.

---
 .../bindings/serial/brcm,bcm7271-uart.yaml    | 96 +++++++++++++++++++
 1 file changed, 96 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml
Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml new file +96 -0
new file mode 100644
index 000000000000..f3d58e613480
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/serial/brcm,bcm7271-uart.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom 8250 based serial port devicetree bindings
+
+maintainers:
+  - Al Cooper <[email protected]>
+
+description: |+
+  The Broadcom UART is based on the basic 8250 UART but with
+  enhancements for more accurate high speed baud rates and support
+  for DMA.
+
+properties:
+  $nodename:
+    pattern: "^serial@[0-9a-f]+$"
+
+  compatible:
+    items:
+      - enum:
+          - brcm,bcm7271-uart
+          - brcm,bcm7278-uart
+  reg:
+    minItems: 1
+    maxItems: 5
+
+  reg-names:
+    description: The UART register block and optionally the DMA register blocks.
+    oneOf:
+      - items:
+          - const: uart
+      - items:
+          - const: uart
+          - const: dma_arb
+          - const: dma_rx
+          - const: dma_tx
+          - const: dma_intr2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    const: sw_baud
+
+  interrupts:
+    minItems: 1
+    maxItems: 2
+
+  interrupt-names:
+    description: The UART interrupt and optionally the DMA interrupt.
+    oneOf:
+      - items:
+          - const: uart
+      - items:
+          - const: uart
+          - const: dma
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - clocks
+  - clock-names
+  - interrupts
+  - interrupt-names
+
+additionalProperties: false
+
+examples:
+  - |
+    serial@840d000 {
+        compatible = "brcm,bcm7271-uart";
+        reg = <0x840d000 0x20>;
+        reg-names = "uart";
+        interrupts = <0x0 0x62 0x4>;
+        interrupt-names = "uart";
+        clocks = <&scmi_clk 190>;
+        clock-names = "sw_baud";
+    };
+
+    serial@840e000 {
+        compatible = "brcm,bcm7271-uart";
+        reg = <0x840e000 0x20>,
+              <0x840e080 0x8>,
+              <0x840e100 0xa8>,
+              <0x840e200 0x4c>,
+              <0x840e300 0x30>;
+        reg-names = "uart", "dma_arb", "dma_rx", "dma_tx", "dma_intr2";
+        interrupts = <0x0 0x62 0x4>, <0x0 0x75 0x4>;
+        interrupt-names = "uart", "dma";
+        clocks = <&scmi_clk 190>;
+        clock-names = "sw_baud";
+    };
Signature · 1 line
2.17.1
author maintainer
#2 lore raw

PATCH v3 2/2 serial: 8250: Add new 8250-core based Broadcom STB driver

In reply to Al Cooper

Add a UART driver for the new Broadcom 8250 based STB UART. The new UART is backward compatible with the standard 8250, but has some additional features. The new features include a high accuracy baud rate clock system and DMA support.

The driver will use the new optional BAUD MUX clock to select the best one of the four master clocks (81MHz, 108MHz, 64MHz and 48MHz) to feed the baud rate selection logic for any requested baud rate. This allows for more accurate BAUD rates when high speed baud rates are selected.

The driver will use the new UART DMA hardware if the UART DMA registers are specified in Device Tree "reg" property.

The driver also sets the UPSTAT_AUTOCTS flag when hardware flow control is enabled. This flag is needed for UARTs that don't assert a CTS changed interrupt when CTS changes and AFE (Hardware Flow Control) is enabled.

The driver also contains a workaround for a bug in the Synopsis 8250 core. The problem is that at high baud rates, the RX partial FIFO timeout interrupt can occur but there is no RX data (DR not set in the LSR register). In this case the driver will not read the Receive Buffer Register, which clears the interrupt, and the system will get continuous UART interrupts until the next RX character arrives. The fix originally suggested by Synopsis was to read the Receive Buffer Register and discard the character when the DR bit in the LSR was not set, to clear the interrupt. The problem was that occasionally a character would arrive just after the DR bit check and a valid character would be discarded. The fix that was added will clear receive interrupts to stop the interrupt, deassert RTS to insure that no new data can arrive, wait for 1.5 character times for the sender to react to RTS and then check for data and either do a dummy read or a valid read. Sysfs error counters were also added and were used to help create test software that would cause the error condition. The counters can be found at:
/sys/devices/platform/rdb/*serial/rx_bad_timeout_late_char /sys/devices/platform/rdb/*serial/rx_bad_timeout_no_char

---
 MAINTAINERS                            |    8 +
 drivers/tty/serial/8250/8250_bcm7271.c | 1099 ++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig        |   11 +
 drivers/tty/serial/8250/Makefile       |    1 +
 drivers/tty/serial/8250/bcm7271_uart.h |  158 ++++
 5 files changed, 1277 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_bcm7271.c
 create mode 100644 drivers/tty/serial/8250/bcm7271_uart.h
MAINTAINERS +8 -0
index 64c7169db617..bb6ad2fc4376 100644
@@ -3582,6 +3582,14 @@ S:	Supported
 F:	Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
 F:	drivers/i2c/busses/i2c-brcmstb.c
 
+BROADCOM BRCMSTB UART DRIVER
+M:	Al Cooper <[email protected]>
+L:	[email protected]
+L:	[email protected]
+S:	Maintained
+F:	Documentation/devicetree/bindings/serial/brcm,bcm7271-uart.yaml
+F:	drivers/tty/serial/8250/8250_bcm7271.c
+
 BROADCOM BRCMSTB USB EHCI DRIVER
 M:	Al Cooper <[email protected]>
 L:	[email protected]
drivers/tty/serial/8250/8250_bcm7271.c new file +1099 -0

1,099 lines not on this page · raw message

drivers/tty/serial/8250/Kconfig +11 -0
index 603137da4736..436a686bf7fd 100644
@@ -501,6 +501,7 @@ config SERIAL_8250_PXA
 	  applicable to both devicetree and legacy boards, and early console is
 	  part of its support.
 
+
 config SERIAL_8250_TEGRA
 	tristate "8250 support for Tegra serial ports"
 	default SERIAL_8250
@@ -510,6 +511,16 @@ config SERIAL_8250_TEGRA
 	  Select this option if you have machine with an NVIDIA Tegra SoC and
 	  wish to enable 8250 serial driver for the Tegra serial interfaces.
 
+config SERIAL_8250_BCM7271
+	bool "Broadcom 8250 based serial port"
+	depends on SERIAL_8250 && (ARCH_BRCMSTB || COMPILE_TEST)
+	default ARCH_BRCMSTB
+	help
+	  If you have a Broadcom STB based board and want to use the
+	  enhanced features of the Broadcom 8250 based serial port,
+	  including DMA support and high accuracy BAUD rates, say
+	  Y to this option. If unsure, say N.
+
 config SERIAL_OF_PLATFORM
 	tristate "Devicetree based probing for 8250 ports"
 	depends on SERIAL_8250 && OF
drivers/tty/serial/8250/Makefile +1 -0
index a8bfb654d490..b9bcd73c8997 100644
@@ -38,6 +38,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS)		+= 8250_lpss.o
 obj-$(CONFIG_SERIAL_8250_MID)		+= 8250_mid.o
 obj-$(CONFIG_SERIAL_8250_PXA)		+= 8250_pxa.o
 obj-$(CONFIG_SERIAL_8250_TEGRA)		+= 8250_tegra.o
+obj-$(CONFIG_SERIAL_8250_BCM7271)	+= 8250_bcm7271.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM)	+= 8250_of.o
 
 CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt
drivers/tty/serial/8250/bcm7271_uart.h new file +158 -0

158 lines not on this page · raw message

Signature · 1 line
2.17.1
#3 lore raw

Re: [PATCH v3 2/2] serial: 8250: Add new 8250-core based Broadcom STB driver

Quoted 54 lines · Al Cooper

Add a UART driver for the new Broadcom 8250 based STB UART. The new UART is backward compatible with the standard 8250, but has some additional features. The new features include a high accuracy baud rate clock system and DMA support.

The driver will use the new optional BAUD MUX clock to select the best one of the four master clocks (81MHz, 108MHz, 64MHz and 48MHz) to feed the baud rate selection logic for any requested baud rate. This allows for more accurate BAUD rates when high speed baud rates are selected.

The driver will use the new UART DMA hardware if the UART DMA registers are specified in Device Tree "reg" property.

The driver also sets the UPSTAT_AUTOCTS flag when hardware flow control is enabled. This flag is needed for UARTs that don't assert a CTS changed interrupt when CTS changes and AFE (Hardware Flow Control) is enabled.

The driver also contains a workaround for a bug in the Synopsis 8250 core. The problem is that at high baud rates, the RX partial FIFO timeout interrupt can occur but there is no RX data (DR not set in the LSR register). In this case the driver will not read the Receive Buffer Register, which clears the interrupt, and the system will get continuous UART interrupts until the next RX character arrives. The fix originally suggested by Synopsis was to read the Receive Buffer Register and discard the character when the DR bit in the LSR was not set, to clear the interrupt. The problem was that occasionally a character would arrive just after the DR bit check and a valid character would be discarded. The fix that was added will clear receive interrupts to stop the interrupt, deassert RTS to insure that no new data can arrive, wait for 1.5 character times for the sender to react to RTS and then check for data and either do a dummy read or a valid read. Sysfs error counters were also added and were used to help create test software that would cause the error condition. The counters can be found at:
/sys/devices/platform/rdb/*serial/rx_bad_timeout_late_char /sys/devices/platform/rdb/*serial/rx_bad_timeout_no_char

---
 MAINTAINERS                            |    8 +
 drivers/tty/serial/8250/8250_bcm7271.c | 1099 ++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig        |   11 +
 drivers/tty/serial/8250/Makefile       |    1 +
 drivers/tty/serial/8250/bcm7271_uart.h |  158 ++++
 5 files changed, 1277 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_bcm7271.c
 create mode 100644 drivers/tty/serial/8250/bcm7271_uart.h
MAINTAINERS +0 -0
index 64c7169db617..bb6ad2fc4376 100644

MAINTAINERS S: Supported · line 3587

Quoted 6 lines
MAINTAINERS +3 -0
@@ -3582,6 +3582,14 @@ S:	Supported
 F:	Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
 F:	drivers/i2c/busses/i2c-brcmstb.c
 
+BROADCOM BRCMSTB UART DRIVER
+M:	Al Cooper <[email protected]>
+L:	[email protected]

This should probably be linux-serial, copy pasted from the USB entry down below presumably.

Signature · 1 line
Florian
#4 lore raw

Re: [PATCH v3 2/2] serial: 8250: Add new 8250-core based Broadcom STB driver

Quoted 60 lines · Florian Fainelli
Quoted 57 lines · Al Cooper

Add a UART driver for the new Broadcom 8250 based STB UART. The new UART is backward compatible with the standard 8250, but has some additional features. The new features include a high accuracy baud rate clock system and DMA support.

The driver will use the new optional BAUD MUX clock to select the best one of the four master clocks (81MHz, 108MHz, 64MHz and 48MHz) to feed the baud rate selection logic for any requested baud rate. This allows for more accurate BAUD rates when high speed baud rates are selected.

The driver will use the new UART DMA hardware if the UART DMA registers are specified in Device Tree "reg" property.

The driver also sets the UPSTAT_AUTOCTS flag when hardware flow control is enabled. This flag is needed for UARTs that don't assert a CTS changed interrupt when CTS changes and AFE (Hardware Flow Control) is enabled.

The driver also contains a workaround for a bug in the Synopsis 8250 core. The problem is that at high baud rates, the RX partial FIFO timeout interrupt can occur but there is no RX data (DR not set in the LSR register). In this case the driver will not read the Receive Buffer Register, which clears the interrupt, and the system will get continuous UART interrupts until the next RX character arrives. The fix originally suggested by Synopsis was to read the Receive Buffer Register and discard the character when the DR bit in the LSR was not set, to clear the interrupt. The problem was that occasionally a character would arrive just after the DR bit check and a valid character would be discarded. The fix that was added will clear receive interrupts to stop the interrupt, deassert RTS to insure that no new data can arrive, wait for 1.5 character times for the sender to react to RTS and then check for data and either do a dummy read or a valid read. Sysfs error counters were also added and were used to help create test software that would cause the error condition. The counters can be found at:
/sys/devices/platform/rdb/*serial/rx_bad_timeout_late_char /sys/devices/platform/rdb/*serial/rx_bad_timeout_no_char

---
 MAINTAINERS                            |    8 +
 drivers/tty/serial/8250/8250_bcm7271.c | 1099 ++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig        |   11 +
 drivers/tty/serial/8250/Makefile       |    1 +
 drivers/tty/serial/8250/bcm7271_uart.h |  158 ++++
 5 files changed, 1277 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_bcm7271.c
 create mode 100644 drivers/tty/serial/8250/bcm7271_uart.h
MAINTAINERS +0 -0
index 64c7169db617..bb6ad2fc4376 100644
@@ -3582,6 +3582,14 @@ S:	Supported
 F:	Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
 F:	drivers/i2c/busses/i2c-brcmstb.c
 
Quoted 6 lines

MAINTAINERS S: Supported · line 3587

Quoted 3 lines
MAINTAINERS +3 -0
+BROADCOM BRCMSTB UART DRIVER
+M:	Al Cooper <[email protected]>
+L:	[email protected]

This should probably be linux-serial, copy pasted from the USB entry down below presumably.

Yes, it should not be linux-usb, and that explains why these serial-driver patches were cc:ed to that list :(

thanks,

greg k-h

maintainer
#5 lore raw

Re: [PATCH v3 2/2] serial: 8250: Add new 8250-core based Broadcom STB driver

In reply to Al Cooper
Quoted 9 lines · Al Cooper

Add a UART driver for the new Broadcom 8250 based STB UART. The new UART is backward compatible with the standard 8250, but has some additional features. The new features include a high accuracy baud rate clock system and DMA support.

The driver will use the new optional BAUD MUX clock to select the best one of the four master clocks (81MHz, 108MHz, 64MHz and 48MHz) to feed the baud rate selection logic for any requested baud rate. This allows for more accurate BAUD rates when high speed baud rates are selected.

...

Quoted 2 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +0 -0

...

drivers/tty/serial/8250/8250_bcm7271.c line 367

Quoted 7 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +7 -0
+static void brcmuart_rx_isr(struct uart_port *up, u32 rx_isr)
+{
+	struct brcmuart_priv *priv = up->private_data;
+	struct device *dev = up->dev;
+	u32 rx_done_isr;
+	u32 check_isr;
+	char seq_err[] = "RX buffer ready out of sequence, restarting RX DMA\n";

What's the purpose of this on-stack variable?

Quoted 14 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +14 -0
+static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv)
+{
+	int x;
+	int rc;
+
+	priv->default_mux_rate = clk_get_rate(priv->baud_mux_clk);
+	dev_dbg(dev, "Default BAUD MUX Clock rate is %lu\n",
+		priv->default_mux_rate);
+
+	for (x = 0; x < ARRAY_SIZE(priv->real_rates); x++) {
+		if (priv->rate_table[x] == 0) {
+			priv->real_rates[x] = 0;
+			continue;
+		}

drivers/tty/serial/8250/8250_bcm7271.c line 551

Quoted 10 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +10 -0
+		rc = clk_set_rate(priv->baud_mux_clk, priv->rate_table[x]);
+		if (rc) {
+			dev_err(dev, "Error selecting BAUD MUX clock for %u\n",
+				priv->rate_table[x]);
+			priv->real_rates[x] = priv->rate_table[x];
+		} else {
+			priv->real_rates[x] = clk_get_rate(priv->baud_mux_clk);
+		}
+	}
+	 clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate);

This is only weirdly indented.

Quoted 65 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +65 -0
+}
+
+static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
+			u32 baud)
+{
+	u32 percent;
+	u32 best_percent = UINT_MAX;
+	u32 quot;
+	u32 best_quot = 1;
+	u32 rate;
+	int best_index = -1;
+	u64 hires_rate;
+	u64 hires_baud;
+	u64 hires_err;
+	int rc;
+	int i;
+	int real_baud;
+
+	/* If the Baud Mux Clock was not specified, just return */
+	if (priv->baud_mux_clk == NULL)
+		return;
+
+	/* Find the closest match for specified baud */
+	for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) {
+		if (priv->real_rates[i] == 0)
+			continue;
+		rate = priv->real_rates[i] / 16;
+		quot = DIV_ROUND_CLOSEST(rate, baud);
+		if (!quot)
+			continue;
+
+		/* increase resolution to get xx.xx percent */
+		hires_rate = (u64)rate * 10000;
+		hires_baud = (u64)baud * 10000;
+
+		hires_err = div_u64(hires_rate, (u64)quot);
+
+		/* get the delta */
+		if (hires_err > hires_baud)
+			hires_err = (hires_err - hires_baud);
+		else
+			hires_err = (hires_baud - hires_err);
+
+		percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud);
+		dev_dbg(up->dev,
+			"Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n",
+			baud, priv->real_rates[i], percent / 100,
+			percent % 100);
+		if (percent < best_percent) {
+			best_percent = percent;
+			best_index = i;
+			best_quot = quot;
+		}
+	}
+	if (best_index == -1) {
+		dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud);
+		return;
+	}
+	rate = priv->real_rates[best_index];
+	rc = clk_set_rate(priv->baud_mux_clk, rate);
+	if (rc)
+		dev_err(up->dev, "Error selecting BAUD MUX clock\n");
+
+	/* Error over 3 percent will cause data errors */
+	if (best_percent > 300)

drivers/tty/serial/8250/8250_bcm7271.c line 626

Quoted 10 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +10 -0
+		dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n",
+			baud, percent / 100, percent % 100);
+
+	real_baud = rate / 16 / best_quot;
+	dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate);
+	dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n",
+		baud, real_baud);
+
+	/* calc nanoseconds for 1.5 characters time at the given baud rate */
+	i = 1000000000 / real_baud / 10;

NSEC_PER_SEC here?

Quoted 5 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +5 -0
+	i += (i / 2);
+	priv->char_wait = ns_to_ktime(i);
+
+	up->uartclk = rate;
+}

...

Quoted 12 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +12 -0
+static int __maybe_unused brcmuart_resume(struct device *dev)
+{
+	struct brcmuart_priv *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(priv->baud_mux_clk);
+	if (ret)
+		dev_err(dev, "Error enabling BAUD MUX clock\n");
+
+	/*
+	 * The hardware goes back to it's default after suspend
+	 * so get the "clk" back in sync.

drivers/tty/serial/8250/8250_bcm7271.c line 1078

Quoted 10 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +10 -0
+	 */
+	ret = clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate);
+	if (ret)
+		dev_err(dev, "Error restoring default BAUD MUX clock\n");
+	if (priv->dma_enabled) {
+		brcmuart_arbitration(priv, 1);
+		brcmuart_init_dma_hardware(priv);
+		start_rx_dma(serial8250_get_port(priv->line));
+	}
+	serial8250_resume_port(priv->line);

All these cannot fail? Or the above can, so does proceeding further without an error make sense?

Quoted 2 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +2 -0
+	return 0;
+}

....

drivers/tty/serial/8250/Kconfig config SERIAL_8250_PXA · line 504

Quoted 7 lines
drivers/tty/serial/8250/Kconfig +1 -0
@@ -501,6 +501,7 @@ config SERIAL_8250_PXA
  	  applicable to both devicetree and legacy boards, and early console is
  	  part of its support.
 
+

Why adding a newline here?

drivers/tty/serial/8250/Kconfig config SERIAL_8250_PXA · line 507

Quoted 3 lines
drivers/tty/serial/8250/Kconfig +0 -0
  config SERIAL_8250_TEGRA
  	tristate "8250 support for Tegra serial ports"
  	default SERIAL_8250

regards,

Signature · 2 lines
js
suse labs
#6 lore raw

Re: [PATCH v3 2/2] serial: 8250: Add new 8250-core based Broadcom STB driver

Quoted 15 lines · Jiri Slaby
Quoted 9 lines · Al Cooper

Add a UART driver for the new Broadcom 8250 based STB UART. The new UART is backward compatible with the standard 8250, but has some additional features. The new features include a high accuracy baud rate clock system and DMA support.

The driver will use the new optional BAUD MUX clock to select the best one of the four master clocks (81MHz, 108MHz, 64MHz and 48MHz) to feed the baud rate selection logic for any requested baud rate. This allows for more accurate BAUD rates when high speed baud rates are selected.

...

Quoted 2 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +0 -0

...

Quoted 9 lines

drivers/tty/serial/8250/8250_bcm7271.c line 367

Quoted 7 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +7 -0
+static void brcmuart_rx_isr(struct uart_port *up, u32 rx_isr)
+{
+     struct brcmuart_priv *priv = up->private_data;
+     struct device *dev = up->dev;
+     u32 rx_done_isr;
+     u32 check_isr;
+     char seq_err[] = "RX buffer ready out of sequence, restarting RX DMA\n";

What's the purpose of this on-stack variable?

This was done to make the line fit in 80 columns. Now that the rule has been relaxed, I'll move it.

Quoted 20 lines
Quoted 20 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +18 -0
+static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv)
+{
+     int x;
+     int rc;
+
+     priv->default_mux_rate = clk_get_rate(priv->baud_mux_clk);
+     dev_dbg(dev, "Default BAUD MUX Clock rate is %lu\n",
+             priv->default_mux_rate);
+
+     for (x = 0; x < ARRAY_SIZE(priv->real_rates); x++) {
+             if (priv->rate_table[x] == 0) {
+                     priv->real_rates[x] = 0;
+                     continue;
+             }
+             rc = clk_set_rate(priv->baud_mux_clk, priv->rate_table[x]);
+             if (rc) {
+                     dev_err(dev, "Error selecting BAUD MUX clock for %u\n",
+                             priv->rate_table[x]);
Quoted 8 lines

drivers/tty/serial/8250/8250_bcm7271.c line 551

Quoted 6 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +6 -0
+                     priv->real_rates[x] = priv->rate_table[x];
+             } else {
+                     priv->real_rates[x] = clk_get_rate(priv->baud_mux_clk);
+             }
+     }
+      clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate);

This is only weirdly indented.

Fixed.

Quoted 71 lines
Quoted 71 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +69 -0
+}
+
+static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
+                     u32 baud)
+{
+     u32 percent;
+     u32 best_percent = UINT_MAX;
+     u32 quot;
+     u32 best_quot = 1;
+     u32 rate;
+     int best_index = -1;
+     u64 hires_rate;
+     u64 hires_baud;
+     u64 hires_err;
+     int rc;
+     int i;
+     int real_baud;
+
+     /* If the Baud Mux Clock was not specified, just return */
+     if (priv->baud_mux_clk == NULL)
+             return;
+
+     /* Find the closest match for specified baud */
+     for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) {
+             if (priv->real_rates[i] == 0)
+                     continue;
+             rate = priv->real_rates[i] / 16;
+             quot = DIV_ROUND_CLOSEST(rate, baud);
+             if (!quot)
+                     continue;
+
+             /* increase resolution to get xx.xx percent */
+             hires_rate = (u64)rate * 10000;
+             hires_baud = (u64)baud * 10000;
+
+             hires_err = div_u64(hires_rate, (u64)quot);
+
+             /* get the delta */
+             if (hires_err > hires_baud)
+                     hires_err = (hires_err - hires_baud);
+             else
+                     hires_err = (hires_baud - hires_err);
+
+             percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud);
+             dev_dbg(up->dev,
+                     "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n",
+                     baud, priv->real_rates[i], percent / 100,
+                     percent % 100);
+             if (percent < best_percent) {
+                     best_percent = percent;
+                     best_index = i;
+                     best_quot = quot;
+             }
+     }
+     if (best_index == -1) {
+             dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud);
+             return;
+     }
+     rate = priv->real_rates[best_index];
+     rc = clk_set_rate(priv->baud_mux_clk, rate);
+     if (rc)
+             dev_err(up->dev, "Error selecting BAUD MUX clock\n");
+
+     /* Error over 3 percent will cause data errors */
+     if (best_percent > 300)
+             dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n",
+                     baud, percent / 100, percent % 100);
+
+     real_baud = rate / 16 / best_quot;
Quoted 8 lines

drivers/tty/serial/8250/8250_bcm7271.c line 626

Quoted 6 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +6 -0
+     dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate);
+     dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n",
+             baud, real_baud);
+
+     /* calc nanoseconds for 1.5 characters time at the given baud rate */
+     i = 1000000000 / real_baud / 10;

NSEC_PER_SEC here?

Fixed.

Quoted 27 lines
Quoted 7 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +5 -0
+     i += (i / 2);
+     priv->char_wait = ns_to_ktime(i);
+
+     up->uartclk = rate;
+}

...

Quoted 17 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +17 -0
+static int __maybe_unused brcmuart_resume(struct device *dev)
+{
+     struct brcmuart_priv *priv = dev_get_drvdata(dev);
+     int ret;
+
+     ret = clk_prepare_enable(priv->baud_mux_clk);
+     if (ret)
+             dev_err(dev, "Error enabling BAUD MUX clock\n");
+
+     /*
+      * The hardware goes back to it's default after suspend
+      * so get the "clk" back in sync.
+      */
+     ret = clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate);
+     if (ret)
+             dev_err(dev, "Error restoring default BAUD MUX clock\n");
+     if (priv->dma_enabled) {
Quoted 8 lines

drivers/tty/serial/8250/8250_bcm7271.c line 1078

Quoted 5 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +5 -0
+             brcmuart_arbitration(priv, 1);
+             brcmuart_init_dma_hardware(priv);
+             start_rx_dma(serial8250_get_port(priv->line));
+     }
+     serial8250_resume_port(priv->line);

All these cannot fail? Or the above can, so does proceeding further without an error make sense?

brcmuart_arbitration() can return a failure and I'll print an error message and return EBUSY if it does.
The other functions always return 0 and I'll change them to return void.

Quoted 10 lines
Quoted 3 lines
drivers/tty/serial/8250/8250_bcm7271.c new file +2 -0
+     return 0;
+}

....

Quoted 6 lines
drivers/tty/serial/8250/Kconfig +0 -0
@@ -501,6 +501,7 @@ config SERIAL_8250_PXA
        applicable to both devicetree and legacy boards, and early console is
        part of its support.
 
Quoted 3 lines

drivers/tty/serial/8250/Kconfig config SERIAL_8250_PXA · line 504

Quoted 1 line
drivers/tty/serial/8250/Kconfig +1 -0
+

Why adding a newline here?

Fixed

Quoted 10 lines

drivers/tty/serial/8250/Kconfig config SERIAL_8250_PXA · line 507

Quoted 5 lines
drivers/tty/serial/8250/Kconfig +0 -0
  config SERIAL_8250_TEGRA
      tristate "8250 support for Tegra serial ports"
      default SERIAL_8250

regards,
--
js
suse labs

Jira, thanks for the review.
Al