Skip to content

Commit

Permalink
usb-serial: Add vizzini driver for Exar USB 2 serial adapters
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
  • Loading branch information
bwhacks authored and shenki committed Nov 15, 2015
1 parent 6a13feb commit 6d9c805
Show file tree
Hide file tree
Showing 3 changed files with 366 additions and 0 deletions.
9 changes: 9 additions & 0 deletions drivers/usb/serial/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -713,4 +713,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.

To compile this driver as a module, choose M here: the
module will be called vizzini.

endif # USB_SERIAL
1 change: 1 addition & 0 deletions drivers/usb/serial/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o
obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o
obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.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
Expand Down
356 changes: 356 additions & 0 deletions drivers/usb/serial/vizzini.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
/*
* 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@kroah.com>
* Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net)
* Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@suse.de>
* Copyright (C) 2009 Outpost Embedded, LLC
* Copyright (C) 2010 Zilogic Systems <code@zilogic.com>
* Copyright (C) 2013 Wei Shuai <cpuwolf@gmail.com>
* Copyright (C) 2013 Linux Foundation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/

#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
#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

#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 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);

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);
}

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

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);
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);
}

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

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);
}

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;

cflag = tty->termios.c_cflag;

block = port->bulk_out_endpointAddress - 1;

vizzini_disable(port);

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

if (cflag & PARENB) {
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);

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

rate = tty_get_baud_rate(tty);
if (rate)
vizzini_set_baud_rate(port, rate);

vizzini_enable(port);
}

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,
};

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
};

module_usb_serial_driver(serial_drivers, id_table);

MODULE_LICENSE("GPL");

0 comments on commit 6d9c805

Please sign in to comment.