Re: R: R: R: [PATCH v1] serial: 8250_fintek: Print Fintek chip name

2020-12-15 Thread Ji-Ze Hong (Peter Hong)

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

2020-12-14 Thread Ji-Ze Hong (Peter Hong)

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()

2020-06-03 Thread Ji-Ze Hong (Peter Hong)

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()

2020-06-03 Thread Ji-Ze Hong (Peter Hong)
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

2020-05-27 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-09-01 Thread Ji-Ze Hong (Peter Hong)

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

2019-08-22 Thread Ji-Ze Hong (Peter Hong)

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

2019-08-18 Thread Ji-Ze Hong (Peter Hong)

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

2019-08-15 Thread Ji-Ze Hong (Peter Hong)
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

2019-07-23 Thread Ji-Ze Hong (Peter Hong)

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

2019-07-23 Thread Ji-Ze Hong (Peter Hong)
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

2019-07-22 Thread Ji-Ze Hong (Peter Hong)

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

2019-07-22 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-10 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-05 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-05 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-05 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-05 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-05 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-05 Thread Ji-Ze Hong (Peter Hong)
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

2019-06-05 Thread Ji-Ze Hong (Peter Hong)
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

2019-04-03 Thread Ji-Ze Hong (Peter Hong)
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

2019-04-01 Thread Ji-Ze Hong (Peter Hong)

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

2019-03-29 Thread Ji-Ze Hong (Peter Hong)
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

2019-03-27 Thread Ji-Ze Hong (Peter Hong)
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

2019-03-27 Thread Ji-Ze Hong (Peter Hong)
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

2019-03-25 Thread Ji-Ze Hong (Peter Hong)

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

2019-03-21 Thread Ji-Ze Hong (Peter Hong)
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

2018-02-08 Thread Ji-Ze Hong (Peter Hong)

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

2018-02-08 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-31 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-31 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-31 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-31 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-22 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-21 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-21 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-21 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-21 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-21 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-21 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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()

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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()

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-10 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-09 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-09 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-09 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-09 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-03 Thread Ji-Ze Hong (Peter Hong)
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

2018-01-01 Thread Ji-Ze Hong (Peter Hong)

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

2018-01-01 Thread Ji-Ze Hong (Peter Hong)

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

2017-12-21 Thread Ji-Ze Hong (Peter Hong)

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

2017-12-21 Thread Ji-Ze Hong (Peter Hong)

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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-15 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-07 Thread Ji-Ze Hong (Peter Hong)
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

2017-11-07 Thread Ji-Ze Hong (Peter Hong)
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

2017-10-17 Thread Ji-Ze Hong (Peter Hong)
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

2017-10-17 Thread Ji-Ze Hong (Peter Hong)
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



  1   2   >