All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: linux-serial@vger.kernel.org, Greg KH <gregkh@linuxfoundation.org>
Cc: "Jiri Slaby" <jirislaby@kernel.org>,
	"Lukas Wunner" <lukas@wunner.de>,
	"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
	"Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>,
	"Vicente Bergas" <vicencb@gmail.com>,
	"Johan Hovold" <johan@kernel.org>,
	heiko@sntech.de, giulio.benetti@micronovasrl.com,
	"Heikki Krogerus" <heikki.krogerus@linux.intel.com>,
	"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
	"Raymond Tan" <raymond.tan@intel.com>,
	"Lakshmi Sowjanya" <lakshmi.sowjanya.d@intel.com>
Subject: [PATCH v5 10/10] serial: 8250_dwlib: Support for 9th bit multipoint addressing
Date: Tue, 26 Apr 2022 15:24:48 +0300	[thread overview]
Message-ID: <20220426122448.38997-11-ilpo.jarvinen@linux.intel.com> (raw)
In-Reply-To: <20220426122448.38997-1-ilpo.jarvinen@linux.intel.com>

This change adds 9th bit multipoint addressing mode for DW UART using
the new ioctls introduced in the previous change. 9th bit addressing
can be used only when HW RS485 is available.

Updating RAR (receive address register) is bit tricky because busy
indication is not be available when DW UART is strictly 16550
compatible, which is the case with the hardware I was testing with. RAR
should not be updated while receive is in progress which is now
achieved by deasserting RE and waiting for one frame (in case rx would
be in progress, the driver seems to have no way of knowing it w/o busy
indication).

Co-developed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Co-developed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Co-developed-by: Raymond Tan <raymond.tan@intel.com>
Signed-off-by: Raymond Tan <raymond.tan@intel.com>
Co-developed-by: Lakshmi Sowjanya <lakshmi.sowjanya.d@intel.com>
Signed-off-by: Lakshmi Sowjanya <lakshmi.sowjanya.d@intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
 drivers/tty/serial/8250/8250_dwlib.c | 142 +++++++++++++++++++++++++++
 drivers/tty/serial/8250/8250_dwlib.h |   2 +
 2 files changed, 144 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c
index fbabfdd8c7b8..f285311a9dd9 100644
--- a/drivers/tty/serial/8250/8250_dwlib.c
+++ b/drivers/tty/serial/8250/8250_dwlib.c
@@ -3,8 +3,10 @@
 
 #include <linux/bitops.h>
 #include <linux/bitfield.h>
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/math.h>
 #include <linux/property.h>
 #include <linux/serial_8250.h>
 #include <linux/serial_core.h>
@@ -16,9 +18,18 @@
 #define DW_UART_DE_EN	0xb0 /* Driver Output Enable Register */
 #define DW_UART_RE_EN	0xb4 /* Receiver Output Enable Register */
 #define DW_UART_DLF	0xc0 /* Divisor Latch Fraction Register */
+#define DW_UART_RAR	0xc4 /* Receive Address Register */
+#define DW_UART_TAR	0xc8 /* Transmit Address Register */
+#define DW_UART_LCR_EXT	0xcc /* Line Extended Control Register */
 #define DW_UART_CPR	0xf4 /* Component Parameter Register */
 #define DW_UART_UCV	0xf8 /* UART Component Version */
 
+/* Receive / Transmit Address Register bits */
+#define DW_UART_ADDR_MASK		GENMASK(7, 0)
+
+/* Line Status Register bits */
+#define DW_UART_LSR_ADDR_RCVD		BIT(8)
+
 /* Transceiver Control Register bits */
 #define DW_UART_TCR_RS485_EN		BIT(0)
 #define DW_UART_TCR_RE_POL		BIT(1)
@@ -28,6 +39,12 @@
 #define DW_UART_TCR_XFER_MODE_SW_DE_OR_RE	FIELD_PREP(DW_UART_TCR_XFER_MODE, 1)
 #define DW_UART_TCR_XFER_MODE_DE_OR_RE		FIELD_PREP(DW_UART_TCR_XFER_MODE, 2)
 
+/* Line Extended Control Register bits */
+#define DW_UART_LCR_EXT_DLS_E		BIT(0)
+#define DW_UART_LCR_EXT_ADDR_MATCH	BIT(1)
+#define DW_UART_LCR_EXT_SEND_ADDR	BIT(2)
+#define DW_UART_LCR_EXT_TRANSMIT_MODE	BIT(3)
+
 /* Component Parameter Register bits */
 #define DW_UART_CPR_ABP_DATA_WIDTH	(3 << 0)
 #define DW_UART_CPR_AFCE_MODE		(1 << 4)
@@ -75,18 +92,132 @@ static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
 	serial8250_do_set_divisor(p, baud, quot, quot_frac);
 }
 
+/*
+ * Wait until re is de-asserted for sure. An ongoing receive will keep
+ * re asserted until end of frame. Without BUSY indication available,
+ * only available course of action is to wait for the time it takes to
+ * receive one frame (there might nothing to receive but w/o BUSY the
+ * driver cannot know).
+ */
+static void dw8250_wait_re_deassert(struct uart_port *p)
+{
+	udelay(p->frame_time);
+}
+
+static void dw8250_update_rar(struct uart_port *p, u32 addr)
+{
+	u32 re_en = dw8250_readl_ext(p, DW_UART_RE_EN);
+
+	/*
+	 * RAR shouldn't be changed while receiving. Thus, de-assert RE_EN
+	 * if asserted and wait.
+	 */
+	if (re_en)
+		dw8250_writel_ext(p, DW_UART_RE_EN, 0);
+	dw8250_wait_re_deassert(p);
+	dw8250_writel_ext(p, DW_UART_RAR, addr);
+	if (re_en)
+		dw8250_writel_ext(p, DW_UART_RE_EN, re_en);
+}
+
+static void dw8250_addrmode_setup(struct uart_port *p, bool enable_addrmode)
+{
+	struct dw8250_port_data *d = p->private_data;
+
+	if (enable_addrmode) {
+		/* Clear RAR & TAR of any previous values */
+		dw8250_writel_ext(p, DW_UART_RAR, 0);
+		dw8250_writel_ext(p, DW_UART_TAR, 0);
+		dw8250_writel_ext(p, DW_UART_LCR_EXT, DW_UART_LCR_EXT_DLS_E);
+	} else {
+		dw8250_writel_ext(p, DW_UART_LCR_EXT, 0);
+	}
+
+	d->addrmode = enable_addrmode;
+}
+
 void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct ktermios *old)
 {
+	struct dw8250_port_data *d = p->private_data;
+
 	p->status &= ~UPSTAT_AUTOCTS;
 	if (termios->c_cflag & CRTSCTS)
 		p->status |= UPSTAT_AUTOCTS;
 
+	if (!(p->rs485.flags & SER_RS485_ENABLED) || !d->hw_rs485_support)
+		termios->c_cflag &= ~ADDRB;
+
+	if (!old || (termios->c_cflag ^ old->c_cflag) & ADDRB)
+		dw8250_addrmode_setup(p, termios->c_cflag & ADDRB);
+
 	serial8250_do_set_termios(p, termios, old);
+
+	if (d->addrmode) {
+		p->ignore_status_mask |= DW_UART_LSR_ADDR_RCVD;
+		p->read_status_mask |= DW_UART_LSR_ADDR_RCVD;
+	}
 }
 EXPORT_SYMBOL_GPL(dw8250_do_set_termios);
 
+static int dw8250_rs485_set_addr(struct uart_port *p, struct serial_addr *addr)
+{
+	struct dw8250_port_data *d = p->private_data;
+	u32 lcr;
+
+	if (!(p->rs485.flags & SER_RS485_ENABLED) || !d->addrmode)
+		return -EINVAL;
+
+	addr->flags &= SER_ADDR_RECV | SER_ADDR_RECV_CLEAR | SER_ADDR_DEST;
+	if (!addr->flags)
+		return -EINVAL;
+
+	lcr = dw8250_readl_ext(p, DW_UART_LCR_EXT);
+	if (addr->flags & SER_ADDR_RECV) {
+		dw8250_update_rar(p, addr->addr & DW_UART_ADDR_MASK);
+		lcr |= DW_UART_LCR_EXT_ADDR_MATCH;
+		addr->flags &= ~SER_ADDR_RECV_CLEAR;
+	} else if (addr->flags & SER_ADDR_RECV_CLEAR) {
+		lcr &= DW_UART_LCR_EXT_ADDR_MATCH;
+	}
+	if (addr->flags & SER_ADDR_DEST) {
+		dw8250_writel_ext(p, DW_UART_TAR, addr->addr & DW_UART_ADDR_MASK);
+		lcr |= DW_UART_LCR_EXT_SEND_ADDR;
+	}
+	dw8250_writel_ext(p, DW_UART_LCR_EXT, lcr);
+
+	return 0;
+}
+
+static int dw8250_rs485_get_addr(struct uart_port *p, struct serial_addr *addr)
+{
+	struct dw8250_port_data *d = p->private_data;
+
+	if (!(p->rs485.flags & SER_RS485_ENABLED) || !d->addrmode)
+		return -EINVAL;
+
+	if (addr->flags == SER_ADDR_DEST) {
+		addr->addr = dw8250_readl_ext(p, DW_UART_TAR) & DW_UART_ADDR_MASK;
+		return 0;
+	}
+	if (addr->flags == SER_ADDR_RECV) {
+		u32 lcr = dw8250_readl_ext(p, DW_UART_LCR_EXT);
+
+		if (!(lcr & DW_UART_LCR_EXT_ADDR_MATCH)) {
+			addr->flags = SER_ADDR_RECV_CLEAR;
+			addr->addr = 0;
+		} else {
+			addr->addr = dw8250_readl_ext(p, DW_UART_RAR) & DW_UART_ADDR_MASK;
+		}
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485)
 {
+	struct dw8250_port_data *d = p->private_data;
 	u32 tcr;
 
 	tcr = dw8250_readl_ext(p, DW_UART_TCR);
@@ -111,6 +242,14 @@ static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485)
 		dw8250_writel_ext(p, DW_UART_DE_EN, 1);
 		dw8250_writel_ext(p, DW_UART_RE_EN, 1);
 	} else {
+		/*
+		 * Don't allow disabling RS485 when address mode is enabled.
+		 * Clearing address mode would require clearing ADDRB in
+		 * termios.
+		 */
+		if (d->addrmode)
+			return -EINVAL;
+
 		rs485->flags = 0;
 
 		tcr &= ~DW_UART_TCR_RS485_EN;
@@ -159,6 +298,9 @@ void dw8250_setup_port(struct uart_port *p)
 	pd->hw_rs485_support = dw8250_detect_rs485_hw(p);
 	if (pd->hw_rs485_support) {
 		p->rs485_config = dw8250_rs485_config;
+		p->set_addr = dw8250_rs485_set_addr;
+		p->get_addr = dw8250_rs485_get_addr;
+		up->lsr_save_mask = LSR_SAVE_FLAGS | DW_UART_LSR_ADDR_RCVD;
 	} else {
 		p->rs485_config = serial8250_em485_config;
 		up->rs485_start_tx = serial8250_em485_start_tx;
diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h
index 055bfdc87985..4fc2e968afcd 100644
--- a/drivers/tty/serial/8250/8250_dwlib.h
+++ b/drivers/tty/serial/8250/8250_dwlib.h
@@ -23,6 +23,8 @@ struct dw8250_port_data {
 
 	/* RS485 variables */
 	bool			hw_rs485_support;
+	/* 9-bit framing (9th bit is address indicator) */
+	bool			addrmode;
 };
 
 struct dw8250_platform_data {
-- 
2.30.2


  parent reply	other threads:[~2022-04-26 12:28 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-26 12:24 [PATCH v5 00/10] Add RS485 support to DW UART Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 01/10] serial: 8250_dwlib: RS485 HW half & full duplex support Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 02/10] serial: 8250_dwlib: Implement SW half " Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 03/10] dt_bindings: rs485: Add receiver enable polarity Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 04/10] ACPI / property: Document RS485 _DSD properties Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 05/10] serial: termbits: ADDRB to indicate 9th bit addressing mode Ilpo Järvinen
2022-04-26 12:24   ` Ilpo Järvinen
2022-04-26 12:24   ` Ilpo Järvinen
2022-04-26 12:52   ` Greg KH
2022-04-26 12:52     ` Greg KH
2022-04-26 12:52     ` Greg KH
2022-04-26 13:12     ` Ilpo Järvinen
2022-04-26 13:12       ` Ilpo Järvinen
2022-04-26 13:12       ` Ilpo Järvinen
2022-04-26 14:01       ` Ilpo Järvinen
2022-04-26 14:01         ` Ilpo Järvinen
2022-04-26 14:01         ` Ilpo Järvinen
2022-04-26 14:10         ` Greg KH
2022-04-26 14:10           ` Greg KH
2022-04-26 14:10           ` Greg KH
2022-04-26 16:29           ` Ilpo Järvinen
2022-04-26 16:29             ` Ilpo Järvinen
2022-04-26 16:29             ` Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 06/10] serial: General support for multipoint addresses Ilpo Järvinen
2022-04-26 12:24   ` Ilpo Järvinen
2022-04-26 12:24   ` Ilpo Järvinen
2022-04-26 12:56   ` Greg KH
2022-04-26 12:56     ` Greg KH
2022-04-26 12:56     ` Greg KH
2022-04-26 13:36     ` Ilpo Järvinen
2022-04-26 13:36       ` Ilpo Järvinen
2022-04-26 13:36       ` Ilpo Järvinen
2022-04-26 13:58       ` Greg KH
2022-04-26 13:58         ` Greg KH
2022-04-26 13:58         ` Greg KH
2022-04-26 12:24 ` [PATCH v5 07/10] serial: 8250: make saved LSR larger Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 08/10] serial: 8250: create lsr_save_mask Ilpo Järvinen
2022-04-26 12:24 ` [PATCH v5 09/10] serial: 8250_lpss: Use 32-bit reads Ilpo Järvinen
2022-04-26 12:24 ` Ilpo Järvinen [this message]
2022-04-26 12:49 ` [PATCH v5 00/10] Add RS485 support to DW UART Greg KH

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=20220426122448.38997-11-ilpo.jarvinen@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=giulio.benetti@micronovasrl.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=heikki.krogerus@linux.intel.com \
    --cc=heiko@sntech.de \
    --cc=jirislaby@kernel.org \
    --cc=johan@kernel.org \
    --cc=lakshmi.sowjanya.d@intel.com \
    --cc=linux-serial@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=raymond.tan@intel.com \
    --cc=u.kleine-koenig@pengutronix.de \
    --cc=vicencb@gmail.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.