Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 66c79319 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'usb-serial-4.10-rc1' of...

Merge tag 'usb-serial-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial

 into usb-next

Johan writes:

USB-serial updates for v4.10-rc1

These updates include a new driver for Fintek F8153x devices, support
for the GPIO functionality on CP2105 devices, and improved support for
CH34X devices.

Included are also some clean ups and fixes for various minor issues.

Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
parents 7faf44bf 3c3dd1e0
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -255,6 +255,16 @@ config USB_SERIAL_F81232
	  To compile this driver as a module, choose M here: the
	  module will be called f81232.

config USB_SERIAL_F8153X
	tristate "USB Fintek F81532/534 Multi-Ports Serial Driver"
	help
	  Say Y here if you want to use the Fintek F81532/534 Multi-Ports
	  USB to serial adapter.

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


config USB_SERIAL_GARMIN
       tristate "USB Garmin GPS driver"
       help
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT) += io_edgeport.o
obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI)		+= io_ti.o
obj-$(CONFIG_USB_SERIAL_EMPEG)			+= empeg.o
obj-$(CONFIG_USB_SERIAL_F81232)			+= f81232.o
obj-$(CONFIG_USB_SERIAL_F8153X)			+= f81534.o
obj-$(CONFIG_USB_SERIAL_FTDI_SIO)		+= ftdi_sio.o
obj-$(CONFIG_USB_SERIAL_GARMIN)			+= garmin_gps.o
obj-$(CONFIG_USB_SERIAL_IPAQ)			+= ipaq.o
+75 −38
Original line number Diff line number Diff line
@@ -61,13 +61,26 @@
 * the Net/FreeBSD uchcom.c driver by Takanori Watanabe.  Domo arigato.
 */

#define CH341_REQ_READ_VERSION 0x5F
#define CH341_REQ_WRITE_REG    0x9A
#define CH341_REQ_READ_REG     0x95
#define CH341_REG_BREAK1       0x05
#define CH341_REG_BREAK2       0x18
#define CH341_NBREAK_BITS_REG1 0x01
#define CH341_NBREAK_BITS_REG2 0x40

#define CH341_REQ_SERIAL_INIT  0xA1
#define CH341_REQ_MODEM_CTRL   0xA4

#define CH341_REG_BREAK        0x05
#define CH341_REG_LCR          0x18
#define CH341_NBREAK_BITS      0x01

#define CH341_LCR_ENABLE_RX    0x80
#define CH341_LCR_ENABLE_TX    0x40
#define CH341_LCR_MARK_SPACE   0x20
#define CH341_LCR_PAR_EVEN     0x10
#define CH341_LCR_ENABLE_PAR   0x08
#define CH341_LCR_STOP_BITS_2  0x04
#define CH341_LCR_CS8          0x03
#define CH341_LCR_CS7          0x02
#define CH341_LCR_CS6          0x01
#define CH341_LCR_CS5          0x00

static const struct usb_device_id id_table[] = {
	{ USB_DEVICE(0x4348, 0x5523) },
@@ -119,10 +132,10 @@ static int ch341_control_in(struct usb_device *dev,
	return r;
}

static int ch341_set_baudrate(struct usb_device *dev,
			      struct ch341_private *priv)
static int ch341_init_set_baudrate(struct usb_device *dev,
				   struct ch341_private *priv, unsigned ctrl)
{
	short a, b;
	short a;
	int r;
	unsigned long factor;
	short divisor;
@@ -142,18 +155,17 @@ static int ch341_set_baudrate(struct usb_device *dev,

	factor = 0x10000 - factor;
	a = (factor & 0xff00) | divisor;
	b = factor & 0xff;

	r = ch341_control_out(dev, 0x9a, 0x1312, a);
	if (!r)
		r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
	/* 0x9c is "enable SFR_UART Control register and timer" */
	r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT,
			      0x9c | (ctrl << 8), a | 0x80);

	return r;
}

static int ch341_set_handshake(struct usb_device *dev, u8 control)
{
	return ch341_control_out(dev, 0xa4, ~control, 0);
	return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0);
}

static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
@@ -167,7 +179,7 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
	if (!buffer)
		return -ENOMEM;

	r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
	r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
	if (r < 0)
		goto out;

@@ -197,24 +209,21 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
		return -ENOMEM;

	/* expect two bytes 0x27 0x00 */
	r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
	if (r < 0)
		goto out;

	r = ch341_control_out(dev, 0xa1, 0, 0);
	r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
	if (r < 0)
		goto out;
	dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);

	r = ch341_set_baudrate(dev, priv);
	r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
	if (r < 0)
		goto out;

	/* expect two bytes 0x56 0x00 */
	r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
	r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size);
	if (r < 0)
		goto out;

	r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
	r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, 0x0050);
	if (r < 0)
		goto out;

@@ -223,11 +232,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
	if (r < 0)
		goto out;

	r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
	if (r < 0)
		goto out;

	r = ch341_set_baudrate(dev, priv);
	r = ch341_init_set_baudrate(dev, priv, 0);
	if (r < 0)
		goto out;

@@ -342,16 +347,53 @@ static void ch341_set_termios(struct tty_struct *tty,
	struct ch341_private *priv = usb_get_serial_port_data(port);
	unsigned baud_rate;
	unsigned long flags;
	unsigned char ctrl;
	int r;

	/* redundant changes may cause the chip to lose bytes */
	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
		return;

	baud_rate = tty_get_baud_rate(tty);

	priv->baud_rate = baud_rate;
	ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;

	switch (C_CSIZE(tty)) {
	case CS5:
		ctrl |= CH341_LCR_CS5;
		break;
	case CS6:
		ctrl |= CH341_LCR_CS6;
		break;
	case CS7:
		ctrl |= CH341_LCR_CS7;
		break;
	case CS8:
		ctrl |= CH341_LCR_CS8;
		break;
	}

	if (C_PARENB(tty)) {
		ctrl |= CH341_LCR_ENABLE_PAR;
		if (C_PARODD(tty) == 0)
			ctrl |= CH341_LCR_PAR_EVEN;
		if (C_CMSPAR(tty))
			ctrl |= CH341_LCR_MARK_SPACE;
	}

	if (C_CSTOPB(tty))
		ctrl |= CH341_LCR_STOP_BITS_2;

	if (baud_rate) {
		spin_lock_irqsave(&priv->lock, flags);
		priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
		spin_unlock_irqrestore(&priv->lock, flags);
		ch341_set_baudrate(port->serial->dev, priv);
		r = ch341_init_set_baudrate(port->serial->dev, priv, ctrl);
		if (r < 0 && old_termios) {
			priv->baud_rate = tty_termios_baud_rate(old_termios);
			tty_termios_copy_hw(&tty->termios, old_termios);
		}
	} else {
		spin_lock_irqsave(&priv->lock, flags);
		priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
@@ -360,17 +402,12 @@ static void ch341_set_termios(struct tty_struct *tty,

	ch341_set_handshake(port->serial->dev, priv->line_control);

	/* Unimplemented:
	 * (cflag & CSIZE) : data bits [5, 8]
	 * (cflag & PARENB) : parity {NONE, EVEN, ODD}
	 * (cflag & CSTOPB) : stop bits [1, 2]
	 */
}

static void ch341_break_ctl(struct tty_struct *tty, int break_state)
{
	const uint16_t ch341_break_reg =
			((uint16_t) CH341_REG_BREAK2 << 8) | CH341_REG_BREAK1;
			((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
	struct usb_serial_port *port = tty->driver_data;
	int r;
	uint16_t reg_contents;
@@ -391,12 +428,12 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
		__func__, break_reg[0], break_reg[1]);
	if (break_state != 0) {
		dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
		break_reg[0] &= ~CH341_NBREAK_BITS_REG1;
		break_reg[1] &= ~CH341_NBREAK_BITS_REG2;
		break_reg[0] &= ~CH341_NBREAK_BITS;
		break_reg[1] &= ~CH341_LCR_ENABLE_TX;
	} else {
		dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
		break_reg[0] |= CH341_NBREAK_BITS_REG1;
		break_reg[1] |= CH341_NBREAK_BITS_REG2;
		break_reg[0] |= CH341_NBREAK_BITS;
		break_reg[1] |= CH341_LCR_ENABLE_TX;
	}
	dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
		__func__, break_reg[0], break_reg[1]);
+393 −18
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@
#include <linux/usb.h>
#include <linux/uaccess.h>
#include <linux/usb/serial.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.h>
#include <linux/mutex.h>

#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"

@@ -33,7 +36,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
static void cp210x_close(struct usb_serial_port *);
static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
static void cp210x_get_termios_port(struct usb_serial_port *port,
	unsigned int *cflagp, unsigned int *baudp);
	tcflag_t *cflagp, unsigned int *baudp);
static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
							struct ktermios *);
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
@@ -44,6 +47,9 @@ static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
static int cp210x_tiocmset_port(struct usb_serial_port *port,
		unsigned int, unsigned int);
static void cp210x_break_ctl(struct tty_struct *, int);
static int cp210x_attach(struct usb_serial *);
static void cp210x_disconnect(struct usb_serial *);
static void cp210x_release(struct usb_serial *);
static int cp210x_port_probe(struct usb_serial_port *);
static int cp210x_port_remove(struct usb_serial_port *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
@@ -209,6 +215,16 @@ static const struct usb_device_id id_table[] = {

MODULE_DEVICE_TABLE(usb, id_table);

struct cp210x_serial_private {
#ifdef CONFIG_GPIOLIB
	struct gpio_chip	gc;
	u8			config;
	u8			gpio_mode;
	bool			gpio_registered;
#endif
	u8			partnum;
};

struct cp210x_port_private {
	__u8			bInterfaceNumber;
	bool			has_swapped_line_ctl;
@@ -230,6 +246,9 @@ static struct usb_serial_driver cp210x_device = {
	.tx_empty		= cp210x_tx_empty,
	.tiocmget		= cp210x_tiocmget,
	.tiocmset		= cp210x_tiocmset,
	.attach			= cp210x_attach,
	.disconnect		= cp210x_disconnect,
	.release		= cp210x_release,
	.port_probe		= cp210x_port_probe,
	.port_remove		= cp210x_port_remove,
	.dtr_rts		= cp210x_dtr_rts
@@ -272,6 +291,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CP210X_SET_CHARS	0x19
#define CP210X_GET_BAUDRATE	0x1D
#define CP210X_SET_BAUDRATE	0x1E
#define CP210X_VENDOR_SPECIFIC	0xFF

/* CP210X_IFC_ENABLE */
#define UART_ENABLE		0x0001
@@ -314,6 +334,21 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CONTROL_WRITE_DTR	0x0100
#define CONTROL_WRITE_RTS	0x0200

/* CP210X_VENDOR_SPECIFIC values */
#define CP210X_READ_LATCH	0x00C2
#define CP210X_GET_PARTNUM	0x370B
#define CP210X_GET_PORTCONFIG	0x370C
#define CP210X_GET_DEVICEMODE	0x3711
#define CP210X_WRITE_LATCH	0x37E1

/* Part number definitions */
#define CP210X_PARTNUM_CP2101	0x01
#define CP210X_PARTNUM_CP2102	0x02
#define CP210X_PARTNUM_CP2103	0x03
#define CP210X_PARTNUM_CP2104	0x04
#define CP210X_PARTNUM_CP2105	0x05
#define CP210X_PARTNUM_CP2108	0x08

/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
struct cp210x_comm_status {
	__le32   ulErrors;
@@ -369,6 +404,60 @@ struct cp210x_flow_ctl {
#define CP210X_SERIAL_RTS_ACTIVE	1
#define CP210X_SERIAL_RTS_FLOW_CTL	2

/* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */
struct cp210x_pin_mode {
	u8	eci;
	u8	sci;
} __packed;

#define CP210X_PIN_MODE_MODEM		0
#define CP210X_PIN_MODE_GPIO		BIT(0)

/*
 * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes.
 * Structure needs padding due to unused/unspecified bytes.
 */
struct cp210x_config {
	__le16	gpio_mode;
	u8	__pad0[2];
	__le16	reset_state;
	u8	__pad1[4];
	__le16	suspend_state;
	u8	sci_cfg;
	u8	eci_cfg;
	u8	device_cfg;
} __packed;

/* GPIO modes */
#define CP210X_SCI_GPIO_MODE_OFFSET	9
#define CP210X_SCI_GPIO_MODE_MASK	GENMASK(11, 9)

#define CP210X_ECI_GPIO_MODE_OFFSET	2
#define CP210X_ECI_GPIO_MODE_MASK	GENMASK(3, 2)

/* CP2105 port configuration values */
#define CP2105_GPIO0_TXLED_MODE		BIT(0)
#define CP2105_GPIO1_RXLED_MODE		BIT(1)
#define CP2105_GPIO1_RS485_MODE		BIT(2)

/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
struct cp210x_gpio_write {
	u8	mask;
	u8	state;
} __packed;

/*
 * Helper to get interface number when we only have struct usb_serial.
 */
static u8 cp210x_interface_num(struct usb_serial *serial)
{
	struct usb_host_interface *cur_altsetting;

	cur_altsetting = serial->interface->cur_altsetting;

	return cur_altsetting->desc.bInterfaceNumber;
}

/*
 * Reads a variable-sized block of CP210X_ registers, identified by req.
 * Returns data into buf in native USB byte order.
@@ -402,7 +491,7 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
		dev_err(&port->dev, "failed get req 0x%x size %d status: %d\n",
				req, bufsize, result);
		if (result >= 0)
			result = -EPROTO;
			result = -EIO;

		/*
		 * FIXME Some callers don't bother to check for error,
@@ -464,6 +553,40 @@ static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val)
	return cp210x_read_reg_block(port, req, val, sizeof(*val));
}

/*
 * Reads a variable-sized vendor block of CP210X_ registers, identified by val.
 * Returns data into buf in native USB byte order.
 */
static int cp210x_read_vendor_block(struct usb_serial *serial, u8 type, u16 val,
				    void *buf, int bufsize)
{
	void *dmabuf;
	int result;

	dmabuf = kmalloc(bufsize, GFP_KERNEL);
	if (!dmabuf)
		return -ENOMEM;

	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
				 CP210X_VENDOR_SPECIFIC, type, val,
				 cp210x_interface_num(serial), dmabuf, bufsize,
				 USB_CTRL_GET_TIMEOUT);
	if (result == bufsize) {
		memcpy(buf, dmabuf, bufsize);
		result = 0;
	} else {
		dev_err(&serial->interface->dev,
			"failed to get vendor val 0x%04x size %d: %d\n", val,
			bufsize, result);
		if (result >= 0)
			result = -EIO;
	}

	kfree(dmabuf);

	return result;
}

/*
 * Writes any 16-bit CP210X_ register (req) whose value is passed
 * entirely in the wValue field of the USB request.
@@ -515,7 +638,7 @@ static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req,
		dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n",
				req, bufsize, result);
		if (result >= 0)
			result = -EPROTO;
			result = -EIO;
	}

	return result;
@@ -533,6 +656,42 @@ static int cp210x_write_u32_reg(struct usb_serial_port *port, u8 req, u32 val)
	return cp210x_write_reg_block(port, req, &le32_val, sizeof(le32_val));
}

#ifdef CONFIG_GPIOLIB
/*
 * Writes a variable-sized vendor block of CP210X_ registers, identified by val.
 * Data in buf must be in native USB byte order.
 */
static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type,
				     u16 val, void *buf, int bufsize)
{
	void *dmabuf;
	int result;

	dmabuf = kmemdup(buf, bufsize, GFP_KERNEL);
	if (!dmabuf)
		return -ENOMEM;

	result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
				 CP210X_VENDOR_SPECIFIC, type, val,
				 cp210x_interface_num(serial), dmabuf, bufsize,
				 USB_CTRL_SET_TIMEOUT);

	kfree(dmabuf);

	if (result == bufsize) {
		result = 0;
	} else {
		dev_err(&serial->interface->dev,
			"failed to set vendor val 0x%04x size %d: %d\n", val,
			bufsize, result);
		if (result >= 0)
			result = -EIO;
	}

	return result;
}
#endif

/*
 * Detect CP2108 GET_LINE_CTL bug and activate workaround.
 * Write a known good value 0x800, read it back.
@@ -683,7 +842,7 @@ static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
	} else {
		dev_err(&port->dev, "failed to get comm status: %d\n", result);
		if (result >= 0)
			result = -EPROTO;
			result = -EIO;
	}

	kfree(sts);
@@ -719,7 +878,7 @@ static void cp210x_get_termios(struct tty_struct *tty,
			&tty->termios.c_cflag, &baud);
		tty_encode_baud_rate(tty, baud, baud);
	} else {
		unsigned int cflag;
		tcflag_t cflag;
		cflag = 0;
		cp210x_get_termios_port(port, &cflag, &baud);
	}
@@ -730,10 +889,10 @@ static void cp210x_get_termios(struct tty_struct *tty,
 * This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
 */
static void cp210x_get_termios_port(struct usb_serial_port *port,
	unsigned int *cflagp, unsigned int *baudp)
	tcflag_t *cflagp, unsigned int *baudp)
{
	struct device *dev = &port->dev;
	unsigned int cflag;
	tcflag_t cflag;
	struct cp210x_flow_ctl flow_ctl;
	u32 baud;
	u16 bits;
@@ -930,16 +1089,9 @@ static void cp210x_set_termios(struct tty_struct *tty,
			dev_dbg(dev, "%s - data bits = 7\n", __func__);
			break;
		case CS8:
			bits |= BITS_DATA_8;
			dev_dbg(dev, "%s - data bits = 8\n", __func__);
			break;
		/*case CS9:
			bits |= BITS_DATA_9;
			dev_dbg(dev, "%s - data bits = 9\n", __func__);
			break;*/
		default:
			dev_dbg(dev, "cp210x driver does not support the number of bits requested, using 8 bit mode\n");
			bits |= BITS_DATA_8;
			dev_dbg(dev, "%s - data bits = 8\n", __func__);
			break;
		}
		if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
@@ -1108,10 +1260,188 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
	cp210x_write_u16_reg(port, CP210X_SET_BREAK, state);
}

#ifdef CONFIG_GPIOLIB
static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset)
{
	struct usb_serial *serial = gpiochip_get_data(gc);
	struct cp210x_serial_private *priv = usb_get_serial_data(serial);

	switch (offset) {
	case 0:
		if (priv->config & CP2105_GPIO0_TXLED_MODE)
			return -ENODEV;
		break;
	case 1:
		if (priv->config & (CP2105_GPIO1_RXLED_MODE |
				    CP2105_GPIO1_RS485_MODE))
			return -ENODEV;
		break;
	}

	return 0;
}

static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
	struct usb_serial *serial = gpiochip_get_data(gc);
	int result;
	u8 buf;

	result = cp210x_read_vendor_block(serial, REQTYPE_INTERFACE_TO_HOST,
					  CP210X_READ_LATCH, &buf, sizeof(buf));
	if (result < 0)
		return result;

	return !!(buf & BIT(gpio));
}

static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
{
	struct usb_serial *serial = gpiochip_get_data(gc);
	struct cp210x_gpio_write buf;

	if (value == 1)
		buf.state = BIT(gpio);
	else
		buf.state = 0;

	buf.mask = BIT(gpio);

	cp210x_write_vendor_block(serial, REQTYPE_HOST_TO_INTERFACE,
				  CP210X_WRITE_LATCH, &buf, sizeof(buf));
}

static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
{
	/* Hardware does not support an input mode */
	return 0;
}

static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
{
	/* Hardware does not support an input mode */
	return -ENOTSUPP;
}

static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
					int value)
{
	return 0;
}

static int cp210x_gpio_set_single_ended(struct gpio_chip *gc, unsigned int gpio,
					enum single_ended_mode mode)
{
	struct usb_serial *serial = gpiochip_get_data(gc);
	struct cp210x_serial_private *priv = usb_get_serial_data(serial);

	/* Succeed only if in correct mode (this can't be set at runtime) */
	if ((mode == LINE_MODE_PUSH_PULL) && (priv->gpio_mode & BIT(gpio)))
		return 0;

	if ((mode == LINE_MODE_OPEN_DRAIN) && !(priv->gpio_mode & BIT(gpio)))
		return 0;

	return -ENOTSUPP;
}

/*
 * This function is for configuring GPIO using shared pins, where other signals
 * are made unavailable by configuring the use of GPIO. This is believed to be
 * only applicable to the cp2105 at this point, the other devices supported by
 * this driver that provide GPIO do so in a way that does not impact other
 * signals and are thus expected to have very different initialisation.
 */
static int cp2105_shared_gpio_init(struct usb_serial *serial)
{
	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
	struct cp210x_pin_mode mode;
	struct cp210x_config config;
	u8 intf_num = cp210x_interface_num(serial);
	int result;

	result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
					  CP210X_GET_DEVICEMODE, &mode,
					  sizeof(mode));
	if (result < 0)
		return result;

	result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
					  CP210X_GET_PORTCONFIG, &config,
					  sizeof(config));
	if (result < 0)
		return result;

	/*  2 banks of GPIO - One for the pins taken from each serial port */
	if (intf_num == 0) {
		if (mode.eci == CP210X_PIN_MODE_MODEM)
			return 0;

		priv->config = config.eci_cfg;
		priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
						CP210X_ECI_GPIO_MODE_MASK) >>
						CP210X_ECI_GPIO_MODE_OFFSET);
		priv->gc.ngpio = 2;
	} else if (intf_num == 1) {
		if (mode.sci == CP210X_PIN_MODE_MODEM)
			return 0;

		priv->config = config.sci_cfg;
		priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
						CP210X_SCI_GPIO_MODE_MASK) >>
						CP210X_SCI_GPIO_MODE_OFFSET);
		priv->gc.ngpio = 3;
	} else {
		return -ENODEV;
	}

	priv->gc.label = "cp210x";
	priv->gc.request = cp210x_gpio_request;
	priv->gc.get_direction = cp210x_gpio_direction_get;
	priv->gc.direction_input = cp210x_gpio_direction_input;
	priv->gc.direction_output = cp210x_gpio_direction_output;
	priv->gc.get = cp210x_gpio_get;
	priv->gc.set = cp210x_gpio_set;
	priv->gc.set_single_ended = cp210x_gpio_set_single_ended;
	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, serial);
	if (!result)
		priv->gpio_registered = true;

	return result;
}

static void cp210x_gpio_remove(struct usb_serial *serial)
{
	struct cp210x_serial_private *priv = usb_get_serial_data(serial);

	if (priv->gpio_registered) {
		gpiochip_remove(&priv->gc);
		priv->gpio_registered = false;
	}
}

#else

static int cp2105_shared_gpio_init(struct usb_serial *serial)
{
	return 0;
}

static void cp210x_gpio_remove(struct usb_serial *serial)
{
	/* Nothing to do */
}

#endif

static int cp210x_port_probe(struct usb_serial_port *port)
{
	struct usb_serial *serial = port->serial;
	struct usb_host_interface *cur_altsetting;
	struct cp210x_port_private *port_priv;
	int ret;

@@ -1119,8 +1449,7 @@ static int cp210x_port_probe(struct usb_serial_port *port)
	if (!port_priv)
		return -ENOMEM;

	cur_altsetting = serial->interface->cur_altsetting;
	port_priv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
	port_priv->bInterfaceNumber = cp210x_interface_num(serial);

	usb_set_serial_port_data(port, port_priv);

@@ -1143,6 +1472,52 @@ static int cp210x_port_remove(struct usb_serial_port *port)
	return 0;
}

static int cp210x_attach(struct usb_serial *serial)
{
	int result;
	struct cp210x_serial_private *priv;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
					  CP210X_GET_PARTNUM, &priv->partnum,
					  sizeof(priv->partnum));
	if (result < 0)
		goto err_free_priv;

	usb_set_serial_data(serial, priv);

	if (priv->partnum == CP210X_PARTNUM_CP2105) {
		result = cp2105_shared_gpio_init(serial);
		if (result < 0) {
			dev_err(&serial->interface->dev,
				"GPIO initialisation failed, continuing without GPIO support\n");
		}
	}

	return 0;
err_free_priv:
	kfree(priv);

	return result;
}

static void cp210x_disconnect(struct usb_serial *serial)
{
	cp210x_gpio_remove(serial);
}

static void cp210x_release(struct usb_serial *serial)
{
	struct cp210x_serial_private *priv = usb_get_serial_data(serial);

	cp210x_gpio_remove(serial);

	kfree(priv);
}

module_usb_serial_driver(serial_drivers, id_table);

MODULE_DESCRIPTION(DRIVER_DESC);
+1409 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading