From 5f0337b549e97fda07ccf48b9eebcee983c255bf Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 20 Nov 2017 17:40:15 +0000 Subject: USB: serial: iuu_phoenix: remove redundant assignment of DIV to itself The assignment of DIV to itself is redundant and can be removed. Signed-off-by: Colin Ian King Signed-off-by: Johan Hovold --- drivers/usb/serial/iuu_phoenix.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 397a8012ffa3..62c91e360baf 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -472,7 +472,6 @@ static int iuu_clk(struct usb_serial_port *port, int dwFrq) } } P2 = ((P - PO) / 2) - 4; - DIV = DIV; PUMP = 0x04; PBmsb = (P2 >> 8 & 0x03); PBlsb = P2 & 0xFF; -- cgit v1.2.3 From d8a42b1ff8a3755cc710785c7e4b5e59636399ca Mon Sep 17 00:00:00 2001 From: Gimcuan Hui Date: Mon, 27 Nov 2017 15:36:51 +0000 Subject: USB: serial: ark3116: clean up return values of register accessors write_reg returns 0 on success, we can make it more explicit by returning number 0 instead of result variable. read_reg should return 0 on success since this is a more common pattern. The user of read_reg has been clean-up and should be at the same commit. Signed-off-by: Gimcuan Hui Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 3c544782f60b..23d46ef87d64 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -83,7 +83,10 @@ static int ark3116_write_reg(struct usb_serial *serial, usb_sndctrlpipe(serial->dev, 0), 0xfe, 0x40, val, reg, NULL, 0, ARK_TIMEOUT); - return result; + if (result) + return result; + + return 0; } static int ark3116_read_reg(struct usb_serial *serial, @@ -105,7 +108,7 @@ static int ark3116_read_reg(struct usb_serial *serial, return result; } - return buf[0]; + return 0; } static inline int calc_divisor(int bps) @@ -355,13 +358,13 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) /* read modem status */ result = ark3116_read_reg(serial, UART_MSR, buf); - if (result < 0) + if (result) goto err_close; priv->msr = *buf; /* read line status */ result = ark3116_read_reg(serial, UART_LSR, buf); - if (result < 0) + if (result) goto err_close; priv->lsr = *buf; -- cgit v1.2.3 From c7b8f77872c73f69a16528a9eb87afefcccdc18b Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 13 Dec 2017 20:34:36 +0800 Subject: USB: serial: io_edgeport: fix possible sleep-in-atomic According to drivers/usb/serial/io_edgeport.c, the driver may sleep under a spinlock. The function call path is: edge_bulk_in_callback (acquire the spinlock) process_rcvd_data process_rcvd_status change_port_settings send_iosp_ext_cmd write_cmd_usb usb_kill_urb --> may sleep To fix it, the redundant usb_kill_urb() is removed from the error path after usb_submit_urb() fails. This possible bug is found by my static analysis tool (DSAC) and checked by my code review. Signed-off-by: Jia-Ju Bai Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 219265ce3711..17283f4b4779 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2282,7 +2282,6 @@ static int write_cmd_usb(struct edgeport_port *edge_port, /* something went wrong */ dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __func__, status); - usb_kill_urb(urb); usb_free_urb(urb); atomic_dec(&CmdUrbs); return status; -- cgit v1.2.3 From 63443a0b2ab277ef677640bc32b4f94661821b70 Mon Sep 17 00:00:00 2001 From: Mikhail Zaytsev Date: Sat, 6 Jan 2018 20:14:02 +0300 Subject: USB: serial: ark3116: remove dummy TIOCSSERIAL ioctl The patch removes unused TIOCSSERIAL ioctl case and adds the default block to the switch. This will make the ioctl return -ENOTTY to user space (e.g. setserial), which indicates that TIOCSSERIAL really isn't supported for these devices currently. Note that these (dummy) ioctl implementations where added by commit 2f430b4bbae7 ("USB: ark3116: Add TIOCGSERIAL and TIOCSSERIAL ioctl calls.") back in 2006. This in turn appears to have been triggered by a change in a user space tool, wvdial, which started erroring out if either was missing. There are some bug reports about that against wvdial from around that time, and looking at the wvstreams (library) code now, it looks like the issue has indeed been resolved by handling errors more gracefully (e.g. just logging them). User space really should not make assumptions about these ioctl always being implemented, but if this turns out to be a problem for anyone using this driver, we'll add TIOCSSERIAL back in some form. Signed-off-by: Mikhail Zaytsev [johan: amend commit message with backstory ] Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 23d46ef87d64..2e957c76f61e 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -418,10 +418,8 @@ static int ark3116_ioctl(struct tty_struct *tty, return -EFAULT; return 0; - case TIOCSSERIAL: - if (copy_from_user(&serstruct, user_arg, sizeof(serstruct))) - return -EFAULT; - return 0; + default: + break; } return -ENOIOCTLCMD; -- cgit v1.2.3 From e255f2078b1206e51f1abd0623902429db35fb5f Mon Sep 17 00:00:00 2001 From: Mikhail Zaytsev Date: Sat, 6 Jan 2018 20:15:22 +0300 Subject: USB: serial: ark3116: move TIOCGSERIAL ioctl case to function The patch moves TIOCGSERIAL ioctl case to get_serial_info function. Signed-off-by: Mikhail Zaytsev [johan: keep the automatic __user pointer variable in ioctl callback ] Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 2e957c76f61e..7796ad8e33c6 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -397,27 +397,33 @@ err_free: return result; } +static int ark3116_get_serial_info(struct usb_serial_port *port, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + + tmp.type = PORT_16654; + tmp.line = port->minor; + tmp.port = port->port_number; + tmp.baud_base = 460800; + + if (copy_to_user(retinfo, &tmp, sizeof(tmp))) + return -EFAULT; + + return 0; +} + static int ark3116_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; - struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; switch (cmd) { case TIOCGSERIAL: - /* XXX: Some of these values are probably wrong. */ - memset(&serstruct, 0, sizeof(serstruct)); - serstruct.type = PORT_16654; - serstruct.line = port->minor; - serstruct.port = port->port_number; - serstruct.custom_divisor = 0; - serstruct.baud_base = 460800; - - if (copy_to_user(user_arg, &serstruct, sizeof(serstruct))) - return -EFAULT; - - return 0; + return ark3116_get_serial_info(port, user_arg); default: break; } -- cgit v1.2.3 From 3aacac02f38543f7a0936fa1e844cd9564b04aaf Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 11 Jan 2018 14:47:15 +0800 Subject: USB: serial: f81534: add high baud rate support The F81532/534 had 4 clocksource 1.846/18.46/14.77/24MHz and baud rates can be up to 1.5Mbits with 24MHz. This device may generate data overrun when baud rate setting to 921600bps or higher with old UART trigger level setting (8x14=112) with full loading. We'll change trigger level from 8x14=112 to 8x8=64 to avoid data overrun. Also the read/write of EP0 will be affected by this patch. The worst case of responding time is 20s when all serial port are full loading and trying to access EP0, so we change EP0 timeout from 10 to 20s. F81532/534 Clock register (offset +08h) Bit0: UART Enable (always on) Bit2-1: Clock source selector 00: 1.846MHz. 01: 18.46MHz. 10: 24MHz. 11: 14.77MHz. Signed-off-by: Ji-Ze Hong (Peter Hong) [ johan: only use GENMASK() for masks ] Signed-off-by: Johan Hovold --- drivers/usb/serial/f81534.c | 93 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e4573b4c8935..cfba05489bfa 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -41,6 +41,7 @@ #define F81534_MODEM_CONTROL_REG (0x04 + F81534_UART_BASE_ADDRESS) #define F81534_LINE_STATUS_REG (0x05 + F81534_UART_BASE_ADDRESS) #define F81534_MODEM_STATUS_REG (0x06 + F81534_UART_BASE_ADDRESS) +#define F81534_CLOCK_REG (0x08 + F81534_UART_BASE_ADDRESS) #define F81534_CONFIG1_REG (0x09 + F81534_UART_BASE_ADDRESS) #define F81534_DEF_CONF_ADDRESS_START 0x3000 @@ -57,7 +58,7 @@ /* Default URB timeout for USB operations */ #define F81534_USB_MAX_RETRY 10 -#define F81534_USB_TIMEOUT 1000 +#define F81534_USB_TIMEOUT 2000 #define F81534_SET_GET_REGISTER 0xA0 #define F81534_NUM_PORT 4 @@ -96,7 +97,6 @@ #define F81534_CMD_READ 0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE 115200 #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORT BIT(7) @@ -106,6 +106,24 @@ #define F81534_1X_RXTRIGGER 0xc3 #define F81534_8X_RXTRIGGER 0xcf +/* + * F81532/534 Clock registers (offset +08h) + * + * Bit0: UART Enable (always on) + * Bit2-1: Clock source selector + * 00: 1.846MHz. + * 01: 18.46MHz. + * 10: 24MHz. + * 11: 14.77MHz. + */ + +#define F81534_UART_EN BIT(0) +#define F81534_CLK_1_846_MHZ 0 +#define F81534_CLK_18_46_MHZ BIT(1) +#define F81534_CLK_24_MHZ BIT(2) +#define F81534_CLK_14_77_MHZ (BIT(1) | BIT(2)) +#define F81534_CLK_MASK GENMASK(2, 1) + static const struct usb_device_id f81534_id_table[] = { { USB_DEVICE(FINTEK_VENDOR_ID_1, FINTEK_DEVICE_ID) }, { USB_DEVICE(FINTEK_VENDOR_ID_2, FINTEK_DEVICE_ID) }, @@ -129,12 +147,18 @@ struct f81534_port_private { struct usb_serial_port *port; unsigned long tx_empty; spinlock_t msr_lock; + u32 baud_base; u8 shadow_mcr; u8 shadow_lcr; u8 shadow_msr; + u8 shadow_clk; u8 phy_num; }; +static u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 }; +static u8 const clock_table[] = { F81534_CLK_1_846_MHZ, F81534_CLK_14_77_MHZ, + F81534_CLK_18_46_MHZ, F81534_CLK_24_MHZ }; + static int f81534_logic_to_phy_port(struct usb_serial *serial, struct usb_serial_port *port) { @@ -460,13 +484,52 @@ static u32 f81534_calc_baud_divisor(u32 baudrate, u32 clockrate) return DIV_ROUND_CLOSEST(clockrate, baudrate); } -static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate, - u8 lcr) +static int f81534_find_clk(u32 baudrate) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { + if (baudrate <= baudrate_table[idx] && + baudrate_table[idx] % baudrate == 0) + return idx; + } + + return -EINVAL; +} + +static int f81534_set_port_config(struct usb_serial_port *port, + struct tty_struct *tty, u32 baudrate, u32 old_baudrate, u8 lcr) { struct f81534_port_private *port_priv = usb_get_serial_port_data(port); u32 divisor; int status; + int i; + int idx; u8 value; + u32 baud_list[] = {baudrate, old_baudrate, F81534_DEFAULT_BAUD_RATE}; + + for (i = 0; i < ARRAY_SIZE(baud_list); ++i) { + idx = f81534_find_clk(baud_list[i]); + if (idx >= 0) { + baudrate = baud_list[i]; + tty_encode_baud_rate(tty, baudrate, baudrate); + break; + } + } + + if (idx < 0) + return -EINVAL; + + port_priv->baud_base = baudrate_table[idx]; + port_priv->shadow_clk &= ~F81534_CLK_MASK; + port_priv->shadow_clk |= clock_table[idx]; + + status = f81534_set_port_register(port, F81534_CLOCK_REG, + port_priv->shadow_clk); + if (status) { + dev_err(&port->dev, "CLOCK_REG setting failed\n"); + return status; + } if (baudrate <= 1200) value = F81534_1X_RXTRIGGER; /* 128 FIFO & TL: 1x */ @@ -482,7 +545,7 @@ static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate, if (baudrate <= 1200) value = UART_FCR_TRIGGER_1 | UART_FCR_ENABLE_FIFO; /* TL: 1 */ else - value = UART_FCR_R_TRIG_11 | UART_FCR_ENABLE_FIFO; /* TL: 14 */ + value = UART_FCR_TRIGGER_8 | UART_FCR_ENABLE_FIFO; /* TL: 8 */ status = f81534_set_port_register(port, F81534_FIFO_CONTROL_REG, value); @@ -491,7 +554,7 @@ static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate, return status; } - divisor = f81534_calc_baud_divisor(baudrate, F81534_MAX_BAUDRATE); + divisor = f81534_calc_baud_divisor(baudrate, port_priv->baud_base); mutex_lock(&port_priv->lcr_mutex); @@ -741,6 +804,7 @@ static void f81534_set_termios(struct tty_struct *tty, u8 new_lcr = 0; int status; u32 baud; + u32 old_baud; if (C_BAUD(tty) == B0) f81534_update_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); @@ -780,18 +844,14 @@ static void f81534_set_termios(struct tty_struct *tty, if (!baud) return; - if (baud > F81534_MAX_BAUDRATE) { - if (old_termios) - baud = tty_termios_baud_rate(old_termios); - else - baud = F81534_DEFAULT_BAUD_RATE; - - tty_encode_baud_rate(tty, baud, baud); - } + if (old_termios) + old_baud = tty_termios_baud_rate(old_termios); + else + old_baud = F81534_DEFAULT_BAUD_RATE; dev_dbg(&port->dev, "%s: baud: %d\n", __func__, baud); - status = f81534_set_port_config(port, baud, new_lcr); + status = f81534_set_port_config(port, tty, baud, old_baud, new_lcr); if (status < 0) { dev_err(&port->dev, "%s: set port config failed: %d\n", __func__, status); @@ -947,7 +1007,7 @@ static int f81534_get_serial_info(struct usb_serial_port *port, tmp.type = PORT_16550A; tmp.port = port->port_number; tmp.line = port->minor; - tmp.baud_base = F81534_MAX_BAUDRATE; + tmp.baud_base = port_priv->baud_base; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -1221,6 +1281,7 @@ static int f81534_port_probe(struct usb_serial_port *port) if (!port_priv) return -ENOMEM; + port_priv->shadow_clk = F81534_UART_EN; spin_lock_init(&port_priv->msr_lock); mutex_init(&port_priv->mcr_mutex); mutex_init(&port_priv->lcr_mutex); -- cgit v1.2.3 From ee0309b46a20304d30c0e0447a2da2a94115731c Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 11 Jan 2018 14:47:16 +0800 Subject: USB: serial: f81534: add auto RTS direction support The F81532/534 had auto RTS direction support for RS485 mode. We'll read it from internal Flash with address 0x2f01~0x2f04 for 4 ports. There are 4 conditions below: 0: F81534_PORT_CONF_RS232. 1: F81534_PORT_CONF_RS485. 2: value error, default to F81534_PORT_CONF_RS232. 3: F81534_PORT_CONF_RS485_INVERT. F81532/534 Clock register (offset +08h) Bit0: UART Enable (always on) Bit2-1: Clock source selector 00: 1.846MHz. 01: 18.46MHz. 10: 24MHz. 11: 14.77MHz. Bit4: Auto direction(RTS) control (RTS pin Low when TX) Bit5: Invert direction(RTS) when Bit4 enabled (RTS pin high when TX) Signed-off-by: Ji-Ze Hong (Peter Hong) [ johan: rename mode-mask define, and only use GENMASK() for masks ] Signed-off-by: Johan Hovold --- drivers/usb/serial/f81534.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index cfba05489bfa..5caa945217b5 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -98,11 +98,16 @@ #define F81534_DEFAULT_BAUD_RATE 9600 +#define F81534_PORT_CONF_RS232 0 +#define F81534_PORT_CONF_RS485 BIT(0) +#define F81534_PORT_CONF_RS485_INVERT (BIT(0) | BIT(1)) +#define F81534_PORT_CONF_MODE_MASK GENMASK(1, 0) #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORT BIT(7) #define F81534_PORT_UNAVAILABLE \ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) + #define F81534_1X_RXTRIGGER 0xc3 #define F81534_8X_RXTRIGGER 0xcf @@ -115,6 +120,8 @@ * 01: 18.46MHz. * 10: 24MHz. * 11: 14.77MHz. + * Bit4: Auto direction(RTS) control (RTS pin Low when TX) + * Bit5: Invert direction(RTS) when Bit4 enabled (RTS pin high when TX) */ #define F81534_UART_EN BIT(0) @@ -123,6 +130,8 @@ #define F81534_CLK_24_MHZ BIT(2) #define F81534_CLK_14_77_MHZ (BIT(1) | BIT(2)) #define F81534_CLK_MASK GENMASK(2, 1) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERT BIT(5) static const struct usb_device_id f81534_id_table[] = { { USB_DEVICE(FINTEK_VENDOR_ID_1, FINTEK_DEVICE_ID) }, @@ -1274,9 +1283,12 @@ static void f81534_lsr_worker(struct work_struct *work) static int f81534_port_probe(struct usb_serial_port *port) { + struct f81534_serial_private *serial_priv; struct f81534_port_private *port_priv; int ret; + u8 value; + serial_priv = usb_get_serial_data(port->serial); port_priv = devm_kzalloc(&port->dev, sizeof(*port_priv), GFP_KERNEL); if (!port_priv) return -ENOMEM; @@ -1309,6 +1321,24 @@ static int f81534_port_probe(struct usb_serial_port *port) if (ret) return ret; + value = serial_priv->conf_data[port_priv->phy_num]; + switch (value & F81534_PORT_CONF_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE | + F81534_CLK_RS485_INVERT; + dev_dbg(&port->dev, "RS485 invert mode\n"); + break; + case F81534_PORT_CONF_RS485: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE; + dev_dbg(&port->dev, "RS485 mode\n"); + break; + + default: + case F81534_PORT_CONF_RS232: + dev_dbg(&port->dev, "RS232 mode\n"); + break; + } + return 0; } -- cgit v1.2.3 From 138651e1186bef33d0d330ca735d85784ee6535b Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 11 Jan 2018 14:47:17 +0800 Subject: USB: serial: f81534: add output pin control The F81532/534 had 3 output pin (M0/SD, M1, M2) with open-drain mode to control transceiver. We'll read it from internal Flash with address 0x2f05~0x2f08 for 4 ports. The value is range from 0 to 7. The M0/SD is MSB of this value. For a examples, If read value is 6, we'll write M0/SD, M1, M2 as 1, 1, 0. Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81534.c | 67 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 5caa945217b5..f448a91be612 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -52,6 +52,7 @@ #define F81534_CUSTOM_NO_CUSTOM_DATA 0xff #define F81534_CUSTOM_VALID_TOKEN 0xf0 #define F81534_CONF_OFFSET 1 +#define F81534_CONF_GPIO_OFFSET 4 #define F81534_MAX_DATA_BLOCK 64 #define F81534_MAX_BUS_RETRY 20 @@ -164,6 +165,23 @@ struct f81534_port_private { u8 phy_num; }; +struct f81534_pin_data { + const u16 reg_addr; + const u8 reg_mask; +}; + +struct f81534_port_out_pin { + struct f81534_pin_data pin[3]; +}; + +/* Pin output value for M2/M1/M0(SD) */ +static const struct f81534_port_out_pin f81534_port_out_pins[] = { + { { { 0x2ae8, BIT(7) }, { 0x2a90, BIT(5) }, { 0x2a90, BIT(4) } } }, + { { { 0x2ae8, BIT(6) }, { 0x2ae8, BIT(0) }, { 0x2ae8, BIT(3) } } }, + { { { 0x2a90, BIT(0) }, { 0x2ae8, BIT(2) }, { 0x2a80, BIT(6) } } }, + { { { 0x2a90, BIT(3) }, { 0x2a90, BIT(2) }, { 0x2a90, BIT(1) } } }, +}; + static u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 }; static u8 const clock_table[] = { F81534_CLK_1_846_MHZ, F81534_CLK_14_77_MHZ, F81534_CLK_18_46_MHZ, F81534_CLK_24_MHZ }; @@ -273,6 +291,22 @@ end: return status; } +static int f81534_set_mask_register(struct usb_serial *serial, u16 reg, + u8 mask, u8 data) +{ + int status; + u8 tmp; + + status = f81534_get_register(serial, reg, &tmp); + if (status) + return status; + + tmp &= ~mask; + tmp |= (mask & data); + + return f81534_set_register(serial, reg, tmp); +} + static int f81534_set_port_register(struct usb_serial_port *port, u16 reg, u8 data) { @@ -1281,6 +1315,37 @@ static void f81534_lsr_worker(struct work_struct *work) dev_warn(&port->dev, "read LSR failed: %d\n", status); } +static int f81534_set_port_output_pin(struct usb_serial_port *port) +{ + struct f81534_serial_private *serial_priv; + struct f81534_port_private *port_priv; + struct usb_serial *serial; + const struct f81534_port_out_pin *pins; + int status; + int i; + u8 value; + u8 idx; + + serial = port->serial; + serial_priv = usb_get_serial_data(serial); + port_priv = usb_get_serial_port_data(port); + + idx = F81534_CONF_GPIO_OFFSET + port_priv->phy_num; + value = serial_priv->conf_data[idx]; + pins = &f81534_port_out_pins[port_priv->phy_num]; + + for (i = 0; i < ARRAY_SIZE(pins->pin); ++i) { + status = f81534_set_mask_register(serial, + pins->pin[i].reg_addr, pins->pin[i].reg_mask, + value & BIT(i) ? pins->pin[i].reg_mask : 0); + if (status) + return status; + } + + dev_dbg(&port->dev, "Output pin (M0/M1/M2): %d\n", value); + return 0; +} + static int f81534_port_probe(struct usb_serial_port *port) { struct f81534_serial_private *serial_priv; @@ -1339,7 +1404,7 @@ static int f81534_port_probe(struct usb_serial_port *port) break; } - return 0; + return f81534_set_port_output_pin(port); } static int f81534_port_remove(struct usb_serial_port *port) -- cgit v1.2.3 From f047d35782553ab862003a58b0fcf7e1b542e4f9 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 11 Jan 2018 14:47:18 +0800 Subject: USB: serial: f81534: only read configuration once In the original code, We'll read configuration in calc_num_ports() and read again in attach(). In fact, we can move all content from attach() to calc_num_ports() to simplify the code. Signed-off-by: Ji-Ze Hong (Peter Hong) [ johan: replace commit summary ] Signed-off-by: Johan Hovold --- drivers/usb/serial/f81534.c | 116 ++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index f448a91be612..9fa990b8766f 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -753,14 +753,14 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) static int f81534_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds) { + struct f81534_serial_private *serial_priv; struct device *dev = &serial->interface->dev; int size_bulk_in = usb_endpoint_maxp(epds->bulk_in[0]); int size_bulk_out = usb_endpoint_maxp(epds->bulk_out[0]); - u8 setting[F81534_CUSTOM_DATA_SIZE]; - u8 setting_idx; u8 num_port = 0; + int index = 0; int status; - size_t i; + int i; if (size_bulk_out != F81534_WRITE_BUFFER_SIZE || size_bulk_in != F81534_MAX_RECEIVE_BLOCK_SIZE) { @@ -768,8 +768,16 @@ static int f81534_calc_num_ports(struct usb_serial *serial, return -ENODEV; } + serial_priv = devm_kzalloc(&serial->interface->dev, + sizeof(*serial_priv), GFP_KERNEL); + if (!serial_priv) + return -ENOMEM; + + usb_set_serial_data(serial, serial_priv); + mutex_init(&serial_priv->urb_mutex); + /* Check had custom setting */ - status = f81534_find_config_idx(serial, &setting_idx); + status = f81534_find_config_idx(serial, &serial_priv->setting_idx); if (status) { dev_err(&serial->interface->dev, "%s: find idx failed: %d\n", __func__, status); @@ -780,11 +788,12 @@ static int f81534_calc_num_ports(struct usb_serial *serial, * We'll read custom data only when data available, otherwise we'll * read default value instead. */ - if (setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) { + if (serial_priv->setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) { status = f81534_read_flash(serial, F81534_CUSTOM_ADDRESS_START + F81534_CONF_OFFSET, - sizeof(setting), setting); + sizeof(serial_priv->conf_data), + serial_priv->conf_data); if (status) { dev_err(&serial->interface->dev, "%s: get custom data failed: %d\n", @@ -794,13 +803,13 @@ static int f81534_calc_num_ports(struct usb_serial *serial, dev_dbg(&serial->interface->dev, "%s: read config from block: %d\n", __func__, - setting_idx); + serial_priv->setting_idx); } else { /* Read default board setting */ status = f81534_read_flash(serial, - F81534_DEF_CONF_ADDRESS_START, F81534_NUM_PORT, - setting); - + F81534_DEF_CONF_ADDRESS_START, + sizeof(serial_priv->conf_data), + serial_priv->conf_data); if (status) { dev_err(&serial->interface->dev, "%s: read failed: %d\n", __func__, @@ -814,7 +823,7 @@ static int f81534_calc_num_ports(struct usb_serial *serial, /* New style, find all possible ports */ for (i = 0; i < F81534_NUM_PORT; ++i) { - if (setting[i] & F81534_PORT_UNAVAILABLE) + if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) continue; ++num_port; @@ -826,6 +835,17 @@ static int f81534_calc_num_ports(struct usb_serial *serial, num_port = 4; /* Nothing found, oldest version IC */ } + /* Assign phy-to-logic mapping */ + for (i = 0; i < F81534_NUM_PORT; ++i) { + if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) + continue; + + serial_priv->tty_idx[i] = index++; + dev_dbg(&serial->interface->dev, + "%s: phy_num: %d, tty_idx: %d\n", __func__, i, + serial_priv->tty_idx[i]); + } + /* * Setup bulk-out endpoint multiplexing. All ports share the same * bulk-out endpoint. @@ -1227,79 +1247,6 @@ static void f81534_write_usb_callback(struct urb *urb) } } -static int f81534_attach(struct usb_serial *serial) -{ - struct f81534_serial_private *serial_priv; - int index = 0; - int status; - int i; - - serial_priv = devm_kzalloc(&serial->interface->dev, - sizeof(*serial_priv), GFP_KERNEL); - if (!serial_priv) - return -ENOMEM; - - usb_set_serial_data(serial, serial_priv); - - mutex_init(&serial_priv->urb_mutex); - - /* Check had custom setting */ - status = f81534_find_config_idx(serial, &serial_priv->setting_idx); - if (status) { - dev_err(&serial->interface->dev, "%s: find idx failed: %d\n", - __func__, status); - return status; - } - - /* - * We'll read custom data only when data available, otherwise we'll - * read default value instead. - */ - if (serial_priv->setting_idx == F81534_CUSTOM_NO_CUSTOM_DATA) { - /* - * The default configuration layout: - * byte 0/1/2/3: uart setting - */ - status = f81534_read_flash(serial, - F81534_DEF_CONF_ADDRESS_START, - F81534_DEF_CONF_SIZE, - serial_priv->conf_data); - if (status) { - dev_err(&serial->interface->dev, - "%s: read reserve data failed: %d\n", - __func__, status); - return status; - } - } else { - /* Only read 8 bytes for mode & GPIO */ - status = f81534_read_flash(serial, - F81534_CUSTOM_ADDRESS_START + - F81534_CONF_OFFSET, - sizeof(serial_priv->conf_data), - serial_priv->conf_data); - if (status) { - dev_err(&serial->interface->dev, - "%s: idx: %d get data failed: %d\n", - __func__, serial_priv->setting_idx, - status); - return status; - } - } - - /* Assign phy-to-logic mapping */ - for (i = 0; i < F81534_NUM_PORT; ++i) { - if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) - continue; - - serial_priv->tty_idx[i] = index++; - dev_dbg(&serial->interface->dev, - "%s: phy_num: %d, tty_idx: %d\n", __func__, i, - serial_priv->tty_idx[i]); - } - - return 0; -} - static void f81534_lsr_worker(struct work_struct *work) { struct f81534_port_private *port_priv; @@ -1543,7 +1490,6 @@ static struct usb_serial_driver f81534_device = { .write = f81534_write, .tx_empty = f81534_tx_empty, .calc_num_ports = f81534_calc_num_ports, - .attach = f81534_attach, .port_probe = f81534_port_probe, .port_remove = f81534_port_remove, .break_ctl = f81534_break_ctl, -- cgit v1.2.3 From bb543ca287f5b14a61533f959e3f62ae58890311 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 11 Jan 2018 14:47:19 +0800 Subject: USB: serial: f81534: add H/W disable port support The F81532/534 can be disable port by manufacturer with following H/W design. 1: Connect DCD/DSR/CTS/RI pin to ground. 2: Connect RX pin to ground. In driver, we'll implements some detect method likes following: 1: Read MSR. 2: Turn MCR LOOP bit on, off and read LSR after delay with 60ms. It'll contain BREAK status in LSR. Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81534.c | 81 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 9fa990b8766f..86edec3e8c05 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -307,6 +307,20 @@ static int f81534_set_mask_register(struct usb_serial *serial, u16 reg, return f81534_set_register(serial, reg, tmp); } +static int f81534_set_phy_port_register(struct usb_serial *serial, int phy, + u16 reg, u8 data) +{ + return f81534_set_register(serial, reg + F81534_UART_OFFSET * phy, + data); +} + +static int f81534_get_phy_port_register(struct usb_serial *serial, int phy, + u16 reg, u8 *data) +{ + return f81534_get_register(serial, reg + F81534_UART_OFFSET * phy, + data); +} + static int f81534_set_port_register(struct usb_serial_port *port, u16 reg, u8 data) { @@ -732,6 +746,70 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) return 0; } +/* + * The F81532/534 will not report serial port to USB serial subsystem when + * H/W DCD/DSR/CTS/RI/RX pin connected to ground. + * + * To detect RX pin status, we'll enable MCR interal loopback, disable it and + * delayed for 60ms. It connected to ground If LSR register report UART_LSR_BI. + */ +static bool f81534_check_port_hw_disabled(struct usb_serial *serial, int phy) +{ + int status; + u8 old_mcr; + u8 msr; + u8 lsr; + u8 msr_mask; + + msr_mask = UART_MSR_DCD | UART_MSR_RI | UART_MSR_DSR | UART_MSR_CTS; + + status = f81534_get_phy_port_register(serial, phy, + F81534_MODEM_STATUS_REG, &msr); + if (status) + return false; + + if ((msr & msr_mask) != msr_mask) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_FIFO_CONTROL_REG, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + if (status) + return false; + + status = f81534_get_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, &old_mcr); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, UART_MCR_LOOP); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, 0x0); + if (status) + return false; + + msleep(60); + + status = f81534_get_phy_port_register(serial, phy, + F81534_LINE_STATUS_REG, &lsr); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, old_mcr); + if (status) + return false; + + if ((lsr & UART_LSR_BI) == UART_LSR_BI) + return true; + + return false; +} + /* * We had 2 generation of F81532/534 IC. All has an internal storage. * @@ -823,6 +901,9 @@ static int f81534_calc_num_ports(struct usb_serial *serial, /* New style, find all possible ports */ for (i = 0; i < F81534_NUM_PORT; ++i) { + if (f81534_check_port_hw_disabled(serial, i)) + serial_priv->conf_data[i] |= F81534_PORT_UNAVAILABLE; + if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) continue; -- cgit v1.2.3 From d1c48227d7c45fbb35c81f846a62ec92a74f4701 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 11 Jan 2018 14:47:20 +0800 Subject: USB: serial: f81534: fix tx error on some baud rate The F81532/534 had 4 clocksource 1.846/18.46/14.77/24MHz and baud rates can be up to 1.5Mbits with 24MHz. But on some baud rate (384~500kps), the TX side will send the data frame too close to treat frame error on RX side. This patch will force all TX data frame with delay 1bit gap. Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81534.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 86edec3e8c05..4dfbff20bda4 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -131,6 +131,7 @@ #define F81534_CLK_24_MHZ BIT(2) #define F81534_CLK_14_77_MHZ (BIT(1) | BIT(2)) #define F81534_CLK_MASK GENMASK(2, 1) +#define F81534_CLK_TX_DELAY_1BIT BIT(3) #define F81534_CLK_RS485_MODE BIT(4) #define F81534_CLK_RS485_INVERT BIT(5) @@ -1386,7 +1387,11 @@ static int f81534_port_probe(struct usb_serial_port *port) if (!port_priv) return -ENOMEM; - port_priv->shadow_clk = F81534_UART_EN; + /* + * We'll make tx frame error when baud rate from 384~500kps. So we'll + * delay all tx data frame with 1bit. + */ + port_priv->shadow_clk = F81534_UART_EN | F81534_CLK_TX_DELAY_1BIT; spin_lock_init(&port_priv->msr_lock); mutex_init(&port_priv->mcr_mutex); mutex_init(&port_priv->lcr_mutex); -- cgit v1.2.3