USB-serial updates for v4.20-rc1

Here are the USB-serial updates for 4.20-rc1, including:
 
  - support for CBUS GPIO on FTDI devices (FTX and FT232R)
  - fix of a long-standing transfer-length bug
 
 Included are also various clean ups.
 
 All have been in linux-next with no reported issues.
 
 Signed-off-by: Johan Hovold <johan@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEHszNKQClByu0A+9RQQ3kT97htJUFAlvIZhIRHGpvaGFuQGtl
 cm5lbC5vcmcACgkQQQ3kT97htJUNIQ//fv+Jn/XQ4H9Ystik+uJbHOSXYG0OJPkz
 IYZcFdlJBwdH9fF0Bof6vRVIlltJH6ZE636qWZi4D0fuS+pjGoyV3XXYAmiJc+d3
 jXZSvuib08s89SYz2sK8CYrlgZYnMkn/HO8SIs/dLE81HvGpb+3qS0kcqBoepo5f
 MP6TA4jBcRkLVe9gEvgNDwm+Vc1I9Myg6Co5SClaNZRnuj4UyreqX1DIp2lU3evw
 gMq1AHZkwijrdES1FbW9SQ4cVujG3KXbKCrrQyXMcTNPX+4BlayiEqC12eNcoP2Q
 Ng1IAXTZWP1jFCDWZ5B2gn6r0jGILn2leFKcazt9B5lN/YTE51YgbV2JHlnc+8IV
 z1dOs3IIINfwTchh8DSW9nS1j8krFzrGebXlcMApuSqq7s7MGZVy7P48FsgtUd6Y
 B2msIjAWxnLf4XQcKWLA9tZj5stBNInSfFFHi4oUhC/A62teC/XFF8505N9y9MVr
 PhgWnH2zd7cayuUoHQTVvVpvmnRyJBLmyawqSCGLQvo22II58zvsvRX7K/Mop1WB
 u7+dxVfVb+fgpBM5RjfNRK/qXux7BqgXQFhNzzCREiKDi+Mv+2VOe3rmXOi8bMHs
 SKY2r70Fk1ihmxSTcUMCTG1bc3BNZwpccgpW9vWw5Kia3lo3JUgd8OSIfxFnLi03
 /GAnh8pnSh8=
 =V6Jo
 -----END PGP SIGNATURE-----

Merge tag 'usb-serial-4.20-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next

Johan writes:

USB-serial updates for v4.20-rc1

Here are the USB-serial updates for 4.20-rc1, including:

 - support for CBUS GPIO on FTDI devices (FTX and FT232R)
 - fix of a long-standing transfer-length bug

Included are also various clean ups.

All have been in linux-next with no reported issues.

Signed-off-by: Johan Hovold <johan@kernel.org>

* tag 'usb-serial-4.20-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial:
  USB: serial: cypress_m8: remove set but not used variable 'iflag'
  USB: serial: cypress_m8: fix interrupt-out transfer length
  USB: serial: ftdi_sio: add support for FT232R CBUS gpios
  USB: serial: ftdi_sio: fix gpio name collisions
  USB: serial: ftdi_sio: implement GPIO support for FT-X devices
  USB: serial: cypress_m8: fix spelling mistake "retreiving" -> "retrieving"
This commit is contained in:
Greg Kroah-Hartman 2018-10-18 14:59:04 +02:00
commit 89303c7ea7
3 changed files with 420 additions and 6 deletions

View File

@ -378,7 +378,7 @@ static int cypress_serial_control(struct tty_struct *tty,
retval = -ENOTTY;
goto out;
}
dev_dbg(dev, "%s - retreiving serial line settings\n", __func__);
dev_dbg(dev, "%s - retrieving serial line settings\n", __func__);
do {
retval = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
@ -769,7 +769,7 @@ send:
usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
port->interrupt_out_buffer, port->interrupt_out_size,
port->interrupt_out_buffer, actual_size,
cypress_write_int_callback, port, priv->write_urb_interval);
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
if (result) {
@ -863,7 +863,7 @@ static void cypress_set_termios(struct tty_struct *tty,
struct cypress_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
int data_bits, stop_bits, parity_type, parity_enable;
unsigned cflag, iflag;
unsigned int cflag;
unsigned long flags;
__u8 oldlines;
int linechange = 0;
@ -899,7 +899,6 @@ static void cypress_set_termios(struct tty_struct *tty,
tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS);
cflag = tty->termios.c_cflag;
iflag = tty->termios.c_iflag;
/* check if there are new settings */
if (old_termios) {

View File

@ -39,6 +39,7 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/serial.h>
#include <linux/gpio/driver.h>
#include <linux/usb/serial.h>
#include "ftdi_sio.h"
#include "ftdi_sio_ids.h"
@ -72,6 +73,15 @@ struct ftdi_private {
unsigned int latency; /* latency setting in use */
unsigned short max_packet_size;
struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() and change_speed() */
#ifdef CONFIG_GPIOLIB
struct gpio_chip gc;
struct mutex gpio_lock; /* protects GPIO state */
bool gpio_registered; /* is the gpiochip in kernel registered */
bool gpio_used; /* true if the user requested a gpio */
u8 gpio_altfunc; /* which pins are in gpio mode */
u8 gpio_output; /* pin directions cache */
u8 gpio_value; /* pin value for outputs */
#endif
};
/* struct ftdi_sio_quirk is used by devices requiring special attention. */
@ -1766,6 +1776,375 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
}
#ifdef CONFIG_GPIOLIB
static int ftdi_set_bitmode(struct usb_serial_port *port, u8 mode)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
int result;
u16 val;
val = (mode << 8) | (priv->gpio_output << 4) | priv->gpio_value;
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
FTDI_SIO_SET_BITMODE_REQUEST,
FTDI_SIO_SET_BITMODE_REQUEST_TYPE, val,
priv->interface, NULL, 0, WDR_TIMEOUT);
if (result < 0) {
dev_err(&serial->interface->dev,
"bitmode request failed for value 0x%04x: %d\n",
val, result);
}
return result;
}
static int ftdi_set_cbus_pins(struct usb_serial_port *port)
{
return ftdi_set_bitmode(port, FTDI_SIO_BITMODE_CBUS);
}
static int ftdi_exit_cbus_mode(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
priv->gpio_output = 0;
priv->gpio_value = 0;
return ftdi_set_bitmode(port, FTDI_SIO_BITMODE_RESET);
}
static int ftdi_gpio_request(struct gpio_chip *gc, unsigned int offset)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
int result;
if (priv->gpio_altfunc & BIT(offset))
return -ENODEV;
mutex_lock(&priv->gpio_lock);
if (!priv->gpio_used) {
/* Set default pin states, as we cannot get them from device */
priv->gpio_output = 0x00;
priv->gpio_value = 0x00;
result = ftdi_set_cbus_pins(port);
if (result) {
mutex_unlock(&priv->gpio_lock);
return result;
}
priv->gpio_used = true;
}
mutex_unlock(&priv->gpio_lock);
return 0;
}
static int ftdi_read_cbus_pins(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
unsigned char *buf;
int result;
buf = kmalloc(1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
result = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_READ_PINS_REQUEST,
FTDI_SIO_READ_PINS_REQUEST_TYPE, 0,
priv->interface, buf, 1, WDR_TIMEOUT);
if (result < 1) {
if (result >= 0)
result = -EIO;
} else {
result = buf[0];
}
kfree(buf);
return result;
}
static int ftdi_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
int result;
result = ftdi_read_cbus_pins(port);
if (result < 0)
return result;
return !!(result & BIT(gpio));
}
static void ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
mutex_lock(&priv->gpio_lock);
if (value)
priv->gpio_value |= BIT(gpio);
else
priv->gpio_value &= ~BIT(gpio);
ftdi_set_cbus_pins(port);
mutex_unlock(&priv->gpio_lock);
}
static int ftdi_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
int result;
result = ftdi_read_cbus_pins(port);
if (result < 0)
return result;
*bits = result & *mask;
return 0;
}
static void ftdi_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
mutex_lock(&priv->gpio_lock);
priv->gpio_value &= ~(*mask);
priv->gpio_value |= *bits & *mask;
ftdi_set_cbus_pins(port);
mutex_unlock(&priv->gpio_lock);
}
static int ftdi_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
return !(priv->gpio_output & BIT(gpio));
}
static int ftdi_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
int result;
mutex_lock(&priv->gpio_lock);
priv->gpio_output &= ~BIT(gpio);
result = ftdi_set_cbus_pins(port);
mutex_unlock(&priv->gpio_lock);
return result;
}
static int ftdi_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
int value)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
int result;
mutex_lock(&priv->gpio_lock);
priv->gpio_output |= BIT(gpio);
if (value)
priv->gpio_value |= BIT(gpio);
else
priv->gpio_value &= ~BIT(gpio);
result = ftdi_set_cbus_pins(port);
mutex_unlock(&priv->gpio_lock);
return result;
}
static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr,
u16 nbytes)
{
int read = 0;
if (addr % 2 != 0)
return -EINVAL;
if (nbytes % 2 != 0)
return -EINVAL;
/* Read EEPROM two bytes at a time */
while (read < nbytes) {
int rv;
rv = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_READ_EEPROM_REQUEST,
FTDI_SIO_READ_EEPROM_REQUEST_TYPE,
0, (addr + read) / 2, dst + read, 2,
WDR_TIMEOUT);
if (rv < 2) {
if (rv >= 0)
return -EIO;
else
return rv;
}
read += rv;
}
return 0;
}
static int ftdi_gpio_init_ft232r(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
u16 cbus_config;
u8 *buf;
int ret;
int i;
buf = kmalloc(2, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = ftdi_read_eeprom(port->serial, buf, 0x14, 2);
if (ret < 0)
goto out_free;
cbus_config = le16_to_cpup((__le16 *)buf);
dev_dbg(&port->dev, "cbus_config = 0x%04x\n", cbus_config);
priv->gc.ngpio = 4;
priv->gpio_altfunc = 0xff;
for (i = 0; i < priv->gc.ngpio; ++i) {
if ((cbus_config & 0xf) == FTDI_FT232R_CBUS_MUX_GPIO)
priv->gpio_altfunc &= ~BIT(i);
cbus_config >>= 4;
}
out_free:
kfree(buf);
return ret;
}
static int ftdi_gpio_init_ftx(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
const u16 cbus_cfg_addr = 0x1a;
const u16 cbus_cfg_size = 4;
u8 *cbus_cfg_buf;
int result;
u8 i;
cbus_cfg_buf = kmalloc(cbus_cfg_size, GFP_KERNEL);
if (!cbus_cfg_buf)
return -ENOMEM;
result = ftdi_read_eeprom(serial, cbus_cfg_buf,
cbus_cfg_addr, cbus_cfg_size);
if (result < 0)
goto out_free;
/* FIXME: FT234XD alone has 1 GPIO, but how to recognize this IC? */
priv->gc.ngpio = 4;
/* Determine which pins are configured for CBUS bitbanging */
priv->gpio_altfunc = 0xff;
for (i = 0; i < priv->gc.ngpio; ++i) {
if (cbus_cfg_buf[i] == FTDI_FTX_CBUS_MUX_GPIO)
priv->gpio_altfunc &= ~BIT(i);
}
out_free:
kfree(cbus_cfg_buf);
return result;
}
static int ftdi_gpio_init(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
int result;
switch (priv->chip_type) {
case FT232RL:
result = ftdi_gpio_init_ft232r(port);
break;
case FTX:
result = ftdi_gpio_init_ftx(port);
break;
default:
return 0;
}
if (result < 0)
return result;
mutex_init(&priv->gpio_lock);
priv->gc.label = "ftdi-cbus";
priv->gc.request = ftdi_gpio_request;
priv->gc.get_direction = ftdi_gpio_direction_get;
priv->gc.direction_input = ftdi_gpio_direction_input;
priv->gc.direction_output = ftdi_gpio_direction_output;
priv->gc.get = ftdi_gpio_get;
priv->gc.set = ftdi_gpio_set;
priv->gc.get_multiple = ftdi_gpio_get_multiple;
priv->gc.set_multiple = ftdi_gpio_set_multiple;
priv->gc.owner = THIS_MODULE;
priv->gc.parent = &serial->interface->dev;
priv->gc.base = -1;
priv->gc.can_sleep = true;
result = gpiochip_add_data(&priv->gc, port);
if (!result)
priv->gpio_registered = true;
return result;
}
static void ftdi_gpio_remove(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
if (priv->gpio_registered) {
gpiochip_remove(&priv->gc);
priv->gpio_registered = false;
}
if (priv->gpio_used) {
/* Exiting CBUS-mode does not reset pin states. */
ftdi_exit_cbus_mode(port);
priv->gpio_used = false;
}
}
#else
static int ftdi_gpio_init(struct usb_serial_port *port)
{
return 0;
}
static void ftdi_gpio_remove(struct usb_serial_port *port) { }
#endif /* CONFIG_GPIOLIB */
/*
* ***************************************************************************
* FTDI driver specific functions
@ -1794,7 +2173,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
{
struct ftdi_private *priv;
const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
int result;
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv)
@ -1813,6 +2192,14 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
priv->latency = 16;
write_latency_timer(port);
create_sysfs_attrs(port);
result = ftdi_gpio_init(port);
if (result < 0) {
dev_err(&port->serial->interface->dev,
"GPIO initialisation failed: %d\n",
result);
}
return 0;
}
@ -1930,6 +2317,8 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
ftdi_gpio_remove(port);
remove_sysfs_attrs(port);
kfree(priv);

View File

@ -35,7 +35,10 @@
#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
#define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */
#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */
#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */
#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */
#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */
/* Interface indices for FT2232, FT2232H and FT4232H devices */
#define INTERFACE_A 1
@ -433,6 +436,29 @@ enum ftdi_sio_baudrate {
* 1 = active
*/
/* FTDI_SIO_SET_BITMODE */
#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40
#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE
/* Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST */
#define FTDI_SIO_BITMODE_RESET 0x00
#define FTDI_SIO_BITMODE_CBUS 0x20
/* FTDI_SIO_READ_PINS */
#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0
#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS
/*
* FTDI_SIO_READ_EEPROM
*
* EEPROM format found in FTDI AN_201, "FT-X MTP memory Configuration",
* http://www.ftdichip.com/Support/Documents/AppNotes/AN_201_FT-X%20MTP%20Memory%20Configuration.pdf
*/
#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0
#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
#define FTDI_FTX_CBUS_MUX_GPIO 0x8
#define FTDI_FT232R_CBUS_MUX_GPIO 0xa
/* Descriptors returned by the device