Re: [PATCH] USB: serial: Add vizzini driver for Exar USB2 serial adapters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jan 17, 2020 at 02:41:48PM +1000, Joel Stanley wrote:
> From: Ben Hutchings <ben@xxxxxxxxxxxxxxx>
> 
> This driver supports the one, two and four port variants of the USB
> serial adapters. These devices are present on the Digilent Atlys FPGA
> board.
> 
> This driver was written by Ben in 2015. Minor cleanups and fixes for the
> 1410 by Joel.
> 
> Signed-off-by: Ben Hutchings <ben@xxxxxxxxxxxxxxx>
> Signed-off-by: Joel Stanley <joel@xxxxxxxxx>
> ---
>  drivers/usb/serial/Kconfig   |   9 +
>  drivers/usb/serial/Makefile  |   1 +
>  drivers/usb/serial/vizzini.c | 370 +++++++++++++++++++++++++++++++++++
>  3 files changed, 380 insertions(+)
>  create mode 100644 drivers/usb/serial/vizzini.c
> 
> diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
> index 25d7e0c36d38..167992ad1d05 100644
> --- a/drivers/usb/serial/Kconfig
> +++ b/drivers/usb/serial/Kconfig
> @@ -654,4 +654,13 @@ config USB_SERIAL_DEBUG
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called usb-debug.
>  
> +config USB_SERIAL_VIZZINI
> +	tristate "USB Exar XR21V141x 'Vizzini' Serial Driver"
> +	help
> +	  Say Y here if you want to use the Exar XR21V1410/1412/141
> +	  USB 2 serial dapters.

typo: adapters

> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called vizzini.
> +
>  endif # USB_SERIAL
> diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
> index 2d491e434f11..9f65a4863f1d 100644
> --- a/drivers/usb/serial/Makefile
> +++ b/drivers/usb/serial/Makefile
> @@ -59,6 +59,7 @@ obj-$(CONFIG_USB_SERIAL_WWAN)			+= usb_wwan.o
>  obj-$(CONFIG_USB_SERIAL_TI)			+= ti_usb_3410_5052.o
>  obj-$(CONFIG_USB_SERIAL_UPD78F0730)		+= upd78f0730.o
>  obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
> +obj-$(CONFIG_USB_SERIAL_VIZZINI)		+= vizzini.o
>  obj-$(CONFIG_USB_SERIAL_WISHBONE)		+= wishbone-serial.o
>  obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
>  obj-$(CONFIG_USB_SERIAL_XIRCOM)			+= keyspan_pda.o
> diff --git a/drivers/usb/serial/vizzini.c b/drivers/usb/serial/vizzini.c
> new file mode 100644
> index 000000000000..f4d1b9a75d8a
> --- /dev/null
> +++ b/drivers/usb/serial/vizzini.c
> @@ -0,0 +1,370 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Based on vizzini driver:
> + *
> + * Copyright (c) 2013 Exar Corporation, Inc.
> + *
> + * Based on USB Serial "Simple" driver
> + *
> + * Copyright (C) 2001-2006,2008,2013 Greg Kroah-Hartman <greg@xxxxxxxxx>
> + * Copyright (C) 2005 Arthur Huillet (ahuillet@xxxxxxxxxxxx)
> + * Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@xxxxxxx>
> + * Copyright (C) 2009 Outpost Embedded, LLC
> + * Copyright (C) 2010 Zilogic Systems <code@xxxxxxxxxxx>
> + * Copyright (C) 2013 Wei Shuai <cpuwolf@xxxxxxxxx>
> + * Copyright (C) 2013 Linux Foundation

This looked weird, but I understand where it comes from now. With a
calc_num_ports() callback, you can drop this bit (see below).

> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/tty.h>
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/usb/serial.h>
> +
> +#define XR_SET_REG			0
> +
> +#define URM_REG_BLOCK			4
> +#define EPLOCALS_REG_BLOCK		0x66
> +
> +#define MEM_EP_LOCALS_SIZE_S		3
> +#define MEM_EP_LOCALS_SIZE		(1 << MEM_EP_LOCALS_SIZE_S)
> +
> +#define EP_WIDE_MODE			0x03
> +
> +#define UART_GPIO_MODE			0x01a
> +
> +#define UART_GPIO_MODE_SEL_M		0x7
> +#define UART_GPIO_MODE_SEL_S		0
> +#define UART_GPIO_MODE_SEL		0x007
> +
> +#define UART_GPIO_MODE_SEL_GPIO		(0x0 << UART_GPIO_MODE_SEL_S)
> +#define UART_GPIO_MODE_SEL_RTS_CTS	(0x1 << UART_GPIO_MODE_SEL_S)
> +#define UART_GPIO_MODE_SEL_DTR_DSR	(0x2 << UART_GPIO_MODE_SEL_S)
> +
> +#define UART_ENABLE			0x003
> +#define UART_ENABLE_TX_M		0x1
> +#define UART_ENABLE_TX_S		0

Looks like several of these mask/shift defines are unused.

> +#define UART_ENABLE_TX			0x001
> +#define UART_ENABLE_RX_M		0x1
> +#define UART_ENABLE_RX_S		1
> +#define UART_ENABLE_RX			0x002
> +
> +#define UART_CLOCK_DIVISOR_0		0x004
> +#define UART_CLOCK_DIVISOR_1		0x005
> +#define UART_CLOCK_DIVISOR_2		0x006
> +
> +#define UART_TX_CLOCK_MASK_0		0x007
> +#define UART_TX_CLOCK_MASK_1		0x008
> +
> +#define UART_RX_CLOCK_MASK_0		0x009
> +#define UART_RX_CLOCK_MASK_1		0x00a
> +
> +#define UART_FORMAT			0x00b
> +
> +#define UART_FORMAT_SIZE_M		0xf
> +#define UART_FORMAT_SIZE_S		0
> +#define UART_FORMAT_SIZE		0x00f

Same here.

> +#define UART_FORMAT_SIZE_7		(0x7 << UART_FORMAT_SIZE_S)
> +#define UART_FORMAT_SIZE_8		(0x8 << UART_FORMAT_SIZE_S)
> +#define UART_FORMAT_SIZE_9		(0x9 << UART_FORMAT_SIZE_S)
> +
> +#define UART_FORMAT_PARITY_M		0x7
> +#define UART_FORMAT_PARITY_S		4
> +#define UART_FORMAT_PARITY		0x070
> +
> +#define UART_FORMAT_PARITY_NONE		(0x0 << UART_FORMAT_PARITY_S)
> +#define UART_FORMAT_PARITY_ODD		(0x1 << UART_FORMAT_PARITY_S)
> +#define UART_FORMAT_PARITY_EVEN		(0x2 << UART_FORMAT_PARITY_S)
> +#define UART_FORMAT_PARITY_1		(0x3 << UART_FORMAT_PARITY_S)
> +#define UART_FORMAT_PARITY_0		(0x4 << UART_FORMAT_PARITY_S)
> +
> +#define UART_FORMAT_STOP_M		0x1
> +#define UART_FORMAT_STOP_S		7
> +#define UART_FORMAT_STOP		0x080
> +
> +#define UART_FORMAT_STOP_1		(0x0 << UART_FORMAT_STOP_S)
> +#define UART_FORMAT_STOP_2		(0x1 << UART_FORMAT_STOP_S)
> +
> +#define UART_FLOW			0x00c
> +
> +#define UART_FLOW_MODE_M		0x7
> +#define UART_FLOW_MODE_S		0
> +#define UART_FLOW_MODE			0x007
> +
> +#define UART_FLOW_MODE_NONE		(0x0 << UART_FLOW_MODE_S)
> +#define UART_FLOW_MODE_HW		(0x1 << UART_FLOW_MODE_S)
> +#define UART_FLOW_MODE_SW		(0x2 << UART_FLOW_MODE_S)
> +
> +#define UART_XON_CHAR			0x010
> +#define UART_XOFF_CHAR			0x011
> +
> +#define URM_ENABLE_BASE			0x010
> +#define URM_ENABLE_0			0x010
> +#define URM_ENABLE_0_TX			0x001
> +#define URM_ENABLE_0_RX			0x002
> +
> +struct vizzini_baud_rate {
> +	unsigned int tx;
> +	unsigned int rx0;
> +	unsigned int rx1;
> +};
> +
> +static const struct vizzini_baud_rate vizzini_baud_rates[] = {
> +	{ 0x000, 0x000, 0x000 },
> +	{ 0x000, 0x000, 0x000 },
> +	{ 0x100, 0x000, 0x100 },
> +	{ 0x020, 0x400, 0x020 },
> +	{ 0x010, 0x100, 0x010 },
> +	{ 0x208, 0x040, 0x208 },
> +	{ 0x104, 0x820, 0x108 },
> +	{ 0x844, 0x210, 0x884 },
> +	{ 0x444, 0x110, 0x444 },
> +	{ 0x122, 0x888, 0x224 },
> +	{ 0x912, 0x448, 0x924 },
> +	{ 0x492, 0x248, 0x492 },
> +	{ 0x252, 0x928, 0x292 },
> +	{ 0x94A, 0x4A4, 0xA52 },
> +	{ 0x52A, 0xAA4, 0x54A },
> +	{ 0xAAA, 0x954, 0x4AA },
> +	{ 0xAAA, 0x554, 0xAAA },
> +	{ 0x555, 0xAD4, 0x5AA },
> +	{ 0xB55, 0xAB4, 0x55A },
> +	{ 0x6B5, 0x5AC, 0xB56 },
> +	{ 0x5B5, 0xD6C, 0x6D6 },
> +	{ 0xB6D, 0xB6A, 0xDB6 },
> +	{ 0x76D, 0x6DA, 0xBB6 },
> +	{ 0xEDD, 0xDDA, 0x76E },
> +	{ 0xDDD, 0xBBA, 0xEEE },
> +	{ 0x7BB, 0xF7A, 0xDDE },
> +	{ 0xF7B, 0xEF6, 0x7DE },
> +	{ 0xDF7, 0xBF6, 0xF7E },
> +	{ 0x7F7, 0xFEE, 0xEFE },
> +	{ 0xFDF, 0xFBE, 0x7FE },
> +	{ 0xF7F, 0xEFE, 0xFFE },
> +	{ 0xFFF, 0xFFE, 0xFFD },
> +};
> +
> +static int vizzini_set_reg(struct usb_serial_port *port,
> +			   unsigned int block, unsigned int regnum,
> +			   unsigned int value)
> +{
> +	dev_dbg(&port->serial->dev->dev, "%s 0x%02x:0x%02x = 0x%02x\n",
> +		__func__, block, regnum, value);

Use &port->dev instead.

> +
> +	return usb_control_msg(port->serial->dev,
> +			       usb_sndctrlpipe(port->serial->dev, 0),
> +			       XR_SET_REG,
> +			       USB_DIR_OUT | USB_TYPE_VENDOR,
> +			       value, regnum | (block << 8),
> +			       NULL, 0,
> +			       5000);

Use USB_CTRL_SET_TIMEOUT

Report errors?

> +}
> +
> +static void vizzini_disable(struct usb_serial_port *port)
> +{
> +	unsigned int block = port->bulk_out_endpointAddress - 1;

You should probably use port->port_number instead of relying on fixed
endpoint addresses which you never verify.

> +
> +	vizzini_set_reg(port, block, UART_ENABLE, 0);
> +	vizzini_set_reg(port, URM_REG_BLOCK, URM_ENABLE_BASE + block, 0);
> +}
> +
> +static void vizzini_enable(struct usb_serial_port *port)
> +{
> +	unsigned int block = port->bulk_out_endpointAddress - 1;
>
> +	vizzini_set_reg(port, URM_REG_BLOCK, URM_ENABLE_BASE + block,
> +			URM_ENABLE_0_TX);

Could you add comment about why this sequence is necessary, if it really
is?

> +	vizzini_set_reg(port, block, UART_ENABLE,
> +			UART_ENABLE_TX | UART_ENABLE_RX);
> +	vizzini_set_reg(port, URM_REG_BLOCK, URM_ENABLE_BASE + block,
> +			URM_ENABLE_0_TX | URM_ENABLE_0_RX);

No error handling?

> +}
> +
> +static void vizzini_set_baud_rate(struct usb_serial_port *port,
> +				  unsigned int rate)
> +{
> +	int		block	= port->bulk_out_endpointAddress - 1;
> +	unsigned int	divisor = 48000000 / rate;

Use a define for the clock frequency?

> +	unsigned int	i	= ((32 * 48000000) / rate) & 0x1f;
> +	unsigned int	tx_mask = vizzini_baud_rates[i].tx;
> +	unsigned int	rx_mask = ((divisor & 1) ? vizzini_baud_rates[i].rx1 :
> +				   vizzini_baud_rates[i].rx0);

Don't do non-trivial initialisations at declaration. And please avoid
the ternary operator.

> +
> +	dev_dbg(&port->serial->dev->dev,
> +		"Setting baud rate to %d: i=%u div=%u tx=%03x rx=%03x\n",
> +		rate, i, divisor, tx_mask, rx_mask);
> +
> +	vizzini_set_reg(port, block, UART_CLOCK_DIVISOR_0,
> +			(divisor >>  0) & 0xff);
> +	vizzini_set_reg(port, block, UART_CLOCK_DIVISOR_1,
> +			(divisor >>  8) & 0xff);
> +	vizzini_set_reg(port, block, UART_CLOCK_DIVISOR_2,
> +			(divisor >> 16) & 0xff);
> +
> +	vizzini_set_reg(port, block, UART_TX_CLOCK_MASK_0,
> +			(tx_mask >>  0) & 0xff);
> +	vizzini_set_reg(port, block, UART_TX_CLOCK_MASK_1,
> +			(tx_mask >>  8) & 0xff);
> +
> +	vizzini_set_reg(port, block, UART_RX_CLOCK_MASK_0,
> +			(rx_mask >>  0) & 0xff);
> +	vizzini_set_reg(port, block, UART_RX_CLOCK_MASK_1,
> +			(rx_mask >>  8) & 0xff);

Error handling?

Can you add some comment about how the masks are used?

> +}
> +
> +static void vizzini_set_termios(struct tty_struct *tty,
> +				struct usb_serial_port *port,
> +				struct ktermios *termios_old)
> +{
> +	unsigned int             cflag, block;
> +	speed_t                  rate;
> +	unsigned int             format_size, format_parity, format_stop, flow, gpio_mode;

One declaration per line please, at least unless they are clearly
related (and stay under 80 cols).

Looks like you can get away with just a fmt variable.

> +
> +	cflag = tty->termios.c_cflag;
> +
> +	block = port->bulk_out_endpointAddress - 1;
> +
> +	vizzini_disable(port);

Do you really need to disable the port?

> +
> +	if ((cflag & CSIZE) == CS7)

Use C_CSIZE() and friends.

> +		format_size = UART_FORMAT_SIZE_7;
> +	else if ((cflag & CSIZE) == CS5)
> +		/* Enabling 5-bit mode is really 9-bit mode! */

What does this mean? That you have redefined CS5?

> +		format_size = UART_FORMAT_SIZE_9;
> +	else
> +		format_size = UART_FORMAT_SIZE_8;

You need to report back the settings actually used (update termios).

> +
> +	if (cflag & PARENB) {

C_PARENB() etc.

> +		if (cflag & PARODD) {
> +			if (cflag & CMSPAR)
> +				format_parity = UART_FORMAT_PARITY_1;
> +			else
> +				format_parity = UART_FORMAT_PARITY_ODD;
> +		} else {
> +			if (cflag & CMSPAR)
> +				format_parity = UART_FORMAT_PARITY_0;
> +			else
> +				format_parity = UART_FORMAT_PARITY_EVEN;
> +		}
> +	} else {
> +		format_parity = UART_FORMAT_PARITY_NONE;
> +	}
> +
> +	if (cflag & CSTOPB)
> +		format_stop = UART_FORMAT_STOP_2;
> +	else
> +		format_stop = UART_FORMAT_STOP_1;
> +
> +	vizzini_set_reg(port, block, UART_FORMAT,
> +			format_size | format_parity | format_stop);

Error handling.

> +
> +	if (cflag & CRTSCTS) {
> +		flow      = UART_FLOW_MODE_HW;
> +		gpio_mode = UART_GPIO_MODE_SEL_RTS_CTS;
> +	} else if (I_IXOFF(tty) || I_IXON(tty)) {
> +		unsigned char   start_char = START_CHAR(tty);
> +		unsigned char   stop_char  = STOP_CHAR(tty);
> +
> +		flow      = UART_FLOW_MODE_SW;
> +		gpio_mode = UART_GPIO_MODE_SEL_GPIO;
> +
> +		vizzini_set_reg(port, block, UART_XON_CHAR, start_char);
> +		vizzini_set_reg(port, block, UART_XOFF_CHAR, stop_char);
> +	} else {
> +		flow      = UART_FLOW_MODE_NONE;
> +		gpio_mode = UART_GPIO_MODE_SEL_GPIO;
> +	}
> +
> +	vizzini_set_reg(port, block, UART_FLOW, flow);
> +	vizzini_set_reg(port, block, UART_GPIO_MODE, gpio_mode);
> +
> +	vizzini_set_reg(port, EPLOCALS_REG_BLOCK,
> +			(block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE,
> +			format_size == UART_FORMAT_SIZE_9);

What's this about?

> +
> +	rate = tty_get_baud_rate(tty);
> +	if (rate)
> +		vizzini_set_baud_rate(port, rate);
> +
> +	vizzini_enable(port);
> +}
> +
> +static int vizzini_probe(struct usb_serial *serial,
> +			 const struct usb_device_id *id)
> +{
> +	struct usb_host_interface *iface = serial->interface->cur_altsetting;
> +	int i;
> +
> +	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
> +		if (usb_endpoint_is_bulk_out(&iface->endpoint[i].desc))
> +			return 0;
> +	}

Set num_bulk_out (and num_bulk_in?) in struct usb_serial_driver and let
core handle this for you, and you can drop probe() altogether.

> +
> +	/* No bulk interfaces found */
> +	dev_dbg(&serial->dev->dev, "Invalid interface, discarding\n");
> +
> +	return -ENODEV;
> +}
> +
> +static const struct usb_device_id id_table[] = {
> +	{ USB_DEVICE(0x04e2, 0x1410), },
> +	{ USB_DEVICE(0x04e2, 0x1412), },
> +	{ USB_DEVICE(0x04e2, 0x1414), },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(usb, id_table);
> +
> +static const struct usb_device_id vizzini_1410_id_table[] = {
> +	{ USB_DEVICE(0x04e2, 0x1410), },
> +	{ },
> +};
> +static struct usb_serial_driver vizzini_1410_device = {
> +	.driver = {
> +		.owner =	THIS_MODULE,
> +		.name =		"vizzini_1410",
> +	},
> +	.id_table =		vizzini_1410_id_table,
> +	.num_ports =		1,
> +	.set_termios =		vizzini_set_termios,
> +	.probe =		vizzini_probe,
> +};
> +
> +static const struct usb_device_id vizzini_1412_id_table[] = {
> +	{ USB_DEVICE(0x04e2, 0x1412), },
> +	{ },
> +};
> +static struct usb_serial_driver vizzini_1412_device = {
> +	.driver = {
> +		.owner =	THIS_MODULE,
> +		.name =		"vizzini_1412",
> +	},
> +	.id_table =		vizzini_1412_id_table,
> +	.num_ports =		2,
> +	.set_termios =		vizzini_set_termios,
> +};
> +
> +static const struct usb_device_id vizzini_1414_id_table[] = {
> +	{ USB_DEVICE(0x04e2, 0x1414), },
> +	{ },
> +};
> +static struct usb_serial_driver vizzini_1414_device = {
> +	.driver = {
> +		.owner =	THIS_MODULE,
> +		.name =		"vizzini_1414",
> +	},
> +	.id_table =		vizzini_1414_id_table,
> +	.num_ports =		4,
> +	.set_termios =		vizzini_set_termios,
> +};
> +
> +static struct usb_serial_driver * const serial_drivers[] = {
> +	&vizzini_1410_device,
> +	&vizzini_1412_device,
> +	&vizzini_1414_device,
> +	NULL
> +};

I think you should use a single struct usb_serial_driver for all three
models and implement the calc_num_ports() callback instead.

If you cannot determine the number of ports at runtime, you can encode
it in the device-id table (driver_info).

> +
> +module_usb_serial_driver(serial_drivers, id_table);
> +
> +MODULE_LICENSE("GPL");

Johan



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux