USB-serial updates for 5.1-rc1
Here are the USB-serial updates for 5.1-rc1, including: - support for the last three GPIOs on cp2102n devices - gpio support for cp2104 - support for using cp210x gpios with autosuspend - proper error handling for unsupported cp2105 line speeds Included is also a new modem device id. All have been in linux-next with no reported issues. Signed-off-by: Johan Hovold <johan@kernel.org> -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQHbPq+cpGvN/peuzMLxc3C7H1lCAUCXG7M3AAKCRALxc3C7H1l CBUpAQD/N1G1G38alGS0zwn/u9krsVyLm5Y1gK8MWXvpkcGBXwEAoSYrMjoRAAdR Ks4rQdSyYonbE3zENXp5d3beCRM4pQY= =GQ5N -----END PGP SIGNATURE----- Merge tag 'usb-serial-5.1-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next Johan writes: USB-serial updates for 5.1-rc1 Here are the USB-serial updates for 5.1-rc1, including: - support for the last three GPIOs on cp2102n devices - gpio support for cp2104 - support for using cp210x gpios with autosuspend - proper error handling for unsupported cp2105 line speeds Included is also a new modem device id. All have been in linux-next with no reported issues. Signed-off-by: Johan Hovold <johan@kernel.org> * tag 'usb-serial-5.1-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial: USB: serial: option: add Telit ME910 ECM composition USB: serial: cp210x: fix GPIO in autosuspend USB: serial: cp210x: add minimum baud rate for CP2105 SCI USB: serial: cp210x: add GPIO support for CP2104 USB: serial: cp210x: support all gpios on CP2102N QFN28 package
This commit is contained in:
Коммит
c9835df3e3
|
@ -245,6 +245,7 @@ struct cp210x_serial_private {
|
|||
u8 gpio_input;
|
||||
#endif
|
||||
u8 partnum;
|
||||
speed_t min_speed;
|
||||
speed_t max_speed;
|
||||
bool use_actual_rate;
|
||||
};
|
||||
|
@ -443,10 +444,10 @@ struct cp210x_pin_mode {
|
|||
#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.
|
||||
* CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes
|
||||
* on a CP2105 chip. Structure needs padding due to unused/unspecified bytes.
|
||||
*/
|
||||
struct cp210x_config {
|
||||
struct cp210x_dual_port_config {
|
||||
__le16 gpio_mode;
|
||||
u8 __pad0[2];
|
||||
__le16 reset_state;
|
||||
|
@ -457,6 +458,19 @@ struct cp210x_config {
|
|||
u8 device_cfg;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xd bytes
|
||||
* on a CP2104 chip. Structure needs padding due to unused/unspecified bytes.
|
||||
*/
|
||||
struct cp210x_single_port_config {
|
||||
__le16 gpio_mode;
|
||||
u8 __pad0[2];
|
||||
__le16 reset_state;
|
||||
u8 __pad1[4];
|
||||
__le16 suspend_state;
|
||||
u8 device_cfg;
|
||||
} __packed;
|
||||
|
||||
/* GPIO modes */
|
||||
#define CP210X_SCI_GPIO_MODE_OFFSET 9
|
||||
#define CP210X_SCI_GPIO_MODE_MASK GENMASK(11, 9)
|
||||
|
@ -464,11 +478,19 @@ struct cp210x_config {
|
|||
#define CP210X_ECI_GPIO_MODE_OFFSET 2
|
||||
#define CP210X_ECI_GPIO_MODE_MASK GENMASK(3, 2)
|
||||
|
||||
#define CP210X_GPIO_MODE_OFFSET 8
|
||||
#define CP210X_GPIO_MODE_MASK GENMASK(11, 8)
|
||||
|
||||
/* 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)
|
||||
|
||||
/* CP2104 port configuration values */
|
||||
#define CP2104_GPIO0_TXLED_MODE BIT(0)
|
||||
#define CP2104_GPIO1_RXLED_MODE BIT(1)
|
||||
#define CP2104_GPIO2_RS485_MODE BIT(2)
|
||||
|
||||
/* CP2102N configuration array indices */
|
||||
#define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2
|
||||
#define CP210X_2NCONFIG_GPIO_MODE_IDX 581
|
||||
|
@ -1051,14 +1073,11 @@ static speed_t cp210x_get_an205_rate(speed_t baud)
|
|||
return cp210x_an205_table1[i].rate;
|
||||
}
|
||||
|
||||
static speed_t cp210x_get_actual_rate(struct usb_serial *serial, speed_t baud)
|
||||
static speed_t cp210x_get_actual_rate(speed_t baud)
|
||||
{
|
||||
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
||||
unsigned int prescale = 1;
|
||||
unsigned int div;
|
||||
|
||||
baud = clamp(baud, 300u, priv->max_speed);
|
||||
|
||||
if (baud <= 365)
|
||||
prescale = 4;
|
||||
|
||||
|
@ -1101,20 +1120,18 @@ static void cp210x_change_speed(struct tty_struct *tty,
|
|||
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
||||
u32 baud;
|
||||
|
||||
baud = tty->termios.c_ospeed;
|
||||
|
||||
/*
|
||||
* This maps the requested rate to the actual rate, a valid rate on
|
||||
* cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed].
|
||||
*
|
||||
* NOTE: B0 is not implemented.
|
||||
*/
|
||||
baud = clamp(tty->termios.c_ospeed, priv->min_speed, priv->max_speed);
|
||||
|
||||
if (priv->use_actual_rate)
|
||||
baud = cp210x_get_actual_rate(serial, baud);
|
||||
baud = cp210x_get_actual_rate(baud);
|
||||
else if (baud < 1000000)
|
||||
baud = cp210x_get_an205_rate(baud);
|
||||
else if (baud > priv->max_speed)
|
||||
baud = priv->max_speed;
|
||||
|
||||
dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud);
|
||||
if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) {
|
||||
|
@ -1353,8 +1370,13 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
|
|||
if (priv->partnum == CP210X_PARTNUM_CP2105)
|
||||
req_type = REQTYPE_INTERFACE_TO_HOST;
|
||||
|
||||
result = usb_autopm_get_interface(serial->interface);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = cp210x_read_vendor_block(serial, req_type,
|
||||
CP210X_READ_LATCH, &buf, sizeof(buf));
|
||||
usb_autopm_put_interface(serial->interface);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
|
@ -1375,6 +1397,10 @@ static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
|||
|
||||
buf.mask = BIT(gpio);
|
||||
|
||||
result = usb_autopm_get_interface(serial->interface);
|
||||
if (result)
|
||||
goto out;
|
||||
|
||||
if (priv->partnum == CP210X_PARTNUM_CP2105) {
|
||||
result = cp210x_write_vendor_block(serial,
|
||||
REQTYPE_HOST_TO_INTERFACE,
|
||||
|
@ -1392,6 +1418,8 @@ static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
|||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
usb_autopm_put_interface(serial->interface);
|
||||
out:
|
||||
if (result < 0) {
|
||||
dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n",
|
||||
result);
|
||||
|
@ -1470,7 +1498,7 @@ static int cp2105_gpioconf_init(struct usb_serial *serial)
|
|||
{
|
||||
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
||||
struct cp210x_pin_mode mode;
|
||||
struct cp210x_config config;
|
||||
struct cp210x_dual_port_config config;
|
||||
u8 intf_num = cp210x_interface_num(serial);
|
||||
u8 iface_config;
|
||||
int result;
|
||||
|
@ -1529,6 +1557,56 @@ static int cp2105_gpioconf_init(struct usb_serial *serial)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cp2104_gpioconf_init(struct usb_serial *serial)
|
||||
{
|
||||
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
||||
struct cp210x_single_port_config config;
|
||||
u8 iface_config;
|
||||
u8 gpio_latch;
|
||||
int result;
|
||||
u8 i;
|
||||
|
||||
result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
|
||||
CP210X_GET_PORTCONFIG, &config,
|
||||
sizeof(config));
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
priv->gc.ngpio = 4;
|
||||
|
||||
iface_config = config.device_cfg;
|
||||
priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
|
||||
CP210X_GPIO_MODE_MASK) >>
|
||||
CP210X_GPIO_MODE_OFFSET);
|
||||
gpio_latch = (u8)((le16_to_cpu(config.reset_state) &
|
||||
CP210X_GPIO_MODE_MASK) >>
|
||||
CP210X_GPIO_MODE_OFFSET);
|
||||
|
||||
/* mark all pins which are not in GPIO mode */
|
||||
if (iface_config & CP2104_GPIO0_TXLED_MODE) /* GPIO 0 */
|
||||
priv->gpio_altfunc |= BIT(0);
|
||||
if (iface_config & CP2104_GPIO1_RXLED_MODE) /* GPIO 1 */
|
||||
priv->gpio_altfunc |= BIT(1);
|
||||
if (iface_config & CP2104_GPIO2_RS485_MODE) /* GPIO 2 */
|
||||
priv->gpio_altfunc |= BIT(2);
|
||||
|
||||
/*
|
||||
* Like CP2102N, CP2104 has also no strict input and output pin
|
||||
* modes.
|
||||
* Do the same input mode emulation as CP2102N.
|
||||
*/
|
||||
for (i = 0; i < priv->gc.ngpio; ++i) {
|
||||
/*
|
||||
* Set direction to "input" iff pin is open-drain and reset
|
||||
* value is 1.
|
||||
*/
|
||||
if (!(priv->gpio_pushpull & BIT(i)) && (gpio_latch & BIT(i)))
|
||||
priv->gpio_input |= BIT(i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cp2102n_gpioconf_init(struct usb_serial *serial)
|
||||
{
|
||||
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
||||
|
@ -1574,12 +1652,6 @@ static int cp2102n_gpioconf_init(struct usb_serial *serial)
|
|||
if (config_version != 0x01)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/*
|
||||
* We only support 4 GPIOs even on the QFN28 package, because
|
||||
* config locations of GPIOs 4-6 determined using reverse
|
||||
* engineering revealed conflicting offsets with other
|
||||
* documented functions. So we'll just play it safe for now.
|
||||
*/
|
||||
priv->gc.ngpio = 4;
|
||||
|
||||
/*
|
||||
|
@ -1594,6 +1666,19 @@ static int cp2102n_gpioconf_init(struct usb_serial *serial)
|
|||
/* 0 indicates GPIO mode, 1 is alternate function */
|
||||
priv->gpio_altfunc = (gpio_ctrl >> 2) & 0x0f;
|
||||
|
||||
if (priv->partnum == CP210X_PARTNUM_CP2102N_QFN28) {
|
||||
/*
|
||||
* For the QFN28 package, GPIO4-6 are controlled by
|
||||
* the low three bits of the mode/latch fields.
|
||||
* Contrary to the document linked above, the bits for
|
||||
* the SUSPEND pins are elsewhere. No alternate
|
||||
* function is available for these pins.
|
||||
*/
|
||||
priv->gc.ngpio = 7;
|
||||
gpio_latch |= (gpio_rst_latch & 7) << 4;
|
||||
priv->gpio_pushpull |= (gpio_pushpull & 7) << 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* The CP2102N does not strictly has input and output pin modes,
|
||||
* it only knows open-drain and push-pull modes which is set at
|
||||
|
@ -1620,6 +1705,9 @@ static int cp210x_gpio_init(struct usb_serial *serial)
|
|||
int result;
|
||||
|
||||
switch (priv->partnum) {
|
||||
case CP210X_PARTNUM_CP2104:
|
||||
result = cp2104_gpioconf_init(serial);
|
||||
break;
|
||||
case CP210X_PARTNUM_CP2105:
|
||||
result = cp2105_gpioconf_init(serial);
|
||||
break;
|
||||
|
@ -1716,6 +1804,7 @@ static void cp210x_init_max_speed(struct usb_serial *serial)
|
|||
{
|
||||
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
||||
bool use_actual_rate = false;
|
||||
speed_t min = 300;
|
||||
speed_t max;
|
||||
|
||||
switch (priv->partnum) {
|
||||
|
@ -1738,6 +1827,7 @@ static void cp210x_init_max_speed(struct usb_serial *serial)
|
|||
use_actual_rate = true;
|
||||
max = 2000000; /* ECI */
|
||||
} else {
|
||||
min = 2400;
|
||||
max = 921600; /* SCI */
|
||||
}
|
||||
break;
|
||||
|
@ -1752,6 +1842,7 @@ static void cp210x_init_max_speed(struct usb_serial *serial)
|
|||
break;
|
||||
}
|
||||
|
||||
priv->min_speed = min;
|
||||
priv->max_speed = max;
|
||||
priv->use_actual_rate = use_actual_rate;
|
||||
}
|
||||
|
|
|
@ -1148,6 +1148,8 @@ static const struct usb_device_id option_ids[] = {
|
|||
.driver_info = NCTRL(0) | RSVD(1) | RSVD(3) },
|
||||
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
|
||||
.driver_info = NCTRL(0) | RSVD(3) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1102, 0xff), /* Telit ME910 (ECM) */
|
||||
.driver_info = NCTRL(0) },
|
||||
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
|
||||
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
|
||||
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
|
||||
|
|
Загрузка…
Ссылка в новой задаче