sc16is7xx: use kworker for RS-485 configuration
[firefly-linux-kernel-4.4.55.git] / drivers / tty / serial / sc16is7xx.c
index 468354ef7baa2f8f9fb160e4b0ef06e407c6e270..9e6576004a427e51cac6f1b59237248a706332f8 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
+#include <linux/spi/spi.h>
 #include <linux/uaccess.h>
 
 #define SC16IS7XX_NAME                 "sc16is7xx"
@@ -300,25 +301,38 @@ struct sc16is7xx_devtype {
        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)
@@ -615,9 +629,7 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
                                               !!(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,
@@ -628,81 +640,115 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
        } 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);
+
+       if ((port->rs485.flags & SER_RS485_ENABLED) &&
+           (port->rs485.delay_rts_before_send > 0))
+               msleep(port->rs485.delay_rts_before_send);
 
-       mutex_lock(&s->mutex);
-       sc16is7xx_handle_tx(&one->port);
-       mutex_unlock(&s->mutex);
+       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)
@@ -713,21 +759,13 @@ 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)
@@ -831,9 +869,8 @@ static void sc16is7xx_set_termios(struct uart_port *port,
 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;
@@ -841,21 +878,23 @@ static int sc16is7xx_config_rs485(struct uart_port *port,
                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;
 }
@@ -916,6 +955,8 @@ static int sc16is7xx_startup(struct uart_port *port)
 
 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 */
@@ -926,6 +967,8 @@ static void sc16is7xx_shutdown(struct uart_port *port)
                              SC16IS7XX_EFCR_TXDISABLE_BIT);
 
        sc16is7xx_power(port, 0);
+
+       flush_kthread_worker(&s->kworker);
 }
 
 static const char *sc16is7xx_type(struct uart_port *port)
@@ -1043,6 +1086,7 @@ static int sc16is7xx_probe(struct device *dev,
                           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;
@@ -1084,6 +1128,16 @@ static int sc16is7xx_probe(struct device *dev,
                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 */
@@ -1099,12 +1153,10 @@ static int sc16is7xx_probe(struct device *dev,
                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;
@@ -1123,10 +1175,9 @@ static int sc16is7xx_probe(struct device *dev,
                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 */
@@ -1134,22 +1185,23 @@ static int sc16is7xx_probe(struct device *dev,
        }
 
        /* 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:
@@ -1170,13 +1222,13 @@ static int sc16is7xx_remove(struct device *dev)
 #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);
@@ -1204,6 +1256,75 @@ static struct regmap_config regcfg = {
        .precious_reg = sc16is7xx_regmap_precious,
 };
 
+#ifdef CONFIG_SERIAL_SC16IS7XX_SPI
+static int sc16is7xx_spi_probe(struct spi_device *spi)
+{
+       struct sc16is7xx_devtype *devtype;
+       unsigned long flags = 0;
+       struct regmap *regmap;
+       int ret;
+
+       /* Setup SPI bus */
+       spi->bits_per_word      = 8;
+       /* only supports mode 0 on SC16IS762 */
+       spi->mode               = spi->mode ? : SPI_MODE_0;
+       spi->max_speed_hz       = spi->max_speed_hz ? : 15000000;
+       ret = spi_setup(spi);
+       if (ret)
+               return ret;
+
+       if (spi->dev.of_node) {
+               const struct of_device_id *of_id =
+                       of_match_device(sc16is7xx_dt_ids, &spi->dev);
+
+               devtype = (struct sc16is7xx_devtype *)of_id->data;
+       } else {
+               const struct spi_device_id *id_entry = spi_get_device_id(spi);
+
+               devtype = (struct sc16is7xx_devtype *)id_entry->driver_data;
+               flags = IRQF_TRIGGER_FALLING;
+       }
+
+       regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) |
+                             (devtype->nr_uart - 1);
+       regmap = devm_regmap_init_spi(spi, &regcfg);
+
+       return sc16is7xx_probe(&spi->dev, devtype, regmap, spi->irq, flags);
+}
+
+static int sc16is7xx_spi_remove(struct spi_device *spi)
+{
+       return sc16is7xx_remove(&spi->dev);
+}
+
+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, },
+       { "sc16is762",  (kernel_ulong_t)&sc16is762_devtype, },
+       { }
+};
+
+MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table);
+
+static struct spi_driver sc16is7xx_spi_uart_driver = {
+       .driver = {
+               .name           = SC16IS7XX_NAME,
+               .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+       },
+       .probe          = sc16is7xx_spi_probe,
+       .remove         = sc16is7xx_spi_remove,
+       .id_table       = sc16is7xx_spi_id_table,
+};
+
+MODULE_ALIAS("spi:sc16is7xx");
+#endif
+
+#ifdef CONFIG_SERIAL_SC16IS7XX_I2C
 static int sc16is7xx_i2c_probe(struct i2c_client *i2c,
                               const struct i2c_device_id *id)
 {
@@ -1235,6 +1356,8 @@ static int sc16is7xx_i2c_remove(struct i2c_client *client)
 
 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, },
@@ -1253,8 +1376,43 @@ static struct i2c_driver sc16is7xx_i2c_uart_driver = {
        .remove         = sc16is7xx_i2c_remove,
        .id_table       = sc16is7xx_i2c_id_table,
 };
-module_i2c_driver(sc16is7xx_i2c_uart_driver);
+
 MODULE_ALIAS("i2c:sc16is7xx");
+#endif
+
+static int __init sc16is7xx_init(void)
+{
+       int ret = 0;
+#ifdef CONFIG_SERIAL_SC16IS7XX_I2C
+       ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver);
+       if (ret < 0) {
+               pr_err("failed to init sc16is7xx i2c --> %d\n", ret);
+               return ret;
+       }
+#endif
+
+#ifdef CONFIG_SERIAL_SC16IS7XX_SPI
+       ret = spi_register_driver(&sc16is7xx_spi_uart_driver);
+       if (ret < 0) {
+               pr_err("failed to init sc16is7xx spi --> %d\n", ret);
+               return ret;
+       }
+#endif
+       return ret;
+}
+module_init(sc16is7xx_init);
+
+static void __exit sc16is7xx_exit(void)
+{
+#ifdef CONFIG_SERIAL_SC16IS7XX_I2C
+       i2c_del_driver(&sc16is7xx_i2c_uart_driver);
+#endif
+
+#ifdef CONFIG_SERIAL_SC16IS7XX_SPI
+       spi_unregister_driver(&sc16is7xx_spi_uart_driver);
+#endif
+}
+module_exit(sc16is7xx_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");