diff options
Diffstat (limited to 'drivers/usb/serial/xr_serial.c')
-rw-r--r-- | drivers/usb/serial/xr_serial.c | 338 |
1 files changed, 262 insertions, 76 deletions
diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 003aa1e04c85..32055c763147 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -29,10 +29,6 @@ struct xr_txrx_clk_mask { #define XR21V141X_MIN_SPEED 46U #define XR21V141X_MAX_SPEED XR_INT_OSC_HZ -/* USB requests */ -#define XR21V141X_SET_REQ 0 -#define XR21V141X_GET_REQ 1 - /* XR21V141X register blocks */ #define XR21V141X_UART_REG_BLOCK 0 #define XR21V141X_UM_REG_BLOCK 4 @@ -65,9 +61,10 @@ struct xr_txrx_clk_mask { #define XR_GPIO_DTR BIT(3) #define XR_GPIO_CTS BIT(4) #define XR_GPIO_RTS BIT(5) - -#define XR21V141X_UART_BREAK_ON 0xff -#define XR21V141X_UART_BREAK_OFF 0 +#define XR_GPIO_CLK BIT(6) +#define XR_GPIO_XEN BIT(7) +#define XR_GPIO_TXT BIT(8) +#define XR_GPIO_RXT BIT(9) #define XR_UART_DATA_MASK GENMASK(3, 0) #define XR_UART_DATA_7 0x7 @@ -90,13 +87,27 @@ struct xr_txrx_clk_mask { #define XR_UART_FLOW_MODE_HW 0x1 #define XR_UART_FLOW_MODE_SW 0x2 -#define XR_GPIO_MODE_MASK GENMASK(2, 0) -#define XR_GPIO_MODE_RTS_CTS 0x1 -#define XR_GPIO_MODE_DTR_DSR 0x2 -#define XR_GPIO_MODE_RS485 0x3 -#define XR_GPIO_MODE_RS485_ADDR 0x4 +#define XR_GPIO_MODE_SEL_MASK GENMASK(2, 0) +#define XR_GPIO_MODE_SEL_RTS_CTS 0x1 +#define XR_GPIO_MODE_SEL_DTR_DSR 0x2 +#define XR_GPIO_MODE_SEL_RS485 0x3 +#define XR_GPIO_MODE_SEL_RS485_ADDR 0x4 +#define XR_GPIO_MODE_TX_TOGGLE 0x100 +#define XR_GPIO_MODE_RX_TOGGLE 0x200 + +#define XR_CUSTOM_DRIVER_ACTIVE 0x1 + +static int xr21v141x_uart_enable(struct usb_serial_port *port); +static int xr21v141x_uart_disable(struct usb_serial_port *port); +static void xr21v141x_set_line_settings(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); struct xr_type { + int reg_width; + u8 reg_recipient; + u8 set_reg; + u8 get_reg; + u8 uart_enable; u8 flow_control; u8 xon_char; @@ -107,15 +118,30 @@ struct xr_type { u8 gpio_set; u8 gpio_clear; u8 gpio_status; + u8 custom_driver; + + bool have_xmit_toggle; + + int (*enable)(struct usb_serial_port *port); + int (*disable)(struct usb_serial_port *port); + void (*set_line_settings)(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios); }; enum xr_type_id { XR21V141X, + XR21B142X, XR_TYPE_COUNT, }; static const struct xr_type xr_types[] = { [XR21V141X] = { + .reg_width = 8, + .reg_recipient = USB_RECIP_DEVICE, + .set_reg = 0x00, + .get_reg = 0x01, + .uart_enable = 0x03, .flow_control = 0x0c, .xon_char = 0x10, @@ -126,25 +152,50 @@ static const struct xr_type xr_types[] = { .gpio_set = 0x1d, .gpio_clear = 0x1e, .gpio_status = 0x1f, + + .enable = xr21v141x_uart_enable, + .disable = xr21v141x_uart_disable, + .set_line_settings = xr21v141x_set_line_settings, + }, + [XR21B142X] = { + .reg_width = 16, + .reg_recipient = USB_RECIP_INTERFACE, + .set_reg = 0x00, + .get_reg = 0x00, + + .uart_enable = 0x00, + .flow_control = 0x06, + .xon_char = 0x07, + .xoff_char = 0x08, + .tx_break = 0x0a, + .gpio_mode = 0x0c, + .gpio_direction = 0x0d, + .gpio_set = 0x0e, + .gpio_clear = 0x0f, + .gpio_status = 0x10, + .custom_driver = 0x60, + + .have_xmit_toggle = true, }, }; struct xr_data { const struct xr_type *type; - u8 channel; /* zero-based index */ + u8 channel; /* zero-based index or interface number */ }; -static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) +static int xr_set_reg(struct usb_serial_port *port, u8 channel, u8 reg, u16 val) { + struct xr_data *data = usb_get_serial_port_data(port); + const struct xr_type *type = data->type; struct usb_serial *serial = port->serial; int ret; - ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - XR21V141X_SET_REQ, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - val, reg | (block << 8), NULL, 0, - USB_CTRL_SET_TIMEOUT); + ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + type->set_reg, + USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, + val, (channel << 8) | reg, NULL, 0, + USB_CTRL_SET_TIMEOUT); if (ret < 0) { dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); return ret; @@ -153,24 +204,33 @@ static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) return 0; } -static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) +static int xr_get_reg(struct usb_serial_port *port, u8 channel, u8 reg, u16 *val) { + struct xr_data *data = usb_get_serial_port_data(port); + const struct xr_type *type = data->type; struct usb_serial *serial = port->serial; u8 *dmabuf; - int ret; + int ret, len; - dmabuf = kmalloc(1, GFP_KERNEL); + if (type->reg_width == 8) + len = 1; + else + len = 2; + + dmabuf = kmalloc(len, GFP_KERNEL); if (!dmabuf) return -ENOMEM; - ret = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - XR21V141X_GET_REQ, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, reg | (block << 8), dmabuf, 1, - USB_CTRL_GET_TIMEOUT); - if (ret == 1) { - *val = *dmabuf; + ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + type->get_reg, + USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, + 0, (channel << 8) | reg, dmabuf, len, + USB_CTRL_GET_TIMEOUT); + if (ret == len) { + if (len == 2) + *val = le16_to_cpup((__le16 *)dmabuf); + else + *val = *dmabuf; ret = 0; } else { dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); @@ -183,24 +243,18 @@ static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) return ret; } -static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u8 val) +static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u16 val) { struct xr_data *data = usb_get_serial_port_data(port); - u8 block; - - block = XR21V141X_UART_REG_BLOCK + data->channel; - return xr_set_reg(port, block, reg, val); + return xr_set_reg(port, data->channel, reg, val); } -static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u8 *val) +static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u16 *val) { struct xr_data *data = usb_get_serial_port_data(port); - u8 block; - block = XR21V141X_UART_REG_BLOCK + data->channel; - - return xr_get_reg(port, block, reg, val); + return xr_get_reg(port, data->channel, reg, val); } static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) @@ -213,6 +267,21 @@ static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); } +static int __xr_uart_enable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + return xr_set_reg_uart(port, data->type->uart_enable, + XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); +} + +static int __xr_uart_disable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + return xr_set_reg_uart(port, data->type->uart_enable, 0); +} + /* * According to datasheet, below is the recommended sequence for enabling UART * module in XR21V141X: @@ -221,9 +290,8 @@ static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) * Enable Tx and Rx * Enable Rx FIFO */ -static int xr_uart_enable(struct usb_serial_port *port) +static int xr21v141x_uart_enable(struct usb_serial_port *port) { - struct xr_data *data = usb_get_serial_port_data(port); int ret; ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, @@ -231,25 +299,23 @@ static int xr_uart_enable(struct usb_serial_port *port) if (ret) return ret; - ret = xr_set_reg_uart(port, data->type->uart_enable, - XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); + ret = __xr_uart_enable(port); if (ret) return ret; ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); if (ret) - xr_set_reg_uart(port, data->type->uart_enable, 0); + __xr_uart_disable(port); return ret; } -static int xr_uart_disable(struct usb_serial_port *port) +static int xr21v141x_uart_disable(struct usb_serial_port *port) { - struct xr_data *data = usb_get_serial_port_data(port); int ret; - ret = xr_set_reg_uart(port, data->type->uart_enable, 0); + ret = __xr_uart_disable(port); if (ret) return ret; @@ -258,11 +324,31 @@ static int xr_uart_disable(struct usb_serial_port *port) return ret; } +static int xr_uart_enable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + if (data->type->enable) + return data->type->enable(port); + + return __xr_uart_enable(port); +} + +static int xr_uart_disable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + if (data->type->disable) + return data->type->disable(port); + + return __xr_uart_disable(port); +} + static int xr_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct xr_data *data = usb_get_serial_port_data(port); - u8 status; + u16 status; int ret; ret = xr_get_reg_uart(port, data->type->gpio_status, &status); @@ -288,8 +374,8 @@ static int xr_tiocmset_port(struct usb_serial_port *port, { struct xr_data *data = usb_get_serial_port_data(port); const struct xr_type *type = data->type; - u8 gpio_set = 0; - u8 gpio_clr = 0; + u16 gpio_set = 0; + u16 gpio_clr = 0; int ret = 0; /* Modem control pins are active low, so set & clr are swapped */ @@ -333,15 +419,15 @@ static void xr_break_ctl(struct tty_struct *tty, int break_state) struct usb_serial_port *port = tty->driver_data; struct xr_data *data = usb_get_serial_port_data(port); const struct xr_type *type = data->type; - u8 state; + u16 state; if (break_state == 0) - state = XR21V141X_UART_BREAK_OFF; + state = 0; else - state = XR21V141X_UART_BREAK_ON; + state = GENMASK(type->reg_width - 1, 0); + + dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); - dev_dbg(&port->dev, "Turning break %s\n", - state == XR21V141X_UART_BREAK_OFF ? "off" : "on"); xr_set_reg_uart(port, type->tx_break, state); } @@ -381,8 +467,7 @@ static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { { 0xfff, 0xffe, 0xffd }, }; -static int xr_set_baudrate(struct tty_struct *tty, - struct usb_serial_port *port) +static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) { u32 divisor, baud, idx; u16 tx_mask, rx_mask; @@ -454,19 +539,26 @@ static void xr_set_flow_mode(struct tty_struct *tty, { struct xr_data *data = usb_get_serial_port_data(port); const struct xr_type *type = data->type; - u8 flow, gpio_mode; + u16 flow, gpio_mode; int ret; ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); if (ret) return; + /* + * According to the datasheets, the UART needs to be disabled while + * writing to the FLOW_CONTROL register (XR21V141X), or any register + * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). + */ + xr_uart_disable(port); + /* Set GPIO mode for controlling the pins manually by default. */ - gpio_mode &= ~XR_GPIO_MODE_MASK; + gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); - gpio_mode |= XR_GPIO_MODE_RTS_CTS; + gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; flow = XR_UART_FLOW_MODE_HW; } else if (I_IXON(tty)) { u8 start_char = START_CHAR(tty); @@ -482,32 +574,26 @@ static void xr_set_flow_mode(struct tty_struct *tty, flow = XR_UART_FLOW_MODE_NONE; } - /* - * As per the datasheet, UART needs to be disabled while writing to - * FLOW_CONTROL register. - */ - xr_uart_disable(port); xr_set_reg_uart(port, type->flow_control, flow); - xr_uart_enable(port); - xr_set_reg_uart(port, type->gpio_mode, gpio_mode); + xr_uart_enable(port); + if (C_BAUD(tty) == B0) xr_dtr_rts(port, 0); else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) xr_dtr_rts(port, 1); } -static void xr_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, - struct ktermios *old_termios) +static void xr21v141x_set_line_settings(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ktermios *termios = &tty->termios; u8 bits = 0; int ret; if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) - xr_set_baudrate(tty, port); + xr21v141x_set_baudrate(tty, port); switch (C_CSIZE(tty)) { case CS5: @@ -555,6 +641,88 @@ static void xr_set_termios(struct tty_struct *tty, ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); if (ret) return; +} + +static void xr_cdc_set_line_coding(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + struct usb_host_interface *alt = port->serial->interface->cur_altsetting; + struct usb_device *udev = port->serial->dev; + struct usb_cdc_line_coding *lc; + int ret; + + lc = kzalloc(sizeof(*lc), GFP_KERNEL); + if (!lc) + return; + + if (tty->termios.c_ospeed) + lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); + else if (old_termios) + lc->dwDTERate = cpu_to_le32(old_termios->c_ospeed); + else + lc->dwDTERate = cpu_to_le32(9600); + + if (C_CSTOPB(tty)) + lc->bCharFormat = USB_CDC_2_STOP_BITS; + else + lc->bCharFormat = USB_CDC_1_STOP_BITS; + + if (C_PARENB(tty)) { + if (C_CMSPAR(tty)) { + if (C_PARODD(tty)) + lc->bParityType = USB_CDC_MARK_PARITY; + else + lc->bParityType = USB_CDC_SPACE_PARITY; + } else { + if (C_PARODD(tty)) + lc->bParityType = USB_CDC_ODD_PARITY; + else + lc->bParityType = USB_CDC_EVEN_PARITY; + } + } else { + lc->bParityType = USB_CDC_NO_PARITY; + } + + switch (C_CSIZE(tty)) { + case CS5: + lc->bDataBits = 5; + break; + case CS6: + lc->bDataBits = 6; + break; + case CS7: + lc->bDataBits = 7; + break; + case CS8: + default: + lc->bDataBits = 8; + break; + } + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_CDC_REQ_SET_LINE_CODING, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, alt->desc.bInterfaceNumber, + lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); + if (ret < 0) + dev_err(&port->dev, "Failed to set line coding: %d\n", ret); + + kfree(lc); +} + +static void xr_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + /* + * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC + * mode upon receiving CDC requests. + */ + if (data->type->set_line_settings) + data->type->set_line_settings(tty, port, old_termios); + else + xr_cdc_set_line_coding(tty, port, old_termios); xr_set_flow_mode(tty, port, old_termios); } @@ -621,11 +789,16 @@ static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) { - u8 mask, mode; + u16 mask, mode; int ret; - /* Configure all pins as GPIO. */ + /* + * Configure all pins as GPIO except for Receive and Transmit Toggle. + */ mode = 0; + if (type->have_xmit_toggle) + mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; + ret = xr_set_reg_uart(port, type->gpio_mode, mode); if (ret) return ret; @@ -664,10 +837,20 @@ static int xr_port_probe(struct usb_serial_port *port) data->type = type; desc = &port->serial->interface->cur_altsetting->desc; - data->channel = desc->bInterfaceNumber / 2; + if (type_id == XR21V141X) + data->channel = desc->bInterfaceNumber / 2; + else + data->channel = desc->bInterfaceNumber; usb_set_serial_port_data(port, data); + if (type->custom_driver) { + ret = xr_set_reg_uart(port, type->custom_driver, + XR_CUSTOM_DRIVER_ACTIVE); + if (ret) + goto err_free; + } + ret = xr_gpio_init(port, type); if (ret) goto err_free; @@ -695,6 +878,9 @@ static const struct usb_device_id id_table[] = { { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, + { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, + { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, + { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, { } }; MODULE_DEVICE_TABLE(usb, id_table); |