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;
}