[PATCH v3 09/11] serial: sc16is7xx: add I/O register translation offset

From: Hugo Villeneuve
Date: Thu May 25 2023 - 00:06:47 EST


From: Hugo Villeneuve <hvilleneuve@xxxxxxxxxxxx>

If the shared GPIO pins on a dual port/channel variant like the
SC16IS752 are configured as GPIOs for port A, and modem control lines
on port A, we need to translate the Linux GPIO offset to an offset
that is compatible with the I/O registers of the SC16IS7XX (IOState,
IODir and IOIntEna).

Add a new variable to store that offset and set it when we detect that
special case.

Signed-off-by: Hugo Villeneuve <hvilleneuve@xxxxxxxxxxxx>
---
drivers/tty/serial/sc16is7xx.c | 54 +++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 97ec532a0a19..c2cfd057ed9a 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -338,6 +338,7 @@ struct sc16is7xx_port {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
int gpio_configured;
+ int gpio_offset;
#endif
unsigned char buf[SC16IS7XX_FIFO_SIZE];
struct kthread_worker kworker;
@@ -1298,12 +1299,50 @@ static const struct uart_ops sc16is7xx_ops = {
};

#ifdef CONFIG_GPIOLIB
+
+/*
+ * We may need to translate the Linux GPIO offset to a SC16IS7XX offset.
+ * This is needed only for the case where a dual port variant is configured to
+ * have only port B as modem status lines.
+ *
+ * Example for SC16IS752/762 with upper bank (port A) set as GPIOs, and
+ * lower bank (port B) set as modem status lines (special case described above):
+ *
+ * Pin GPIO pin Linux GPIO SC16IS7XX
+ * name function offset offset
+ * --------------------------------------------------
+ * GPIO7/RIA GPIO7 3 7
+ * GPIO6/CDA GPIO6 2 6
+ * GPIO5/DTRA GPIO5 1 5
+ * GPIO4/DSRA GPIO4 0 4
+ * GPIO3/RIB RIB N/A N/A
+ * GPIO2/CDB CDB N/A N/A
+ * GPIO1/DTRB DTRB N/A N/A
+ * GPIO0/DSRB DSRB N/A N/A
+ *
+ * Example for SC16IS750/760 with upper bank (7..4) set as modem status lines,
+ * and lower bank (3..0) as GPIOs:
+ *
+ * Pin GPIO pin Linux GPIO SC16IS7XX
+ * name function offset offset
+ * --------------------------------------------------
+ * GPIO7/RI RI N/A N/A
+ * GPIO6/CD CD N/A N/A
+ * GPIO5/DTR DTR N/A N/A
+ * GPIO4/DSR DSR N/A N/A
+ * GPIO3 GPIO3 3 3
+ * GPIO2 GPIO2 2 2
+ * GPIO1 GPIO1 1 1
+ * GPIO0 GPIO0 0 0
+ */
+
static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset)
{
unsigned int val;
struct sc16is7xx_port *s = gpiochip_get_data(chip);
struct uart_port *port = &s->p[0].port;

+ offset += s->gpio_offset;
val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);

return !!(val & BIT(offset));
@@ -1314,6 +1353,7 @@ static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
struct sc16is7xx_port *s = gpiochip_get_data(chip);
struct uart_port *port = &s->p[0].port;

+ offset += s->gpio_offset;
sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset),
val ? BIT(offset) : 0);
}
@@ -1324,6 +1364,7 @@ static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
struct sc16is7xx_port *s = gpiochip_get_data(chip);
struct uart_port *port = &s->p[0].port;

+ offset += s->gpio_offset;
sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0);

return 0;
@@ -1336,6 +1377,8 @@ static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip,
struct uart_port *port = &s->p[0].port;
u8 state = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);

+ offset += s->gpio_offset;
+
if (val)
state |= BIT(offset);
else
@@ -1395,6 +1438,7 @@ static int sc16is7xx_probe(struct device *dev,

#ifdef CONFIG_GPIOLIB
s->gpio_configured = devtype->nr_gpio;
+ s->gpio_offset = 0;
#endif /* CONFIG_GPIOLIB */

/* Always ask for fixed clock rate from a property. */
@@ -1529,16 +1573,24 @@ static int sc16is7xx_probe(struct device *dev,
#endif /* CONFIG_GPIOLIB */
}

- if (val)
+ if (val) {
+#ifdef CONFIG_GPIOLIB
+ /* Additional I/O regs offset. */
+ if (val == SC16IS7XX_IOCONTROL_MODEM_B_BIT)
+ s->gpio_offset = SC16IS7XX_GPIOS_PER_BANK;
+#endif /* CONFIG_GPIOLIB */
+
regmap_update_bits(
s->regmap,
SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT,
SC16IS7XX_IOCONTROL_MODEM_A_BIT |
SC16IS7XX_IOCONTROL_MODEM_B_BIT, val);
+ }
}

#ifdef CONFIG_GPIOLIB
dev_dbg(dev, "GPIOs to configure: %d\n", s->gpio_configured);
+ dev_dbg(dev, "GPIOs offset: %d\n", s->gpio_offset);

if (s->gpio_configured) {
/* Setup GPIO controller */
--
2.30.2