1c2d405aaSManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0+ 2c2d405aaSManivannan Sadhasivam /* 3c2d405aaSManivannan Sadhasivam * MaxLinear/Exar USB to Serial driver 4c2d405aaSManivannan Sadhasivam * 5c2d405aaSManivannan Sadhasivam * Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org> 6c2d405aaSManivannan Sadhasivam * 7c2d405aaSManivannan Sadhasivam * Based on the initial driver written by Patong Yang: 8c2d405aaSManivannan Sadhasivam * 9c2d405aaSManivannan Sadhasivam * https://lore.kernel.org/r/20180404070634.nhspvmxcjwfgjkcv@advantechmxl-desktop 10c2d405aaSManivannan Sadhasivam * 11c2d405aaSManivannan Sadhasivam * Copyright (c) 2018 Patong Yang <patong.mxl@gmail.com> 12c2d405aaSManivannan Sadhasivam */ 13c2d405aaSManivannan Sadhasivam 14c2d405aaSManivannan Sadhasivam #include <linux/kernel.h> 15c2d405aaSManivannan Sadhasivam #include <linux/module.h> 16c2d405aaSManivannan Sadhasivam #include <linux/slab.h> 17c2d405aaSManivannan Sadhasivam #include <linux/tty.h> 18c2d405aaSManivannan Sadhasivam #include <linux/usb.h> 195fec21e7SJohan Hovold #include <linux/usb/cdc.h> 20c2d405aaSManivannan Sadhasivam #include <linux/usb/serial.h> 21c2d405aaSManivannan Sadhasivam 22c2d405aaSManivannan Sadhasivam struct xr_txrx_clk_mask { 23c2d405aaSManivannan Sadhasivam u16 tx; 24c2d405aaSManivannan Sadhasivam u16 rx0; 25c2d405aaSManivannan Sadhasivam u16 rx1; 26c2d405aaSManivannan Sadhasivam }; 27c2d405aaSManivannan Sadhasivam 28c2d405aaSManivannan Sadhasivam #define XR_INT_OSC_HZ 48000000U 29c2d405aaSManivannan Sadhasivam #define XR21V141X_MIN_SPEED 46U 30c2d405aaSManivannan Sadhasivam #define XR21V141X_MAX_SPEED XR_INT_OSC_HZ 31c2d405aaSManivannan Sadhasivam 32*f865e614SJohan Hovold /* USB requests */ 33c2d405aaSManivannan Sadhasivam #define XR21V141X_SET_REQ 0 34c2d405aaSManivannan Sadhasivam #define XR21V141X_GET_REQ 1 35c2d405aaSManivannan Sadhasivam 36*f865e614SJohan Hovold /* XR21V141X register blocks */ 37*f865e614SJohan Hovold #define XR21V141X_UART_REG_BLOCK 0 38*f865e614SJohan Hovold #define XR21V141X_UM_REG_BLOCK 4 39*f865e614SJohan Hovold #define XR21V141X_UART_CUSTOM_BLOCK 0x66 40*f865e614SJohan Hovold 41*f865e614SJohan Hovold /* XR21V141X UART registers */ 42c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_0 0x04 43c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_1 0x05 44c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_2 0x06 45c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_0 0x07 46c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_1 0x08 47c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_0 0x09 48c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_1 0x0a 49*f865e614SJohan Hovold #define XR21V141X_REG_FORMAT 0x0b 50c2d405aaSManivannan Sadhasivam 51*f865e614SJohan Hovold /* XR21V141X UART Manager registers */ 52c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_FIFO_ENABLE_REG 0x10 53c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_TX_FIFO 0x01 54c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_RX_FIFO 0x02 55c2d405aaSManivannan Sadhasivam 56c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_RX_FIFO_RESET 0x18 57c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_TX_FIFO_RESET 0x1c 58c2d405aaSManivannan Sadhasivam 59958d6b95SJohan Hovold #define XR_UART_ENABLE_TX 0x1 60958d6b95SJohan Hovold #define XR_UART_ENABLE_RX 0x2 61c2d405aaSManivannan Sadhasivam 62958d6b95SJohan Hovold #define XR_GPIO_RI BIT(0) 63958d6b95SJohan Hovold #define XR_GPIO_CD BIT(1) 64958d6b95SJohan Hovold #define XR_GPIO_DSR BIT(2) 65958d6b95SJohan Hovold #define XR_GPIO_DTR BIT(3) 66958d6b95SJohan Hovold #define XR_GPIO_CTS BIT(4) 67958d6b95SJohan Hovold #define XR_GPIO_RTS BIT(5) 68c2d405aaSManivannan Sadhasivam 69c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_BREAK_ON 0xff 70c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_BREAK_OFF 0 71c2d405aaSManivannan Sadhasivam 72958d6b95SJohan Hovold #define XR_UART_DATA_MASK GENMASK(3, 0) 73958d6b95SJohan Hovold #define XR_UART_DATA_7 0x7 74958d6b95SJohan Hovold #define XR_UART_DATA_8 0x8 75c2d405aaSManivannan Sadhasivam 76958d6b95SJohan Hovold #define XR_UART_PARITY_MASK GENMASK(6, 4) 77958d6b95SJohan Hovold #define XR_UART_PARITY_SHIFT 4 78958d6b95SJohan Hovold #define XR_UART_PARITY_NONE (0x0 << XR_UART_PARITY_SHIFT) 79958d6b95SJohan Hovold #define XR_UART_PARITY_ODD (0x1 << XR_UART_PARITY_SHIFT) 80958d6b95SJohan Hovold #define XR_UART_PARITY_EVEN (0x2 << XR_UART_PARITY_SHIFT) 81958d6b95SJohan Hovold #define XR_UART_PARITY_MARK (0x3 << XR_UART_PARITY_SHIFT) 82958d6b95SJohan Hovold #define XR_UART_PARITY_SPACE (0x4 << XR_UART_PARITY_SHIFT) 83c2d405aaSManivannan Sadhasivam 84958d6b95SJohan Hovold #define XR_UART_STOP_MASK BIT(7) 85958d6b95SJohan Hovold #define XR_UART_STOP_SHIFT 7 86958d6b95SJohan Hovold #define XR_UART_STOP_1 (0x0 << XR_UART_STOP_SHIFT) 87958d6b95SJohan Hovold #define XR_UART_STOP_2 (0x1 << XR_UART_STOP_SHIFT) 88c2d405aaSManivannan Sadhasivam 89958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_NONE 0x0 90958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_HW 0x1 91958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_SW 0x2 92c2d405aaSManivannan Sadhasivam 93958d6b95SJohan Hovold #define XR_GPIO_MODE_MASK GENMASK(2, 0) 94958d6b95SJohan Hovold #define XR_GPIO_MODE_RTS_CTS 0x1 95958d6b95SJohan Hovold #define XR_GPIO_MODE_DTR_DSR 0x2 96958d6b95SJohan Hovold #define XR_GPIO_MODE_RS485 0x3 97958d6b95SJohan Hovold #define XR_GPIO_MODE_RS485_ADDR 0x4 98c2d405aaSManivannan Sadhasivam 99*f865e614SJohan Hovold struct xr_type { 100*f865e614SJohan Hovold u8 uart_enable; 101*f865e614SJohan Hovold u8 flow_control; 102*f865e614SJohan Hovold u8 xon_char; 103*f865e614SJohan Hovold u8 xoff_char; 104*f865e614SJohan Hovold u8 tx_break; 105*f865e614SJohan Hovold u8 gpio_mode; 106*f865e614SJohan Hovold u8 gpio_direction; 107*f865e614SJohan Hovold u8 gpio_set; 108*f865e614SJohan Hovold u8 gpio_clear; 109*f865e614SJohan Hovold u8 gpio_status; 110*f865e614SJohan Hovold }; 111*f865e614SJohan Hovold 112*f865e614SJohan Hovold enum xr_type_id { 113*f865e614SJohan Hovold XR21V141X, 114*f865e614SJohan Hovold XR_TYPE_COUNT, 115*f865e614SJohan Hovold }; 116*f865e614SJohan Hovold 117*f865e614SJohan Hovold static const struct xr_type xr_types[] = { 118*f865e614SJohan Hovold [XR21V141X] = { 119*f865e614SJohan Hovold .uart_enable = 0x03, 120*f865e614SJohan Hovold .flow_control = 0x0c, 121*f865e614SJohan Hovold .xon_char = 0x10, 122*f865e614SJohan Hovold .xoff_char = 0x11, 123*f865e614SJohan Hovold .tx_break = 0x14, 124*f865e614SJohan Hovold .gpio_mode = 0x1a, 125*f865e614SJohan Hovold .gpio_direction = 0x1b, 126*f865e614SJohan Hovold .gpio_set = 0x1d, 127*f865e614SJohan Hovold .gpio_clear = 0x1e, 128*f865e614SJohan Hovold .gpio_status = 0x1f, 129*f865e614SJohan Hovold }, 130*f865e614SJohan Hovold }; 131c2d405aaSManivannan Sadhasivam 13223b7998eSJohan Hovold struct xr_data { 133*f865e614SJohan Hovold const struct xr_type *type; 13423b7998eSJohan Hovold u8 channel; /* zero-based index */ 13523b7998eSJohan Hovold }; 13623b7998eSJohan Hovold 137c2d405aaSManivannan Sadhasivam static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) 138c2d405aaSManivannan Sadhasivam { 139c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 140c2d405aaSManivannan Sadhasivam int ret; 141c2d405aaSManivannan Sadhasivam 142c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 143c2d405aaSManivannan Sadhasivam usb_sndctrlpipe(serial->dev, 0), 144c2d405aaSManivannan Sadhasivam XR21V141X_SET_REQ, 14572fc7fc7SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 14672fc7fc7SJohan Hovold val, reg | (block << 8), NULL, 0, 147c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 148c2d405aaSManivannan Sadhasivam if (ret < 0) { 149c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 150c2d405aaSManivannan Sadhasivam return ret; 151c2d405aaSManivannan Sadhasivam } 152c2d405aaSManivannan Sadhasivam 153c2d405aaSManivannan Sadhasivam return 0; 154c2d405aaSManivannan Sadhasivam } 155c2d405aaSManivannan Sadhasivam 156c2d405aaSManivannan Sadhasivam static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) 157c2d405aaSManivannan Sadhasivam { 158c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 159c2d405aaSManivannan Sadhasivam u8 *dmabuf; 160c2d405aaSManivannan Sadhasivam int ret; 161c2d405aaSManivannan Sadhasivam 162c2d405aaSManivannan Sadhasivam dmabuf = kmalloc(1, GFP_KERNEL); 163c2d405aaSManivannan Sadhasivam if (!dmabuf) 164c2d405aaSManivannan Sadhasivam return -ENOMEM; 165c2d405aaSManivannan Sadhasivam 166c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 167c2d405aaSManivannan Sadhasivam usb_rcvctrlpipe(serial->dev, 0), 168c2d405aaSManivannan Sadhasivam XR21V141X_GET_REQ, 16972fc7fc7SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 17072fc7fc7SJohan Hovold 0, reg | (block << 8), dmabuf, 1, 171c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 172c2d405aaSManivannan Sadhasivam if (ret == 1) { 173c2d405aaSManivannan Sadhasivam *val = *dmabuf; 174c2d405aaSManivannan Sadhasivam ret = 0; 175c2d405aaSManivannan Sadhasivam } else { 176c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 177c2d405aaSManivannan Sadhasivam if (ret >= 0) 178c2d405aaSManivannan Sadhasivam ret = -EIO; 179c2d405aaSManivannan Sadhasivam } 180c2d405aaSManivannan Sadhasivam 181c2d405aaSManivannan Sadhasivam kfree(dmabuf); 182c2d405aaSManivannan Sadhasivam 183c2d405aaSManivannan Sadhasivam return ret; 184c2d405aaSManivannan Sadhasivam } 185c2d405aaSManivannan Sadhasivam 186c2d405aaSManivannan Sadhasivam static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u8 val) 187c2d405aaSManivannan Sadhasivam { 18823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 18923b7998eSJohan Hovold u8 block; 19023b7998eSJohan Hovold 19123b7998eSJohan Hovold block = XR21V141X_UART_REG_BLOCK + data->channel; 19223b7998eSJohan Hovold 19323b7998eSJohan Hovold return xr_set_reg(port, block, reg, val); 194c2d405aaSManivannan Sadhasivam } 195c2d405aaSManivannan Sadhasivam 196c2d405aaSManivannan Sadhasivam static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u8 *val) 197c2d405aaSManivannan Sadhasivam { 19823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 19923b7998eSJohan Hovold u8 block; 20023b7998eSJohan Hovold 20123b7998eSJohan Hovold block = XR21V141X_UART_REG_BLOCK + data->channel; 20223b7998eSJohan Hovold 20323b7998eSJohan Hovold return xr_get_reg(port, block, reg, val); 204c2d405aaSManivannan Sadhasivam } 205c2d405aaSManivannan Sadhasivam 20623b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 207c2d405aaSManivannan Sadhasivam { 20823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 20923b7998eSJohan Hovold u8 reg; 21023b7998eSJohan Hovold 21123b7998eSJohan Hovold reg = reg_base + data->channel; 21223b7998eSJohan Hovold 213c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 214c2d405aaSManivannan Sadhasivam } 215c2d405aaSManivannan Sadhasivam 216c2d405aaSManivannan Sadhasivam /* 217c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 218c2d405aaSManivannan Sadhasivam * module in XR21V141X: 219c2d405aaSManivannan Sadhasivam * 220c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 221c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 222c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 223c2d405aaSManivannan Sadhasivam */ 224c2d405aaSManivannan Sadhasivam static int xr_uart_enable(struct usb_serial_port *port) 225c2d405aaSManivannan Sadhasivam { 226*f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 227c2d405aaSManivannan Sadhasivam int ret; 228c2d405aaSManivannan Sadhasivam 229c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 230c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 231c2d405aaSManivannan Sadhasivam if (ret) 232c2d405aaSManivannan Sadhasivam return ret; 233c2d405aaSManivannan Sadhasivam 234*f865e614SJohan Hovold ret = xr_set_reg_uart(port, data->type->uart_enable, 235958d6b95SJohan Hovold XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); 236c2d405aaSManivannan Sadhasivam if (ret) 237c2d405aaSManivannan Sadhasivam return ret; 238c2d405aaSManivannan Sadhasivam 239c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 240c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 241c2d405aaSManivannan Sadhasivam if (ret) 242*f865e614SJohan Hovold xr_set_reg_uart(port, data->type->uart_enable, 0); 243c2d405aaSManivannan Sadhasivam 244c2d405aaSManivannan Sadhasivam return ret; 245c2d405aaSManivannan Sadhasivam } 246c2d405aaSManivannan Sadhasivam 247c2d405aaSManivannan Sadhasivam static int xr_uart_disable(struct usb_serial_port *port) 248c2d405aaSManivannan Sadhasivam { 249*f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 250c2d405aaSManivannan Sadhasivam int ret; 251c2d405aaSManivannan Sadhasivam 252*f865e614SJohan Hovold ret = xr_set_reg_uart(port, data->type->uart_enable, 0); 253c2d405aaSManivannan Sadhasivam if (ret) 254c2d405aaSManivannan Sadhasivam return ret; 255c2d405aaSManivannan Sadhasivam 256c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 257c2d405aaSManivannan Sadhasivam 258c2d405aaSManivannan Sadhasivam return ret; 259c2d405aaSManivannan Sadhasivam } 260c2d405aaSManivannan Sadhasivam 261c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 262c2d405aaSManivannan Sadhasivam { 263c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 264*f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 265c2d405aaSManivannan Sadhasivam u8 status; 266c2d405aaSManivannan Sadhasivam int ret; 267c2d405aaSManivannan Sadhasivam 268*f865e614SJohan Hovold ret = xr_get_reg_uart(port, data->type->gpio_status, &status); 269c2d405aaSManivannan Sadhasivam if (ret) 270c2d405aaSManivannan Sadhasivam return ret; 271c2d405aaSManivannan Sadhasivam 272c2d405aaSManivannan Sadhasivam /* 273c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 274c2d405aaSManivannan Sadhasivam * and '1' means not active. 275c2d405aaSManivannan Sadhasivam */ 276958d6b95SJohan Hovold ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | 277958d6b95SJohan Hovold ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | 278958d6b95SJohan Hovold ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | 279958d6b95SJohan Hovold ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | 280958d6b95SJohan Hovold ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | 281958d6b95SJohan Hovold ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); 282c2d405aaSManivannan Sadhasivam 283c2d405aaSManivannan Sadhasivam return ret; 284c2d405aaSManivannan Sadhasivam } 285c2d405aaSManivannan Sadhasivam 286c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 287c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 288c2d405aaSManivannan Sadhasivam { 289*f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 290*f865e614SJohan Hovold const struct xr_type *type = data->type; 291c2d405aaSManivannan Sadhasivam u8 gpio_set = 0; 292c2d405aaSManivannan Sadhasivam u8 gpio_clr = 0; 293c2d405aaSManivannan Sadhasivam int ret = 0; 294c2d405aaSManivannan Sadhasivam 295c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 296c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 297958d6b95SJohan Hovold gpio_clr |= XR_GPIO_RTS; 298c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 299958d6b95SJohan Hovold gpio_clr |= XR_GPIO_DTR; 300c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 301958d6b95SJohan Hovold gpio_set |= XR_GPIO_RTS; 302c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 303958d6b95SJohan Hovold gpio_set |= XR_GPIO_DTR; 304c2d405aaSManivannan Sadhasivam 305c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 306c2d405aaSManivannan Sadhasivam if (gpio_clr) 307*f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); 308c2d405aaSManivannan Sadhasivam 309c2d405aaSManivannan Sadhasivam if (gpio_set) 310*f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); 311c2d405aaSManivannan Sadhasivam 312c2d405aaSManivannan Sadhasivam return ret; 313c2d405aaSManivannan Sadhasivam } 314c2d405aaSManivannan Sadhasivam 315c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 316c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 317c2d405aaSManivannan Sadhasivam { 318c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 319c2d405aaSManivannan Sadhasivam 320c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 321c2d405aaSManivannan Sadhasivam } 322c2d405aaSManivannan Sadhasivam 323c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 324c2d405aaSManivannan Sadhasivam { 325c2d405aaSManivannan Sadhasivam if (on) 326c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 327c2d405aaSManivannan Sadhasivam else 328c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 329c2d405aaSManivannan Sadhasivam } 330c2d405aaSManivannan Sadhasivam 331c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 332c2d405aaSManivannan Sadhasivam { 333c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 334*f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 335*f865e614SJohan Hovold const struct xr_type *type = data->type; 336c2d405aaSManivannan Sadhasivam u8 state; 337c2d405aaSManivannan Sadhasivam 338c2d405aaSManivannan Sadhasivam if (break_state == 0) 339c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_OFF; 340c2d405aaSManivannan Sadhasivam else 341c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_ON; 342c2d405aaSManivannan Sadhasivam 343c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Turning break %s\n", 344c2d405aaSManivannan Sadhasivam state == XR21V141X_UART_BREAK_OFF ? "off" : "on"); 345*f865e614SJohan Hovold xr_set_reg_uart(port, type->tx_break, state); 346c2d405aaSManivannan Sadhasivam } 347c2d405aaSManivannan Sadhasivam 348c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 349c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 350c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 351c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 352c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 353c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 354c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 355c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 356c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 357c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 358c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 359c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 360c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 361c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 362c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 363c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 364c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 365c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 366c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 367c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 368c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 369c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 370c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 371c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 372c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 373c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 374c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 375c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 376c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 377c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 378c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 379c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 380c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 381c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 382c2d405aaSManivannan Sadhasivam }; 383c2d405aaSManivannan Sadhasivam 384c2d405aaSManivannan Sadhasivam static int xr_set_baudrate(struct tty_struct *tty, 385c2d405aaSManivannan Sadhasivam struct usb_serial_port *port) 386c2d405aaSManivannan Sadhasivam { 387c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 388c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 389c2d405aaSManivannan Sadhasivam int ret; 390c2d405aaSManivannan Sadhasivam 39155317e22SJohan Hovold baud = tty->termios.c_ospeed; 39255317e22SJohan Hovold if (!baud) 39355317e22SJohan Hovold return 0; 39455317e22SJohan Hovold 39555317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 396c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 397c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 398c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 399c2d405aaSManivannan Sadhasivam 400c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 401c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 402c2d405aaSManivannan Sadhasivam else 403c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 404c2d405aaSManivannan Sadhasivam 405c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 406c2d405aaSManivannan Sadhasivam /* 407c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 408c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 409c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 410c2d405aaSManivannan Sadhasivam */ 411c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 412c2d405aaSManivannan Sadhasivam divisor & 0xff); 413c2d405aaSManivannan Sadhasivam if (ret) 414c2d405aaSManivannan Sadhasivam return ret; 415c2d405aaSManivannan Sadhasivam 416c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 417c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 418c2d405aaSManivannan Sadhasivam if (ret) 419c2d405aaSManivannan Sadhasivam return ret; 420c2d405aaSManivannan Sadhasivam 421c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 422c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 423c2d405aaSManivannan Sadhasivam if (ret) 424c2d405aaSManivannan Sadhasivam return ret; 425c2d405aaSManivannan Sadhasivam 426c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 427c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 428c2d405aaSManivannan Sadhasivam if (ret) 429c2d405aaSManivannan Sadhasivam return ret; 430c2d405aaSManivannan Sadhasivam 431c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 432c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 433c2d405aaSManivannan Sadhasivam if (ret) 434c2d405aaSManivannan Sadhasivam return ret; 435c2d405aaSManivannan Sadhasivam 436c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 437c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 438c2d405aaSManivannan Sadhasivam if (ret) 439c2d405aaSManivannan Sadhasivam return ret; 440c2d405aaSManivannan Sadhasivam 441c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 442c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 443c2d405aaSManivannan Sadhasivam if (ret) 444c2d405aaSManivannan Sadhasivam return ret; 445c2d405aaSManivannan Sadhasivam 446c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 447c2d405aaSManivannan Sadhasivam 448c2d405aaSManivannan Sadhasivam return 0; 449c2d405aaSManivannan Sadhasivam } 450c2d405aaSManivannan Sadhasivam 451c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 45255317e22SJohan Hovold struct usb_serial_port *port, 45355317e22SJohan Hovold struct ktermios *old_termios) 454c2d405aaSManivannan Sadhasivam { 455*f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 456*f865e614SJohan Hovold const struct xr_type *type = data->type; 457c2d405aaSManivannan Sadhasivam u8 flow, gpio_mode; 458c2d405aaSManivannan Sadhasivam int ret; 459c2d405aaSManivannan Sadhasivam 460*f865e614SJohan Hovold ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); 461c2d405aaSManivannan Sadhasivam if (ret) 462c2d405aaSManivannan Sadhasivam return; 463c2d405aaSManivannan Sadhasivam 464465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 465958d6b95SJohan Hovold gpio_mode &= ~XR_GPIO_MODE_MASK; 466465d3b3aSJohan Hovold 46755317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 468c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 469958d6b95SJohan Hovold gpio_mode |= XR_GPIO_MODE_RTS_CTS; 470958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_HW; 471c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 472c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 473c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 474c2d405aaSManivannan Sadhasivam 475c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 476958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_SW; 477c2d405aaSManivannan Sadhasivam 478*f865e614SJohan Hovold xr_set_reg_uart(port, type->xon_char, start_char); 479*f865e614SJohan Hovold xr_set_reg_uart(port, type->xoff_char, stop_char); 480c2d405aaSManivannan Sadhasivam } else { 481c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 482958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_NONE; 483c2d405aaSManivannan Sadhasivam } 484c2d405aaSManivannan Sadhasivam 485c2d405aaSManivannan Sadhasivam /* 486c2d405aaSManivannan Sadhasivam * As per the datasheet, UART needs to be disabled while writing to 487c2d405aaSManivannan Sadhasivam * FLOW_CONTROL register. 488c2d405aaSManivannan Sadhasivam */ 489c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 490*f865e614SJohan Hovold xr_set_reg_uart(port, type->flow_control, flow); 491c2d405aaSManivannan Sadhasivam xr_uart_enable(port); 492c2d405aaSManivannan Sadhasivam 493*f865e614SJohan Hovold xr_set_reg_uart(port, type->gpio_mode, gpio_mode); 49455317e22SJohan Hovold 49555317e22SJohan Hovold if (C_BAUD(tty) == B0) 49655317e22SJohan Hovold xr_dtr_rts(port, 0); 49755317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 49855317e22SJohan Hovold xr_dtr_rts(port, 1); 499c2d405aaSManivannan Sadhasivam } 500c2d405aaSManivannan Sadhasivam 501c2d405aaSManivannan Sadhasivam static void xr_set_termios(struct tty_struct *tty, 502c2d405aaSManivannan Sadhasivam struct usb_serial_port *port, 503c2d405aaSManivannan Sadhasivam struct ktermios *old_termios) 504c2d405aaSManivannan Sadhasivam { 505c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 506c2d405aaSManivannan Sadhasivam u8 bits = 0; 507c2d405aaSManivannan Sadhasivam int ret; 508c2d405aaSManivannan Sadhasivam 509736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 510c2d405aaSManivannan Sadhasivam xr_set_baudrate(tty, port); 511c2d405aaSManivannan Sadhasivam 512c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 513c2d405aaSManivannan Sadhasivam case CS5: 514c2d405aaSManivannan Sadhasivam case CS6: 515c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 516c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 517c2d405aaSManivannan Sadhasivam if (old_termios) 518c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 519c2d405aaSManivannan Sadhasivam else 520ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 521ea7ada4dSJohan Hovold 522ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 523958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 524ea7ada4dSJohan Hovold else 525958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 526c2d405aaSManivannan Sadhasivam break; 527c2d405aaSManivannan Sadhasivam case CS7: 528958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 529c2d405aaSManivannan Sadhasivam break; 530c2d405aaSManivannan Sadhasivam case CS8: 531c2d405aaSManivannan Sadhasivam default: 532958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 533c2d405aaSManivannan Sadhasivam break; 534c2d405aaSManivannan Sadhasivam } 535c2d405aaSManivannan Sadhasivam 536c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 537c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 538c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 539958d6b95SJohan Hovold bits |= XR_UART_PARITY_MARK; 540c2d405aaSManivannan Sadhasivam else 541958d6b95SJohan Hovold bits |= XR_UART_PARITY_SPACE; 542c2d405aaSManivannan Sadhasivam } else { 543c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 544958d6b95SJohan Hovold bits |= XR_UART_PARITY_ODD; 545c2d405aaSManivannan Sadhasivam else 546958d6b95SJohan Hovold bits |= XR_UART_PARITY_EVEN; 547c2d405aaSManivannan Sadhasivam } 548c2d405aaSManivannan Sadhasivam } 549c2d405aaSManivannan Sadhasivam 550c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 551958d6b95SJohan Hovold bits |= XR_UART_STOP_2; 552c2d405aaSManivannan Sadhasivam else 553958d6b95SJohan Hovold bits |= XR_UART_STOP_1; 554c2d405aaSManivannan Sadhasivam 555c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 556c2d405aaSManivannan Sadhasivam if (ret) 557c2d405aaSManivannan Sadhasivam return; 558c2d405aaSManivannan Sadhasivam 55955317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 560c2d405aaSManivannan Sadhasivam } 561c2d405aaSManivannan Sadhasivam 562c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 563c2d405aaSManivannan Sadhasivam { 564c2d405aaSManivannan Sadhasivam int ret; 565c2d405aaSManivannan Sadhasivam 566c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 567c2d405aaSManivannan Sadhasivam if (ret) { 568c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 569c2d405aaSManivannan Sadhasivam return ret; 570c2d405aaSManivannan Sadhasivam } 571c2d405aaSManivannan Sadhasivam 572c2d405aaSManivannan Sadhasivam /* Setup termios */ 573c2d405aaSManivannan Sadhasivam if (tty) 574c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 575c2d405aaSManivannan Sadhasivam 576c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 577c2d405aaSManivannan Sadhasivam if (ret) { 578c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 579c2d405aaSManivannan Sadhasivam return ret; 580c2d405aaSManivannan Sadhasivam } 581c2d405aaSManivannan Sadhasivam 582c2d405aaSManivannan Sadhasivam return 0; 583c2d405aaSManivannan Sadhasivam } 584c2d405aaSManivannan Sadhasivam 585c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 586c2d405aaSManivannan Sadhasivam { 587c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 588c2d405aaSManivannan Sadhasivam 589c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 590c2d405aaSManivannan Sadhasivam } 591c2d405aaSManivannan Sadhasivam 592c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 593c2d405aaSManivannan Sadhasivam { 5945fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 5955fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 5965fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 5975fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 5985fec21e7SJohan Hovold struct usb_interface *data; 5995fec21e7SJohan Hovold int ret; 6005fec21e7SJohan Hovold 6015fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 6025fec21e7SJohan Hovold if (ret < 0) 603c2d405aaSManivannan Sadhasivam return -ENODEV; 604c2d405aaSManivannan Sadhasivam 6055fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 6065fec21e7SJohan Hovold if (!desc) 6075fec21e7SJohan Hovold return -ENODEV; 6085fec21e7SJohan Hovold 6095fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 6105fec21e7SJohan Hovold if (!data) 6115fec21e7SJohan Hovold return -ENODEV; 6125fec21e7SJohan Hovold 6135fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 6145fec21e7SJohan Hovold if (ret) 6155fec21e7SJohan Hovold return ret; 6165fec21e7SJohan Hovold 617*f865e614SJohan Hovold usb_set_serial_data(serial, (void *)id->driver_info); 618*f865e614SJohan Hovold 619c2d405aaSManivannan Sadhasivam return 0; 620c2d405aaSManivannan Sadhasivam } 621c2d405aaSManivannan Sadhasivam 622*f865e614SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) 62349036fd0SJohan Hovold { 62449036fd0SJohan Hovold u8 mask, mode; 62549036fd0SJohan Hovold int ret; 62649036fd0SJohan Hovold 62749036fd0SJohan Hovold /* Configure all pins as GPIO. */ 62849036fd0SJohan Hovold mode = 0; 629*f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_mode, mode); 63049036fd0SJohan Hovold if (ret) 63149036fd0SJohan Hovold return ret; 63249036fd0SJohan Hovold 63349036fd0SJohan Hovold /* 63449036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 63549036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 63649036fd0SJohan Hovold */ 637958d6b95SJohan Hovold mask = XR_GPIO_DTR | XR_GPIO_RTS; 638*f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_direction, mask); 63949036fd0SJohan Hovold if (ret) 64049036fd0SJohan Hovold return ret; 64149036fd0SJohan Hovold 642*f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, mask); 64349036fd0SJohan Hovold if (ret) 64449036fd0SJohan Hovold return ret; 64549036fd0SJohan Hovold 64649036fd0SJohan Hovold return 0; 64749036fd0SJohan Hovold } 64849036fd0SJohan Hovold 64923b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 65023b7998eSJohan Hovold { 65123b7998eSJohan Hovold struct usb_interface_descriptor *desc; 652*f865e614SJohan Hovold const struct xr_type *type; 65323b7998eSJohan Hovold struct xr_data *data; 654*f865e614SJohan Hovold enum xr_type_id type_id; 65549036fd0SJohan Hovold int ret; 65623b7998eSJohan Hovold 657*f865e614SJohan Hovold type_id = (int)(unsigned long)usb_get_serial_data(port->serial); 658*f865e614SJohan Hovold type = &xr_types[type_id]; 659*f865e614SJohan Hovold 66023b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 66123b7998eSJohan Hovold if (!data) 66223b7998eSJohan Hovold return -ENOMEM; 66323b7998eSJohan Hovold 664*f865e614SJohan Hovold data->type = type; 665*f865e614SJohan Hovold 66623b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 66723b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 66823b7998eSJohan Hovold 66923b7998eSJohan Hovold usb_set_serial_port_data(port, data); 67023b7998eSJohan Hovold 671*f865e614SJohan Hovold ret = xr_gpio_init(port, type); 67249036fd0SJohan Hovold if (ret) 67349036fd0SJohan Hovold goto err_free; 67449036fd0SJohan Hovold 67523b7998eSJohan Hovold return 0; 67649036fd0SJohan Hovold 67749036fd0SJohan Hovold err_free: 67849036fd0SJohan Hovold kfree(data); 67949036fd0SJohan Hovold 68049036fd0SJohan Hovold return ret; 68123b7998eSJohan Hovold } 68223b7998eSJohan Hovold 68323b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 68423b7998eSJohan Hovold { 68523b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 68623b7998eSJohan Hovold 68723b7998eSJohan Hovold kfree(data); 68823b7998eSJohan Hovold } 68923b7998eSJohan Hovold 690*f865e614SJohan Hovold #define XR_DEVICE(vid, pid, type) \ 691*f865e614SJohan Hovold USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ 692*f865e614SJohan Hovold .driver_info = (type) 693*f865e614SJohan Hovold 694c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 695*f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, 696*f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, 697*f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, 698c2d405aaSManivannan Sadhasivam { } 699c2d405aaSManivannan Sadhasivam }; 700c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 701c2d405aaSManivannan Sadhasivam 702c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 703c2d405aaSManivannan Sadhasivam .driver = { 704c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 705c2d405aaSManivannan Sadhasivam .name = "xr_serial", 706c2d405aaSManivannan Sadhasivam }, 707c2d405aaSManivannan Sadhasivam .id_table = id_table, 708c2d405aaSManivannan Sadhasivam .num_ports = 1, 709c2d405aaSManivannan Sadhasivam .probe = xr_probe, 71023b7998eSJohan Hovold .port_probe = xr_port_probe, 71123b7998eSJohan Hovold .port_remove = xr_port_remove, 712c2d405aaSManivannan Sadhasivam .open = xr_open, 713c2d405aaSManivannan Sadhasivam .close = xr_close, 714c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 715c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 716c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 717c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 718c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 719c2d405aaSManivannan Sadhasivam }; 720c2d405aaSManivannan Sadhasivam 721c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 722c2d405aaSManivannan Sadhasivam &xr_device, NULL 723c2d405aaSManivannan Sadhasivam }; 724c2d405aaSManivannan Sadhasivam 725c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 726c2d405aaSManivannan Sadhasivam 727c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 728c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 729c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 730