AnsweredAssumed Answered

sc16is7xx driver does not transmit with crtscts enabled from linux

Question asked by Kevin Chaves on May 2, 2018

So first I'm using the 4.9 driver in the 3.14 kernel, the major changes I had to make were minor renames kthread_queue to queue_kthread and added a couple of header files.  I also needed to add a gpio-enable entry to toggle the chip enable pin, I'm not sure if there is a generic way of doing it in the device tree. It is connected to a bluetooth module via uart and cts/rts both hooked up. 

 

When using 'stty -F /dev/ttySC0 115200 crtscts', linux will wait for something before calling its start_tx in the serial core. I have not been able to dig deep enough into this problem but I got around the issue by disabling crtscts in the patching tool and forcing auto rts/cts to be enabled for line 0. 

 

diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 793395451982..bceeaf6f33e6 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -29,6 +29,8 @@
#include <linux/tty_flip.h>
#include <linux/spi/spi.h>
#include <linux/uaccess.h>
+#include <linux/sched/rt.h>
+#include <linux/of_gpio.h>
#define SC16IS7XX_NAME         "sc16is7xx"
#define SC16IS7XX_MAX_DEVS     8
@@ -333,6 +335,9 @@ struct sc16is7xx_port {
   struct task_struct      *kworker_task;
   struct kthread_work     irq_work;
   struct sc16is7xx_one        p[0];
+
+   unsigned long enable_gpio_flags;
+   int enable_gpio;
};
static unsigned long sc16is7xx_lines;
@@ -708,7 +713,7 @@ static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
{
   struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
-   kthread_queue_work(&s->kworker, &s->irq_work);
+   queue_kthread_work(&s->kworker, &s->irq_work);
   return IRQ_HANDLED;
}
@@ -784,7 +789,7 @@ static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
   one->config.flags |= SC16IS7XX_RECONF_IER;
   one->config.ier_clear |= bit;
-   kthread_queue_work(&s->kworker, &one->reg_work);
+   queue_kthread_work(&s->kworker, &one->reg_work);
}
static void sc16is7xx_stop_tx(struct uart_port *port)
@@ -802,7 +807,7 @@ static void sc16is7xx_start_tx(struct uart_port *port)
   struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
   struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
-   kthread_queue_work(&s->kworker, &one->tx_work);
+   queue_kthread_work(&s->kworker, &one->tx_work);
}
static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
@@ -828,7 +833,7 @@ static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
   struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
   one->config.flags |= SC16IS7XX_RECONF_MD;
-   kthread_queue_work(&s->kworker, &one->reg_work);
+   queue_kthread_work(&s->kworker, &one->reg_work);
}
static void sc16is7xx_break_ctl(struct uart_port *port, int break_state)
@@ -903,9 +908,18 @@ static void sc16is7xx_set_termios(struct uart_port *port,
   regcache_cache_bypass(s->regmap, true);
   sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
   sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
-   if (termios->c_cflag & CRTSCTS)
+
+
+   /*
+       This is a hack, the linux serial core driver will wait for something
+       to know if the chip is ready to send... it never is so just force it
+       to be enabled for port 0, the rest of the bluetooth software will have
+       to disable CRTSCTS from now on.
+   */
+   if (termios->c_cflag & CRTSCTS || 0 == sc16is7xx_line(port))
       flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
           SC16IS7XX_EFR_AUTORTS_BIT;
+
   if (termios->c_iflag & IXON)
       flow |= SC16IS7XX_EFR_SWFLOW3_BIT;
   if (termios->c_iflag & IXOFF)
@@ -957,7 +971,7 @@ static int sc16is7xx_config_rs485(struct uart_port *port,
   port->rs485 = *rs485;
   one->config.flags |= SC16IS7XX_RECONF_RS485;
-   kthread_queue_work(&s->kworker, &one->reg_work);
+   queue_kthread_work(&s->kworker, &one->reg_work);
   return 0;
}
@@ -1030,7 +1044,7 @@ static void sc16is7xx_shutdown(struct uart_port *port)
   sc16is7xx_power(port, 0);
-   kthread_flush_worker(&s->kworker);
+   flush_kthread_worker(&s->kworker);
}
static const char *sc16is7xx_type(struct uart_port *port)
@@ -1097,7 +1111,8 @@ static const struct uart_ops sc16is7xx_ops = {
static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset)
{
   unsigned int val;
-   struct sc16is7xx_port *s = gpiochip_get_data(chip);
+   struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+                       gpio);
   struct uart_port *port = &s->p[0].port;
   val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);
@@ -1107,7 +1122,8 @@ static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset)
static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
-   struct sc16is7xx_port *s = gpiochip_get_data(chip);
+   struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+                       gpio);
   struct uart_port *port = &s->p[0].port;
   sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset),
@@ -1117,7 +1133,8 @@ static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
                    unsigned offset)
{
-   struct sc16is7xx_port *s = gpiochip_get_data(chip);
+   struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+                       gpio);
   struct uart_port *port = &s->p[0].port;
   sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0);
@@ -1128,7 +1145,8 @@ static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip,
                    unsigned offset, int val)
{
-   struct sc16is7xx_port *s = gpiochip_get_data(chip);
+   struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+                       gpio);
   struct uart_port *port = &s->p[0].port;
   u8 state = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);
@@ -1150,7 +1168,7 @@ static int sc16is7xx_probe(struct device *dev,
{
   struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
   unsigned long freq, *pfreq = dev_get_platdata(dev);
-   int i, ret;
+   int i, ret, gpioflags;
   struct sc16is7xx_port *s;
   if (IS_ERR(regmap))
@@ -1180,8 +1198,44 @@ static int sc16is7xx_probe(struct device *dev,
   s->devtype = devtype;
   dev_set_drvdata(dev, s);
-   kthread_init_worker(&s->kworker);
-   kthread_init_work(&s->irq_work, sc16is7xx_ist);
+   s->enable_gpio = of_get_named_gpio_flags(dev->of_node,
+                                           "enable-gpios", 0,
+                                           &gpioflags);
+   if (gpio_is_valid(s->enable_gpio)) {
+       unsigned int value;
+
+       if (gpioflags & OF_GPIO_ACTIVE_LOW)
+           s->enable_gpio_flags |= GPIO_ACTIVE_LOW;
+
+       ret = gpio_request(s->enable_gpio, "enable");
+       if (ret < 0) {
+           dev_err(dev, "failed to request GPIO#%u: %d\n",
+               s->enable_gpio, ret);
+           return ret;
+       }
+
+       value = (s->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
+
+       ret = gpio_direction_output(s->enable_gpio, value);
+       if (ret < 0) {
+           dev_err(dev, "failed to setup GPIO%u: %d\n",
+               s->enable_gpio, ret);
+           goto free_gpio;
+       }
+   }
+
+   // turn on the chip
+   if (gpio_is_valid(s->enable_gpio)) {
+       if (s->enable_gpio_flags & GPIO_ACTIVE_LOW)
+           gpio_set_value(s->enable_gpio, 0);
+       else
+           gpio_set_value(s->enable_gpio, 1);
+   }
+
+   mdelay(30);
+
+   init_kthread_worker(&s->kworker);
+   init_kthread_work(&s->irq_work, sc16is7xx_ist);
   s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
                "sc16is7xx");
   if (IS_ERR(s->kworker_task)) {
@@ -1194,7 +1248,7 @@ static int sc16is7xx_probe(struct device *dev,
   if (devtype->nr_gpio) {
       /* Setup GPIO cotroller */
       s->gpio.owner        = THIS_MODULE;
-       s->gpio.parent       = dev;
+       s->gpio.dev      = dev;
       s->gpio.label        = dev_name(dev);
       s->gpio.direction_input  = sc16is7xx_gpio_direction_input;
       s->gpio.get      = sc16is7xx_gpio_get;
@@ -1203,7 +1257,7 @@ static int sc16is7xx_probe(struct device *dev,
       s->gpio.base         = -1;
       s->gpio.ngpio        = devtype->nr_gpio;
       s->gpio.can_sleep    = 1;
-       ret = gpiochip_add_data(&s->gpio, s);
+       ret = gpiochip_add(&s->gpio);
       if (ret)
           goto out_thread;
   }
@@ -1238,8 +1292,8 @@ static int sc16is7xx_probe(struct device *dev,
                SC16IS7XX_EFCR_RXDISABLE_BIT |
                SC16IS7XX_EFCR_TXDISABLE_BIT);
       /* Initialize kthread work structs */
-       kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
-       kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
+       init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
+       init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
       /* Register port */
       uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
@@ -1286,6 +1340,10 @@ static int sc16is7xx_probe(struct device *dev,
   if (!IS_ERR(s->clk))
       clk_disable_unprepare(s->clk);
+free_gpio:
+   if (gpio_is_valid(s->enable_gpio))
+       gpio_free(s->enable_gpio);
+
   return ret;
}
@@ -1305,12 +1363,15 @@ static int sc16is7xx_remove(struct device *dev)
       sc16is7xx_power(&s->p[i].port, 0);
   }
-   kthread_flush_worker(&s->kworker);
+   flush_kthread_worker(&s->kworker);
   kthread_stop(s->kworker_task);
   if (!IS_ERR(s->clk))
       clk_disable_unprepare(s->clk);
+   if (gpio_is_valid(s->enable_gpio))
+       gpio_free(s->enable_gpio);
+
   return 0;
}

Outcomes