int nr_uart;
};
+#define SC16IS7XX_RECONF_MD (1 << 0)
+#define SC16IS7XX_RECONF_IER (1 << 1)
+#define SC16IS7XX_RECONF_RS485 (1 << 2)
+
+struct sc16is7xx_one_config {
+ unsigned int flags;
+ u8 ier_clear;
+};
+
struct sc16is7xx_one {
struct uart_port port;
- struct work_struct tx_work;
- struct work_struct md_work;
+ struct kthread_work tx_work;
+ struct kthread_work reg_work;
+ struct sc16is7xx_one_config config;
};
struct sc16is7xx_port {
struct uart_driver uart;
struct sc16is7xx_devtype *devtype;
struct regmap *regmap;
- struct mutex mutex;
struct clk *clk;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
#endif
unsigned char buf[SC16IS7XX_FIFO_SIZE];
+ struct kthread_worker kworker;
+ struct task_struct *kworker_task;
+ struct kthread_work irq_work;
struct sc16is7xx_one p[0];
};
+#define to_sc16is7xx_port(p,e) ((container_of((p), struct sc16is7xx_port, e)))
#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e)))
static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg)
!!(msr & SC16IS7XX_MSR_CTS_BIT));
break;
case SC16IS7XX_IIR_THRI_SRC:
- mutex_lock(&s->mutex);
sc16is7xx_handle_tx(port);
- mutex_unlock(&s->mutex);
break;
default:
dev_err_ratelimited(port->dev,
} while (1);
}
-static irqreturn_t sc16is7xx_ist(int irq, void *dev_id)
+static void sc16is7xx_ist(struct kthread_work *ws)
{
- struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
+ struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work);
int i;
for (i = 0; i < s->uart.nr; ++i)
sc16is7xx_port_irq(s, i);
+}
+
+static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
+{
+ struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
+
+ queue_kthread_work(&s->kworker, &s->irq_work);
return IRQ_HANDLED;
}
-static void sc16is7xx_wq_proc(struct work_struct *ws)
+static void sc16is7xx_tx_proc(struct kthread_work *ws)
{
- struct sc16is7xx_one *one = to_sc16is7xx_one(ws, tx_work);
- struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev);
+ struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port);
- mutex_lock(&s->mutex);
- sc16is7xx_handle_tx(&one->port);
- mutex_unlock(&s->mutex);
+ if ((port->rs485.flags & SER_RS485_ENABLED) &&
+ (port->rs485.delay_rts_before_send > 0))
+ msleep(port->rs485.delay_rts_before_send);
+
+ sc16is7xx_handle_tx(port);
}
-static void sc16is7xx_stop_tx(struct uart_port* port)
+static void sc16is7xx_reconf_rs485(struct uart_port *port)
{
- struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
- struct circ_buf *xmit = &one->port.state->xmit;
-
- /* handle rs485 */
- if (port->rs485.flags & SER_RS485_ENABLED) {
- /* do nothing if current tx not yet completed */
- int lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
- if (!(lsr & SC16IS7XX_LSR_TEMT_BIT))
- return;
-
- if (uart_circ_empty(xmit) &&
- (port->rs485.delay_rts_after_send > 0))
- mdelay(port->rs485.delay_rts_after_send);
+ const u32 mask = SC16IS7XX_EFCR_AUTO_RS485_BIT |
+ SC16IS7XX_EFCR_RTS_INVERT_BIT;
+ u32 efcr = 0;
+ struct serial_rs485 *rs485 = &port->rs485;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&port->lock, irqflags);
+ if (rs485->flags & SER_RS485_ENABLED) {
+ efcr |= SC16IS7XX_EFCR_AUTO_RS485_BIT;
+
+ if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+ efcr |= SC16IS7XX_EFCR_RTS_INVERT_BIT;
}
+ spin_unlock_irqrestore(&port->lock, irqflags);
- sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
- SC16IS7XX_IER_THRI_BIT,
- 0);
+ sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr);
}
-static void sc16is7xx_stop_rx(struct uart_port* port)
+static void sc16is7xx_reg_proc(struct kthread_work *ws)
{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(ws, reg_work);
+ struct sc16is7xx_one_config config;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&one->port.lock, irqflags);
+ config = one->config;
+ memset(&one->config, 0, sizeof(one->config));
+ spin_unlock_irqrestore(&one->port.lock, irqflags);
+
+ if (config.flags & SC16IS7XX_RECONF_MD)
+ sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG,
+ SC16IS7XX_MCR_LOOP_BIT,
+ (one->port.mctrl & TIOCM_LOOP) ?
+ SC16IS7XX_MCR_LOOP_BIT : 0);
+
+ if (config.flags & SC16IS7XX_RECONF_IER)
+ sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG,
+ config.ier_clear, 0);
+
+ if (config.flags & SC16IS7XX_RECONF_RS485)
+ sc16is7xx_reconf_rs485(&one->port);
+}
+
+static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
- one->port.read_status_mask &= ~SC16IS7XX_LSR_DR_BIT;
- sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
- SC16IS7XX_LSR_DR_BIT,
- 0);
+ one->config.flags |= SC16IS7XX_RECONF_IER;
+ one->config.ier_clear |= bit;
+ queue_kthread_work(&s->kworker, &one->reg_work);
+}
+
+static void sc16is7xx_stop_tx(struct uart_port *port)
+{
+ sc16is7xx_ier_clear(port, SC16IS7XX_IER_THRI_BIT);
+}
+
+static void sc16is7xx_stop_rx(struct uart_port *port)
+{
+ sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT);
}
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);
- /* handle rs485 */
- if ((port->rs485.flags & SER_RS485_ENABLED) &&
- (port->rs485.delay_rts_before_send > 0)) {
- mdelay(port->rs485.delay_rts_before_send);
- }
-
- if (!work_pending(&one->tx_work))
- schedule_work(&one->tx_work);
+ queue_kthread_work(&s->kworker, &one->tx_work);
}
static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
{
- unsigned int lvl, lsr;
+ unsigned int lsr;
- lvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
- return ((lsr & SC16IS7XX_LSR_THRE_BIT) && !lvl) ? TIOCSER_TEMT : 0;
+ return (lsr & SC16IS7XX_LSR_TEMT_BIT) ? TIOCSER_TEMT : 0;
}
static unsigned int sc16is7xx_get_mctrl(struct uart_port *port)
return TIOCM_DSR | TIOCM_CAR;
}
-static void sc16is7xx_md_proc(struct work_struct *ws)
-{
- struct sc16is7xx_one *one = to_sc16is7xx_one(ws, md_work);
-
- sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG,
- SC16IS7XX_MCR_LOOP_BIT,
- (one->port.mctrl & TIOCM_LOOP) ?
- SC16IS7XX_MCR_LOOP_BIT : 0);
-}
-
static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
- schedule_work(&one->md_work);
+ one->config.flags |= SC16IS7XX_RECONF_MD;
+ queue_kthread_work(&s->kworker, &one->reg_work);
}
static void sc16is7xx_break_ctl(struct uart_port *port, int break_state)
static int sc16is7xx_config_rs485(struct uart_port *port,
struct serial_rs485 *rs485)
{
- const u32 mask = SC16IS7XX_EFCR_AUTO_RS485_BIT |
- SC16IS7XX_EFCR_RTS_INVERT_BIT;
- u32 efcr = 0;
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
if (rs485->flags & SER_RS485_ENABLED) {
bool rts_during_rx, rts_during_tx;
rts_during_rx = rs485->flags & SER_RS485_RTS_AFTER_SEND;
rts_during_tx = rs485->flags & SER_RS485_RTS_ON_SEND;
- efcr |= SC16IS7XX_EFCR_AUTO_RS485_BIT;
-
- if (!rts_during_rx && rts_during_tx)
- /* default */;
- else if (rts_during_rx && !rts_during_tx)
- efcr |= SC16IS7XX_EFCR_RTS_INVERT_BIT;
- else
+ if (rts_during_rx == rts_during_tx)
dev_err(port->dev,
"unsupported RTS signalling on_send:%d after_send:%d - exactly one of RS485 RTS flags should be set\n",
rts_during_tx, rts_during_rx);
- }
- sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr);
+ /*
+ * RTS signal is handled by HW, it's timing can't be influenced.
+ * However, it's sometimes useful to delay TX even without RTS
+ * control therefore we try to handle .delay_rts_before_send.
+ */
+ if (rs485->delay_rts_after_send)
+ return -EINVAL;
+ }
port->rs485 = *rs485;
+ one->config.flags |= SC16IS7XX_RECONF_RS485;
+ queue_kthread_work(&s->kworker, &one->reg_work);
return 0;
}
static void sc16is7xx_shutdown(struct uart_port *port)
{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+
/* Disable all interrupts */
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0);
/* Disable TX/RX */
SC16IS7XX_EFCR_TXDISABLE_BIT);
sc16is7xx_power(port, 0);
+
+ flush_kthread_worker(&s->kworker);
}
static const char *sc16is7xx_type(struct uart_port *port)
struct sc16is7xx_devtype *devtype,
struct regmap *regmap, int irq, unsigned long flags)
{
+ struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
unsigned long freq, *pfreq = dev_get_platdata(dev);
int i, ret;
struct sc16is7xx_port *s;
goto out_clk;
}
+ 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)) {
+ ret = PTR_ERR(s->kworker_task);
+ goto out_uart;
+ }
+ sched_setscheduler(s->kworker_task, SCHED_FIFO, &sched_param);
+
#ifdef CONFIG_GPIOLIB
if (devtype->nr_gpio) {
/* Setup GPIO cotroller */
s->gpio.can_sleep = 1;
ret = gpiochip_add(&s->gpio);
if (ret)
- goto out_uart;
+ goto out_thread;
}
#endif
- mutex_init(&s->mutex);
-
for (i = 0; i < devtype->nr_uart; ++i) {
/* Initialize port data */
s->p[i].port.line = i;
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG,
SC16IS7XX_EFCR_RXDISABLE_BIT |
SC16IS7XX_EFCR_TXDISABLE_BIT);
- /* Initialize queue for start TX */
- INIT_WORK(&s->p[i].tx_work, sc16is7xx_wq_proc);
- /* Initialize queue for changing mode */
- INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc);
+ /* Initialize kthread work structs */
+ 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(&s->uart, &s->p[i].port);
/* Go to suspend mode */
}
/* Setup interrupt */
- ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_ist,
- IRQF_ONESHOT | flags, dev_name(dev), s);
+ ret = devm_request_irq(dev, irq, sc16is7xx_irq,
+ IRQF_ONESHOT | flags, dev_name(dev), s);
if (!ret)
return 0;
for (i = 0; i < s->uart.nr; i++)
uart_remove_one_port(&s->uart, &s->p[i].port);
- mutex_destroy(&s->mutex);
-
#ifdef CONFIG_GPIOLIB
if (devtype->nr_gpio)
gpiochip_remove(&s->gpio);
-out_uart:
+out_thread:
#endif
+ kthread_stop(s->kworker_task);
+
+out_uart:
uart_unregister_driver(&s->uart);
out_clk:
#endif
for (i = 0; i < s->uart.nr; i++) {
- cancel_work_sync(&s->p[i].tx_work);
- cancel_work_sync(&s->p[i].md_work);
uart_remove_one_port(&s->uart, &s->p[i].port);
sc16is7xx_power(&s->p[i].port, 0);
}
- mutex_destroy(&s->mutex);
+ flush_kthread_worker(&s->kworker);
+ kthread_stop(s->kworker_task);
+
uart_unregister_driver(&s->uart);
if (!IS_ERR(s->clk))
clk_disable_unprepare(s->clk);
static const struct spi_device_id sc16is7xx_spi_id_table[] = {
{ "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, },
+ { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, },
+ { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, },
{ "sc16is750", (kernel_ulong_t)&sc16is750_devtype, },
{ "sc16is752", (kernel_ulong_t)&sc16is752_devtype, },
{ "sc16is760", (kernel_ulong_t)&sc16is760_devtype, },
static const struct i2c_device_id sc16is7xx_i2c_id_table[] = {
{ "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, },
+ { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, },
+ { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, },
{ "sc16is750", (kernel_ulong_t)&sc16is750_devtype, },
{ "sc16is752", (kernel_ulong_t)&sc16is752_devtype, },
{ "sc16is760", (kernel_ulong_t)&sc16is760_devtype, },