sc16is7xx driver does not transmit with crtscts enabled from linux

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

sc16is7xx driver does not transmit with crtscts enabled from linux

757 Views
kchaves
Contributor I

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;
}
Labels (1)
0 Kudos
0 Replies