Add support for MaxLinear/Exar USB to serial converters
Subsystems (3)
v2 cover Add support for MaxLinear/Exar USB to serial converters
From: Manivannan Sadhasivam <[email protected]>
Hello,
This series adds support for MaxLinear/Exar USB to serial converters. This driver only supports XR21V141X series but provision has been made to support other series in future.
This driver is inspired from the initial one submitted by Patong Yang:
https://patchwork.kernel.org/patch/10543261/
While the initial driver was a custom tty USB driver exposing whole new serial interface ttyXRUSBn, this version is completely based on USB serial core thus exposing the interfaces as ttyUSBn. This will avoid the overhead of exposing a new USB serial interface which the userspace tools are unaware of.
This series has been tested with Hikey970 board hosting XR21V141X chip.
Thanks,
Mani
Changes in v2:
* Dropped the code related to handling variable register size. It's all u8 now.
* Dropped the header file and moved the contents to driver itself.
* Added Linus's reviewed-by tag for gpiochip patch.
* Added PID to gpiochip label
* Dropped gpiochip for interface 0
Manivannan Sadhasivam (2):
usb: serial: Add MaxLinear/Exar USB to Serial driver
usb: serial: xr_serial: Add gpiochip support
drivers/usb/serial/Kconfig | 9 + drivers/usb/serial/Makefile | 1 + drivers/usb/serial/xr_serial.c | 833 +++++++++++++++++++++++++++++++++ 3 files changed, 843 insertions(+) create mode 100644 drivers/usb/serial/xr_serial.c
Signature · 1 line
2.17.1
PATCH v2 1/2 usb: serial: Add MaxLinear/Exar USB to Serial driver
From: Manivannan Sadhasivam <[email protected]>
Add support for MaxLinear/Exar USB to Serial converters. This driver only supports XR21V141X series but provision has been made to support other series in future.
This driver is inspired from the initial one submitted by Patong Yang:
https://patchwork.kernel.org/patch/10543261/
While the initial driver was a custom tty USB driver exposing whole new serial interface ttyXRUSBn, this version is completely based on USB serial core thus exposing the interfaces as ttyUSBn. This will avoid the overhead of exposing a new USB serial interface which the userspace tools are unaware of.
--- drivers/usb/serial/Kconfig | 9 + drivers/usb/serial/Makefile | 1 + drivers/usb/serial/xr_serial.c | 638 +++++++++++++++++++++++++++++++++ 3 files changed, 648 insertions(+) create mode 100644 drivers/usb/serial/xr_serial.c
drivers/usb/serial/Kconfig +9 -0
@@ -644,6 +644,15 @@ config USB_SERIAL_UPD78F0730 To compile this driver as a module, choose M here: the module will be called upd78f0730. +config USB_SERIAL_XR + tristate "USB MaxLinear/Exar USB to Serial driver" + help + Say Y here if you want to use MaxLinear/Exar USB to Serial converter + devices. + + To compile this driver as a module, choose M here: the + module will be called xr_serial. + config USB_SERIAL_DEBUG tristate "USB Debugging Device" help
drivers/usb/serial/Makefile +1 -0
@@ -62,4 +62,5 @@ obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o +obj-$(CONFIG_USB_SERIAL_XR) += xr_serial.o obj-$(CONFIG_USB_SERIAL_XSENS_MT) += xsens_mt.o
drivers/usb/serial/xr_serial.c new file +638 -0
638 lines not on this page · raw message
Signature · 1 line
2.17.1
Greg KH
maintainerRe: [PATCH v2 1/2] usb: serial: Add MaxLinear/Exar USB to Serial driver
drivers/usb/serial/xr_serial.c line 588
Quoted 7 lines · On Thu, Apr 30, 2020 at 10:06:35AM +0530, [email protected] wrote:
drivers/usb/serial/xr_serial.c new file +7
-0+ port_priv->idProduct =le16_to_cpu (serial->dev->descriptor.idProduct); + +/* XR21V141X specific settings */ +if (port_priv->idProduct ==XR21V141X_ID ) { + port_priv->regs = &xr21v141x_regs; + port_priv->reg_width =1 ; + }
Shouldn't you abort here if idProduct does not have this value, as that's all you test for everywhere else. Do you even need to have this test and save the value everywhere at all?
thanks,
greg k-h
Re: [PATCH v2 1/2] usb: serial: Add MaxLinear/Exar USB to Serial driver
Quoted 13 lines · Greg KH
drivers/usb/serial/xr_serial.c line 588
Quoted 7 lines · On Thu, Apr 30, 2020 at 10:06:35AM +0530, [email protected] wrote:
drivers/usb/serial/xr_serial.c new file +7
-0+ port_priv->idProduct =le16_to_cpu (serial->dev->descriptor.idProduct); + +/* XR21V141X specific settings */ +if (port_priv->idProduct ==XR21V141X_ID ) { + port_priv->regs = &xr21v141x_regs; + port_priv->reg_width =1 ; + }Shouldn't you abort here if idProduct does not have this value, as that's all you test for everywhere else. Do you even need to have this test and save the value everywhere at all?
You're right. It's not required now. Will remove it and also the reg_width.
Thanks,
Mani
Quoted 3 lines
thanks,
greg k-h
PATCH v2 2/2 usb: serial: xr_serial: Add gpiochip support
In reply to [email protected]From: Manivannan Sadhasivam <[email protected]>
Add gpiochip support for Maxlinear/Exar USB to serial converter for controlling the available gpios.
--- drivers/usb/serial/xr_serial.c | 197 ++++++++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 1 deletion(-)
drivers/usb/serial/xr_serial.c +196 -1
@@ -7,6 +7,7 @@* Copyright (c) 2020 Manivannan Sadhasivam <[email protected]> */ +#include <linux/gpio/driver.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h>
@@ -32,6 +33,11 @@ struct xr_uart_regs { };struct xr_port_private { +#ifdef CONFIG_GPIOLIB +struct gpio_chip gc; +bool gpio_registered; +u8 gpio_altfunc; +#endif const struct xr_uart_regs *regs;u16 idProduct;u8 reg_width;
@@ -570,6 +576,194 @@ static void xr_break_ctl(struct tty_struct *tty, int break_state) state); } +#ifdef CONFIG_GPIOLIB + +static int xr_gpio_request (struct gpio_chip *gc,unsigned int offset) +{ +struct usb_serial_port *port =gpiochip_get_data (gc); +struct xr_port_private *port_priv =usb_get_serial_port_data (port); + +/* Check if the requested GPIO is occupied */ +if (port_priv->gpio_altfunc &BIT (offset)) +return -ENODEV ; + +return 0 ; +} + +static int xr_gpio_get (struct gpio_chip *gc,unsigned int gpio) +{ +struct usb_serial_port *port =gpiochip_get_data (gc); +struct xr_port_private *port_priv =usb_get_serial_port_data (port); +int ret; +u8 gpio_status; + + ret =xr_get_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_status, &gpio_status); +if (ret) +return ret; + +return !!(gpio_status &BIT (gpio)); +} + +static void xr_gpio_set (struct gpio_chip *gc,unsigned int gpio,int val) +{ +struct usb_serial_port *port =gpiochip_get_data (gc); +struct xr_port_private *port_priv =usb_get_serial_port_data (port); + +if (val) +xr_set_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_set,BIT (gpio)); +else +xr_set_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_clr,BIT (gpio)); +} + +static int xr_gpio_direction_get (struct gpio_chip *gc,unsigned int gpio) +{ +struct usb_serial_port *port =gpiochip_get_data (gc); +struct xr_port_private *port_priv =usb_get_serial_port_data (port); +int ret; +u8 gpio_dir; + + ret =xr_get_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_dir, &gpio_dir); +if (ret) +return ret; + +/* Logic 0 = input and Logic 1 = output */ +return !(gpio_dir &BIT (gpio)); +} + +static int xr_gpio_direction_input (struct gpio_chip *gc,unsigned int gpio) +{ +struct usb_serial_port *port =gpiochip_get_data (gc); +struct xr_port_private *port_priv =usb_get_serial_port_data (port); +int ret; +u8 gpio_dir; + + ret =xr_get_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_dir, &gpio_dir); +if (ret) +return ret; + + gpio_dir &= ~BIT (gpio); + +return xr_set_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_dir, gpio_dir); +} + +static int xr_gpio_direction_output (struct gpio_chip *gc,unsigned int gpio, +int val) +{ +struct usb_serial_port *port =gpiochip_get_data (gc); +struct xr_port_private *port_priv =usb_get_serial_port_data (port); +int ret; +u8 gpio_dir; + + ret =xr_get_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_dir, &gpio_dir); +if (ret) +return ret; + + gpio_dir |=BIT (gpio); + + ret =xr_set_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_dir, gpio_dir); +if (ret) +return ret; + +xr_gpio_set (gc, gpio, val); + +return 0 ; +} + +static int xr21v141x_gpio_init (struct usb_serial_port *port) +{ +struct xr_port_private *port_priv =usb_get_serial_port_data (port); +int ret; +u8 gpio_mode; + + port_priv->gc.ngpio =6 ; + + ret =xr_get_reg (port,XR21V141X_UART_REG_BLOCK , + port_priv->regs->gpio_mode, &gpio_mode); +if (ret) +return ret; + +/* Mark all pins which are not in GPIO mode */ +if (gpio_mode &UART_MODE_RTS_CTS ) + port_priv->gpio_altfunc |= (BIT (4 ) |BIT (5 )); +else if (gpio_mode &UART_MODE_DTR_DSR ) + port_priv->gpio_altfunc |= (BIT (2 ) |BIT (3 )); +else if (gpio_mode &UART_MODE_RS485 ) + port_priv->gpio_altfunc |=BIT (5 ); +else if (gpio_mode &UART_MODE_RS485_ADDR ) + port_priv->gpio_altfunc |=BIT (5 ); +else + port_priv->gpio_altfunc =0 ;/* All GPIOs are available */ + +return ret; +} + +static int xr_gpio_init (struct usb_serial_port *port) +{ +struct xr_port_private *port_priv =usb_get_serial_port_data (port); +int ret =0 ; + +/* Don't register gpiochip for interface 0 */ +if (port->minor ==0 ) +return ret; + +if (port_priv->idProduct ==XR21V141X_ID ) + ret =xr21v141x_gpio_init (port); + +if (ret <0 ) +return ret; + + port_priv->gc.label =devm_kasprintf (&port->dev,GFP_KERNEL ,"XR%04x" , + port_priv->idProduct); + port_priv->gc.request = xr_gpio_request; + port_priv->gc.get_direction = xr_gpio_direction_get; + port_priv->gc.direction_input = xr_gpio_direction_input; + port_priv->gc.direction_output = xr_gpio_direction_output; + port_priv->gc.get = xr_gpio_get; + port_priv->gc.set = xr_gpio_set; + port_priv->gc.owner =THIS_MODULE ; + port_priv->gc.parent = &port->dev; + port_priv->gc.base =-1 ; + port_priv->gc.can_sleep = true; + + ret =gpiochip_add_data (&port_priv->gc, port); +if (!ret) + port_priv->gpio_registered = true; + +return ret; +} + +static void xr_gpio_remove (struct usb_serial_port *port) +{ +struct xr_port_private *port_priv =usb_get_serial_port_data (port); + +if (port_priv->gpio_registered) { +gpiochip_remove (&port_priv->gc); + port_priv->gpio_registered = false; + } +} + +#else + +static int xr_gpio_init (struct usb_serial_port *port) +{ +return 0 ; +} + +static void xr_gpio_remove (struct usb_serial_port *port) +{ +/* Nothing to do */ +} + +#endif +static int xr_port_probe (struct usb_serial_port *port) {struct usb_serial *serial = port->serial;
@@ -589,13 +783,14 @@ static int xr_port_probe(struct usb_serial_port *port)usb_set_serial_port_data (port, port_priv); -return 0 ; +return xr_gpio_init (port); }static int xr_port_remove (struct usb_serial_port *port) {struct xr_port_private *port_priv =usb_get_serial_port_data (port); +xr_gpio_remove (port);kfree (port_priv);return 0 ;
Signature · 1 line
2.17.1