public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Max Filippov <jcmvbkbc@gmail.com>
To: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Jiri Slaby <jirislaby@kernel.org>,
	Max Filippov <jcmvbkbc@gmail.com>
Subject: [PATCH] serial/esp32_uart: use prescaler when available
Date: Wed, 18 Oct 2023 12:12:52 -0700	[thread overview]
Message-ID: <20231018191252.1551972-1-jcmvbkbc@gmail.com> (raw)

esp32s3 variant of the esp32 UART has limited baudrate divisor range
that does not allow it to use 9600 and lower rates with 40MHz input
clock. Use clock prescaler present in this UART variant to help with
that.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
---
 drivers/tty/serial/esp32_uart.c | 49 +++++++++++++++++++++++++++++++--
 1 file changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c
index 82033470db44..85c9c5ad7cc5 100644
--- a/drivers/tty/serial/esp32_uart.c
+++ b/drivers/tty/serial/esp32_uart.c
@@ -67,6 +67,26 @@
 #define ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT	10
 #define ESP32_UART_RX_FLOW_EN			BIT(23)
 #define ESP32S3_UART_RX_FLOW_EN			BIT(22)
+#define ESP32S3_UART_CLK_CONF_REG	0x78
+#define ESP32S3_UART_SCLK_DIV_B			GENMASK(5, 0)
+#define ESP32S3_UART_SCLK_DIV_A			GENMASK(11, 6)
+#define ESP32S3_UART_SCLK_DIV_NUM		GENMASK(19, 12)
+#define ESP32S3_UART_SCLK_SEL			GENMASK(21, 20)
+#define APB_CLK					1
+#define RC_FAST_CLK				2
+#define XTAL_CLK				3
+#define ESP32S3_UART_SCLK_EN			BIT(22)
+#define ESP32S3_UART_RST_CORE			BIT(23)
+#define ESP32S3_UART_TX_SCLK_EN			BIT(24)
+#define ESP32S3_UART_RX_SCLK_EN			BIT(25)
+#define ESP32S3_UART_TX_RST_CORE		BIT(26)
+#define ESP32S3_UART_RX_RST_CORE		BIT(27)
+
+#define ESP32S3_UART_CLK_CONF_DEFAULT \
+	(ESP32S3_UART_RX_SCLK_EN | \
+	 ESP32S3_UART_TX_SCLK_EN | \
+	 ESP32S3_UART_SCLK_EN | \
+	 FIELD_PREP(ESP32S3_UART_SCLK_SEL, XTAL_CLK))
 
 struct esp32_port {
 	struct uart_port port;
@@ -80,6 +100,7 @@ struct esp32_uart_variant {
 	u32 txfifo_empty_thrhd_shift;
 	u32 rx_flow_en;
 	const char *type;
+	bool has_clkconf;
 };
 
 static const struct esp32_uart_variant esp32_variant = {
@@ -98,6 +119,7 @@ static const struct esp32_uart_variant esp32s3_variant = {
 	.txfifo_empty_thrhd_shift = ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT,
 	.rx_flow_en = ESP32S3_UART_RX_FLOW_EN,
 	.type = "ESP32S3 UART",
+	.has_clkconf = true,
 };
 
 static const struct of_device_id esp32_uart_dt_ids[] = {
@@ -314,6 +336,9 @@ static int esp32_uart_startup(struct uart_port *port)
 	}
 
 	spin_lock_irqsave(&port->lock, flags);
+	if (port_variant(port)->has_clkconf)
+		esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
+				 ESP32S3_UART_CLK_CONF_DEFAULT);
 	esp32_uart_write(port, UART_CONF1_REG,
 			 (1 << UART_RXFIFO_FULL_THRHD_SHIFT) |
 			 (1 << port_variant(port)->txfifo_empty_thrhd_shift));
@@ -335,10 +360,24 @@ static void esp32_uart_shutdown(struct uart_port *port)
 
 static bool esp32_uart_set_baud(struct uart_port *port, u32 baud)
 {
-	u32 div = port->uartclk / baud;
-	u32 frag = (port->uartclk * 16) / baud - div * 16;
+	u32 sclk = port->uartclk;
+	u32 div = sclk / baud;
+
+	if (port_variant(port)->has_clkconf) {
+		u32 sclk_div = div / port_variant(port)->clkdiv_mask;
+
+		if (div > port_variant(port)->clkdiv_mask) {
+			sclk /= (sclk_div + 1);
+			div = sclk / baud;
+		}
+		esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
+				 FIELD_PREP(ESP32S3_UART_SCLK_DIV_NUM, sclk_div) |
+				 ESP32S3_UART_CLK_CONF_DEFAULT);
+	}
 
 	if (div <= port_variant(port)->clkdiv_mask) {
+		u32 frag = (sclk * 16) / baud - div * 16;
+
 		esp32_uart_write(port, UART_CLKDIV_REG,
 				 div | FIELD_PREP(UART_CLKDIV_FRAG, frag));
 		return true;
@@ -355,11 +394,15 @@ static void esp32_uart_set_termios(struct uart_port *port,
 	u32 conf0, conf1;
 	u32 baud;
 	const u32 rx_flow_en = port_variant(port)->rx_flow_en;
+	u32 max_div = port_variant(port)->clkdiv_mask;
 
 	termios->c_cflag &= ~CMSPAR;
 
+	if (port_variant(port)->has_clkconf)
+		max_div *= FIELD_MAX(ESP32S3_UART_SCLK_DIV_NUM);
+
 	baud = uart_get_baud_rate(port, termios, old,
-				  port->uartclk / port_variant(port)->clkdiv_mask,
+				  port->uartclk / max_div,
 				  port->uartclk / 16);
 
 	spin_lock_irqsave(&port->lock, flags);
-- 
2.30.2


                 reply	other threads:[~2023-10-18 19:13 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231018191252.1551972-1-jcmvbkbc@gmail.com \
    --to=jcmvbkbc@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jirislaby@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox