From 5bdb48b501e8366d5fd361cd402816aa3e525895 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 7 Jun 2016 18:59:21 +0300 Subject: serial: max310x: Fix RS485 handling RS485 IOCTLs uses spinlocks, so move regmap functions into a separate work queue. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) (limited to 'drivers/tty/serial/max310x.c') diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 3f6e0ab725fe..bf6e3e08ec67 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -262,6 +262,7 @@ struct max310x_one { struct uart_port port; struct work_struct tx_work; struct work_struct md_work; + struct work_struct rs_work; }; struct max310x_port { @@ -877,36 +878,45 @@ static void max310x_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); } -static int max310x_rs485_config(struct uart_port *port, - struct serial_rs485 *rs485) +static void max310x_rs_proc(struct work_struct *ws) { + struct max310x_one *one = container_of(ws, struct max310x_one, rs_work); unsigned int val; - if (rs485->delay_rts_before_send > 0x0f || - rs485->delay_rts_after_send > 0x0f) - return -ERANGE; + val = (one->port.rs485.delay_rts_before_send << 4) | + one->port.rs485.delay_rts_after_send; + max310x_port_write(&one->port, MAX310X_HDPIXDELAY_REG, val); - val = (rs485->delay_rts_before_send << 4) | - rs485->delay_rts_after_send; - max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val); - if (rs485->flags & SER_RS485_ENABLED) { - max310x_port_update(port, MAX310X_MODE1_REG, + if (one->port.rs485.flags & SER_RS485_ENABLED) { + max310x_port_update(&one->port, MAX310X_MODE1_REG, MAX310X_MODE1_TRNSCVCTRL_BIT, MAX310X_MODE1_TRNSCVCTRL_BIT); - max310x_port_update(port, MAX310X_MODE2_REG, + max310x_port_update(&one->port, MAX310X_MODE2_REG, MAX310X_MODE2_ECHOSUPR_BIT, MAX310X_MODE2_ECHOSUPR_BIT); } else { - max310x_port_update(port, MAX310X_MODE1_REG, + max310x_port_update(&one->port, MAX310X_MODE1_REG, MAX310X_MODE1_TRNSCVCTRL_BIT, 0); - max310x_port_update(port, MAX310X_MODE2_REG, + max310x_port_update(&one->port, MAX310X_MODE2_REG, MAX310X_MODE2_ECHOSUPR_BIT, 0); } +} + +static int max310x_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct max310x_one *one = container_of(port, struct max310x_one, port); + + if ((rs485->delay_rts_before_send > 0x0f) || + (rs485->delay_rts_after_send > 0x0f)) + return -ERANGE; rs485->flags &= SER_RS485_RTS_ON_SEND | SER_RS485_ENABLED; memset(rs485->padding, 0, sizeof(rs485->padding)); port->rs485 = *rs485; + schedule_work(&one->rs_work); + return 0; } @@ -1214,8 +1224,10 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, MAX310X_MODE1_IRQSEL_BIT); /* Initialize queue for start TX */ INIT_WORK(&s->p[i].tx_work, max310x_wq_proc); - /* Initialize queue for changing mode */ + /* Initialize queue for changing LOOPBACK mode */ INIT_WORK(&s->p[i].md_work, max310x_md_proc); + /* Initialize queue for changing RS485 mode */ + INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); /* Register port */ uart_add_one_port(&s->uart, &s->p[i].port); /* Go to suspend mode */ @@ -1257,6 +1269,7 @@ static int max310x_remove(struct device *dev) for (i = 0; i < s->uart.nr; i++) { cancel_work_sync(&s->p[i].tx_work); cancel_work_sync(&s->p[i].md_work); + cancel_work_sync(&s->p[i].rs_work); uart_remove_one_port(&s->uart, &s->p[i].port); s->devtype->power(&s->p[i].port, 0); } -- cgit v1.2.3 From 0e8cc7c2ea025b96b4ed57b52c61f2d982a95ddb Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 7 Jun 2016 18:59:23 +0300 Subject: serial: max310x: Using resource-managed function for GPIO Use resource managed functions devm_gpiochip_add_data() to make error & exit path a bit simpler. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/tty/serial/max310x.c') diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index bf6e3e08ec67..6e2edbb942d6 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1193,9 +1193,11 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, s->gpio.base = -1; s->gpio.ngpio = devtype->nr * 4; s->gpio.can_sleep = 1; - ret = gpiochip_add_data(&s->gpio, s); - if (ret) - goto out_uart; + ret = devm_gpiochip_add_data(dev, &s->gpio, s); + if (ret) { + uart_unregister_driver(&s->uart); + goto out_clk; + } #endif mutex_init(&s->mutex); @@ -1244,11 +1246,6 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, mutex_destroy(&s->mutex); -#ifdef CONFIG_GPIOLIB - gpiochip_remove(&s->gpio); - -out_uart: -#endif uart_unregister_driver(&s->uart); out_clk: @@ -1262,10 +1259,6 @@ static int max310x_remove(struct device *dev) struct max310x_port *s = dev_get_drvdata(dev); int i; -#ifdef CONFIG_GPIOLIB - gpiochip_remove(&s->gpio); -#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); -- cgit v1.2.3 From 6286767ad3afc886ec6473409aa02191cb54f97b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 7 Jun 2016 18:59:24 +0300 Subject: serial: max310x: Register UART driver at module_init This patch moves UART registration at module_init initcall, so this should helps to add multiple chip support in the future. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 79 +++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 31 deletions(-) (limited to 'drivers/tty/serial/max310x.c') diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 6e2edbb942d6..66dd3e24f3d6 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1,7 +1,7 @@ /* * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver * - * Copyright (C) 2012-2014 Alexander Shiyan + * Copyright (C) 2012-2016 Alexander Shiyan * * Based on max3100.c, by Christian Pellegrin * Based on max3110.c, by Feng Tang @@ -32,6 +32,7 @@ #define MAX310X_NAME "max310x" #define MAX310X_MAJOR 204 #define MAX310X_MINOR 209 +#define MAX310X_UART_NR 4 /* MAX310X register definitions */ #define MAX310X_RHR_REG (0x00) /* RX FIFO */ @@ -266,7 +267,6 @@ struct max310x_one { }; struct max310x_port { - struct uart_driver uart; struct max310x_devtype *devtype; struct regmap *regmap; struct mutex mutex; @@ -277,6 +277,15 @@ struct max310x_port { struct max310x_one p[0]; }; +static struct uart_driver max310x_uart = { + .owner = THIS_MODULE, + .driver_name = MAX310X_NAME, + .dev_name = "ttyMAX", + .major = MAX310X_MAJOR, + .minor = MAX310X_MINOR, + .nr = MAX310X_UART_NR, +}; + static u8 max310x_port_read(struct uart_port *port, u8 reg) { struct max310x_port *s = dev_get_drvdata(port->dev); @@ -716,13 +725,13 @@ static irqreturn_t max310x_ist(int irq, void *dev_id) { struct max310x_port *s = (struct max310x_port *)dev_id; - if (s->uart.nr > 1) { + if (s->devtype->nr > 1) { do { unsigned int val = ~0; WARN_ON_ONCE(regmap_read(s->regmap, MAX310X_GLOBALIRQ_REG, &val)); - val = ((1 << s->uart.nr) - 1) & ~val; + val = ((1 << s->devtype->nr) - 1) & ~val; if (!val) break; max310x_port_irq(s, fls(val) - 1); @@ -1019,8 +1028,8 @@ static int __maybe_unused max310x_suspend(struct device *dev) struct max310x_port *s = dev_get_drvdata(dev); int i; - for (i = 0; i < s->uart.nr; i++) { - uart_suspend_port(&s->uart, &s->p[i].port); + for (i = 0; i < s->devtype->nr; i++) { + uart_suspend_port(&max310x_uart, &s->p[i].port); s->devtype->power(&s->p[i].port, 0); } @@ -1032,9 +1041,9 @@ static int __maybe_unused max310x_resume(struct device *dev) struct max310x_port *s = dev_get_drvdata(dev); int i; - for (i = 0; i < s->uart.nr; i++) { + for (i = 0; i < s->devtype->nr; i++) { s->devtype->power(&s->p[i].port, 1); - uart_resume_port(&s->uart, &s->p[i].port); + uart_resume_port(&max310x_uart, &s->p[i].port); } return 0; @@ -1169,18 +1178,6 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, uartclk = max310x_set_ref_clk(s, freq, xtal); dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); - /* Register UART driver */ - s->uart.owner = THIS_MODULE; - s->uart.dev_name = "ttyMAX"; - s->uart.major = MAX310X_MAJOR; - s->uart.minor = MAX310X_MINOR; - s->uart.nr = devtype->nr; - ret = uart_register_driver(&s->uart); - if (ret) { - dev_err(dev, "Registering UART driver failed\n"); - goto out_clk; - } - #ifdef CONFIG_GPIOLIB /* Setup GPIO cotroller */ s->gpio.owner = THIS_MODULE; @@ -1194,10 +1191,8 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, s->gpio.ngpio = devtype->nr * 4; s->gpio.can_sleep = 1; ret = devm_gpiochip_add_data(dev, &s->gpio, s); - if (ret) { - uart_unregister_driver(&s->uart); + if (ret) goto out_clk; - } #endif mutex_init(&s->mutex); @@ -1231,7 +1226,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); /* Register port */ - uart_add_one_port(&s->uart, &s->p[i].port); + uart_add_one_port(&max310x_uart, &s->p[i].port); /* Go to suspend mode */ devtype->power(&s->p[i].port, 0); } @@ -1246,8 +1241,6 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, mutex_destroy(&s->mutex); - uart_unregister_driver(&s->uart); - out_clk: clk_disable_unprepare(s->clk); @@ -1259,16 +1252,15 @@ static int max310x_remove(struct device *dev) struct max310x_port *s = dev_get_drvdata(dev); int i; - for (i = 0; i < s->uart.nr; i++) { + for (i = 0; i < s->devtype->nr; i++) { cancel_work_sync(&s->p[i].tx_work); cancel_work_sync(&s->p[i].md_work); cancel_work_sync(&s->p[i].rs_work); - uart_remove_one_port(&s->uart, &s->p[i].port); + uart_remove_one_port(&max310x_uart, &s->p[i].port); s->devtype->power(&s->p[i].port, 0); } mutex_destroy(&s->mutex); - uart_unregister_driver(&s->uart); clk_disable_unprepare(s->clk); return 0; @@ -1341,7 +1333,7 @@ static const struct spi_device_id max310x_id_table[] = { }; MODULE_DEVICE_TABLE(spi, max310x_id_table); -static struct spi_driver max310x_uart_driver = { +static struct spi_driver max310x_spi_driver = { .driver = { .name = MAX310X_NAME, .of_match_table = of_match_ptr(max310x_dt_ids), @@ -1351,9 +1343,34 @@ static struct spi_driver max310x_uart_driver = { .remove = max310x_spi_remove, .id_table = max310x_id_table, }; -module_spi_driver(max310x_uart_driver); #endif +static int __init max310x_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&max310x_uart); + if (ret) + return ret; + +#ifdef CONFIG_SPI_MASTER + spi_register_driver(&max310x_spi_driver); +#endif + + return 0; +} +module_init(max310x_uart_init); + +static void __exit max310x_uart_exit(void) +{ +#ifdef CONFIG_SPI_MASTER + spi_unregister_driver(&max310x_spi_driver); +#endif + + uart_unregister_driver(&max310x_uart); +} +module_exit(max310x_uart_exit); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Shiyan "); MODULE_DESCRIPTION("MAX310X serial driver"); -- cgit v1.2.3 From e940e817a701dcaf0d218571c251af0cb6d1bbfb Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 7 Jun 2016 18:59:25 +0300 Subject: serial: max310x: Remove duplicate bits definition Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/tty/serial/max310x.c') diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 66dd3e24f3d6..e73ae6e79b64 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -156,10 +156,6 @@ #define MAX310X_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ #define MAX310X_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ #define MAX310X_LCR_RTS_BIT (1 << 7) /* RTS pin control */ -#define MAX310X_LCR_WORD_LEN_5 (0x00) -#define MAX310X_LCR_WORD_LEN_6 (0x01) -#define MAX310X_LCR_WORD_LEN_7 (0x02) -#define MAX310X_LCR_WORD_LEN_8 (0x03) /* IRDA register bits */ #define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ @@ -806,7 +802,7 @@ static void max310x_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - unsigned int lcr, flow = 0; + unsigned int lcr = 0, flow = 0; int baud; /* Mask termios capabilities we don't support */ @@ -815,17 +811,16 @@ static void max310x_set_termios(struct uart_port *port, /* Word size */ switch (termios->c_cflag & CSIZE) { case CS5: - lcr = MAX310X_LCR_WORD_LEN_5; break; case CS6: - lcr = MAX310X_LCR_WORD_LEN_6; + lcr = MAX310X_LCR_LENGTH0_BIT; break; case CS7: - lcr = MAX310X_LCR_WORD_LEN_7; + lcr = MAX310X_LCR_LENGTH1_BIT; break; case CS8: default: - lcr = MAX310X_LCR_WORD_LEN_8; + lcr = MAX310X_LCR_LENGTH1_BIT | MAX310X_LCR_LENGTH0_BIT; break; } -- cgit v1.2.3 From c8246fefe2c995e128345614fb98b5ed5014318b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 7 Jun 2016 18:59:26 +0300 Subject: serial: max310x: Unregister UARTs on error Add uart_remove_one_port() for error path. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/tty/serial/max310x.c') diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index e73ae6e79b64..347f6e8dae39 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1234,6 +1234,9 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, dev_err(dev, "Unable to reguest IRQ %i\n", irq); + for (i = 0; i < devtype->nr; i++) + uart_remove_one_port(&max310x_uart, &s->p[i].port); + mutex_destroy(&s->mutex); out_clk: -- cgit v1.2.3 From 78adccac1ff9f485ccf427f337982799ce68738e Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 7 Jun 2016 18:59:27 +0300 Subject: serial: max310x: Assign port line automatically This patch makes assignment of port line automatically, so now user allow to use several MAX310X chips. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'drivers/tty/serial/max310x.c') diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 347f6e8dae39..9360801df3c4 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -32,7 +32,7 @@ #define MAX310X_NAME "max310x" #define MAX310X_MAJOR 204 #define MAX310X_MINOR 209 -#define MAX310X_UART_NR 4 +#define MAX310X_UART_NRMAX 16 /* MAX310X register definitions */ #define MAX310X_RHR_REG (0x00) /* RX FIFO */ @@ -279,9 +279,11 @@ static struct uart_driver max310x_uart = { .dev_name = "ttyMAX", .major = MAX310X_MAJOR, .minor = MAX310X_MINOR, - .nr = MAX310X_UART_NR, + .nr = MAX310X_UART_NRMAX, }; +static DECLARE_BITMAP(max310x_lines, MAX310X_UART_NRMAX); + static u8 max310x_port_read(struct uart_port *port, u8 reg) { struct max310x_port *s = dev_get_drvdata(port->dev); @@ -600,9 +602,7 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) unsigned int sts, ch, flag; if (unlikely(rxlen >= port->fifosize)) { - dev_warn_ratelimited(port->dev, - "Port %i: Possible RX FIFO overrun\n", - port->line); + dev_warn_ratelimited(port->dev, "Possible RX FIFO overrun\n"); port->icount.buf_overrun++; /* Ensure sanity of RX level */ rxlen = port->fifosize; @@ -1193,8 +1193,16 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, mutex_init(&s->mutex); for (i = 0; i < devtype->nr; i++) { + unsigned int line; + + line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); + if (line == MAX310X_UART_NRMAX) { + ret = -ERANGE; + goto out_uart; + } + /* Initialize port data */ - s->p[i].port.line = i; + s->p[i].port.line = line; s->p[i].port.dev = dev; s->p[i].port.irq = irq; s->p[i].port.type = PORT_MAX310X; @@ -1220,8 +1228,15 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); + /* Register port */ - uart_add_one_port(&max310x_uart, &s->p[i].port); + ret = uart_add_one_port(&max310x_uart, &s->p[i].port); + if (ret) { + s->p[i].port.dev = NULL; + goto out_uart; + } + set_bit(line, max310x_lines); + /* Go to suspend mode */ devtype->power(&s->p[i].port, 0); } @@ -1234,8 +1249,13 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, dev_err(dev, "Unable to reguest IRQ %i\n", irq); - for (i = 0; i < devtype->nr; i++) - uart_remove_one_port(&max310x_uart, &s->p[i].port); +out_uart: + for (i = 0; i < devtype->nr; i++) { + if (s->p[i].port.dev) { + uart_remove_one_port(&max310x_uart, &s->p[i].port); + clear_bit(s->p[i].port.line, max310x_lines); + } + } mutex_destroy(&s->mutex); @@ -1255,6 +1275,7 @@ static int max310x_remove(struct device *dev) cancel_work_sync(&s->p[i].md_work); cancel_work_sync(&s->p[i].rs_work); uart_remove_one_port(&max310x_uart, &s->p[i].port); + clear_bit(s->p[i].port.line, max310x_lines); s->devtype->power(&s->p[i].port, 0); } @@ -1347,6 +1368,8 @@ static int __init max310x_uart_init(void) { int ret; + bitmap_zero(max310x_lines, MAX310X_UART_NRMAX); + ret = uart_register_driver(&max310x_uart); if (ret) return ret; -- cgit v1.2.3