Re: R: R: R: [PATCH v1] serial: 8250_fintek: Print Fintek chip name
Hi, Flavio Suligoi 於 2020/12/15 下午 11:06 寫道: we produce some x86 boards with multistandard RS232/422/485 ports and, to have this feature, in some of these boards, we use a Fintek uart or superIO. So this additional info "extra_name" can be useful for a quick check if the serial ports are multistandard or not, without any other investigations, but using only a simple command like: dmesg| grep ttyS But as they work the same, why does it matter? Yes you are right, by the user point of view, they are the same. Userspace should not care here. Isn't there some other id you can read/query for a hardware database tool to determine this? As Greg mentions, The userspace don't care what IC they are using. We can use Linux RS485 API to control or check the serial port. https://www.kernel.org/doc/html/latest/driver-api/serial/serial-rs485.html -- With Best Regards, Peter Hong
Re: [PATCH v1] serial: 8250_fintek: Print Fintek chip name
Hi, Greg Kroah-Hartman 於 2020/12/14 下午 09:42 寫道: pdata->pid = chip; + + pr_info("%s%s%s Fintek %s\n", + uart->port.dev ? dev_name(uart->port.dev) : "", + uart->port.dev ? ": " : "", + uart->port.name, + chip_name); Drivers, if all goes well, should not print anything to the kernel log. This isn't ok. And even if it was, dev_info() would be the correct thing to do... Maybe can transform pr_info() to dev_dbg() for debug usage ? -- With Best Regards, Peter Hong
Re: [PATCH 1/1] driver core: Fix unbalance probe_count in really_probe()
Hi Geert, Geert Uytterhoeven 於 2020/6/3 下午 03:13 寫道: Hi Ji-Ze, If devres_head is not empty, you have a serious problem on your system, as those resources may be in an unknown state (e.g. freed but still in use). While I had missed the probe_count imbalance when implementing the original change, it may actually be safer to not decrease probe_count, to prevent further probes from happening. But I guess it doesn't matter: if you get here, your system is in a bad state anyway. We want to fix the shutdown/reboot freeze issue and bisect to this patch and found if the probe_count != 0, the PC will stuck with wait_for_device_probe() with shutdown/reboot forever. So we just change the increment after return -EBUSY. In this case, it maybe 8250_PNP & serial 8250 platform driver resources conflict. I'll try to dump more message to debug. IMO, the shutdown/reboot operation should not block. with serial8250 platform driver. e.g. AOPEN DE6200. The conflict boot dmesg below: Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled 00:03: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 921600) is a 16550A 00:04: ttyS1 at I/O 0x2f8 (irq = 3, base_baud = 921600) is a 16550A 00:05: ttyS2 at I/O 0x3e8 (irq = 5, base_baud = 921600) is a 16550A serial8250: ttyS3 at I/O 0x2e8 (irq = 3, base_baud = 921600) is a 16550A Reboot/Shutdown will freeze in wait_for_device_probe(), message as following: INFQ: task systemd-shutdown: 1 blocked for more than 120 seconds. Now, how did you get to this state, i.e. which driver triggered the "Resources present before probing" message? Because that is the root issue that must be fixed, and the probe_count imbalance is IMHO just a red herring. Sorry for lost important dmesg: Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled 00:03: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 921600) is a 16550A 00:04: ttyS1 at I/O 0x2f8 (irq = 3, base_baud = 921600) is a 16550A 00:05: ttyS2 at I/O 0x3e8 (irq = 5, base_baud = 921600) is a 16550A serial8250: ttyS3 at I/O 0x2e8 (irq = 3, base_baud = 921600) is a 16550A platform serial8250: Resources present before probing ^^ -- With Best Regards, Peter Hong
[PATCH 1/1] driver core: Fix unbalance probe_count in really_probe()
In previous patch, using return -EBUSY in really_probe() instead WARN_ON() only. The following is the partial code. ... atomic_inc(_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); if (!list_empty(>devres_head)) { dev_crit(dev, "Resources present before probing\n"); return -EBUSY; } ... When the devres_head is not empty, this code will return -EBUSY to prevent resource conflict, but it forgot to balance probe_count. We can move the increasement code below the resource checking. ... pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); if (!list_empty(>devres_head)) { dev_crit(dev, "Resources present before probing\n"); return -EBUSY; } atomic_inc(_count); ... The original code will cause lots motherboard freeze in reboot/shutdown with systemd message "Reached target Reboot" or "Reached target Shutdown" with serial8250 platform driver. e.g. AOPEN DE6200. The conflict boot dmesg below: Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled 00:03: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 921600) is a 16550A 00:04: ttyS1 at I/O 0x2f8 (irq = 3, base_baud = 921600) is a 16550A 00:05: ttyS2 at I/O 0x3e8 (irq = 5, base_baud = 921600) is a 16550A serial8250: ttyS3 at I/O 0x2e8 (irq = 3, base_baud = 921600) is a 16550A Reboot/Shutdown will freeze in wait_for_device_probe(), message as following: INFQ: task systemd-shutdown: 1 blocked for more than 120 seconds. Not tainted 5.7.0-rc7-tty-next+ #241 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. Call Trace: __schedule+0x3d2/0x700 ? printk+0x52/0x6e schedule+0x4f/0xc0 wait_for_device_probe+0xbb/0xl40 ? wait_woken+0x80/0x80 device_shutdown+0xl5/0xle0 kernel_power_off+0x35/0x70 __do_sys_reboot+0xla0/0x220 ? do_sigtimedwait+0xld0/0x210 ? do.writev+0x6a/0xll0 ? do.writev+0x6a/0xll0 ? sigprocmask+0x6f/Oxa0 __64_sys_reboot+0xle/0x20 do_syscall_64+0x57/0xlb0 Fixes: 7c35e699c88b ("driver core: Print device when resources present in really_probe()") Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/base/dd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 9a1d940342ac..5173b0766a26 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -487,7 +487,6 @@ static int really_probe(struct device *dev, struct device_driver *drv) if (ret) return ret; - atomic_inc(_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); if (!list_empty(>devres_head)) { @@ -495,6 +494,8 @@ static int really_probe(struct device *dev, struct device_driver *drv) return -EBUSY; } + atomic_inc(_count); + re_probe: dev->driver = drv; -- 2.17.1
[PATCH V1 1/1] serial: 8250_fintek: Add F81966 Support
Fintek F81966 is a LPC/eSPI to 6 UARTs SuperIO. It has fully compatible with F81866. It's also need check the IRQ mode with system assigned. F81966 IRQ Mode setting: 0xf0 Bit1: IRQ_MODE0 Bit0: Share mode (always on) 0xf6 Bit3: IRQ_MODE1 Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 Signed-off-by: Ji-Ze Hong (Peter Hong) Cc: Ji-Ze Hong (Peter Hong) --- drivers/tty/serial/8250/8250_fintek.c | 13 +++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 31c91c2f8c6e..d1d253c4b518 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -19,6 +19,7 @@ #define CHIP_ID2 0x21 #define CHIP_ID_F81865 0x0407 #define CHIP_ID_F81866 0x1010 +#define CHIP_ID_F81966 0x0215 #define CHIP_ID_F81216AD 0x1602 #define CHIP_ID_F81216H 0x0501 #define CHIP_ID_F81216 0x0802 @@ -62,9 +63,9 @@ #define F81216_LDN_HIGH0x4 /* - * F81866 registers + * F81866/966 registers * - * The IRQ setting mode of F81866 is not the same with F81216 series. + * The IRQ setting mode of F81866/966 is not the same with F81216 series. * Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 * Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 * @@ -155,6 +156,7 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) switch (chip) { case CHIP_ID_F81865: case CHIP_ID_F81866: + case CHIP_ID_F81966: case CHIP_ID_F81216AD: case CHIP_ID_F81216H: case CHIP_ID_F81216: @@ -171,6 +173,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, int *max) { switch (pdata->pid) { + case CHIP_ID_F81966: case CHIP_ID_F81865: case CHIP_ID_F81866: *min = F81866_LDN_LOW; @@ -248,6 +251,7 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) sio_write_reg(pdata, LDN, pdata->index); switch (pdata->pid) { + case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, F81866_FIFO_CTRL, F81866_IRQ_MODE1, 0); @@ -274,6 +278,7 @@ static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) { switch (pdata->pid) { case CHIP_ID_F81216H: /* 128Bytes FIFO */ + case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, FIFO_CTRL, FIFO_MODE_MASK | RXFTHR_MODE_MASK, @@ -291,6 +296,7 @@ static void fintek_8250_goto_highspeed(struct uart_8250_port *uart, sio_write_reg(pdata, LDN, pdata->index); switch (pdata->pid) { + case CHIP_ID_F81966: case CHIP_ID_F81866: /* set uart clock for high speed serial mode */ sio_write_mask_reg(pdata, F81866_UART_CLK, F81866_UART_CLK_MASK, @@ -327,6 +333,7 @@ static void fintek_8250_set_termios(struct uart_port *port, case CHIP_ID_F81216H: reg = RS485; break; + case CHIP_ID_F81966: case CHIP_ID_F81866: reg = F81866_UART_CLK; break; @@ -373,6 +380,7 @@ static void fintek_8250_set_termios_handler(struct uart_8250_port *uart) switch (pdata->pid) { case CHIP_ID_F81216H: + case CHIP_ID_F81966: case CHIP_ID_F81866: uart->port.set_termios = fintek_8250_set_termios; break; @@ -443,6 +451,7 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) switch (pdata->pid) { case CHIP_ID_F81216AD: case CHIP_ID_F81216H: + case CHIP_ID_F81966: case CHIP_ID_F81866: case CHIP_ID_F81865: uart->port.rs485_config = fintek_8250_rs485_config; -- 2.17.1
[PATCH V2 4/7] USB: serial: f81232: Add F81534A support
The Fintek F81532A/534A/535/536 is USB-to-2/4/8/12 serial ports device and the serial port is default disabled when plugin computer. The IC is contains devices as following: 1. HUB (all devices is connected with this hub) 2. GPIO/Control device. (enable serial port and control GPIOs) 3. serial port 1 to x (2/4/8/12) It's most same with F81232, the UART device is difference as follow: 1. TX/RX bulk size is 128/512bytes 2. RX bulk layout change: F81232: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... F81534A:[LEN][Data.][LSR] Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 131 ++-- 1 file changed, 127 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index e4db0aec9af0..36a17aedc2ae 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Fintek F81232 USB to serial adaptor driver + * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver * * Copyright (C) 2012 Greg Kroah-Hartman (gre...@linuxfoundation.org) * Copyright (C) 2012 Linux Foundation @@ -21,11 +22,36 @@ #include #include +#define F81232_ID \ + { USB_DEVICE(0x1934, 0x0706) } /* 1 port UART device */ + +#define F81534A_SERIES_ID \ + { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \ + { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */ + static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x1934, 0x0706) }, + F81232_ID, + { } /* Terminating entry */ +}; + +static const struct usb_device_id f81534a_id_table[] = { + F81534A_SERIES_ID, + { } /* Terminating entry */ +}; + +static const struct usb_device_id all_serial_id_table[] = { + F81232_ID, + F81534A_SERIES_ID, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE(usb, id_table); +MODULE_DEVICE_TABLE(usb, all_serial_id_table); /* Maximum baudrate for F81232 */ #define F81232_MAX_BAUDRATE150 @@ -35,6 +61,10 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_REGISTER_REQUEST0xa0 #define F81232_GET_REGISTER0xc0 #define F81232_SET_REGISTER0x40 +#define F81534A_REGISTER_REQUEST F81232_REGISTER_REQUEST +#define F81534A_GET_REGISTER F81232_GET_REGISTER +#define F81534A_SET_REGISTER F81232_SET_REGISTER +#define F81534A_ACCESS_REG_RETRY 2 #define SERIAL_BASE_ADDRESS0x0120 #define RECEIVE_BUFFER_REGISTER(0x00 + SERIAL_BASE_ADDRESS) @@ -61,6 +91,11 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) #define F81232_CLK_MASKGENMASK(1, 0) +#define F81534A_MODE_CONF_REG 0x107 +#define F81534A_TRIGGER_MASK GENMASK(3, 2) +#define F81534A_TRIGGER_MULTPILE_4XBIT(3) +#define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) + struct f81232_private { struct mutex lock; u8 modem_control; @@ -383,6 +418,46 @@ static void f81232_process_read_urb(struct urb *urb) tty_flip_buffer_push(>port); } +static void f81534a_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + char tty_flag; + unsigned int i; + u8 lsr; + u8 len; + + if (urb->actual_length < 3) { + dev_err(>dev, "error actual_length: %d\n", + urb->actual_length); + return; + } + + len = data[0]; + if (len != urb->actual_length) { + dev_err(>dev, "len(%d) != urb->actual_length(%d)\n", len, + urb->actual_length); + return; + } + + /* bulk-in data: [LEN][Data.][LSR] */ + lsr = data[len - 1]; + tty_flag = f81232_handle_lsr(port, lsr); + + if (port->port.console && port->sysrq) { + for (i = 1; i < urb->actual_length - 1; ++i) + if (!usb_serial_handle_sysrq_char(port, data[i])) + tty_insert_flip_char
[PATCH V2 7/7] USB: serial: f81232: Add gpiolib to GPIO device
The Fintek F81534A series contains 3 GPIOs per UART and The max GPIOs is 12x3 = 36 GPIOs and this patch will implements GPIO device as a gpiochip to control all GPIO pins even transforms to transceiver pins. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 249 1 file changed, 249 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 82cc1e6cff62..dc9b28738b80 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,8 @@ MODULE_DEVICE_TABLE(usb, all_serial_id_table); #define F81534A_TRIGGER_MULTPILE_4XBIT(3) #define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) +#define F81534A_MAX_PORT 12 + /* Serial port self GPIO control, 2bytes [control data][input data] */ #define F81534A_GPIO_REG 0x10e #define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */ @@ -115,6 +118,13 @@ MODULE_DEVICE_TABLE(usb, all_serial_id_table); #define F81534A_CMD_ENABLE_PORT0x116 +/* + * Control device global GPIO control, + * 2bytes [control data][input data] + */ +#define F81534A_CTRL_GPIO_REG 0x1601 +#define F81534A_CTRL_GPIO_MAX_PIN 3 + struct f81232_private { struct mutex lock; u8 modem_control; @@ -126,6 +136,12 @@ struct f81232_private { struct usb_serial_port *port; }; +struct f81534a_ctrl_private { + struct usb_interface *intf; + struct gpio_chip chip; + struct mutex lock; +}; + static u32 const baudrate_table[] = { 115200, 921600, 1152000, 150 }; static u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ, F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ }; @@ -863,6 +879,50 @@ static void f81232_lsr_worker(struct work_struct *work) dev_warn(>dev, "read LSR failed: %d\n", status); } +static int f81534a_ctrl_get_register(struct usb_device *dev, u16 reg, u16 size, + void *val) +{ + int retry = F81534A_ACCESS_REG_RETRY; + int status; + u8 *tmp; + + tmp = kmalloc(size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + while (retry--) { + status = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + F81534A_REGISTER_REQUEST, + F81534A_GET_REGISTER, + reg, + 0, + tmp, + size, + USB_CTRL_GET_TIMEOUT); + if (status != size) { + status = usb_translate_errors(status); + if (status == -EIO) + continue; + + status = -EIO; + } else { + status = 0; + memcpy(val, tmp, size); + } + + break; + } + + if (status) { + dev_err(>dev, "get reg: %x, failed status: %d\n", reg, + status); + } + + kfree(tmp); + return status; +} + static int f81534a_ctrl_set_register(struct usb_device *dev, u16 reg, u16 size, void *val) { @@ -908,6 +968,182 @@ static int f81534a_ctrl_set_register(struct usb_device *dev, u16 reg, u16 size, return status; } +#ifdef CONFIG_GPIOLIB +static int f81534a_ctrl_set_mask_register(struct usb_device *dev, u16 reg, + u8 mask, u8 val) +{ + int status; + u8 tmp; + + status = f81534a_ctrl_get_register(dev, reg, 1, ); + if (status) + return status; + + + tmp = (tmp & ~mask) | (val & mask); + + status = f81534a_ctrl_set_register(dev, reg, 1, ); + if (status) + return status; + + return 0; +} + +static int f81534a_gpio_get(struct gpio_chip *chip, unsigned int gpio_num) +{ + struct f81534a_ctrl_private *priv = gpiochip_get_data(chip); + struct usb_interface *intf = priv->intf; + struct usb_device *dev = interface_to_usbdev(intf); + int status; + u8 tmp[2]; + int set; + int idx; + int reg; + + set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN; + idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN; + reg = F81534A_CTRL_GPIO_REG + set; + + mutex_lock(>lock); + + status = f81534a_ctrl_get_register(dev, reg, sizeof(tmp), tmp); + if (status) { + mutex_unlock(>lock); + return status; + } + + mutex_unlock(>lock); + + return !!(tmp[1] & BIT(idx)); +} + +static in
[PATCH V2 3/7] USB: serial: f81232: Use devm_kzalloc
Use devm_kzalloc() to replace kzalloc(). Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 13 + 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index b42b3738a768..e4db0aec9af0 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -756,7 +756,7 @@ static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(>dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -772,16 +772,6 @@ static int f81232_port_probe(struct usb_serial_port *port) return 0; } -static int f81232_port_remove(struct usb_serial_port *port) -{ - struct f81232_private *priv; - - priv = usb_get_serial_port_data(port); - kfree(priv); - - return 0; -} - static int f81232_suspend(struct usb_serial *serial, pm_message_t message) { struct usb_serial_port *port = serial->port[0]; @@ -841,7 +831,6 @@ static struct usb_serial_driver f81232_device = { .process_read_urb = f81232_process_read_urb, .read_int_callback =f81232_read_int_callback, .port_probe = f81232_port_probe, - .port_remove = f81232_port_remove, .suspend = f81232_suspend, .resume = f81232_resume, }; -- 2.17.1
[PATCH V2 1/7] USB: serial: f81232: Extract LSR handler
Extract LSR handler to function. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 53 + 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 43fa1f0716b7..c07d376c743d 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -322,10 +322,38 @@ static void f81232_read_int_callback(struct urb *urb) __func__, retval); } +static char f81232_handle_lsr(struct usb_serial_port *port, u8 lsr) +{ + struct f81232_private *priv = usb_get_serial_port_data(port); + char tty_flag = TTY_NORMAL; + + if (!(lsr & UART_LSR_BRK_ERROR_BITS)) + return tty_flag; + + if (lsr & UART_LSR_BI) { + tty_flag = TTY_BREAK; + port->icount.brk++; + usb_serial_handle_break(port); + } else if (lsr & UART_LSR_PE) { + tty_flag = TTY_PARITY; + port->icount.parity++; + } else if (lsr & UART_LSR_FE) { + tty_flag = TTY_FRAME; + port->icount.frame++; + } + + if (lsr & UART_LSR_OE) { + port->icount.overrun++; + schedule_work(>lsr_work); + tty_insert_flip_char(>port, 0, TTY_OVERRUN); + } + + return tty_flag; +} + static void f81232_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct f81232_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; char tty_flag; unsigned int i; @@ -341,29 +369,8 @@ static void f81232_process_read_urb(struct urb *urb) /* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */ for (i = 0; i < urb->actual_length; i += 2) { - tty_flag = TTY_NORMAL; lsr = data[i]; - - if (lsr & UART_LSR_BRK_ERROR_BITS) { - if (lsr & UART_LSR_BI) { - tty_flag = TTY_BREAK; - port->icount.brk++; - usb_serial_handle_break(port); - } else if (lsr & UART_LSR_PE) { - tty_flag = TTY_PARITY; - port->icount.parity++; - } else if (lsr & UART_LSR_FE) { - tty_flag = TTY_FRAME; - port->icount.frame++; - } - - if (lsr & UART_LSR_OE) { - port->icount.overrun++; - schedule_work(>lsr_work); - tty_insert_flip_char(>port, 0, - TTY_OVERRUN); - } - } + tty_flag = f81232_handle_lsr(port, lsr); if (port->port.console && port->sysrq) { if (usb_serial_handle_sysrq_char(port, data[i + 1])) -- 2.17.1
[PATCH V2 0/7] Add Fintek F81534A series usb-to-serial driver
The Fintek F81532A/534A/535/536 is USB-to-2/4/8/12 serial ports device and the serial port is default disabled when plugin computer. The part number is a bit same with F81532/534, but F81534A series UART core is enhanced from F81232, not F81532/534. The IC is contains devices as following: 1. HUB (all devices is connected with this hub) 2. GPIO/Control device. (enable serial port and control all GPIOs) 3. serial port 1 to x (2/4/8/12) It's most same with F81232, the UART device is difference as follow: 1. TX/RX bulk size is 128/512bytes 2. RX bulk layout change: F81232: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... F81534A:[LEN][Data.][LSR] We'll try to do some code refacting before add F81534A series. Ji-Ze Hong (Peter Hong) (7): USB: serial: f81232: Extract LSR handler USB: serial: f81232: Add tx_empty function USB: serial: f81232: Use devm_kzalloc USB: serial: f81232: Add F81534A support USB: serial: f81232: Set F81534A serial port with RS232 mode USB: serial: f81232: Add generator for F81534A USB: serial: f81232: Add gpiolib to GPIO device drivers/usb/serial/f81232.c | 604 ++-- 1 file changed, 570 insertions(+), 34 deletions(-) -- 2.17.1
[PATCH V2 6/7] USB: serial: f81232: Add generator for F81534A
The Fintek F81534A series is contains 1 HUB / 1 GPIO device / n UARTs, but the UART is default disable and need enabled by GPIO device(2c42/16F8). When F81534A plug to host, we can only see 1 HUB & 1 GPIO device and we need write 0x8fff to GPIO device register F81534A_CMD_ENABLE_PORT(116h) to enable all available serial ports. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 135 +++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 01cb5a5ea1d2..82cc1e6cff62 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -36,6 +36,9 @@ { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \ { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */ +#define F81534A_CTRL_ID\ + { USB_DEVICE(0x2c42, 0x16f8) } /* Global control device */ + static const struct usb_device_id id_table[] = { F81232_ID, { } /* Terminating entry */ @@ -46,6 +49,11 @@ static const struct usb_device_id f81534a_id_table[] = { { } /* Terminating entry */ }; +static const struct usb_device_id f81534a_ctrl_id_table[] = { + F81534A_CTRL_ID, + { } /* Terminating entry */ +}; + static const struct usb_device_id all_serial_id_table[] = { F81232_ID, F81534A_SERIES_ID, @@ -105,6 +113,8 @@ MODULE_DEVICE_TABLE(usb, all_serial_id_table); #define F81534A_GPIO_MODE1_OUTPUT BIT(1) #define F81534A_GPIO_MODE0_OUTPUT BIT(0) +#define F81534A_CMD_ENABLE_PORT0x116 + struct f81232_private { struct mutex lock; u8 modem_control; @@ -853,6 +863,95 @@ static void f81232_lsr_worker(struct work_struct *work) dev_warn(>dev, "read LSR failed: %d\n", status); } +static int f81534a_ctrl_set_register(struct usb_device *dev, u16 reg, u16 size, + void *val) +{ + int retry = F81534A_ACCESS_REG_RETRY; + int status; + u8 *tmp; + + tmp = kmalloc(size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + memcpy(tmp, val, size); + + while (retry--) { + status = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + F81534A_REGISTER_REQUEST, + F81534A_SET_REGISTER, + reg, + 0, + tmp, + size, + USB_CTRL_SET_TIMEOUT); + if (status != size) { + status = usb_translate_errors(status); + if (status == -EIO) + continue; + + status = -EIO; + } else { + status = 0; + } + + break; + } + + if (status) { + dev_err(>dev, "set ctrl reg: %x, failed status: %d\n", reg, + status); + } + + kfree(tmp); + return status; +} + +static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev(intf); + unsigned char enable[2]; + int status; + + /* enable all available serial ports */ + enable[0] = 0xff; + enable[1] = 0x8f; + + status = f81534a_ctrl_set_register(dev, F81534A_CMD_ENABLE_PORT, + sizeof(enable), enable); + if (status) + dev_warn(>dev, "set CMD_ENABLE_PORT failed: %d\n", status); + + return status; +} + +static int f81534a_ctrl_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int status; + + status = f81534a_ctrl_enable_all_ports(intf); + if (status) + return status; + + dev = usb_get_dev(dev); + return 0; +} + +static void f81534a_ctrl_disconnect(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev(intf); + + usb_put_dev(dev); +} + +static int f81534a_ctrl_resume(struct usb_interface *intf) +{ + return f81534a_ctrl_enable_all_ports(intf); +} + static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; @@ -980,7 +1079,41 @@ static struct usb_serial_driver * const serial_drivers[] = { NULL, }; -module_usb_serial_driver(serial_drivers, all_serial_id_table); +static struct usb_driver f81534a_ctrl_driver = { + .name = "f81534a_ctrl", + .id_table = f81534a_ctrl
[PATCH V2 5/7] USB: serial: f81232: Set F81534A serial port with RS232 mode
The Fintek F81532A/534A/535/536 is USB-to-2/4/8/12 serial ports device and the serial ports are default disabled. Each port contains max 3 pins GPIO and the 3 pins are default pull high with input mode. When the serial port had activated (running probe()), we'll transform the 3 pins from GPIO function publicly to control Tranceiver privately use. We'll default set to 0/0/1 for control transceiver to RS232 mode. Otherwise, If the serial port is not active, the 3 pins is in GPIO mode and controlled by global GPIO device with VID/PID: 2c42/16f8. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 17 + 1 file changed, 17 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 36a17aedc2ae..01cb5a5ea1d2 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -96,6 +96,15 @@ MODULE_DEVICE_TABLE(usb, all_serial_id_table); #define F81534A_TRIGGER_MULTPILE_4XBIT(3) #define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) +/* Serial port self GPIO control, 2bytes [control data][input data] */ +#define F81534A_GPIO_REG 0x10e +#define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */ +#define F81534A_GPIO_MODE1_DIR BIT(5) +#define F81534A_GPIO_MODE0_DIR BIT(4) +#define F81534A_GPIO_MODE2_OUTPUT BIT(2) +#define F81534A_GPIO_MODE1_OUTPUT BIT(1) +#define F81534A_GPIO_MODE0_OUTPUT BIT(0) + struct f81232_private { struct mutex lock; u8 modem_control; @@ -866,6 +875,14 @@ static int f81232_port_probe(struct usb_serial_port *port) static int f81534a_port_probe(struct usb_serial_port *port) { + int status; + + /* tri-state with pull-high, default RS232 Mode */ + status = f81232_set_register(port, F81534A_GPIO_REG, + F81534A_GPIO_MODE2_DIR); + if (status) + return status; + return f81232_port_probe(port); } -- 2.17.1
[PATCH V2 2/7] USB: serial: f81232: Add tx_empty function
Add tx_empty() function for F81232. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 18 ++ 1 file changed, 18 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index c07d376c743d..b42b3738a768 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -685,6 +685,23 @@ static void f81232_dtr_rts(struct usb_serial_port *port, int on) f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); } +static bool f81232_tx_empty(struct usb_serial_port *port) +{ + int status; + u8 tmp; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, ); + if (status) { + dev_err(>dev, "get LSR status failed: %d\n", status); + return false; + } + + if ((tmp & UART_LSR_TEMT) != UART_LSR_TEMT) + return false; + + return true; +} + static int f81232_carrier_raised(struct usb_serial_port *port) { u8 msr; @@ -820,6 +837,7 @@ static struct usb_serial_driver f81232_device = { .tiocmget = f81232_tiocmget, .tiocmset = f81232_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, + .tx_empty = f81232_tx_empty, .process_read_urb = f81232_process_read_urb, .read_int_callback =f81232_read_int_callback, .port_probe = f81232_port_probe, -- 2.17.1
Re: [PATCH V1 3/6] USB: serial: f81232: Add generator for F81534A
Hi Johan, Johan Hovold 於 2019/8/28 下午 11:02 寫道: On Thu, Jun 06, 2019 at 10:54:13AM +0800, Ji-Ze Hong (Peter Hong) wrote: The Fintek F81534A series is contains 1 HUB / 1 GPIO device / n UARTs, but the UART is default disable and need enabled by GPIO device(2c42/16F8). When F81534A plug to host, we can only see 1 HUB & 1 GPIO device, add GPIO device USB interface to device_list and trigger generate worker, f81534a_generate_worker to run f81534a_ctrl_generate_ports(). The operation in f81534a_ctrl_generate_ports() as following: 1: Write 0x8fff to F81534A_CMD_ENABLE_PORT register for enable all UART device. 2: Read port existence & current status from F81534A_CMD_PORT_STATUS register. the higher 16bit will indicate the UART existence. If the UART is existence, we'll check it GPIO mode as long as not default value (default is all input mode). 3: 1 GPIO device will check with max 15s and check next GPIO device when timeout. (F81534A_CTRL_RETRY * F81534A_CTRL_TIMER) Signed-off-by: Ji-Ze Hong (Peter Hong) This is all looks crazy... Please better describe how the device works, and you want to implement support. I'll try to refactor more simply for first add into kernel. --- drivers/usb/serial/f81232.c | 356 +++- 1 file changed, 355 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 75dfc0b9ef30..e9470fb0d691 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -41,6 +41,12 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +static const struct usb_device_id f81534a_ctrl_id_table[] = { + { USB_DEVICE(0x2c42, 0x16f8) }, /* Global control device */ + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, f81534a_ctrl_id_table); You can only have one MODULE_DEVICE_TABLE()... I had a question about this. In this file, we'll need support 3 sets of id f81232(1)/f81534a(9)/f81534a_ctrl(1). So I will refactor the code about id section to the below due to the id table will use more than once: === #define F81232_ID \ { USB_DEVICE(0x1934, 0x0706) } /* 1 port UART device */ #define F81534A_SERIES_ID \ { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \ { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \ { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \ { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \ { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \ { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \ { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \ { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \ { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */ #define F81534A_CTRL_ID \ { USB_DEVICE(0x2c42, 0x16f8) } /* Global control device */ static const struct usb_device_id id_table[] = { F81232_ID, { } /* Terminating entry */ }; static const struct usb_device_id f81534a_id_table[] = { F81534A_SERIES_ID, { } /* Terminating entry */ }; static const struct usb_device_id f81534a_ctrl_id_table[] = { F81534A_CTRL_ID, { } /* Terminating entry */ }; static const struct usb_device_id all_serial_id_table[] = { F81232_ID, F81534A_SERIES_ID, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, all_serial_id_table); === but the checkpatch.pl give me the warning below: ERROR: Macros with complex values should be enclosed in parentheses #42: FILE: f81232.c:28: +#define F81534A_SERIES_ID \ + { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \ + { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */ Is any suggestion ? Thanks -- With Best Regards, Peter Hong
Re: [PATCH V1 1/1] serial: 8250_pci: Add F81504A series Support
Hi, Greg KH 於 2019/8/23 上午 05:15 寫道: Andy Shevchenko 於 2019/8/16 下午 07:26 寫道: We have 8250_fintek. Isn't it a right place to add these? The 8250_fintek implements PNP device with id PNP0501. Should I also implements PCIe device in this file? Does it use the same logic? If so, that makes sense, but if you can not share anything, then no, it does not make sense. It's same with old series F81504/508/512 and the old series had implement in 8250_pci.c (pbn_fintek_4/pbn_fintek_8/pbn_fintek_12). So I decide implements the new series in 8250_pci.c Thanks -- With Best Regards, Peter Hong
Re: [PATCH V1 1/1] serial: 8250_pci: Add F81504A series Support
Hi, Andy Shevchenko 於 2019/8/16 下午 07:26 寫道: On Fri, Aug 16, 2019 at 01:27:29PM +0800, Ji-Ze Hong (Peter Hong) wrote: Fintek F81504A/508A/512A is PCIE to 4/8/12 UARTs device. It's support IO/MMIO/PCIE conf to access all functions. The old F81504/508/512 is only support IO. We have 8250_fintek. Isn't it a right place to add these? The 8250_fintek implements PNP device with id PNP0501. Should I also implements PCIe device in this file? -- With Best Regards, Peter Hong
[PATCH V1 1/1] serial: 8250_pci: Add F81504A series Support
Fintek F81504A/508A/512A is PCIE to 4/8/12 UARTs device. It's support IO/MMIO/PCIE conf to access all functions. The old F81504/508/512 is only support IO. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/tty/serial/8250/8250_pci.c | 121 + 1 file changed, 121 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index c0d10a35bf70..80737862bbef 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -43,6 +43,11 @@ struct pci_serial_quirk { void(*exit)(struct pci_dev *dev); }; +struct f815xxa_data { + spinlock_t lock; + int idx; +}; + #define PCI_NUM_BAR_RESOURCES 6 struct serial_private { @@ -1707,6 +1712,77 @@ static int pci_fintek_init(struct pci_dev *dev) return max_port; } +static void f815xxa_mem_serial_out(struct uart_port *p, int offset, int value) +{ + struct f815xxa_data *data = p->private_data; + unsigned long flags; + + spin_lock_irqsave(>lock, flags); + writeb(value, p->membase + offset); + readb(p->membase + UART_SCR); /* Dummy read for flush pcie tx queue */ + spin_unlock_irqrestore(>lock, flags); +} + +static int pci_fintek_f815xxa_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + struct pci_dev *pdev = priv->dev; + struct f815xxa_data *data; + + data = devm_kzalloc(>dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->idx = idx; + spin_lock_init(>lock); + + port->port.private_data = data; + port->port.iotype = UPIO_MEM; + port->port.flags |= UPF_IOREMAP; + port->port.mapbase = pci_resource_start(pdev, 0) + 8 * idx; + port->port.serial_out = f815xxa_mem_serial_out; + + return 0; +} + +static int pci_fintek_f815xxa_init(struct pci_dev *dev) +{ + u32 max_port, i; + int config_base; + + if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) + return -ENODEV; + + switch (dev->device) { + case 0x1204: /* 4 ports */ + case 0x1208: /* 8 ports */ + max_port = dev->device & 0xff; + break; + case 0x1212: /* 12 ports */ + max_port = 12; + break; + default: + return -EINVAL; + } + + /* Set to mmio decode */ + pci_write_config_byte(dev, 0x209, 0x40); + + for (i = 0; i < max_port; ++i) { + /* UART0 configuration offset start from 0x2A0 */ + config_base = 0x2A0 + 0x08 * i; + + /* Select 128-byte FIFO and 8x FIFO threshold */ + pci_write_config_byte(dev, config_base + 0x01, 0x33); + + /* Enable UART I/O port */ + pci_write_config_byte(dev, config_base + 0, 0x01); + } + + return max_port; +} + static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) @@ -2781,6 +2857,30 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_fintek_setup, .init = pci_fintek_init, }, + { + .vendor = 0x1c29, + .device = 0x1204, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_f815xxa_setup, + .init = pci_fintek_f815xxa_init, + }, + { + .vendor = 0x1c29, + .device = 0x1208, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_f815xxa_setup, + .init = pci_fintek_f815xxa_init, + }, + { + .vendor = 0x1c29, + .device = 0x1212, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_f815xxa_setup, + .init = pci_fintek_f815xxa_init, + }, /* * Default "match everything" terminator entry @@ -2976,6 +3076,9 @@ enum pci_board_num_t { pbn_fintek_4, pbn_fintek_8, pbn_fintek_12, + pbn_fintek_F81504A, + pbn_fintek_F81508A, + pbn_fintek_F81512A, pbn_wch382_2, pbn_wch384_4, pbn_pericom_PI7C9X7951, @@ -3732,6 +3835,21 @@ static struct pciserial_board pci_boards[] = { .base_baud = 115200, .first_offset = 0x40, }, + [pbn_fintek_F81504A] = { + .num_ports = 4, +
Re: [PATCH V2 1/1] can: sja1000: f81601: add Fintek F81601 support
Hi, Saeed Mahameed 於 2019/7/24 上午 05:38 寫道: On Mon, 2019-07-22 at 14:22 +0800, Ji-Ze Hong (Peter Hong) wrote: This patch add support for Fintek PCIE to 2 CAN controller support Signed-off-by: Ji-Ze Hong (Peter Hong) --- Changelog: v2: 1: Fix comment on the spinlock with write access. 2: Use ARRAY_SIZE instead of F81601_PCI_MAX_CHAN. 3: Check the strap pin outside the loop. 4: Fix the cleanup issue in f81601_pci_add_card(). 5: Remove unused "channels" in struct f81601_pci_card. drivers/net/can/sja1000/Kconfig | 8 ++ drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/f81601.c | 215 +++ 3 files changed, 224 insertions(+) create mode 100644 drivers/net/can/sja1000/f81601.c diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index f6dc89927ece..8588323c5138 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -101,4 +101,12 @@ config CAN_TSCAN1 IRQ numbers are read from jumpers JP4 and JP5, SJA1000 IO base addresses are chosen heuristically (first that works). +config CAN_F81601 + tristate "Fintek F81601 PCIE to 2 CAN Controller" + depends on PCI + help + This driver adds support for Fintek F81601 PCIE to 2 CAN Controller. + It had internal 24MHz clock source, but it can be changed by + manufacturer. We can use modinfo to get usage for parameters. + Visit http://www.fintek.com.tw to get more information. endif diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 9253aaf9e739..6f6268543bd9 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o obj-$(CONFIG_CAN_TSCAN1) += tscan1.o +obj-$(CONFIG_CAN_F81601) += f81601.o diff --git a/drivers/net/can/sja1000/f81601.c b/drivers/net/can/sja1000/f81601.c new file mode 100644 index ..3c378de8764d --- /dev/null +++ b/drivers/net/can/sja1000/f81601.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Fintek F81601 PCIE to 2 CAN controller driver + * + * Copyright (C) 2019 Peter Hong + * Copyright (C) 2019 Linux Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sja1000.h" + +#define F81601_PCI_MAX_CHAN2 + +#define F81601_DECODE_REG 0x209 +#define F81601_IO_MODE BIT(7) +#define F81601_MEM_MODEBIT(6) +#define F81601_CFG_MODEBIT(5) +#define F81601_CAN2_INTERNAL_CLK BIT(3) +#define F81601_CAN1_INTERNAL_CLK BIT(2) +#define F81601_CAN2_EN BIT(1) +#define F81601_CAN1_EN BIT(0) + +#define F81601_TRAP_REG0x20a +#define F81601_CAN2_HAS_EN BIT(4) + +struct f81601_pci_card { + void __iomem *addr; + spinlock_t lock;/* use this spin lock only for write access */ + struct pci_dev *dev; + struct net_device *net_dev[F81601_PCI_MAX_CHAN]; +}; + +static const struct pci_device_id f81601_pci_tbl[] = { + { PCI_DEVICE(0x1c29, 0x1703) }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, f81601_pci_tbl); + +static bool internal_clk = 1; +module_param(internal_clk, bool, 0444); +MODULE_PARM_DESC(internal_clk, "Use internal clock, default 1 (24MHz)"); + +static unsigned int external_clk; +module_param(external_clk, uint, 0444); +MODULE_PARM_DESC(external_clk, "External Clock, must spec when internal_clk = 0"); + +static u8 f81601_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + port); +} + +static void f81601_pci_write_reg(const struct sja1000_priv *priv, int port, +u8 val) +{ + struct f81601_pci_card *card = priv->priv; + unsigned long flags; + + spin_lock_irqsave(>lock, flags); + writeb(val, priv->reg_base + port); + readb(priv->reg_base); + spin_unlock_irqrestore(>lock, flags); +} + +static void f81601_pci_del_card(struct pci_dev *pdev) +{ + struct f81601_pci_card *card = pci_get_drvdata(pdev); + struct net_device *dev; + int i = 0; + + for (i = 0; i < ARRAY_SIZE(card->net_dev); i++) { + dev = card->net_dev[i]; + if (!dev) + continue; + + dev_info(>dev, "%s: Removing %s\n", __func__, dev->name); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } + + pcim_iounmap(pdev, card->addr); +} + +/* Probe F81601 based device for the SJA1000 chips and register each + * available CAN ch
[PATCH V3 1/1] can: sja1000: f81601: add Fintek F81601 support
This patch add support for Fintek PCIE to 2 CAN controller support Signed-off-by: Ji-Ze Hong (Peter Hong) --- v3: 1: Fix module parameter "internal_clk" default from 1 to true. 2: Remove non-usable pcim_iounmap(). v2: 1: Fix comment on the spinlock with write access. 2: Use ARRAY_SIZE instead of F81601_PCI_MAX_CHAN. 3: Check the strap pin outside the loop. 4: Fix the cleanup issue in f81601_pci_add_card(). 5: Remove unused "channels" in struct f81601_pci_card. drivers/net/can/sja1000/Kconfig | 8 ++ drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/f81601.c | 213 +++ 3 files changed, 222 insertions(+) create mode 100644 drivers/net/can/sja1000/f81601.c diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index f6dc89927ece..8588323c5138 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -101,4 +101,12 @@ config CAN_TSCAN1 IRQ numbers are read from jumpers JP4 and JP5, SJA1000 IO base addresses are chosen heuristically (first that works). +config CAN_F81601 + tristate "Fintek F81601 PCIE to 2 CAN Controller" + depends on PCI + help + This driver adds support for Fintek F81601 PCIE to 2 CAN Controller. + It had internal 24MHz clock source, but it can be changed by + manufacturer. We can use modinfo to get usage for parameters. + Visit http://www.fintek.com.tw to get more information. endif diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 9253aaf9e739..6f6268543bd9 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o obj-$(CONFIG_CAN_TSCAN1) += tscan1.o +obj-$(CONFIG_CAN_F81601) += f81601.o diff --git a/drivers/net/can/sja1000/f81601.c b/drivers/net/can/sja1000/f81601.c new file mode 100644 index ..3d0436efead9 --- /dev/null +++ b/drivers/net/can/sja1000/f81601.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Fintek F81601 PCIE to 2 CAN controller driver + * + * Copyright (C) 2019 Peter Hong + * Copyright (C) 2019 Linux Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sja1000.h" + +#define F81601_PCI_MAX_CHAN2 + +#define F81601_DECODE_REG 0x209 +#define F81601_IO_MODE BIT(7) +#define F81601_MEM_MODEBIT(6) +#define F81601_CFG_MODEBIT(5) +#define F81601_CAN2_INTERNAL_CLK BIT(3) +#define F81601_CAN1_INTERNAL_CLK BIT(2) +#define F81601_CAN2_EN BIT(1) +#define F81601_CAN1_EN BIT(0) + +#define F81601_TRAP_REG0x20a +#define F81601_CAN2_HAS_EN BIT(4) + +struct f81601_pci_card { + void __iomem *addr; + spinlock_t lock;/* use this spin lock only for write access */ + struct pci_dev *dev; + struct net_device *net_dev[F81601_PCI_MAX_CHAN]; +}; + +static const struct pci_device_id f81601_pci_tbl[] = { + { PCI_DEVICE(0x1c29, 0x1703) }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, f81601_pci_tbl); + +static bool internal_clk = true; +module_param(internal_clk, bool, 0444); +MODULE_PARM_DESC(internal_clk, "Use internal clock, default true (24MHz)"); + +static unsigned int external_clk; +module_param(external_clk, uint, 0444); +MODULE_PARM_DESC(external_clk, "External clock when internal_clk disabled"); + +static u8 f81601_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + port); +} + +static void f81601_pci_write_reg(const struct sja1000_priv *priv, int port, +u8 val) +{ + struct f81601_pci_card *card = priv->priv; + unsigned long flags; + + spin_lock_irqsave(>lock, flags); + writeb(val, priv->reg_base + port); + readb(priv->reg_base); + spin_unlock_irqrestore(>lock, flags); +} + +static void f81601_pci_del_card(struct pci_dev *pdev) +{ + struct f81601_pci_card *card = pci_get_drvdata(pdev); + struct net_device *dev; + int i = 0; + + for (i = 0; i < ARRAY_SIZE(card->net_dev); i++) { + dev = card->net_dev[i]; + if (!dev) + continue; + + dev_info(>dev, "%s: Removing %s\n", __func__, dev->name); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } +} + +/* Probe F81601 based device for the SJA1000 chips and register each + * available CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int f
Re: [PATCH V2 1/1] can: sja1000: f81601: add Fintek F81601 support
Hi Marc, Marc Kleine-Budde 於 2019/7/22 下午 04:15 寫道: On 7/22/19 8:22 AM, Ji-Ze Hong (Peter Hong) wrote: >> +/* Probe F81601 based device for the SJA1000 chips and register each + * available CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int f81601_pci_add_card(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct sja1000_priv *priv; + struct net_device *dev; + struct f81601_pci_card *card; + int err, i, count; + u8 tmp; + + if (pcim_enable_device(pdev) < 0) { I'm missing a corresponding disable_device(). I'm using managed pcim_enable_device(), Does it need call pci_disable_device() ?? Thanks -- With Best Regards, Peter Hong
[PATCH V2 1/1] can: sja1000: f81601: add Fintek F81601 support
This patch add support for Fintek PCIE to 2 CAN controller support Signed-off-by: Ji-Ze Hong (Peter Hong) --- Changelog: v2: 1: Fix comment on the spinlock with write access. 2: Use ARRAY_SIZE instead of F81601_PCI_MAX_CHAN. 3: Check the strap pin outside the loop. 4: Fix the cleanup issue in f81601_pci_add_card(). 5: Remove unused "channels" in struct f81601_pci_card. drivers/net/can/sja1000/Kconfig | 8 ++ drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/f81601.c | 215 +++ 3 files changed, 224 insertions(+) create mode 100644 drivers/net/can/sja1000/f81601.c diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index f6dc89927ece..8588323c5138 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -101,4 +101,12 @@ config CAN_TSCAN1 IRQ numbers are read from jumpers JP4 and JP5, SJA1000 IO base addresses are chosen heuristically (first that works). +config CAN_F81601 + tristate "Fintek F81601 PCIE to 2 CAN Controller" + depends on PCI + help + This driver adds support for Fintek F81601 PCIE to 2 CAN Controller. + It had internal 24MHz clock source, but it can be changed by + manufacturer. We can use modinfo to get usage for parameters. + Visit http://www.fintek.com.tw to get more information. endif diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 9253aaf9e739..6f6268543bd9 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o obj-$(CONFIG_CAN_TSCAN1) += tscan1.o +obj-$(CONFIG_CAN_F81601) += f81601.o diff --git a/drivers/net/can/sja1000/f81601.c b/drivers/net/can/sja1000/f81601.c new file mode 100644 index ..3c378de8764d --- /dev/null +++ b/drivers/net/can/sja1000/f81601.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Fintek F81601 PCIE to 2 CAN controller driver + * + * Copyright (C) 2019 Peter Hong + * Copyright (C) 2019 Linux Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sja1000.h" + +#define F81601_PCI_MAX_CHAN2 + +#define F81601_DECODE_REG 0x209 +#define F81601_IO_MODE BIT(7) +#define F81601_MEM_MODEBIT(6) +#define F81601_CFG_MODEBIT(5) +#define F81601_CAN2_INTERNAL_CLK BIT(3) +#define F81601_CAN1_INTERNAL_CLK BIT(2) +#define F81601_CAN2_EN BIT(1) +#define F81601_CAN1_EN BIT(0) + +#define F81601_TRAP_REG0x20a +#define F81601_CAN2_HAS_EN BIT(4) + +struct f81601_pci_card { + void __iomem *addr; + spinlock_t lock;/* use this spin lock only for write access */ + struct pci_dev *dev; + struct net_device *net_dev[F81601_PCI_MAX_CHAN]; +}; + +static const struct pci_device_id f81601_pci_tbl[] = { + { PCI_DEVICE(0x1c29, 0x1703) }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, f81601_pci_tbl); + +static bool internal_clk = 1; +module_param(internal_clk, bool, 0444); +MODULE_PARM_DESC(internal_clk, "Use internal clock, default 1 (24MHz)"); + +static unsigned int external_clk; +module_param(external_clk, uint, 0444); +MODULE_PARM_DESC(external_clk, "External Clock, must spec when internal_clk = 0"); + +static u8 f81601_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + port); +} + +static void f81601_pci_write_reg(const struct sja1000_priv *priv, int port, +u8 val) +{ + struct f81601_pci_card *card = priv->priv; + unsigned long flags; + + spin_lock_irqsave(>lock, flags); + writeb(val, priv->reg_base + port); + readb(priv->reg_base); + spin_unlock_irqrestore(>lock, flags); +} + +static void f81601_pci_del_card(struct pci_dev *pdev) +{ + struct f81601_pci_card *card = pci_get_drvdata(pdev); + struct net_device *dev; + int i = 0; + + for (i = 0; i < ARRAY_SIZE(card->net_dev); i++) { + dev = card->net_dev[i]; + if (!dev) + continue; + + dev_info(>dev, "%s: Removing %s\n", __func__, dev->name); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } + + pcim_iounmap(pdev, card->addr); +} + +/* Probe F81601 based device for the SJA1000 chips and register each + * available CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int f81601_pci_add_card(struct pci_dev *pdev, + const
[RESEND PATCH V1] can: sja1000: f81601: add Fintek F81601 support
This patch add support for Fintek PCIE to 2 CAN controller support Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/net/can/sja1000/Kconfig | 8 ++ drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/f81601.c | 223 +++ 3 files changed, 232 insertions(+) create mode 100644 drivers/net/can/sja1000/f81601.c diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index f6dc89927ece..8588323c5138 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -101,4 +101,12 @@ config CAN_TSCAN1 IRQ numbers are read from jumpers JP4 and JP5, SJA1000 IO base addresses are chosen heuristically (first that works). +config CAN_F81601 + tristate "Fintek F81601 PCIE to 2 CAN Controller" + depends on PCI + help + This driver adds support for Fintek F81601 PCIE to 2 CAN Controller. + It had internal 24MHz clock source, but it can be changed by + manufacturer. We can use modinfo to get usage for parameters. + Visit http://www.fintek.com.tw to get more information. endif diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 9253aaf9e739..6f6268543bd9 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o obj-$(CONFIG_CAN_TSCAN1) += tscan1.o +obj-$(CONFIG_CAN_F81601) += f81601.o diff --git a/drivers/net/can/sja1000/f81601.c b/drivers/net/can/sja1000/f81601.c new file mode 100644 index ..1578bb837aaf --- /dev/null +++ b/drivers/net/can/sja1000/f81601.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Fintek F81601 PCIE to 2 CAN controller driver + * + * Copyright (C) 2019 Peter Hong + * Copyright (C) 2019 Linux Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sja1000.h" + +#define F81601_PCI_MAX_CHAN2 + +#define F81601_DECODE_REG 0x209 +#define F81601_IO_MODE BIT(7) +#define F81601_MEM_MODEBIT(6) +#define F81601_CFG_MODEBIT(5) +#define F81601_CAN2_INTERNAL_CLK BIT(3) +#define F81601_CAN1_INTERNAL_CLK BIT(2) +#define F81601_CAN2_EN BIT(1) +#define F81601_CAN1_EN BIT(0) + +#define F81601_TRAP_REG0x20a +#define F81601_CAN2_HAS_EN BIT(4) + +struct f81601_pci_card { + int channels; /* detected channels count */ + void __iomem *addr; + spinlock_t lock;/* for access mem io */ + struct pci_dev *dev; + struct net_device *net_dev[F81601_PCI_MAX_CHAN]; +}; + +static const struct pci_device_id f81601_pci_tbl[] = { + { PCI_DEVICE(0x1c29, 0x1703) }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, f81601_pci_tbl); + +static bool internal_clk = 1; +module_param(internal_clk, bool, 0444); +MODULE_PARM_DESC(internal_clk, "Use internal clock, default 1 (24MHz)"); + +static unsigned int external_clk; +module_param(external_clk, uint, 0444); +MODULE_PARM_DESC(external_clk, "External Clock, must spec when internal_clk = 0"); + +static u8 f81601_pci_read_reg(const struct sja1000_priv *priv, int port) +{ + return readb(priv->reg_base + port); +} + +static void f81601_pci_write_reg(const struct sja1000_priv *priv, int port, +u8 val) +{ + struct f81601_pci_card *card = priv->priv; + unsigned long flags; + + spin_lock_irqsave(>lock, flags); + writeb(val, priv->reg_base + port); + readb(priv->reg_base); + spin_unlock_irqrestore(>lock, flags); +} + +static void f81601_pci_del_card(struct pci_dev *pdev) +{ + struct f81601_pci_card *card = pci_get_drvdata(pdev); + struct net_device *dev; + int i = 0; + + for (i = 0; i < F81601_PCI_MAX_CHAN; i++) { + dev = card->net_dev[i]; + if (!dev) + continue; + + dev_info(>dev, "%s: Removing %s\n", __func__, dev->name); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } + + pcim_iounmap(pdev, card->addr); +} + +/* Probe F81601 based device for the SJA1000 chips and register each + * available CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int f81601_pci_add_card(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct sja1000_priv *priv; + struct net_device *dev; + struct f81601_pci_card *card; + int err, i; + u8 tmp; + + if (pcim_enable_device(pdev) < 0) { + dev_err(>dev, "Failed to enabl
[PATCH V1 2/6] USB: serial: f81232: Force F81534A with RS232 mode
Force F81534A series UARTs with RS232 mode in port_probe(). Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 15 +++ 1 file changed, 15 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 84efcc66aa56..75dfc0b9ef30 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -83,12 +83,22 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_F81232_TYPE 1 #define F81232_F81534A_TYPE2 +/* Serial port self GPIO control, 2bytes [control data][input data] */ +#define F81534A_GPIO_REG 0x10e +#define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */ +#define F81534A_GPIO_MODE1_DIR BIT(5) +#define F81534A_GPIO_MODE0_DIR BIT(4) +#define F81534A_GPIO_MODE2_OUTPUT BIT(2) +#define F81534A_GPIO_MODE1_OUTPUT BIT(1) +#define F81534A_GPIO_MODE0_OUTPUT BIT(0) + struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; u8 shadow_lcr; u8 device_type; + u8 gpio_mode; speed_t baud_base; struct work_struct lsr_work; struct work_struct interrupt_work; @@ -871,6 +881,11 @@ static int f81232_port_probe(struct usb_serial_port *port) switch (priv->device_type) { case F81232_F81534A_TYPE: priv->process_read_urb = f81534a_process_read_urb; + priv->gpio_mode = F81534A_GPIO_MODE2_DIR; + + /* tri-state with pull-high, default RS232 Mode */ + status = f81232_set_register(port, F81534A_GPIO_REG, + priv->gpio_mode); break; case F81232_F81232_TYPE: -- 2.7.4
[PATCH V1 5/6] USB: serial: f81232: Use devm_kzalloc
Use devm_kzalloc() to replace kzalloc(). Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 13 + 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 7d1ec8f9d168..708d85c7d822 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -1198,7 +1198,7 @@ static int f81232_port_probe(struct usb_serial_port *port) struct f81232_private *priv; int status = 0; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(>dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1234,16 +1234,6 @@ static int f81232_port_probe(struct usb_serial_port *port) return status; } -static int f81232_port_remove(struct usb_serial_port *port) -{ - struct f81232_private *priv; - - priv = usb_get_serial_port_data(port); - kfree(priv); - - return 0; -} - static int f81232_suspend(struct usb_serial *serial, pm_message_t message) { struct usb_serial_port *port = serial->port[0]; @@ -1301,7 +1291,6 @@ static struct usb_serial_driver f81232_device = { .process_read_urb = f81232_read_urb_proxy, .read_int_callback =f81232_read_int_callback, .port_probe = f81232_port_probe, - .port_remove = f81232_port_remove, .suspend = f81232_suspend, .resume = f81232_resume, }; -- 2.7.4
[PATCH V1 6/6] USB: serial: f81232: Add gpiolib to GPIO device
The Fintek F81534A series contains 3 GPIOs per UART and The max GPIOs is 12x3 = 36 GPIOs. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 210 1 file changed, 210 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 708d85c7d822..a53240bc164a 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -132,6 +133,7 @@ struct f81232_private { struct f81534a_ctrl_private { struct usb_interface *intf; + struct gpio_chip chip; struct mutex lock; int device_idx; }; @@ -1007,6 +1009,204 @@ static int f81534a_ctrl_set_register(struct usb_device *dev, u16 reg, u16 size, return status; } +static int f81534a_ctrl_set_mask_register(struct usb_device *dev, u16 reg, + u8 mask, u8 val) +{ + int status; + u8 tmp; + + status = f81534a_ctrl_get_register(dev, reg, 1, ); + if (status) + return status; + + + tmp = (tmp & ~mask) | (val & mask); + + status = f81534a_ctrl_set_register(dev, reg, 1, ); + if (status) + return status; + + return 0; +} + +#ifdef CONFIG_GPIOLIB +static int f81534a_gpio_get(struct gpio_chip *chip, unsigned int gpio_num) +{ + struct f81534a_ctrl_private *priv = gpiochip_get_data(chip); + struct usb_interface *intf = priv->intf; + struct usb_device *dev = interface_to_usbdev(intf); + int status; + u8 tmp[2]; + int set; + int idx; + + set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN; + idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN; + + status = mutex_lock_interruptible(>lock); + if (status) + return -EINTR; + + status = f81534a_ctrl_get_register(dev, F81534A_CTRL_GPIO_REG + set, + sizeof(tmp), tmp); + if (status) { + mutex_unlock(>lock); + return status; + } + + mutex_unlock(>lock); + + return !!(tmp[1] & BIT(idx)); +} + +static int f81534a_gpio_direction_in(struct gpio_chip *chip, + unsigned int gpio_num) +{ + struct f81534a_ctrl_private *priv = gpiochip_get_data(chip); + struct usb_interface *intf = priv->intf; + struct usb_device *dev = interface_to_usbdev(intf); + int status; + int set; + int idx; + u8 mask; + + set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN; + idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN; + mask = F81534A_GPIO_MODE0_DIR << idx; + + status = mutex_lock_interruptible(>lock); + if (status) + return -EINTR; + + status = f81534a_ctrl_set_mask_register(dev, F81534A_CTRL_GPIO_REG + + set, mask, mask); + if (status) { + mutex_unlock(>lock); + return status; + } + + mutex_unlock(>lock); + + return 0; +} + +static int f81534a_gpio_direction_out(struct gpio_chip *chip, +unsigned int gpio_num, int val) +{ + struct f81534a_ctrl_private *priv = gpiochip_get_data(chip); + struct usb_interface *intf = priv->intf; + struct usb_device *dev = interface_to_usbdev(intf); + int status; + int set; + int idx; + u8 mask; + u8 data; + + set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN; + idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN; + mask = (F81534A_GPIO_MODE0_DIR << idx) | BIT(idx); + data = val ? BIT(idx) : 0; + + status = mutex_lock_interruptible(>lock); + if (status) + return -EINTR; + + status = f81534a_ctrl_set_mask_register(dev, F81534A_CTRL_GPIO_REG + + set, mask, data); + if (status) { + mutex_unlock(>lock); + return status; + } + + mutex_unlock(>lock); + + return 0; +} + +static void f81534a_gpio_set(struct gpio_chip *chip, unsigned int gpio_num, + int val) +{ + f81534a_gpio_direction_out(chip, gpio_num, val); +} + +static int f81534a_gpio_get_direction(struct gpio_chip *chip, + unsigned int gpio_num) +{ + struct f81534a_ctrl_private *priv = gpiochip_get_data(chip); + struct usb_interface *intf = priv->intf; + struct usb_device *dev = interface_to_usbdev(intf); + int status; + u8 tmp[2]; + int set; + int idx; + u8 mask; + + set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN; + idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN; + mask = F81534A_GPIO_MODE0_DIR << idx; + + status = mutex_lock_interruptible(>lock); + if (status) + return -EINTR;
[PATCH V1 1/6] USB: serial: f81232: Add F81534A support
The Fintek F81532A/534A/535/536 is USB-to-2/4/8/12 serial ports device. It's most same with F81232, the UART device is difference as follow: 1. TX/RX bulk size is 128/512bytes 2. RX bulk layout change: F81232: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... F81534A:[LEN][Data.][LSR] Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 153 +--- 1 file changed, 144 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 43fa1f0716b7..84efcc66aa56 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Fintek F81232 USB to serial adaptor driver + * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver * * Copyright (C) 2012 Greg Kroah-Hartman (gre...@linuxfoundation.org) * Copyright (C) 2012 Linux Foundation @@ -22,7 +23,20 @@ #include static const struct usb_device_id id_table[] = { + /* F81232 */ { USB_DEVICE(0x1934, 0x0706) }, + + /* F81532A/534A/535/536 */ + { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ + { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ + { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ + { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ + { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ + + { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ + { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ + { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ + { USB_DEVICE(0x2c42, 0x1636) }, /* 12 port UART device */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); @@ -61,15 +75,25 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) #define F81232_CLK_MASKGENMASK(1, 0) +#define F81534A_MODE_CONF_REG 0x107 +#define F81534A_TRIGGER_MASK GENMASK(3, 2) +#define F81534A_TRIGGER_MULTPILE_4XBIT(3) +#define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) + +#define F81232_F81232_TYPE 1 +#define F81232_F81534A_TYPE2 + struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; u8 shadow_lcr; + u8 device_type; speed_t baud_base; struct work_struct lsr_work; struct work_struct interrupt_work; struct usb_serial_port *port; + void (*process_read_urb)(struct urb *urb); }; static u32 const baudrate_table[] = { 115200, 921600, 1152000, 150 }; @@ -376,6 +400,78 @@ static void f81232_process_read_urb(struct urb *urb) tty_flip_buffer_push(>port); } +static void f81534a_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct f81232_private *priv = usb_get_serial_port_data(port); + unsigned char *data = urb->transfer_buffer; + char tty_flag; + unsigned int i; + u8 lsr; + u8 len; + + if (urb->status) { + dev_err(>dev, "urb->status: %d\n", urb->status); + return; + } + + if (!urb->actual_length) { + dev_err(>dev, "urb->actual_length == 0\n"); + return; + } + + len = data[0]; + if (len != urb->actual_length) { + dev_err(>dev, "len(%d) != urb->actual_length(%d)\n", len, + urb->actual_length); + return; + } + + /* bulk-in data: [LEN][Data.][LSR] */ + tty_flag = TTY_NORMAL; + + lsr = data[len - 1]; + if (lsr & UART_LSR_BRK_ERROR_BITS) { + if (lsr & UART_LSR_BI) { + tty_flag = TTY_BREAK; + port->icount.brk++; + usb_serial_handle_break(port); + } else if (lsr & UART_LSR_PE) { + tty_flag = TTY_PARITY; + port->icount.parity++; + } else if (lsr & UART_LSR_FE) { + tty_flag = TTY_FRAME; + port->icount.frame++; + } + + if (lsr & UART_LSR_OE) { + port->icount.overrun++; + schedule_work(>lsr_work); + tty_insert_flip_char(>port, 0, TTY_OVERRUN); + } + } + + for (i = 1; i < urb->actual_length - 1; i++) { + if (port->port.console && port->sysrq) { + if (usb_serial_handle_sysrq_char(port, dat
[PATCH V1 3/6] USB: serial: f81232: Add generator for F81534A
The Fintek F81534A series is contains 1 HUB / 1 GPIO device / n UARTs, but the UART is default disable and need enabled by GPIO device(2c42/16F8). When F81534A plug to host, we can only see 1 HUB & 1 GPIO device, add GPIO device USB interface to device_list and trigger generate worker, f81534a_generate_worker to run f81534a_ctrl_generate_ports(). The operation in f81534a_ctrl_generate_ports() as following: 1: Write 0x8fff to F81534A_CMD_ENABLE_PORT register for enable all UART device. 2: Read port existence & current status from F81534A_CMD_PORT_STATUS register. the higher 16bit will indicate the UART existence. If the UART is existence, we'll check it GPIO mode as long as not default value (default is all input mode). 3: 1 GPIO device will check with max 15s and check next GPIO device when timeout. (F81534A_CTRL_RETRY * F81534A_CTRL_TIMER) Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 356 +++- 1 file changed, 355 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 75dfc0b9ef30..e9470fb0d691 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -41,6 +41,12 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +static const struct usb_device_id f81534a_ctrl_id_table[] = { + { USB_DEVICE(0x2c42, 0x16f8) }, /* Global control device */ + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, f81534a_ctrl_id_table); + /* Maximum baudrate for F81232 */ #define F81232_MAX_BAUDRATE150 #define F81232_DEF_BAUDRATE9600 @@ -49,6 +55,10 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_REGISTER_REQUEST0xa0 #define F81232_GET_REGISTER0xc0 #define F81232_SET_REGISTER0x40 +#define F81534A_REGISTER_REQUEST F81232_REGISTER_REQUEST +#define F81534A_GET_REGISTER F81232_GET_REGISTER +#define F81534A_SET_REGISTER F81232_SET_REGISTER +#define F81534A_ACCESS_REG_RETRY 2 #define SERIAL_BASE_ADDRESS0x0120 #define RECEIVE_BUFFER_REGISTER(0x00 + SERIAL_BASE_ADDRESS) @@ -83,6 +93,10 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_F81232_TYPE 1 #define F81232_F81534A_TYPE2 +#define F81534A_MAX_PORT 12 +#define F81534A_CTRL_TIMER 1000 +#define F81534A_CTRL_RETRY 15 + /* Serial port self GPIO control, 2bytes [control data][input data] */ #define F81534A_GPIO_REG 0x10e #define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */ @@ -92,6 +106,16 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81534A_GPIO_MODE1_OUTPUT BIT(1) #define F81534A_GPIO_MODE0_OUTPUT BIT(0) +#define F81534A_CMD_ENABLE_PORT0x116 +#define F81534A_CMD_PORT_STATUS0x117 + +/* + * Control device global GPIO control, + * 2bytes [control data][input data] + */ +#define F81534A_CTRL_GPIO_REG 0x1601 +#define F81534A_CTRL_GPIO_MAX_PIN 3 + struct f81232_private { struct mutex lock; u8 modem_control; @@ -106,10 +130,27 @@ struct f81232_private { void (*process_read_urb)(struct urb *urb); }; +struct f81534a_ctrl_private { + struct usb_interface *intf; + struct mutex lock; + int device_idx; +}; + +struct f81534a_device { + struct list_head list; + struct usb_interface *intf; + int check_index; + int check_retry; +}; + static u32 const baudrate_table[] = { 115200, 921600, 1152000, 150 }; static u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ, F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ }; +struct delayed_work f81534a_generate_worker; +static DEFINE_MUTEX(device_mutex); +static LIST_HEAD(device_list); + static int calc_baud_divisor(speed_t baudrate, speed_t clockrate) { if (!baudrate) @@ -859,6 +900,281 @@ static void f81232_lsr_worker(struct work_struct *work) dev_warn(>dev, "read LSR failed: %d\n", status); } +static int f81534a_ctrl_get_register(struct usb_device *dev, u16 reg, u16 size, + void *val) +{ + int retry = F81534A_ACCESS_REG_RETRY; + int status; + u8 *tmp; + + tmp = kmalloc(size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + while (retry--) { + status = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + F81534A_REGISTER_REQUEST, + F81534A_GET_REGISTER, + reg, +
[PATCH V1 4/6] USB: serial: f81232: Add tx_empty function
Add tx_empty() function for F81232 & F81534A series. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index e9470fb0d691..7d1ec8f9d168 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -850,6 +850,24 @@ static void f81232_dtr_rts(struct usb_serial_port *port, int on) f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); } +static bool f81232_tx_empty(struct usb_serial_port *port) +{ + int status; + u8 tmp; + u8 both_empty = UART_LSR_TEMT | UART_LSR_THRE; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, ); + if (status) { + dev_err(>dev, "get LSR status failed: %d\n", status); + return false; + } + + if ((tmp & both_empty) != both_empty) + return false; + + return true; +} + static int f81232_carrier_raised(struct usb_serial_port *port) { u8 msr; @@ -1279,6 +1297,7 @@ static struct usb_serial_driver f81232_device = { .tiocmget = f81232_tiocmget, .tiocmset = f81232_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, + .tx_empty = f81232_tx_empty, .process_read_urb = f81232_read_urb_proxy, .read_int_callback =f81232_read_int_callback, .port_probe = f81232_port_probe, -- 2.7.4
[PATCH V1 0/6] USB: serial: f81232: Add F81534A support
This series patches will add Fintek F81532A/534A/535/536 support and refactoring some source code. The Fintek F81532A/534A/535/536 is USB-to-2/4/8/12 serial ports device. It cotains a HUB, a GPIO device and 2/4/8/12 serial ports. The F81534A series will default enable only HUB & GPIO device when plugged and disable UARTs as default. We need control GPIO device to enable serial port with special sequence. The most serial port features of F81534A series is same with F81232. That's the difference with following: 1. More RX FIFO and cache. (128byte FIFO + max to 128bytes*4 cache) 2. up to 3MBits baudrate. 3. 3x GPIOs per port to control transceiver. 4. UART devices need enabled by GPIO device register. Ji-Ze Hong (Peter Hong) (6): USB: serial: f81232: Add F81534A support USB: serial: f81232: Force F81534A with RS232 mode USB: serial: f81232: Add generator for F81534A USB: serial: f81232: Add tx_empty function USB: serial: f81232: Use devm_kzalloc USB: serial: f81232: Add gpiolib to GPIO device drivers/usb/serial/f81232.c | 760 ++-- 1 file changed, 741 insertions(+), 19 deletions(-) -- 2.7.4
[PATCH V6 1/3] USB: serial: f81232: clear overrun flag
The F81232 will report data and LSR with bulk like following format: bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... LSR will auto clear frame/parity/break error flag when reading by H/W, but overrrun will only cleared when reading LSR. So this patch add a worker to read LSR when overrun and flush the worker on close() & suspend(). Cc: Oliver Neukum Signed-off-by: Ji-Ze Hong (Peter Hong) --- V6: 1: Add deferred_lsr_work_needed to re-trigger when f81232_resume() v5: 1: Source code base revert to v3 and remove all v4 changes. 2: Add serial->suspending check in f81232_process_read_urb() before schedule_work(>lsr_work) to avoid race condition. v4: 1: Add serial->suspending check in f81232_lsr_worker() to avoid re-trigger 2: Add port_priv-lsr_work_resched to re-trigger LSR worker v3: 1: Add flush_work(_priv->lsr_work) in f81232_suspend(). v2: 1: Add flush_work(_priv->lsr_work) in f81232_close(). drivers/usb/serial/f81232.c | 55 + 1 file changed, 55 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 0dcdcb4b2cde..600e1f4d94e0 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -41,12 +41,15 @@ MODULE_DEVICE_TABLE(usb, id_table); #define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS) #define LINE_CONTROL_REGISTER (0x03 + SERIAL_BASE_ADDRESS) #define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS) +#define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; + bool deferred_lsr_work_needed; + struct work_struct lsr_work; struct work_struct interrupt_work; struct usb_serial_port *port; }; @@ -282,6 +285,8 @@ static void f81232_read_int_callback(struct urb *urb) static void f81232_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; + struct usb_serial *serial = port->serial; + struct f81232_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; char tty_flag; unsigned int i; @@ -315,6 +320,12 @@ static void f81232_process_read_urb(struct urb *urb) if (lsr & UART_LSR_OE) { port->icount.overrun++; + + if (!serial->suspending) + schedule_work(>lsr_work); + else + priv->deferred_lsr_work_needed = true; + tty_insert_flip_char(>port, 0, TTY_OVERRUN); } @@ -556,9 +567,12 @@ static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port) static void f81232_close(struct usb_serial_port *port) { + struct f81232_private *port_priv = usb_get_serial_port_data(port); + f81232_port_disable(port); usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); + flush_work(_priv->lsr_work); } static void f81232_dtr_rts(struct usb_serial_port *port, int on) @@ -603,6 +617,21 @@ static void f81232_interrupt_work(struct work_struct *work) f81232_read_msr(priv->port); } +static void f81232_lsr_worker(struct work_struct *work) +{ + struct f81232_private *priv; + struct usb_serial_port *port; + int status; + u8 tmp; + + priv = container_of(work, struct f81232_private, lsr_work); + port = priv->port; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, ); + if (status) + dev_warn(>dev, "read LSR failed: %d\n", status); +} + static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; @@ -613,6 +642,7 @@ static int f81232_port_probe(struct usb_serial_port *port) mutex_init(>lock); INIT_WORK(>interrupt_work, f81232_interrupt_work); + INIT_WORK(>lsr_work, f81232_lsr_worker); usb_set_serial_port_data(port, priv); @@ -632,6 +662,29 @@ static int f81232_port_remove(struct usb_serial_port *port) return 0; } +static int f81232_suspend(struct usb_serial *serial, pm_message_t message) +{ + struct f81232_private *port_priv; + + port_priv = usb_get_serial_port_data(serial->port[0]); + flush_work(_priv->lsr_work); + + return 0; +} + +static int f81232_resume(struct usb_serial *serial) +{ + struct f81232_private *port_priv; + + port_priv = usb_get_serial_port_data(serial->port[0]); + if (port_priv->deferred_lsr_work_needed) { +
Re: [RESEND PATCH V3 1/3] USB: serial: f81232: clear overrun flag
Oliver Neukum 於 2019/4/1 下午 04:34 寫道: On Mo, 2019-04-01 at 14:00 +0800, Ji-Ze Hong (Peter Hong) wrote: Hi, I am afraid there is a race condiion in this code. @@ -315,6 +318,7 @@ static void f81232_process_read_urb(struct urb *urb) if (lsr & UART_LSR_OE) { port->icount.overrun++; + schedule_work(>lsr_work); Unconditionally scheduled tty_insert_flip_char(>port, 0, TTY_OVERRUN); } [..] +static int f81232_suspend(struct usb_serial *serial, pm_message_t message) +{ + struct f81232_private *port_priv; + + port_priv = usb_get_serial_port_data(serial->port[0]); + flush_work(_priv->lsr_work); + + return 0; +} + static struct usb_serial_driver f81232_device = { .driver = { .owner =THIS_MODULE, @@ -655,6 +688,7 @@ static struct usb_serial_driver f81232_device = { .read_int_callback =f81232_read_int_callback, .port_probe = f81232_port_probe, .port_remove = f81232_port_remove, + .suspend = f81232_suspend, Please have a look at: int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_serial *serial = usb_get_intfdata(intf); int i, r = 0; serial->suspending = 1; /* * serial->type->suspend() MUST return 0 in system sleep context, * otherwise, the resume callback has to recover device from * previous suspend failure. */ if (serial->type->suspend) { r = serial->type->suspend(serial, message); if (r < 0) { serial->suspending = 0; goto err_out; } } for (i = 0; i < serial->num_ports; ++i) usb_serial_port_poison_urbs(serial->port[i]); err_out: return r; } EXPORT_SYMBOL(usb_serial_suspend); As you can see, the suspend method is called first and then the URBs are poisoned. That means that after you have flushed the work, it may be submitted again. The fix would be to test the 'suspending' flag before you schedule work (and recheck the need to schedule it during resume) Thanks for report the race condition issue. It's seems the same bug in f81534.c. I'll try to fix it on f81232.c then fix f81534.c too. -- With Best Regards, Peter Hong
[PATCH V3 3/3] USB: serial: f81232: implement break control
Implement Fintek F81232 break on/off with LCR register. It's the same with 16550A LCR register layout. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 29 +++-- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index e7862997b6e4..9377b827313a 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -65,6 +65,7 @@ struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; + u8 shadow_lcr; speed_t baud_base; struct work_struct lsr_work; struct work_struct interrupt_work; @@ -377,13 +378,23 @@ static void f81232_process_read_urb(struct urb *urb) static void f81232_break_ctl(struct tty_struct *tty, int break_state) { - /* FIXME - Stubbed out for now */ + struct usb_serial_port *port = tty->driver_data; + struct f81232_private *priv = usb_get_serial_port_data(port); + int status; - /* -* break_state = -1 to turn on break, and 0 to turn off break -* see drivers/char/tty_io.c to see it used. -* last_set_data_urb_value NEVER has the break bit set in it. -*/ + mutex_lock(>lock); + + if (break_state) + priv->shadow_lcr |= UART_LCR_SBC; + else + priv->shadow_lcr &= ~UART_LCR_SBC; + + status = f81232_set_register(port, LINE_CONTROL_REGISTER, + priv->shadow_lcr); + if (status) + dev_err(>dev, "set break failed: %d\n", status); + + mutex_unlock(>lock); } static int f81232_find_clk(speed_t baudrate) @@ -519,6 +530,7 @@ static int f81232_port_disable(struct usb_serial_port *port) static void f81232_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct f81232_private *priv = usb_get_serial_port_data(port); u8 new_lcr = 0; int status = 0; speed_t baudrate; @@ -572,11 +584,16 @@ static void f81232_set_termios(struct tty_struct *tty, break; } + mutex_lock(>lock); + + new_lcr |= (priv->shadow_lcr & UART_LCR_SBC); status = f81232_set_register(port, LINE_CONTROL_REGISTER, new_lcr); if (status) { dev_err(>dev, "%s failed to set LCR: %d\n", __func__, status); } + + mutex_unlock(>lock); } static int f81232_tiocmget(struct tty_struct *tty) -- 2.7.4
[PATCH V2 2/2] watchdog: f71808e_wdt: fix F81866 bit operation
Fix error bit operation in watchdog_start() Fixes: 14b24a88a3660 ("watchdog: f71808e_wdt: Add F81866 support") Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/watchdog/f71808e_wdt.c | 14 +++--- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index bd2ced9f39f4..afd1446241b3 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -339,6 +339,7 @@ static int f71862fg_pin_configure(unsigned short ioaddr) static int watchdog_start(void) { int err; + u8 tmp; /* Make sure we don't die as soon as the watchdog is enabled below */ err = watchdog_keepalive(); @@ -388,19 +389,18 @@ static int watchdog_start(void) break; case f81866: - /* Set pin 70 to WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, - BIT(3) | BIT(0)); - superio_set_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, - BIT(2)); /* * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0. * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch: * BIT5: 0 -> WDTRST# * 1 -> GPIO15 */ - superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, - BIT(5)); + tmp = superio_inb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL); + tmp &= ~(BIT(3) | BIT(0)); + tmp |= BIT(2); + superio_outb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, tmp); + + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, 5); break; default: -- 2.7.4
[PATCH V2 1/2] watchdog: f71808e_wdt: separate declaration and assignment
Separate declaration and assignment in watchdog_start() Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/watchdog/f71808e_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 9a1c761258ce..bd2ced9f39f4 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -338,8 +338,10 @@ static int f71862fg_pin_configure(unsigned short ioaddr) static int watchdog_start(void) { + int err; + /* Make sure we don't die as soon as the watchdog is enabled below */ - int err = watchdog_keepalive(); + err = watchdog_keepalive(); if (err) return err; -- 2.7.4
Re: [PATCH V1 1/1] watchdog: f71808e_wdt: fix F81866 bit operation
Guenter Roeck 於 2019/3/22 下午 09:06 寫道: On 3/21/19 8:36 PM, Ji-Ze Hong (Peter Hong) wrote: Fix error bit operation in watchdog_start() Hmm ... does that mean it never worked ? Did you test it this time ? Sorry for lacking test procedure. I had only test the functional (reset) , not to test the register value. The F81866 PIN70 (WDTRST#/GPIO15) is default set to WDTRST# function and the old code only change register 27h bit(4) - PORT_4E_EN. If the mainboard entry port is 4Eh, the old code is equal to nothing done, but when the mainboard entry port is 2Eh, this code will make the change from entry port 2Eh to 4Eh. https://html.alldatasheet.com/html-pdf/459086/FINTEK/F81866AD/26531/119/F81866AD.html A secondary concern is that the watchdog doesn't _have_ to trigger WDTRST, but may also trigger PWOK. But that may be a separate issue. Out watchdog is only support WDTRST#. Please add: Fixes: 14b24a88a3660 ("watchdog: f71808e_wdt: Add F81866 support") OK, I'll add to v2 diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 9a1c761258ce..9129485732c7 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -387,18 +387,17 @@ static int watchdog_start(void) case f81866: /* Set pin 70 to WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, - BIT(3) | BIT(0)); - superio_set_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, - BIT(2)); + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, 3); + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, 0); + superio_set_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, 2); Better use superio_inb()/superio_outb(). The above is (much) more expensive, and we have no real idea what the impact of changing one bit at a time may be. Could I add a superio_mask_write(reg, mask, data) with v2 patch like following fintek driver ? https://elixir.bootlin.com/linux/latest/source/drivers/tty/serial/8250/8250_fintek.c#L113 Thanks -- With Best Regards, Peter Hong
[PATCH V1 1/1] watchdog: f71808e_wdt: fix F81866 bit operation
Fix error bit operation in watchdog_start() Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/watchdog/f71808e_wdt.c | 11 +-- 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 9a1c761258ce..9129485732c7 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -387,18 +387,17 @@ static int watchdog_start(void) case f81866: /* Set pin 70 to WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, - BIT(3) | BIT(0)); - superio_set_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, - BIT(2)); + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, 3); + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, 0); + superio_set_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, 2); + /* * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0. * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch: * BIT5: 0 -> WDTRST# * 1 -> GPIO15 */ - superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, - BIT(5)); + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, 5); break; default: -- 2.7.4
Re: [PATCH 3/5] USB: serial: f81232: enable remote wakeup via RX/RI pin
Hi Johan, Johan Hovold 於 2018/2/4 上午 09:46 寫道: On Thu, Feb 01, 2018 at 11:13:01AM +0800, Ji-Ze Hong (Peter Hong) wrote: Our USB-To-Serial support RI/ RX remote wakeup by Modem, Fax or other peripherals and we had tested it by following procedure with device_set_wakeup_enable() enabled: 1. Using pm-suspend to S3 2. Trigger a pulse to RI/RX to wake up system. In our test, we can do remote wakeup only with device_set_wakeup_enable() enabled. Yeah, but you need to enable it though sysfs. Not every device should be able to wake the system up. That's a decision left for user space. Should we add device_set_wakeup_capable() & device_set_wakeup_enable() like following link?? https://elixir.free-electrons.com/linux/latest/source/drivers/media/rc/mceusb.c#L1476 No, your driver should not call device_set_wakeup_enable() itself. Just set the wakeup capable flag in probe. And if you can disable the wake up feature, this needs to be done at suspend depending on what user space has requested. We'll change to device_set_wakeup_capable() and send the v2 patch when test OK. Thanks -- With Best Regards, Peter Hong
Re: [PATCH 3/5] USB: serial: f81232: enable remote wakeup via RX/RI pin
Hi Johan, Johan Hovold 於 2018/2/4 上午 09:46 寫道: On Thu, Feb 01, 2018 at 11:13:01AM +0800, Ji-Ze Hong (Peter Hong) wrote: Our USB-To-Serial support RI/ RX remote wakeup by Modem, Fax or other peripherals and we had tested it by following procedure with device_set_wakeup_enable() enabled: 1. Using pm-suspend to S3 2. Trigger a pulse to RI/RX to wake up system. In our test, we can do remote wakeup only with device_set_wakeup_enable() enabled. Yeah, but you need to enable it though sysfs. Not every device should be able to wake the system up. That's a decision left for user space. Should we add device_set_wakeup_capable() & device_set_wakeup_enable() like following link?? https://elixir.free-electrons.com/linux/latest/source/drivers/media/rc/mceusb.c#L1476 No, your driver should not call device_set_wakeup_enable() itself. Just set the wakeup capable flag in probe. And if you can disable the wake up feature, this needs to be done at suspend depending on what user space has requested. We'll change to device_set_wakeup_capable() and send the v2 patch when test OK. Thanks -- With Best Regards, Peter Hong
Re: [PATCH 5/5] USB: serial: f81232: fix bulk_in/out size
Hi Johan, Johan Hovold 於 2018/1/30 下午 12:11 寫道: On Mon, Jan 22, 2018 at 03:58:47PM +0800, Ji-Ze Hong (Peter Hong) wrote: diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index a054f69446fd..f3ee537d643c 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -769,8 +769,7 @@ static struct usb_serial_driver f81232_device = { }, .id_table = id_table, .num_ports =1, - .bulk_in_size = 256, - .bulk_out_size =256, + .bulk_out_size =16, So it seems you should really be setting bulk_in_size to 64 here (and possibly leave bulk_out_size unset) as that would appear to match your device buffer sizes. Yes, we want to set the bulk_in_size as 64. The public datasheet has some error with bulk in/out, the correct size is 64. We had test the bulk_out_size set the same with internal TX FIFO will make the best performance in tests, but it's ok to set 64. In my opinion , I'll prefer to set 16. The following information is the F81232 dump by lsusb: Bus 002 Device 007: ID 1934:0706 Feature Integration Technology Inc. (Fintek) Device Descriptor: bLength18 bDescriptorType 1 bcdUSB 1.10 bDeviceClass0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize016 idVendor 0x1934 Feature Integration Technology Inc. (Fintek) idProduct 0x0706 bcdDevice0.01 iManufacturer 1 FINTEK iProduct2 USB TO UART BRIDGE iSerial 3 88635600168801 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 39 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber0 bAlternateSetting 0 bNumEndpoints 3 bInterfaceClass 0 (Defined at Interface level) bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes3 Transfer TypeInterrupt Synch Type None Usage Type Data wMaxPacketSize 0x0010 1x 16 bytes bInterval 10 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x82 EP 2 IN bmAttributes2 Transfer TypeBulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes2 Transfer TypeBulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Device Status: 0x (Bus Powered) Thanks -- With Best Regards, Peter Hong
Re: [PATCH 5/5] USB: serial: f81232: fix bulk_in/out size
Hi Johan, Johan Hovold 於 2018/1/30 下午 12:11 寫道: On Mon, Jan 22, 2018 at 03:58:47PM +0800, Ji-Ze Hong (Peter Hong) wrote: diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index a054f69446fd..f3ee537d643c 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -769,8 +769,7 @@ static struct usb_serial_driver f81232_device = { }, .id_table = id_table, .num_ports =1, - .bulk_in_size = 256, - .bulk_out_size =256, + .bulk_out_size =16, So it seems you should really be setting bulk_in_size to 64 here (and possibly leave bulk_out_size unset) as that would appear to match your device buffer sizes. Yes, we want to set the bulk_in_size as 64. The public datasheet has some error with bulk in/out, the correct size is 64. We had test the bulk_out_size set the same with internal TX FIFO will make the best performance in tests, but it's ok to set 64. In my opinion , I'll prefer to set 16. The following information is the F81232 dump by lsusb: Bus 002 Device 007: ID 1934:0706 Feature Integration Technology Inc. (Fintek) Device Descriptor: bLength18 bDescriptorType 1 bcdUSB 1.10 bDeviceClass0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize016 idVendor 0x1934 Feature Integration Technology Inc. (Fintek) idProduct 0x0706 bcdDevice0.01 iManufacturer 1 FINTEK iProduct2 USB TO UART BRIDGE iSerial 3 88635600168801 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 39 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber0 bAlternateSetting 0 bNumEndpoints 3 bInterfaceClass 0 (Defined at Interface level) bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes3 Transfer TypeInterrupt Synch Type None Usage Type Data wMaxPacketSize 0x0010 1x 16 bytes bInterval 10 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x82 EP 2 IN bmAttributes2 Transfer TypeBulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes2 Transfer TypeBulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Device Status: 0x (Bus Powered) Thanks -- With Best Regards, Peter Hong
Re: [PATCH 3/5] USB: serial: f81232: enable remote wakeup via RX/RI pin
Hi Johan, Johan Hovold 於 2018/1/30 上午 11:57 寫道: On Mon, Jan 22, 2018 at 03:58:45PM +0800, Ji-Ze Hong (Peter Hong) wrote: The F81232 can do remote wakeup via RX/RI pin with pulse. This patch will use device_set_wakeup_enable to enable this feature. This is a policy decision that should be made by user space by setting the power/wakeup attribute, and not something that something that drivers should enable directly themselves. Perhaps you really wanted to use device_set_wakeup_capable()? But then you also need to honour the current setting in suspend() as well. How have you tested this feature? Our USB-To-Serial support RI/ RX remote wakeup by Modem, Fax or other peripherals and we had tested it by following procedure with device_set_wakeup_enable() enabled: 1. Using pm-suspend to S3 2. Trigger a pulse to RI/RX to wake up system. In our test, we can do remote wakeup only with device_set_wakeup_enable() enabled. Should we add device_set_wakeup_capable() & device_set_wakeup_enable() like following link?? https://elixir.free-electrons.com/linux/latest/source/drivers/media/rc/mceusb.c#L1476 Thanks -- With Best Regards, Peter Hong
Re: [PATCH 3/5] USB: serial: f81232: enable remote wakeup via RX/RI pin
Hi Johan, Johan Hovold 於 2018/1/30 上午 11:57 寫道: On Mon, Jan 22, 2018 at 03:58:45PM +0800, Ji-Ze Hong (Peter Hong) wrote: The F81232 can do remote wakeup via RX/RI pin with pulse. This patch will use device_set_wakeup_enable to enable this feature. This is a policy decision that should be made by user space by setting the power/wakeup attribute, and not something that something that drivers should enable directly themselves. Perhaps you really wanted to use device_set_wakeup_capable()? But then you also need to honour the current setting in suspend() as well. How have you tested this feature? Our USB-To-Serial support RI/ RX remote wakeup by Modem, Fax or other peripherals and we had tested it by following procedure with device_set_wakeup_enable() enabled: 1. Using pm-suspend to S3 2. Trigger a pulse to RI/RX to wake up system. In our test, we can do remote wakeup only with device_set_wakeup_enable() enabled. Should we add device_set_wakeup_capable() & device_set_wakeup_enable() like following link?? https://elixir.free-electrons.com/linux/latest/source/drivers/media/rc/mceusb.c#L1476 Thanks -- With Best Regards, Peter Hong
Re: [PATCH 2/5] USB: serial: f81232: add high baud rate support
Hi Andy, Andy Shevchenko 於 2018/1/22 下午 10:55 寫道: On Mon, Jan 22, 2018 at 9:58 AM, Ji-Ze Hong (Peter Hong) <hpe...@gmail.com> wrote: The F81232 had 4 clocksource 1.846/18.46/14.77/24MHz and baud rates can be up to 1.5Mbits with 24MHz. F81232 Clock registers (106h) Bit1-0: Clock source selector 00: 1.846MHz. 01: 18.46MHz. 10: 24MHz. 11: 14.77MHz. Hmm... Why not to provide a proper clk driver (based on table variant of clk-divider) and use it here? It seems too complex to use clock framework in this driver. What do you think about this, Johan ? Thanks -- With Best Regards, Peter Hong
Re: [PATCH 2/5] USB: serial: f81232: add high baud rate support
Hi Andy, Andy Shevchenko 於 2018/1/22 下午 10:55 寫道: On Mon, Jan 22, 2018 at 9:58 AM, Ji-Ze Hong (Peter Hong) wrote: The F81232 had 4 clocksource 1.846/18.46/14.77/24MHz and baud rates can be up to 1.5Mbits with 24MHz. F81232 Clock registers (106h) Bit1-0: Clock source selector 00: 1.846MHz. 01: 18.46MHz. 10: 24MHz. 11: 14.77MHz. Hmm... Why not to provide a proper clk driver (based on table variant of clk-divider) and use it here? It seems too complex to use clock framework in this driver. What do you think about this, Johan ? Thanks -- With Best Regards, Peter Hong
Re: [PATCH 1/5] USB: serial: f81232: clear overrun flag
Hi Oliver, Oliver Neukum 於 2018/1/22 下午 06:06 寫道: +static void f81232_lsr_worker(struct work_struct *work) +{ + struct f81232_private *priv; + struct usb_serial_port *port; + int status; + u8 tmp; + + priv = container_of(work, struct f81232_private, lsr_work); + port = priv->port; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, ); + if (status) + dev_warn(>dev, "read LSR failed: %d\n", status); +} Hi, I am afraid this is incomplete. You are scheduling a work that does IO. Hence you must cancel that work when the driver is unbound from the interface. You must also not do IO like this while the system is suspending. We'll add cancel worker operation on close() and suspend() to prevent from I/O operations in wrong time with next patch version. Thanks -- With Best Regards, Peter Hong
Re: [PATCH 1/5] USB: serial: f81232: clear overrun flag
Hi Oliver, Oliver Neukum 於 2018/1/22 下午 06:06 寫道: +static void f81232_lsr_worker(struct work_struct *work) +{ + struct f81232_private *priv; + struct usb_serial_port *port; + int status; + u8 tmp; + + priv = container_of(work, struct f81232_private, lsr_work); + port = priv->port; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, ); + if (status) + dev_warn(>dev, "read LSR failed: %d\n", status); +} Hi, I am afraid this is incomplete. You are scheduling a work that does IO. Hence you must cancel that work when the driver is unbound from the interface. You must also not do IO like this while the system is suspending. We'll add cancel worker operation on close() and suspend() to prevent from I/O operations in wrong time with next patch version. Thanks -- With Best Regards, Peter Hong
[PATCH 1/5] USB: serial: f81232: clear overrun flag
The F81232 will report data and LSR with bulk like following format: bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... LSR will auto clear frame/parity/break error flag when reading by H/W, but overrrun will only cleared when reading LSR. So this patch add a worker to read LSR when OE. Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81232.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 96036f87b1de..46836041c50e 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -41,12 +41,14 @@ MODULE_DEVICE_TABLE(usb, id_table); #define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS) #define LINE_CONTROL_REGISTER (0x03 + SERIAL_BASE_ADDRESS) #define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS) +#define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; + struct work_struct lsr_work; struct work_struct interrupt_work; struct usb_serial_port *port; }; @@ -282,6 +284,7 @@ static void f81232_read_int_callback(struct urb *urb) static void f81232_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; + struct f81232_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; char tty_flag; unsigned int i; @@ -315,6 +318,7 @@ static void f81232_process_read_urb(struct urb *urb) if (lsr & UART_LSR_OE) { port->icount.overrun++; + schedule_work(>lsr_work); tty_insert_flip_char(>port, 0, TTY_OVERRUN); } @@ -623,6 +627,21 @@ static void f81232_interrupt_work(struct work_struct *work) f81232_read_msr(priv->port); } +static void f81232_lsr_worker(struct work_struct *work) +{ + struct f81232_private *priv; + struct usb_serial_port *port; + int status; + u8 tmp; + + priv = container_of(work, struct f81232_private, lsr_work); + port = priv->port; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, ); + if (status) + dev_warn(>dev, "read LSR failed: %d\n", status); +} + static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; @@ -633,6 +652,7 @@ static int f81232_port_probe(struct usb_serial_port *port) mutex_init(>lock); INIT_WORK(>interrupt_work, f81232_interrupt_work); + INIT_WORK(>lsr_work, f81232_lsr_worker); usb_set_serial_port_data(port, priv); -- 2.7.4
[PATCH 1/5] USB: serial: f81232: clear overrun flag
The F81232 will report data and LSR with bulk like following format: bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... LSR will auto clear frame/parity/break error flag when reading by H/W, but overrrun will only cleared when reading LSR. So this patch add a worker to read LSR when OE. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 96036f87b1de..46836041c50e 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -41,12 +41,14 @@ MODULE_DEVICE_TABLE(usb, id_table); #define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS) #define LINE_CONTROL_REGISTER (0x03 + SERIAL_BASE_ADDRESS) #define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS) +#define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; + struct work_struct lsr_work; struct work_struct interrupt_work; struct usb_serial_port *port; }; @@ -282,6 +284,7 @@ static void f81232_read_int_callback(struct urb *urb) static void f81232_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; + struct f81232_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; char tty_flag; unsigned int i; @@ -315,6 +318,7 @@ static void f81232_process_read_urb(struct urb *urb) if (lsr & UART_LSR_OE) { port->icount.overrun++; + schedule_work(>lsr_work); tty_insert_flip_char(>port, 0, TTY_OVERRUN); } @@ -623,6 +627,21 @@ static void f81232_interrupt_work(struct work_struct *work) f81232_read_msr(priv->port); } +static void f81232_lsr_worker(struct work_struct *work) +{ + struct f81232_private *priv; + struct usb_serial_port *port; + int status; + u8 tmp; + + priv = container_of(work, struct f81232_private, lsr_work); + port = priv->port; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, ); + if (status) + dev_warn(>dev, "read LSR failed: %d\n", status); +} + static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; @@ -633,6 +652,7 @@ static int f81232_port_probe(struct usb_serial_port *port) mutex_init(>lock); INIT_WORK(>interrupt_work, f81232_interrupt_work); + INIT_WORK(>lsr_work, f81232_lsr_worker); usb_set_serial_port_data(port, priv); -- 2.7.4
[PATCH 3/5] USB: serial: f81232: enable remote wakeup via RX/RI pin
The F81232 can do remote wakeup via RX/RI pin with pulse. This patch will use device_set_wakeup_enable to enable this feature. Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81232.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index bdd7f337cd5f..dadf024ae494 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -742,6 +742,7 @@ static int f81232_port_probe(struct usb_serial_port *port) port->port.drain_delay = 256; priv->port = port; + device_set_wakeup_enable(>serial->dev->dev, true); return 0; } @@ -752,6 +753,7 @@ static int f81232_port_remove(struct usb_serial_port *port) priv = usb_get_serial_port_data(port); kfree(priv); + device_set_wakeup_enable(>serial->dev->dev, false); return 0; } -- 2.7.4
[PATCH 3/5] USB: serial: f81232: enable remote wakeup via RX/RI pin
The F81232 can do remote wakeup via RX/RI pin with pulse. This patch will use device_set_wakeup_enable to enable this feature. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index bdd7f337cd5f..dadf024ae494 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -742,6 +742,7 @@ static int f81232_port_probe(struct usb_serial_port *port) port->port.drain_delay = 256; priv->port = port; + device_set_wakeup_enable(>serial->dev->dev, true); return 0; } @@ -752,6 +753,7 @@ static int f81232_port_remove(struct usb_serial_port *port) priv = usb_get_serial_port_data(port); kfree(priv); + device_set_wakeup_enable(>serial->dev->dev, false); return 0; } -- 2.7.4
[PATCH 2/5] USB: serial: f81232: add high baud rate support
The F81232 had 4 clocksource 1.846/18.46/14.77/24MHz and baud rates can be up to 1.5Mbits with 24MHz. F81232 Clock registers (106h) Bit1-0: Clock source selector 00: 1.846MHz. 01: 18.46MHz. 10: 24MHz. 11: 14.77MHz. Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81232.c | 105 +++- 1 file changed, 94 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 46836041c50e..bdd7f337cd5f 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -28,7 +28,8 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); /* Maximum baudrate for F81232 */ -#define F81232_MAX_BAUDRATE115200 +#define F81232_MAX_BAUDRATE150 +#define F81232_DEF_BAUDRATE9600 /* USB Control EP parameter */ #define F81232_REGISTER_REQUEST0xa0 @@ -44,18 +45,42 @@ MODULE_DEVICE_TABLE(usb, id_table); #define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) +/* + * F81232 Clock registers (106h) + * + * Bit1-0: Clock source selector + * 00: 1.846MHz. + * 01: 18.46MHz. + * 10: 24MHz. + * 11: 14.77MHz. + */ +#define F81232_CLK_REGISTER0x106 +#define F81232_CLK_1_846_MHZ 0 +#define F81232_CLK_18_46_MHZ BIT(0) +#define F81232_CLK_24_MHZ BIT(1) +#define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) +#define F81232_CLK_MASKGENMASK(1, 0) + struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; + speed_t baud_base; struct work_struct lsr_work; struct work_struct interrupt_work; struct usb_serial_port *port; }; -static int calc_baud_divisor(speed_t baudrate) +static u32 const baudrate_table[] = { 115200, 921600, 1152000, 150 }; +static u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ, + F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ }; + +static int calc_baud_divisor(speed_t baudrate, speed_t clockrate) { - return DIV_ROUND_CLOSEST(F81232_MAX_BAUDRATE, baudrate); + if (!baudrate) + return 0; + + return DIV_ROUND_CLOSEST(clockrate, baudrate); } static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) @@ -129,6 +154,21 @@ static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) return status; } +static int f81232_set_mask_register(struct usb_serial_port *port, u16 reg, + u8 mask, u8 val) +{ + int status; + u8 tmp; + + status = f81232_get_register(port, reg, ); + if (status) + return status; + + tmp = (tmp & ~mask) | (val & mask); + + return f81232_set_register(port, reg, tmp); +} + static void f81232_read_msr(struct usb_serial_port *port) { int status; @@ -346,13 +386,53 @@ static void f81232_break_ctl(struct tty_struct *tty, int break_state) */ } -static void f81232_set_baudrate(struct usb_serial_port *port, speed_t baudrate) +static int f81232_find_clk(speed_t 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 void f81232_set_baudrate(struct tty_struct *tty, + struct usb_serial_port *port, speed_t baudrate, + speed_t old_baudrate) { + struct f81232_private *priv = usb_get_serial_port_data(port); u8 lcr; int divisor; int status = 0; + int i; + int idx; + speed_t baud_list[] = {baudrate, old_baudrate, F81232_DEF_BAUDRATE}; + + for (i = 0; i < ARRAY_SIZE(baud_list); ++i) { + idx = f81232_find_clk(baud_list[i]); + if (idx >= 0) { + baudrate = baud_list[i]; + tty_encode_baud_rate(tty, baudrate, baudrate); + break; + } + } - divisor = calc_baud_divisor(baudrate); + if (idx < 0) + return; + + priv->baud_base = baudrate_table[idx]; + divisor = calc_baud_divisor(baudrate, priv->baud_base); + + status = f81232_set_mask_register(port, F81232_CLK_REGISTER, + F81232_CLK_MASK, clock_table[idx]); + if (status) { + dev_err(>dev, "%s failed to set CLK_R
[PATCH 5/5] USB: serial: f81232: fix bulk_in/out size
Fix Fintek F81232 bulk_in/out size to 64/16 according to the spec. http://html.alldatasheet.com/html-pdf/406315/FINTEK/F81232/1762/8/F81232.html Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81232.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index a054f69446fd..f3ee537d643c 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -769,8 +769,7 @@ static struct usb_serial_driver f81232_device = { }, .id_table = id_table, .num_ports =1, - .bulk_in_size = 256, - .bulk_out_size =256, + .bulk_out_size =16, .open = f81232_open, .close =f81232_close, .dtr_rts = f81232_dtr_rts, -- 2.7.4
[PATCH 4/5] USB: serial: f81232: implement break control
Implement Fintek F81232 break on/off with LCR register. It's the same with 16550A LCR register layout. Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81232.c | 17 +++-- 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index dadf024ae494..a054f69446fd 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -377,13 +377,18 @@ static void f81232_process_read_urb(struct urb *urb) static void f81232_break_ctl(struct tty_struct *tty, int break_state) { - /* FIXME - Stubbed out for now */ + struct usb_serial_port *port = tty->driver_data; + struct f81232_private *priv = usb_get_serial_port_data(port); + int status; - /* -* break_state = -1 to turn on break, and 0 to turn off break -* see drivers/char/tty_io.c to see it used. -* last_set_data_urb_value NEVER has the break bit set in it. -*/ + mutex_lock(>lock); + + status = f81232_set_mask_register(port, LINE_CONTROL_REGISTER, + UART_LCR_SBC, !!break_state); + if (status) + dev_err(>dev, "set break failed: %d\n", status); + + mutex_unlock(>lock); } static int f81232_find_clk(speed_t baudrate) -- 2.7.4
[PATCH 2/5] USB: serial: f81232: add high baud rate support
The F81232 had 4 clocksource 1.846/18.46/14.77/24MHz and baud rates can be up to 1.5Mbits with 24MHz. F81232 Clock registers (106h) Bit1-0: Clock source selector 00: 1.846MHz. 01: 18.46MHz. 10: 24MHz. 11: 14.77MHz. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 105 +++- 1 file changed, 94 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 46836041c50e..bdd7f337cd5f 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -28,7 +28,8 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); /* Maximum baudrate for F81232 */ -#define F81232_MAX_BAUDRATE115200 +#define F81232_MAX_BAUDRATE150 +#define F81232_DEF_BAUDRATE9600 /* USB Control EP parameter */ #define F81232_REGISTER_REQUEST0xa0 @@ -44,18 +45,42 @@ MODULE_DEVICE_TABLE(usb, id_table); #define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) +/* + * F81232 Clock registers (106h) + * + * Bit1-0: Clock source selector + * 00: 1.846MHz. + * 01: 18.46MHz. + * 10: 24MHz. + * 11: 14.77MHz. + */ +#define F81232_CLK_REGISTER0x106 +#define F81232_CLK_1_846_MHZ 0 +#define F81232_CLK_18_46_MHZ BIT(0) +#define F81232_CLK_24_MHZ BIT(1) +#define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) +#define F81232_CLK_MASKGENMASK(1, 0) + struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; + speed_t baud_base; struct work_struct lsr_work; struct work_struct interrupt_work; struct usb_serial_port *port; }; -static int calc_baud_divisor(speed_t baudrate) +static u32 const baudrate_table[] = { 115200, 921600, 1152000, 150 }; +static u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ, + F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ }; + +static int calc_baud_divisor(speed_t baudrate, speed_t clockrate) { - return DIV_ROUND_CLOSEST(F81232_MAX_BAUDRATE, baudrate); + if (!baudrate) + return 0; + + return DIV_ROUND_CLOSEST(clockrate, baudrate); } static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) @@ -129,6 +154,21 @@ static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) return status; } +static int f81232_set_mask_register(struct usb_serial_port *port, u16 reg, + u8 mask, u8 val) +{ + int status; + u8 tmp; + + status = f81232_get_register(port, reg, ); + if (status) + return status; + + tmp = (tmp & ~mask) | (val & mask); + + return f81232_set_register(port, reg, tmp); +} + static void f81232_read_msr(struct usb_serial_port *port) { int status; @@ -346,13 +386,53 @@ static void f81232_break_ctl(struct tty_struct *tty, int break_state) */ } -static void f81232_set_baudrate(struct usb_serial_port *port, speed_t baudrate) +static int f81232_find_clk(speed_t 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 void f81232_set_baudrate(struct tty_struct *tty, + struct usb_serial_port *port, speed_t baudrate, + speed_t old_baudrate) { + struct f81232_private *priv = usb_get_serial_port_data(port); u8 lcr; int divisor; int status = 0; + int i; + int idx; + speed_t baud_list[] = {baudrate, old_baudrate, F81232_DEF_BAUDRATE}; + + for (i = 0; i < ARRAY_SIZE(baud_list); ++i) { + idx = f81232_find_clk(baud_list[i]); + if (idx >= 0) { + baudrate = baud_list[i]; + tty_encode_baud_rate(tty, baudrate, baudrate); + break; + } + } - divisor = calc_baud_divisor(baudrate); + if (idx < 0) + return; + + priv->baud_base = baudrate_table[idx]; + divisor = calc_baud_divisor(baudrate, priv->baud_base); + + status = f81232_set_mask_register(port, F81232_CLK_REGISTER, + F81232_CLK_MASK, clock_table[idx]); + if (status) { + dev_err(>dev, "%s failed to set CLK_REG: %d\n", +
[PATCH 5/5] USB: serial: f81232: fix bulk_in/out size
Fix Fintek F81232 bulk_in/out size to 64/16 according to the spec. http://html.alldatasheet.com/html-pdf/406315/FINTEK/F81232/1762/8/F81232.html Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index a054f69446fd..f3ee537d643c 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -769,8 +769,7 @@ static struct usb_serial_driver f81232_device = { }, .id_table = id_table, .num_ports =1, - .bulk_in_size = 256, - .bulk_out_size =256, + .bulk_out_size =16, .open = f81232_open, .close =f81232_close, .dtr_rts = f81232_dtr_rts, -- 2.7.4
[PATCH 4/5] USB: serial: f81232: implement break control
Implement Fintek F81232 break on/off with LCR register. It's the same with 16550A LCR register layout. Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81232.c | 17 +++-- 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index dadf024ae494..a054f69446fd 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -377,13 +377,18 @@ static void f81232_process_read_urb(struct urb *urb) static void f81232_break_ctl(struct tty_struct *tty, int break_state) { - /* FIXME - Stubbed out for now */ + struct usb_serial_port *port = tty->driver_data; + struct f81232_private *priv = usb_get_serial_port_data(port); + int status; - /* -* break_state = -1 to turn on break, and 0 to turn off break -* see drivers/char/tty_io.c to see it used. -* last_set_data_urb_value NEVER has the break bit set in it. -*/ + mutex_lock(>lock); + + status = f81232_set_mask_register(port, LINE_CONTROL_REGISTER, + UART_LCR_SBC, !!break_state); + if (status) + dev_err(>dev, "set break failed: %d\n", status); + + mutex_unlock(>lock); } static int f81232_find_clk(speed_t baudrate) -- 2.7.4
[PATCH V3 3/6] 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) <hpeter+linux_ker...@gmail.com> --- V3: 1: change reg_mask type from u16 to u8. 2: change space around "{" & "}". V2: 1: Fix for space between brace. 2: Remain the old pin control method. drivers/usb/serial/f81534.c | 67 - 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 2201be577dd3..e7dd01310f20 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_OFFSET4 #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, 150 }; 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 @@ static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data) 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, ); + 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(>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 = _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(>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) -- 2.7.4
[PATCH V3 3/6] 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) --- V3: 1: change reg_mask type from u16 to u8. 2: change space around "{" & "}". V2: 1: Fix for space between brace. 2: Remain the old pin control method. drivers/usb/serial/f81534.c | 67 - 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 2201be577dd3..e7dd01310f20 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_OFFSET4 #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, 150 }; 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 @@ static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data) 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, ); + 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(>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 = _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(>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) -- 2.7.4
[PATCH V3 2/6] 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) <hpeter+linux_ker...@gmail.com> --- V3: 1: change some BIT() operation to GENMASK(). 2: change some dev_info() to dev_dbg(). V2: 1: Read the configure data from flash and save it to shadow clock register. drivers/usb/serial/f81534.c | 30 ++ 1 file changed, 30 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 1563d3f34381..2201be577dd3 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 GENMASK(1, 0) #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) #define F81534_PORT_UNAVAILABLE\ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) +#define F81534_UART_MODE_MASK GENMASK(1, 0) + #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf @@ -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 GENMASK(2, 1) #define F81534_CLK_MASKGENMASK(2, 1) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERTBIT(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(>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_UART_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE | + F81534_CLK_RS485_INVERT; + dev_dbg(>dev, "RS485 invert mode.\n"); + break; + case F81534_PORT_CONF_RS485: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE; + dev_dbg(>dev, "RS485 mode.\n"); + break; + + default: + case F81534_PORT_CONF_RS232: + dev_dbg(>dev, "RS232 mode.\n"); + break; + } + return 0; } -- 2.7.4
[PATCH V3 2/6] 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) --- V3: 1: change some BIT() operation to GENMASK(). 2: change some dev_info() to dev_dbg(). V2: 1: Read the configure data from flash and save it to shadow clock register. drivers/usb/serial/f81534.c | 30 ++ 1 file changed, 30 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 1563d3f34381..2201be577dd3 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 GENMASK(1, 0) #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) #define F81534_PORT_UNAVAILABLE\ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) +#define F81534_UART_MODE_MASK GENMASK(1, 0) + #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf @@ -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 GENMASK(2, 1) #define F81534_CLK_MASKGENMASK(2, 1) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERTBIT(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(>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_UART_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE | + F81534_CLK_RS485_INVERT; + dev_dbg(>dev, "RS485 invert mode.\n"); + break; + case F81534_PORT_CONF_RS485: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE; + dev_dbg(>dev, "RS485 mode.\n"); + break; + + default: + case F81534_PORT_CONF_RS232: + dev_dbg(>dev, "RS232 mode.\n"); + break; + } + return 0; } -- 2.7.4
[PATCH V3 5/6] 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) <hpeter+linux_ker...@gmail.com> --- V3: 1: Separate old patch into refacting and H/W disable patches. V2: 1: f81534_check_port_hw_disabled() change return type from int to bool. 2: Add help function f81534_set_phy_port_register() / f81534_get_phy_port_register() for f81534_check_port_hw_disabled() to read register without port. 3: Re-write f81534_calc_num_ports() & f81534_attach() to reduce the f81534_check_port_hw_disabled() repeatedly called. drivers/usb/serial/f81534.c | 81 + 1 file changed, 81 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index bc0a4bd5dcec..08ef72ea2eec 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) { @@ -733,6 +747,70 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * 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, ); + 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, _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, ); + 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. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -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; -- 2.7.4
[PATCH V3 5/6] 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) --- V3: 1: Separate old patch into refacting and H/W disable patches. V2: 1: f81534_check_port_hw_disabled() change return type from int to bool. 2: Add help function f81534_set_phy_port_register() / f81534_get_phy_port_register() for f81534_check_port_hw_disabled() to read register without port. 3: Re-write f81534_calc_num_ports() & f81534_attach() to reduce the f81534_check_port_hw_disabled() repeatedly called. drivers/usb/serial/f81534.c | 81 + 1 file changed, 81 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index bc0a4bd5dcec..08ef72ea2eec 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) { @@ -733,6 +747,70 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * 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, ); + 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, _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, ); + 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. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -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; -- 2.7.4
[PATCH V3 4/6] usb: serial: f81534: refactoring calc_num_ports()
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) <hpeter+linux_ker...@gmail.com> --- V3: 1: First introduced in this series patches. drivers/usb/serial/f81534.c | 116 1 file changed, 31 insertions(+), 85 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e7dd01310f20..bc0a4bd5dcec 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 = >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(>interface->dev, + sizeof(*serial_priv), GFP_KERNEL); + if (!serial_priv) + return -ENOMEM; + + usb_set_serial_data(serial, serial_priv); + mutex_init(_priv->urb_mutex); + /* Check had custom setting */ - status = f81534_find_config_idx(serial, _idx); + status = f81534_find_config_idx(serial, _priv->setting_idx); if (status) { dev_err(>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(>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(>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(>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(>interface->dev, + "%s: phy_num: %d, tty_idx: %d\n", __func__, i, +
[PATCH V3 4/6] usb: serial: f81534: refactoring calc_num_ports()
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) --- V3: 1: First introduced in this series patches. drivers/usb/serial/f81534.c | 116 1 file changed, 31 insertions(+), 85 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e7dd01310f20..bc0a4bd5dcec 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 = >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(>interface->dev, + sizeof(*serial_priv), GFP_KERNEL); + if (!serial_priv) + return -ENOMEM; + + usb_set_serial_data(serial, serial_priv); + mutex_init(_priv->urb_mutex); + /* Check had custom setting */ - status = f81534_find_config_idx(serial, _idx); + status = f81534_find_config_idx(serial, _priv->setting_idx); if (status) { dev_err(>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(>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(>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(>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(>interface->dev, + "%s: phy_num: %d, tty_idx: %d\n", __func__, i, + serial_priv->tty_idx
[PATCH V3 1/6] 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) <hpeter+linux_ker...@gmail.com> --- V3: 1: Separate UART Enable bit from clock sources and active it in port_probe() with port_priv->shadow_clk. 2: Add sanity check for clocksource idx in set_port_config(). 3: change calc_baud_divisor() parameter from F81534_MAX_BAUDRATE to priv->baud_base. v2: 1: Add commit message for F81534_USB_TIMEOUT from 1000 to 2000 and trigger level from UART_FCR_R_TRIG_11 to UART_FCR_TRIGGER_8. 2: Separate UART Enable bit from clock sources. 3: Add help function "f81534_find_clk()" to calculate baud rate and clock source 4: Add shadow clock register for future use. drivers/usb/serial/f81534.c | 93 + 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e4573b4c8935..1563d3f34381 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_REGISTER0xA0 #define F81534_NUM_PORT4 @@ -96,7 +97,6 @@ #define F81534_CMD_READ0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE115200 #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) @@ -106,6 +106,24 @@ #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf +/* + * 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 GENMASK(2, 1) +#define F81534_CLK_MASKGENMASK(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, 150 }; +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) + re
[PATCH V3 6/6] 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) <hpeter+linux_ker...@gmail.com> --- V3: 1: had not noticeable changes. V2: 1: First introduced in this series patches. drivers/usb/serial/f81534.c | 8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 08ef72ea2eec..92c9d8f18a5a 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -131,6 +131,8 @@ #define F81534_CLK_24_MHZ BIT(2) #define F81534_CLK_14_77_MHZ GENMASK(2, 1) #define F81534_CLK_MASKGENMASK(2, 1) +#define F81534_CLK_TX_DELAY_1BIT BIT(3) + #define F81534_CLK_RS485_MODE BIT(4) #define F81534_CLK_RS485_INVERTBIT(5) @@ -1386,7 +1388,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(_priv->msr_lock); mutex_init(_priv->mcr_mutex); mutex_init(_priv->lcr_mutex); -- 2.7.4
[PATCH V3 1/6] 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) --- V3: 1: Separate UART Enable bit from clock sources and active it in port_probe() with port_priv->shadow_clk. 2: Add sanity check for clocksource idx in set_port_config(). 3: change calc_baud_divisor() parameter from F81534_MAX_BAUDRATE to priv->baud_base. v2: 1: Add commit message for F81534_USB_TIMEOUT from 1000 to 2000 and trigger level from UART_FCR_R_TRIG_11 to UART_FCR_TRIGGER_8. 2: Separate UART Enable bit from clock sources. 3: Add help function "f81534_find_clk()" to calculate baud rate and clock source 4: Add shadow clock register for future use. drivers/usb/serial/f81534.c | 93 + 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e4573b4c8935..1563d3f34381 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_REGISTER0xA0 #define F81534_NUM_PORT4 @@ -96,7 +97,6 @@ #define F81534_CMD_READ0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE115200 #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) @@ -106,6 +106,24 @@ #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf +/* + * 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 GENMASK(2, 1) +#define F81534_CLK_MASKGENMASK(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, 150 }; +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; + } + + re
[PATCH V3 6/6] 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) --- V3: 1: had not noticeable changes. V2: 1: First introduced in this series patches. drivers/usb/serial/f81534.c | 8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 08ef72ea2eec..92c9d8f18a5a 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -131,6 +131,8 @@ #define F81534_CLK_24_MHZ BIT(2) #define F81534_CLK_14_77_MHZ GENMASK(2, 1) #define F81534_CLK_MASKGENMASK(2, 1) +#define F81534_CLK_TX_DELAY_1BIT BIT(3) + #define F81534_CLK_RS485_MODE BIT(4) #define F81534_CLK_RS485_INVERTBIT(5) @@ -1386,7 +1388,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(_priv->msr_lock); mutex_init(_priv->mcr_mutex); mutex_init(_priv->lcr_mutex); -- 2.7.4
Re: [PATCH V2 1/5] usb: serial: f81534: add high baud rate support
Hi Johan, Johan Hovold 於 2018/1/10 下午 04:49 寫道: Normally, the communication with F81534 ep0 will take less than 1 sec (even only some milliseconds), but It maybe take much long time with huge loading with UART functional. We had tested it on BurnInTest, 4 ports with 921600bps + MSR status check to perform huge loading test. The worst case to read MSR register via ep0 will take 15~18 seconds. So We'll still remain the max waiting time for access ep0 with 2x10=20s in high baud rate mode. Wow, that's unfortunate. But note that your patch only doubles the timeout to 2000 ms, that is, two seconds and not twenty: -#define F81534_USB_TIMEOUT 1000 +#define F81534_USB_TIMEOUT 2000 If you really intended to increase this to twenty seconds, then please do so in a separate (preparatory) patch where you describe why that is needed (e.g. what you wrote above). In f81534_set_register()/f81534_get_register(), We'll use a while loop with 10 times to get/set register and the timeout is 1000ms. So the total minimum retry timeout is 1000x10=10s. But when introducing the high baud rate support, 10s is not enough for heavily loading. We had tested the minimum retry is 16~18s, so we enlarge the F81534_USB_TIMEOUT from 1000 to 2000 and the total minimum retry timeout is 20s. Should I separate it as 2 patches? This issue is due to introducing high baud patch. Thanks -- With Best Regards, Peter Hong
Re: [PATCH V2 1/5] usb: serial: f81534: add high baud rate support
Hi Johan, Johan Hovold 於 2018/1/10 下午 04:49 寫道: Normally, the communication with F81534 ep0 will take less than 1 sec (even only some milliseconds), but It maybe take much long time with huge loading with UART functional. We had tested it on BurnInTest, 4 ports with 921600bps + MSR status check to perform huge loading test. The worst case to read MSR register via ep0 will take 15~18 seconds. So We'll still remain the max waiting time for access ep0 with 2x10=20s in high baud rate mode. Wow, that's unfortunate. But note that your patch only doubles the timeout to 2000 ms, that is, two seconds and not twenty: -#define F81534_USB_TIMEOUT 1000 +#define F81534_USB_TIMEOUT 2000 If you really intended to increase this to twenty seconds, then please do so in a separate (preparatory) patch where you describe why that is needed (e.g. what you wrote above). In f81534_set_register()/f81534_get_register(), We'll use a while loop with 10 times to get/set register and the timeout is 1000ms. So the total minimum retry timeout is 1000x10=10s. But when introducing the high baud rate support, 10s is not enough for heavily loading. We had tested the minimum retry is 16~18s, so we enlarge the F81534_USB_TIMEOUT from 1000 to 2000 and the total minimum retry timeout is 20s. Should I separate it as 2 patches? This issue is due to introducing high baud patch. Thanks -- With Best Regards, Peter Hong
Re: [PATCH V2 5/5] usb: serial: f81534: fix tx error on some baud rate
Hi Johan, Johan Hovold 於 2018/1/9 下午 07:32 寫道: On Thu, Jan 04, 2018 at 10:29:21AM +0800, Ji-Ze Hong (Peter Hong) wrote: + /* +* 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_CLK_TX_DELAY_1BIT; You don't wan't to enable this only for the affected rates? This bit will control all transmit TX frame always delay 1 bit on enabled, but It'll transmit TX frame randomly delay 1 bit on disabled. We had tested it with BurnInTest to check the performance, It'll not make the performance regression. So we'll directly add it on all baud rate. Thanks -- With Best Regards, Peter Hong
Re: [PATCH V2 5/5] usb: serial: f81534: fix tx error on some baud rate
Hi Johan, Johan Hovold 於 2018/1/9 下午 07:32 寫道: On Thu, Jan 04, 2018 at 10:29:21AM +0800, Ji-Ze Hong (Peter Hong) wrote: + /* +* 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_CLK_TX_DELAY_1BIT; You don't wan't to enable this only for the affected rates? This bit will control all transmit TX frame always delay 1 bit on enabled, but It'll transmit TX frame randomly delay 1 bit on disabled. We had tested it with BurnInTest to check the performance, It'll not make the performance regression. So we'll directly add it on all baud rate. Thanks -- With Best Regards, Peter Hong
Re: [PATCH V2 1/5] usb: serial: f81534: add high baud rate support
Hi Johan, Johan Hovold 於 2018/1/9 下午 07:08 寫道: On Thu, Jan 04, 2018 at 10:29:17AM +0800, Ji-Ze Hong (Peter Hong) wrote: 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. Surely you meant 1 and 2 seconds respectively here? And if you have indeed measured response times close to 2000 ms then perhaps you want to add even more margin? Normally, the communication with F81534 ep0 will take less than 1 sec (even only some milliseconds), but It maybe take much long time with huge loading with UART functional. We had tested it on BurnInTest, 4 ports with 921600bps + MSR status check to perform huge loading test. The worst case to read MSR register via ep0 will take 15~18 seconds. So We'll still remain the max waiting time for access ep0 with 2x10=20s in high baud rate mode. Thanks -- With Best Regards, Peter Hong
Re: [PATCH V2 1/5] usb: serial: f81534: add high baud rate support
Hi Johan, Johan Hovold 於 2018/1/9 下午 07:08 寫道: On Thu, Jan 04, 2018 at 10:29:17AM +0800, Ji-Ze Hong (Peter Hong) wrote: 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. Surely you meant 1 and 2 seconds respectively here? And if you have indeed measured response times close to 2000 ms then perhaps you want to add even more margin? Normally, the communication with F81534 ep0 will take less than 1 sec (even only some milliseconds), but It maybe take much long time with huge loading with UART functional. We had tested it on BurnInTest, 4 ports with 921600bps + MSR status check to perform huge loading test. The worst case to read MSR register via ep0 will take 15~18 seconds. So We'll still remain the max waiting time for access ep0 with 2x10=20s in high baud rate mode. Thanks -- With Best Regards, Peter Hong
[PATCH V2 1/5] 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) <hpeter+linux_ker...@gmail.com> --- v2: 1: Add commit message for F81534_USB_TIMEOUT from 1000 to 2000 and trigger level from UART_FCR_R_TRIG_11 to UART_FCR_TRIGGER_8. 2: Separate UART Enable bit from clock sources. 3: Add help function "f81534_find_clk()" to calculate baud rate and clock source 4: Add shadow clock register for future use. drivers/usb/serial/f81534.c | 87 - 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e4573b4c8935..758ef0424164 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_REGISTER0xA0 #define F81534_NUM_PORT4 @@ -96,7 +97,6 @@ #define F81534_CMD_READ0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE115200 #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) @@ -106,6 +106,23 @@ #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf +/* + * 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 F81534_UART_EN +#define F81534_CLK_18_46_MHZ (F81534_UART_EN | BIT(1)) +#define F81534_CLK_24_MHZ (F81534_UART_EN | BIT(2)) +#define F81534_CLK_14_77_MHZ (F81534_UART_EN | BIT(1) | BIT(2)) + 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 +146,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, 150}; +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 +483,48 @@ 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;
[PATCH V2 5/5] 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) <hpeter+linux_ker...@gmail.com> --- V2: 1: First introduced in this series patches. drivers/usb/serial/f81534.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index a4666171239a..513805eeae6a 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -130,6 +130,7 @@ #define F81534_CLK_18_46_MHZ (F81534_UART_EN | BIT(1)) #define F81534_CLK_24_MHZ (F81534_UART_EN | BIT(2)) #define F81534_CLK_14_77_MHZ (F81534_UART_EN | BIT(1) | BIT(2)) +#define F81534_CLK_TX_DELAY_1BIT BIT(3) #define F81534_CLK_RS485_MODE BIT(4) #define F81534_CLK_RS485_INVERTBIT(5) @@ -1438,6 +1439,11 @@ static int f81534_port_probe(struct usb_serial_port *port) break; } + /* +* 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_CLK_TX_DELAY_1BIT; return f81534_set_port_output_pin(port); } -- 2.7.4
[PATCH V2 1/5] 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) --- v2: 1: Add commit message for F81534_USB_TIMEOUT from 1000 to 2000 and trigger level from UART_FCR_R_TRIG_11 to UART_FCR_TRIGGER_8. 2: Separate UART Enable bit from clock sources. 3: Add help function "f81534_find_clk()" to calculate baud rate and clock source 4: Add shadow clock register for future use. drivers/usb/serial/f81534.c | 87 - 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e4573b4c8935..758ef0424164 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_REGISTER0xA0 #define F81534_NUM_PORT4 @@ -96,7 +97,6 @@ #define F81534_CMD_READ0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE115200 #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) @@ -106,6 +106,23 @@ #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf +/* + * 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 F81534_UART_EN +#define F81534_CLK_18_46_MHZ (F81534_UART_EN | BIT(1)) +#define F81534_CLK_24_MHZ (F81534_UART_EN | BIT(2)) +#define F81534_CLK_14_77_MHZ (F81534_UART_EN | BIT(1) | BIT(2)) + 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 +146,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, 150}; +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 +483,48 @@ 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; + i
[PATCH V2 5/5] 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) --- V2: 1: First introduced in this series patches. drivers/usb/serial/f81534.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index a4666171239a..513805eeae6a 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -130,6 +130,7 @@ #define F81534_CLK_18_46_MHZ (F81534_UART_EN | BIT(1)) #define F81534_CLK_24_MHZ (F81534_UART_EN | BIT(2)) #define F81534_CLK_14_77_MHZ (F81534_UART_EN | BIT(1) | BIT(2)) +#define F81534_CLK_TX_DELAY_1BIT BIT(3) #define F81534_CLK_RS485_MODE BIT(4) #define F81534_CLK_RS485_INVERTBIT(5) @@ -1438,6 +1439,11 @@ static int f81534_port_probe(struct usb_serial_port *port) break; } + /* +* 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_CLK_TX_DELAY_1BIT; return f81534_set_port_output_pin(port); } -- 2.7.4
[PATCH V2 3/5] 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) <hpeter+linux_ker...@gmail.com> --- V2: 1: Fix for space between brace. 2: Remain the old pin control method. drivers/usb/serial/f81534.c | 67 - 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 8a778bc1d492..7f175f39a171 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_OFFSET4 #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 u16 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, 150}; 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 @@ static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data) 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, ); + 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) { @@ -1278,6 +1312,37 @@ static void f81534_lsr_worker(struct work_struct *work) dev_warn(>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 = _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(>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; @@ -1335,7 +1400,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) -- 2.7.4
[PATCH V2 3/5] 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) --- V2: 1: Fix for space between brace. 2: Remain the old pin control method. drivers/usb/serial/f81534.c | 67 - 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 8a778bc1d492..7f175f39a171 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_OFFSET4 #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 u16 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, 150}; 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 @@ static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data) 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, ); + 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) { @@ -1278,6 +1312,37 @@ static void f81534_lsr_worker(struct work_struct *work) dev_warn(>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 = _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(>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; @@ -1335,7 +1400,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) -- 2.7.4
[PATCH V2 4/5] 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) <hpeter+linux_ker...@gmail.com> --- V2: 1: f81534_check_port_hw_disabled() change return type from int to bool. 2: Add help function f81534_set_phy_port_register() / f81534_get_phy_port_register() for f81534_check_port_hw_disabled() to read register without port. 3: Re-write f81534_calc_num_ports() & f81534_attach() to reduce the f81534_check_port_hw_disabled() repeatedly called. drivers/usb/serial/f81534.c | 160 +++- 1 file changed, 99 insertions(+), 61 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 7f175f39a171..a4666171239a 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) { @@ -730,6 +744,70 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * 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, ); + 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, _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, ); + 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. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -750,11 +828,10 @@ 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 = >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 status; size_t i; @@ -765,8 +842,15 @@ static int f81534_calc_num_ports(struct usb_serial *serial,
[PATCH V2 4/5] 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) --- V2: 1: f81534_check_port_hw_disabled() change return type from int to bool. 2: Add help function f81534_set_phy_port_register() / f81534_get_phy_port_register() for f81534_check_port_hw_disabled() to read register without port. 3: Re-write f81534_calc_num_ports() & f81534_attach() to reduce the f81534_check_port_hw_disabled() repeatedly called. drivers/usb/serial/f81534.c | 160 +++- 1 file changed, 99 insertions(+), 61 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 7f175f39a171..a4666171239a 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) { @@ -730,6 +744,70 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * 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, ); + 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, _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, ); + 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. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -750,11 +828,10 @@ 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 = >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 status; size_t i; @@ -765,8 +842,15 @@ static int f81534_calc_num_ports(struct usb_serial *serial, return -ENODEV;
[PATCH V2 2/5] 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) <hpeter+linux_ker...@gmail.com> --- V2: 1: Read the configure data from flash and save it to shadow clock register. drivers/usb/serial/f81534.c | 34 +- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 758ef0424164..8a778bc1d492 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_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) #define F81534_PORT_UNAVAILABLE\ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) +#define F81534_UART_MODE_MASK (BIT(0) | BIT(1)) + #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf @@ -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,9 @@ #define F81534_CLK_24_MHZ (F81534_UART_EN | BIT(2)) #define F81534_CLK_14_77_MHZ (F81534_UART_EN | BIT(1) | BIT(2)) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERTBIT(5) + 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) }, @@ -517,7 +527,8 @@ static int f81534_set_port_config(struct usb_serial_port *port, } port_priv->baud_base = baudrate_table[idx]; - port_priv->shadow_clk = clock_table[idx]; + port_priv->shadow_clk &= ~F81534_CLK_14_77_MHZ; + port_priv->shadow_clk |= clock_table[idx]; status = f81534_set_port_register(port, F81534_CLOCK_REG, port_priv->shadow_clk); @@ -1269,9 +1280,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(>dev, sizeof(*port_priv), GFP_KERNEL); if (!port_priv) return -ENOMEM; @@ -1303,6 +1317,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_UART_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + port_priv->shadow_clk = F81534_CLK_RS485_MODE | + F81534_CLK_RS485_INVERT; + dev_info(>dev, "RS485 invert mode.\n"); + break; + case F81534_PORT_CONF_RS485: + port_priv->shadow_clk = F81534_CLK_RS485_MODE; + dev_info(>dev, "RS485 mode.\n"); + break; + + default: + case F81534_PORT_CONF_RS232: + dev_info(>dev, "RS232 mode.\n"); + break; + } + return 0; } -- 2.7.4
[PATCH V2 2/5] 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) --- V2: 1: Read the configure data from flash and save it to shadow clock register. drivers/usb/serial/f81534.c | 34 +- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 758ef0424164..8a778bc1d492 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_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) #define F81534_PORT_UNAVAILABLE\ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) +#define F81534_UART_MODE_MASK (BIT(0) | BIT(1)) + #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf @@ -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,9 @@ #define F81534_CLK_24_MHZ (F81534_UART_EN | BIT(2)) #define F81534_CLK_14_77_MHZ (F81534_UART_EN | BIT(1) | BIT(2)) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERTBIT(5) + 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) }, @@ -517,7 +527,8 @@ static int f81534_set_port_config(struct usb_serial_port *port, } port_priv->baud_base = baudrate_table[idx]; - port_priv->shadow_clk = clock_table[idx]; + port_priv->shadow_clk &= ~F81534_CLK_14_77_MHZ; + port_priv->shadow_clk |= clock_table[idx]; status = f81534_set_port_register(port, F81534_CLOCK_REG, port_priv->shadow_clk); @@ -1269,9 +1280,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(>dev, sizeof(*port_priv), GFP_KERNEL); if (!port_priv) return -ENOMEM; @@ -1303,6 +1317,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_UART_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + port_priv->shadow_clk = F81534_CLK_RS485_MODE | + F81534_CLK_RS485_INVERT; + dev_info(>dev, "RS485 invert mode.\n"); + break; + case F81534_PORT_CONF_RS485: + port_priv->shadow_clk = F81534_CLK_RS485_MODE; + dev_info(>dev, "RS485 mode.\n"); + break; + + default: + case F81534_PORT_CONF_RS232: + dev_info(>dev, "RS232 mode.\n"); + break; + } + return 0; } -- 2.7.4
Re: [PATCH V1 3/4] usb: serial: f81534: add output pin control
Hi Johan, In this code, I'm only read/write 3 registers of 0x2ae8, 0x2a90, 0x2a80, but some register will read/write more than once. Should I change the code from port_probe() to attach() and re-write it as: 1: read the 3 register 2: change them will 12 pin desire value 3: write it back Is it ok? Do you expect these pins to ever be changed after probe? If not, then perhaps it can be moved to attach(), but otherwise I guess they should be set at port_probe(). By using shadow registers, you should be able to reduce the number of device accesses, but perhaps it's not worth the complexity. Do you have a rough idea about how long these register updates take? I was just worried that these changes will add up to really long probe times. I had measured the time of the loop in f81534_set_port_output_pin() via getnstimeofday() with 685.410 ~ 3681.682us per port, but normally with 600~800us per port. So I prefer remain the current method of f81534_set_port_output_pin(). Is it ok? Thanks -- With Best Regards, Peter Hong
Re: [PATCH V1 3/4] usb: serial: f81534: add output pin control
Hi Johan, In this code, I'm only read/write 3 registers of 0x2ae8, 0x2a90, 0x2a80, but some register will read/write more than once. Should I change the code from port_probe() to attach() and re-write it as: 1: read the 3 register 2: change them will 12 pin desire value 3: write it back Is it ok? Do you expect these pins to ever be changed after probe? If not, then perhaps it can be moved to attach(), but otherwise I guess they should be set at port_probe(). By using shadow registers, you should be able to reduce the number of device accesses, but perhaps it's not worth the complexity. Do you have a rough idea about how long these register updates take? I was just worried that these changes will add up to really long probe times. I had measured the time of the loop in f81534_set_port_output_pin() via getnstimeofday() with 685.410 ~ 3681.682us per port, but normally with 600~800us per port. So I prefer remain the current method of f81534_set_port_output_pin(). Is it ok? Thanks -- With Best Regards, Peter Hong
Re: [PATCH V1 3/4] usb: serial: f81534: add output pin control
Hi Johan, Johan Hovold 於 2017/12/19 上午 12:06 寫道: On Thu, Nov 16, 2017 at 03:46:08PM +0800, Ji-Ze Hong (Peter Hong) wrote: +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 = _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; + } You're using 24 (get or set) accesses to update these three registers here. Why not read them out (if necessary), determine their new values and then write them back when done instead? In this code, I'm only read/write 3 registers of 0x2ae8, 0x2a90, 0x2a80, but some register will read/write more than once. Should I change the code from port_probe() to attach() and re-write it as: 1: read the 3 register 2: change them will 12 pin desire value 3: write it back Is it ok? Thanks -- With Best Regards, Peter Hong
Re: [PATCH V1 3/4] usb: serial: f81534: add output pin control
Hi Johan, Johan Hovold 於 2017/12/19 上午 12:06 寫道: On Thu, Nov 16, 2017 at 03:46:08PM +0800, Ji-Ze Hong (Peter Hong) wrote: +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 = _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; + } You're using 24 (get or set) accesses to update these three registers here. Why not read them out (if necessary), determine their new values and then write them back when done instead? In this code, I'm only read/write 3 registers of 0x2ae8, 0x2a90, 0x2a80, but some register will read/write more than once. Should I change the code from port_probe() to attach() and re-write it as: 1: read the 3 register 2: change them will 12 pin desire value 3: write it back Is it ok? Thanks -- With Best Regards, Peter Hong
[PATCH V1 2/4] 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) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81534.c | 54 +++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 76c676ef5f0d..b2d10309c335 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -102,11 +102,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_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) #define F81534_PORT_UNAVAILABLE\ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) +#define F81534_UART_MODE_MASK (BIT(0) | BIT(1)) + #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf @@ -119,6 +124,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_CLK_1_846_MHZ BIT(0) @@ -126,6 +133,9 @@ #define F81534_CLK_24_MHZ (BIT(0) | BIT(2)) #define F81534_CLK_14_77_MHZ (BIT(0) | BIT(1) | BIT(2)) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERTBIT(5) + 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) }, @@ -485,16 +495,20 @@ 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); + struct f81534_serial_private *serial_priv; u32 divisor; int status; int idx; u8 value; + u8 tmp; static u32 const baudrate_table[] = {115200, 921600, 1152000, 150}; static u8 const clock_table[] = {F81534_CLK_1_846_MHZ, F81534_CLK_14_77_MHZ, F81534_CLK_18_46_MHZ, F81534_CLK_24_MHZ}; + serial_priv = usb_get_serial_data(port->serial); + do { for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { if (baudrate <= baudrate_table[idx] && @@ -520,8 +534,25 @@ static int f81534_set_port_config(struct usb_serial_port *port, } while (1); port_priv->baud_base = baudrate_table[idx]; - status = f81534_set_port_register(port, F81534_CLOCK_REG, - clock_table[idx]); + tmp = serial_priv->conf_data[port_priv->phy_num]; + + switch (tmp & F81534_UART_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + value = F81534_CLK_RS485_MODE | F81534_CLK_RS485_INVERT; + break; + case F81534_PORT_CONF_RS485: + value = F81534_CLK_RS485_MODE; + break; + + default: + /* fall through, default RS232 Mode */ + case F81534_PORT_CONF_RS232: + value = 0; + break; + } + + value |= clock_table[idx]; + status = f81534_set_port_register(port, F81534_CLOCK_REG, value); if (status) { dev_err(>dev, "CLOCK_REG setting failed\n"); return status; @@ -1270,9 +1301,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(>dev, sizeof(*port_priv), GFP_KERNEL); if (!port_priv) return -ENOMEM; @@ -1304,6 +1338,22 @@ static int f81534_port_probe(struct usb_serial_port *port) if (ret)
[PATCH V1 2/4] 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) --- drivers/usb/serial/f81534.c | 54 +++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 76c676ef5f0d..b2d10309c335 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -102,11 +102,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_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) #define F81534_PORT_UNAVAILABLE\ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) +#define F81534_UART_MODE_MASK (BIT(0) | BIT(1)) + #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf @@ -119,6 +124,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_CLK_1_846_MHZ BIT(0) @@ -126,6 +133,9 @@ #define F81534_CLK_24_MHZ (BIT(0) | BIT(2)) #define F81534_CLK_14_77_MHZ (BIT(0) | BIT(1) | BIT(2)) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERTBIT(5) + 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) }, @@ -485,16 +495,20 @@ 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); + struct f81534_serial_private *serial_priv; u32 divisor; int status; int idx; u8 value; + u8 tmp; static u32 const baudrate_table[] = {115200, 921600, 1152000, 150}; static u8 const clock_table[] = {F81534_CLK_1_846_MHZ, F81534_CLK_14_77_MHZ, F81534_CLK_18_46_MHZ, F81534_CLK_24_MHZ}; + serial_priv = usb_get_serial_data(port->serial); + do { for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { if (baudrate <= baudrate_table[idx] && @@ -520,8 +534,25 @@ static int f81534_set_port_config(struct usb_serial_port *port, } while (1); port_priv->baud_base = baudrate_table[idx]; - status = f81534_set_port_register(port, F81534_CLOCK_REG, - clock_table[idx]); + tmp = serial_priv->conf_data[port_priv->phy_num]; + + switch (tmp & F81534_UART_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + value = F81534_CLK_RS485_MODE | F81534_CLK_RS485_INVERT; + break; + case F81534_PORT_CONF_RS485: + value = F81534_CLK_RS485_MODE; + break; + + default: + /* fall through, default RS232 Mode */ + case F81534_PORT_CONF_RS232: + value = 0; + break; + } + + value |= clock_table[idx]; + status = f81534_set_port_register(port, F81534_CLOCK_REG, value); if (status) { dev_err(>dev, "CLOCK_REG setting failed\n"); return status; @@ -1270,9 +1301,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(>dev, sizeof(*port_priv), GFP_KERNEL); if (!port_priv) return -ENOMEM; @@ -1304,6 +1338,22 @@ static int f81534_port_probe(struct usb_serial_port *port) if (ret) return ret; +
[PATCH V1 4/4] 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) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81534.c | 74 + 1 file changed, 74 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 30b966d71ae8..18bd2a478199 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -751,6 +751,74 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * 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 int 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_register(serial, + F81534_MODEM_STATUS_REG + phy * 0x10, ); + if (status) + return status; + + if ((msr & msr_mask) != msr_mask) + return 0; + + status = f81534_set_register(serial, + F81534_FIFO_CONTROL_REG + phy * 0x10, + UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + if (status) + return status; + + status = f81534_get_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, + _mcr); + if (status) + return status; + + status = f81534_set_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, + UART_MCR_LOOP); + if (status) + return status; + + status = f81534_set_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, 0x0); + if (status) + return status; + + msleep(60); + + status = f81534_get_register(serial, + F81534_LINE_STATUS_REG + phy * 0x10, ); + if (status) + return status; + + status = f81534_set_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, + old_mcr); + if (status) + return status; + + if ((lsr & UART_LSR_BI) == UART_LSR_BI) + return -ENODEV; + + return 0; +} + +/* * We had 2 generation of F81532/534 IC. All has an internal storage. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -832,6 +900,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)) + continue; + if (setting[i] & F81534_PORT_UNAVAILABLE) continue; @@ -1306,6 +1377,9 @@ static int f81534_attach(struct usb_serial *serial) /* Assign phy-to-logic mapping */ 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; -- 2.7.4
[PATCH V1 4/4] 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) --- drivers/usb/serial/f81534.c | 74 + 1 file changed, 74 insertions(+) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 30b966d71ae8..18bd2a478199 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -751,6 +751,74 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * 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 int 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_register(serial, + F81534_MODEM_STATUS_REG + phy * 0x10, ); + if (status) + return status; + + if ((msr & msr_mask) != msr_mask) + return 0; + + status = f81534_set_register(serial, + F81534_FIFO_CONTROL_REG + phy * 0x10, + UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + if (status) + return status; + + status = f81534_get_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, + _mcr); + if (status) + return status; + + status = f81534_set_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, + UART_MCR_LOOP); + if (status) + return status; + + status = f81534_set_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, 0x0); + if (status) + return status; + + msleep(60); + + status = f81534_get_register(serial, + F81534_LINE_STATUS_REG + phy * 0x10, ); + if (status) + return status; + + status = f81534_set_register(serial, + F81534_MODEM_CONTROL_REG + phy * 0x10, + old_mcr); + if (status) + return status; + + if ((lsr & UART_LSR_BI) == UART_LSR_BI) + return -ENODEV; + + return 0; +} + +/* * We had 2 generation of F81532/534 IC. All has an internal storage. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -832,6 +900,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)) + continue; + if (setting[i] & F81534_PORT_UNAVAILABLE) continue; @@ -1306,6 +1377,9 @@ static int f81534_attach(struct usb_serial *serial) /* Assign phy-to-logic mapping */ 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; -- 2.7.4
[PATCH V1 1/4] 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. 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) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81534.c | 84 - 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index cb8214860192..76c676ef5f0d 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -45,6 +45,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 @@ -61,7 +62,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_REGISTER0xA0 #define F81534_NUM_PORT4 @@ -100,7 +101,6 @@ #define F81534_CMD_READ0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE115200 #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) @@ -110,6 +110,22 @@ #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf +/* + * 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_CLK_1_846_MHZ BIT(0) +#define F81534_CLK_18_46_MHZ (BIT(0) | BIT(1)) +#define F81534_CLK_24_MHZ (BIT(0) | BIT(2)) +#define F81534_CLK_14_77_MHZ (BIT(0) | BIT(1) | BIT(2)) + 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) }, @@ -133,6 +149,7 @@ 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; @@ -464,13 +481,51 @@ 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_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 idx; u8 value; + static u32 const baudrate_table[] = {115200, 921600, 1152000, + 150}; + static u8 const clock_table[] = {F81534_CLK_1_846_MHZ, + F81534_CLK_14_77_MHZ, F81534_CLK_18_46_MHZ, + F81534_CLK_24_MHZ}; + + do { + for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { + if (baudrate <= baudrate_table[idx] && + baudrate_table[idx] % baudrate == 0) + break; + } + + /* Found acceptable baud rate */ + if (idx != ARRAY_SIZE(baudrate_table)) + break; + + if (baudrate == old_baudrate && + old_baudrate != F81534_DEFAULT_BAUD_RATE) + old_baudrate = F81534_DEFAULT_BAUD_RATE; + + dev_warn(>dev, + "baudrate: %d not supported, change to: %d\n", + baudrate, old_baudrate); + + baudrate = old_baudrate; + tty_encode_baud_rate(tty, baudrate, baudrate); + + } while (1); + + port_priv->baud_base = baudrate_table[idx]; + status = f81534_set_port_register(port, F81534_CLOCK_REG, + clock_table[idx]); + if (status) { + dev_err(>dev, "CLOCK_REG setting failed\n"); + return status; + } if (baudrate <= 1200) value = F815
[PATCH V1 1/4] 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. 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) --- drivers/usb/serial/f81534.c | 84 - 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index cb8214860192..76c676ef5f0d 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -45,6 +45,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 @@ -61,7 +62,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_REGISTER0xA0 #define F81534_NUM_PORT4 @@ -100,7 +101,6 @@ #define F81534_CMD_READ0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE115200 #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORTBIT(7) @@ -110,6 +110,22 @@ #define F81534_1X_RXTRIGGER0xc3 #define F81534_8X_RXTRIGGER0xcf +/* + * 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_CLK_1_846_MHZ BIT(0) +#define F81534_CLK_18_46_MHZ (BIT(0) | BIT(1)) +#define F81534_CLK_24_MHZ (BIT(0) | BIT(2)) +#define F81534_CLK_14_77_MHZ (BIT(0) | BIT(1) | BIT(2)) + 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) }, @@ -133,6 +149,7 @@ 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; @@ -464,13 +481,51 @@ 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_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 idx; u8 value; + static u32 const baudrate_table[] = {115200, 921600, 1152000, + 150}; + static u8 const clock_table[] = {F81534_CLK_1_846_MHZ, + F81534_CLK_14_77_MHZ, F81534_CLK_18_46_MHZ, + F81534_CLK_24_MHZ}; + + do { + for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { + if (baudrate <= baudrate_table[idx] && + baudrate_table[idx] % baudrate == 0) + break; + } + + /* Found acceptable baud rate */ + if (idx != ARRAY_SIZE(baudrate_table)) + break; + + if (baudrate == old_baudrate && + old_baudrate != F81534_DEFAULT_BAUD_RATE) + old_baudrate = F81534_DEFAULT_BAUD_RATE; + + dev_warn(>dev, + "baudrate: %d not supported, change to: %d\n", + baudrate, old_baudrate); + + baudrate = old_baudrate; + tty_encode_baud_rate(tty, baudrate, baudrate); + + } while (1); + + port_priv->baud_base = baudrate_table[idx]; + status = f81534_set_port_register(port, F81534_CLOCK_REG, + clock_table[idx]); + if (status) { + dev_err(>dev, "CLOCK_REG setting failed\n"); + return status; + } if (baudrate <= 1200) value = F81534_1X_RXTRIGGER;
[PATCH V1 3/4] 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. Register mapping for output value: Port 0: M2: 0x2ae8 bit7, M1: 0x2a90 bit5, M0/SD: 0x2a90 bit4 Port 1: M2: 0x2ae8 bit6, M1: 0x2ae8 bit0, M0/SD: 0x2ae8 bit3 Port 2: M2: 0x2a90 bit0, M1: 0x2ae8 bit2, M0/SD: 0x2a80 bit6 Port 3: M2: 0x2a90 bit3, M1: 0x2a90 bit2, M0/SD: 0x2a90 bit1 Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/usb/serial/f81534.c | 67 - 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index b2d10309c335..30b966d71ae8 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -56,6 +56,7 @@ #define F81534_CUSTOM_NO_CUSTOM_DATA 0xff #define F81534_CUSTOM_VALID_TOKEN 0xf0 #define F81534_CONF_OFFSET 1 +#define F81534_CONF_GPIO_OFFSET4 #define F81534_MAX_DATA_BLOCK 64 #define F81534_MAX_BUS_RETRY 20 @@ -166,6 +167,23 @@ struct f81534_port_private { u8 phy_num; }; +struct f81534_pin_data { + const u16 reg_addr; + const u16 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 int f81534_logic_to_phy_port(struct usb_serial *serial, struct usb_serial_port *port) { @@ -271,6 +289,22 @@ static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data) 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, ); + 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) { @@ -1299,6 +1333,37 @@ static void f81534_lsr_worker(struct work_struct *work) dev_warn(>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 = _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_info(>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; @@ -1354,7 +1419,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) -- 2.7.4
[PATCH V1 3/4] 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. Register mapping for output value: Port 0: M2: 0x2ae8 bit7, M1: 0x2a90 bit5, M0/SD: 0x2a90 bit4 Port 1: M2: 0x2ae8 bit6, M1: 0x2ae8 bit0, M0/SD: 0x2ae8 bit3 Port 2: M2: 0x2a90 bit0, M1: 0x2ae8 bit2, M0/SD: 0x2a80 bit6 Port 3: M2: 0x2a90 bit3, M1: 0x2a90 bit2, M0/SD: 0x2a90 bit1 Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/usb/serial/f81534.c | 67 - 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index b2d10309c335..30b966d71ae8 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -56,6 +56,7 @@ #define F81534_CUSTOM_NO_CUSTOM_DATA 0xff #define F81534_CUSTOM_VALID_TOKEN 0xf0 #define F81534_CONF_OFFSET 1 +#define F81534_CONF_GPIO_OFFSET4 #define F81534_MAX_DATA_BLOCK 64 #define F81534_MAX_BUS_RETRY 20 @@ -166,6 +167,23 @@ struct f81534_port_private { u8 phy_num; }; +struct f81534_pin_data { + const u16 reg_addr; + const u16 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 int f81534_logic_to_phy_port(struct usb_serial *serial, struct usb_serial_port *port) { @@ -271,6 +289,22 @@ static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data) 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, ); + 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) { @@ -1299,6 +1333,37 @@ static void f81534_lsr_worker(struct work_struct *work) dev_warn(>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 = _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_info(>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; @@ -1354,7 +1419,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) -- 2.7.4
[PATCH V1 1/1] serial: 8250_fintek: Fix crash with baud rate B0
The 8250_fintek.c is support the Fintek F81866/F81216 with dynamic clock. But It'll generate "division by zero" exception and crash in fintek_8250_set_termios() with baud rate 0 on baudrate_table[i] % baud. It can be tested with following C code: ... struct termios options; tcgetattr(fd, ); ... options.c_cflag = CS8 | CREAD; /* baud rate 0 */ tcsetattr(fd, TCSANOW, ); tcflush(fd, TCIOFLUSH); Fixes: 195638b6d44f ("serial: 8250_fintek: UART dynamic clocksource on Fintek F81866") Reported-by: Lukas Redlinger <rel+ker...@agilox.net> Cc: Lukas Redlinger <rel+ker...@agilox.net> Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/tty/serial/8250/8250_fintek.c | 11 +-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index c41cbb52f1fe..3d66c2c0d7ee 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -312,6 +312,13 @@ void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, F81866_UART_CLK_14_769MHZ, F81866_UART_CLK_18_432MHZ, F81866_UART_CLK_24MHZ }; + /* +* We'll use serial8250_do_set_termios() for baud = 0, otherwise It'll +* crash on baudrate_table[i] % baud with "division by zero". +*/ + if (!baud) + goto exit; + switch (pdata->pid) { case CHIP_ID_F81216H: reg = RS485; @@ -324,8 +331,7 @@ void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, dev_warn(port->dev, "%s: pid: %x Not support. use default set_termios.\n", __func__, pdata->pid); - serial8250_do_set_termios(port, termios, old); - return; + goto exit; } for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { @@ -353,6 +359,7 @@ void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, tty_termios_encode_baud_rate(termios, baud, baud); } +exit: serial8250_do_set_termios(port, termios, old); } -- 2.7.4
[PATCH V1 1/1] serial: 8250_fintek: Fix crash with baud rate B0
The 8250_fintek.c is support the Fintek F81866/F81216 with dynamic clock. But It'll generate "division by zero" exception and crash in fintek_8250_set_termios() with baud rate 0 on baudrate_table[i] % baud. It can be tested with following C code: ... struct termios options; tcgetattr(fd, ); ... options.c_cflag = CS8 | CREAD; /* baud rate 0 */ tcsetattr(fd, TCSANOW, ); tcflush(fd, TCIOFLUSH); Fixes: 195638b6d44f ("serial: 8250_fintek: UART dynamic clocksource on Fintek F81866") Reported-by: Lukas Redlinger Cc: Lukas Redlinger Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/tty/serial/8250/8250_fintek.c | 11 +-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index c41cbb52f1fe..3d66c2c0d7ee 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -312,6 +312,13 @@ void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, F81866_UART_CLK_14_769MHZ, F81866_UART_CLK_18_432MHZ, F81866_UART_CLK_24MHZ }; + /* +* We'll use serial8250_do_set_termios() for baud = 0, otherwise It'll +* crash on baudrate_table[i] % baud with "division by zero". +*/ + if (!baud) + goto exit; + switch (pdata->pid) { case CHIP_ID_F81216H: reg = RS485; @@ -324,8 +331,7 @@ void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, dev_warn(port->dev, "%s: pid: %x Not support. use default set_termios.\n", __func__, pdata->pid); - serial8250_do_set_termios(port, termios, old); - return; + goto exit; } for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { @@ -353,6 +359,7 @@ void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, tty_termios_encode_baud_rate(termios, baud, baud); } +exit: serial8250_do_set_termios(port, termios, old); } -- 2.7.4
[PATCH V1 1/1] serial: 8250_fintek: Fix finding base_port with activated SuperIO
The SuperIO will be configured at boot time by BIOS, but some BIOS will not deactivate the SuperIO when the end of configuration. It'll lead to mismatch for pdata->base_port in probe_setup_port(). So we'll deactivate all SuperIO before activate special base_port in fintek_8250_enter_key(). Tested on iBASE MI802. Cc: sta...@vger.kernel.org Tested-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_ker...@gmail.com> --- drivers/tty/serial/8250/8250_fintek.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 96cc45f25ee9..b34623914413 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -128,6 +128,9 @@ static int fintek_8250_enter_key(u16 base_port, u8 key) if (!request_muxed_region(base_port, 2, "8250_fintek")) return -EBUSY; + /* Force to deactive all SuperIO in this base_port */ + outb(EXIT_KEY, base_port + ADDR_PORT); + outb(key, base_port + ADDR_PORT); outb(key, base_port + ADDR_PORT); return 0; -- 2.7.4
[PATCH V1 1/1] serial: 8250_fintek: Fix finding base_port with activated SuperIO
The SuperIO will be configured at boot time by BIOS, but some BIOS will not deactivate the SuperIO when the end of configuration. It'll lead to mismatch for pdata->base_port in probe_setup_port(). So we'll deactivate all SuperIO before activate special base_port in fintek_8250_enter_key(). Tested on iBASE MI802. Cc: sta...@vger.kernel.org Tested-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Ji-Ze Hong (Peter Hong) --- drivers/tty/serial/8250/8250_fintek.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 96cc45f25ee9..b34623914413 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -128,6 +128,9 @@ static int fintek_8250_enter_key(u16 base_port, u8 key) if (!request_muxed_region(base_port, 2, "8250_fintek")) return -EBUSY; + /* Force to deactive all SuperIO in this base_port */ + outb(EXIT_KEY, base_port + ADDR_PORT); + outb(key, base_port + ADDR_PORT); outb(key, base_port + ADDR_PORT); return 0; -- 2.7.4