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 32c2d405aaSManivannan Sadhasivam /* USB Requests */ 33c2d405aaSManivannan Sadhasivam #define XR21V141X_SET_REQ 0 34c2d405aaSManivannan Sadhasivam #define XR21V141X_GET_REQ 1 35c2d405aaSManivannan Sadhasivam 36c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_0 0x04 37c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_1 0x05 38c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_2 0x06 39c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_0 0x07 40c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_1 0x08 41c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_0 0x09 42c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_1 0x0a 43c2d405aaSManivannan Sadhasivam 44c2d405aaSManivannan Sadhasivam /* XR21V141X register blocks */ 45c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_REG_BLOCK 0 46c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_REG_BLOCK 4 47c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_CUSTOM_BLOCK 0x66 48c2d405aaSManivannan Sadhasivam 49c2d405aaSManivannan Sadhasivam /* XR21V141X UART Manager Registers */ 50c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_FIFO_ENABLE_REG 0x10 51c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_TX_FIFO 0x01 52c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_RX_FIFO 0x02 53c2d405aaSManivannan Sadhasivam 54c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_RX_FIFO_RESET 0x18 55c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_TX_FIFO_RESET 0x1c 56c2d405aaSManivannan Sadhasivam 57c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_ENABLE_TX 0x1 58c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_ENABLE_RX 0x2 59c2d405aaSManivannan Sadhasivam 605f70fe32SJohan Hovold #define XR21V141X_GPIO_RI BIT(0) 615f70fe32SJohan Hovold #define XR21V141X_GPIO_CD BIT(1) 625f70fe32SJohan Hovold #define XR21V141X_GPIO_DSR BIT(2) 635f70fe32SJohan Hovold #define XR21V141X_GPIO_DTR BIT(3) 645f70fe32SJohan Hovold #define XR21V141X_GPIO_CTS BIT(4) 655f70fe32SJohan Hovold #define XR21V141X_GPIO_RTS BIT(5) 66c2d405aaSManivannan Sadhasivam 67c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_BREAK_ON 0xff 68c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_BREAK_OFF 0 69c2d405aaSManivannan Sadhasivam 70c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_DATA_MASK GENMASK(3, 0) 71c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_DATA_7 0x7 72c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_DATA_8 0x8 73c2d405aaSManivannan Sadhasivam 74c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_MASK GENMASK(6, 4) 7535567511SJohan Hovold #define XR21V141X_UART_PARITY_SHIFT 4 7635567511SJohan Hovold #define XR21V141X_UART_PARITY_NONE (0x0 << XR21V141X_UART_PARITY_SHIFT) 7735567511SJohan Hovold #define XR21V141X_UART_PARITY_ODD (0x1 << XR21V141X_UART_PARITY_SHIFT) 7835567511SJohan Hovold #define XR21V141X_UART_PARITY_EVEN (0x2 << XR21V141X_UART_PARITY_SHIFT) 7935567511SJohan Hovold #define XR21V141X_UART_PARITY_MARK (0x3 << XR21V141X_UART_PARITY_SHIFT) 8035567511SJohan Hovold #define XR21V141X_UART_PARITY_SPACE (0x4 << XR21V141X_UART_PARITY_SHIFT) 81c2d405aaSManivannan Sadhasivam 82c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_STOP_MASK BIT(7) 8335567511SJohan Hovold #define XR21V141X_UART_STOP_SHIFT 7 8435567511SJohan Hovold #define XR21V141X_UART_STOP_1 (0x0 << XR21V141X_UART_STOP_SHIFT) 8535567511SJohan Hovold #define XR21V141X_UART_STOP_2 (0x1 << XR21V141X_UART_STOP_SHIFT) 86c2d405aaSManivannan Sadhasivam 87c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_FLOW_MODE_NONE 0x0 88c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_FLOW_MODE_HW 0x1 89c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_FLOW_MODE_SW 0x2 90c2d405aaSManivannan Sadhasivam 913c369a85SJohan Hovold #define XR21V141X_GPIO_MODE_MASK GENMASK(2, 0) 923c369a85SJohan Hovold #define XR21V141X_GPIO_MODE_RTS_CTS 0x1 933c369a85SJohan Hovold #define XR21V141X_GPIO_MODE_DTR_DSR 0x2 943c369a85SJohan Hovold #define XR21V141X_GPIO_MODE_RS485 0x3 953c369a85SJohan Hovold #define XR21V141X_GPIO_MODE_RS485_ADDR 0x4 96c2d405aaSManivannan Sadhasivam 97c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_ENABLE 0x03 98c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_FORMAT 0x0b 99c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_FLOW_CTRL 0x0c 100c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_XON_CHAR 0x10 101c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_XOFF_CHAR 0x11 102c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_LOOPBACK 0x12 103c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_TX_BREAK 0x14 104c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_RS845_DELAY 0x15 105c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_MODE 0x1a 106c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_DIR 0x1b 107c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_INT_MASK 0x1c 108c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_SET 0x1d 109c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_CLR 0x1e 110c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_STATUS 0x1f 111c2d405aaSManivannan Sadhasivam 11223b7998eSJohan Hovold struct xr_data { 11323b7998eSJohan Hovold u8 channel; /* zero-based index */ 11423b7998eSJohan Hovold }; 11523b7998eSJohan Hovold 116c2d405aaSManivannan Sadhasivam static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) 117c2d405aaSManivannan Sadhasivam { 118c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 119c2d405aaSManivannan Sadhasivam int ret; 120c2d405aaSManivannan Sadhasivam 121c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 122c2d405aaSManivannan Sadhasivam usb_sndctrlpipe(serial->dev, 0), 123c2d405aaSManivannan Sadhasivam XR21V141X_SET_REQ, 12472fc7fc7SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 12572fc7fc7SJohan Hovold val, reg | (block << 8), NULL, 0, 126c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 127c2d405aaSManivannan Sadhasivam if (ret < 0) { 128c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 129c2d405aaSManivannan Sadhasivam return ret; 130c2d405aaSManivannan Sadhasivam } 131c2d405aaSManivannan Sadhasivam 132c2d405aaSManivannan Sadhasivam return 0; 133c2d405aaSManivannan Sadhasivam } 134c2d405aaSManivannan Sadhasivam 135c2d405aaSManivannan Sadhasivam static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) 136c2d405aaSManivannan Sadhasivam { 137c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 138c2d405aaSManivannan Sadhasivam u8 *dmabuf; 139c2d405aaSManivannan Sadhasivam int ret; 140c2d405aaSManivannan Sadhasivam 141c2d405aaSManivannan Sadhasivam dmabuf = kmalloc(1, GFP_KERNEL); 142c2d405aaSManivannan Sadhasivam if (!dmabuf) 143c2d405aaSManivannan Sadhasivam return -ENOMEM; 144c2d405aaSManivannan Sadhasivam 145c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 146c2d405aaSManivannan Sadhasivam usb_rcvctrlpipe(serial->dev, 0), 147c2d405aaSManivannan Sadhasivam XR21V141X_GET_REQ, 14872fc7fc7SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 14972fc7fc7SJohan Hovold 0, reg | (block << 8), dmabuf, 1, 150c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 151c2d405aaSManivannan Sadhasivam if (ret == 1) { 152c2d405aaSManivannan Sadhasivam *val = *dmabuf; 153c2d405aaSManivannan Sadhasivam ret = 0; 154c2d405aaSManivannan Sadhasivam } else { 155c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 156c2d405aaSManivannan Sadhasivam if (ret >= 0) 157c2d405aaSManivannan Sadhasivam ret = -EIO; 158c2d405aaSManivannan Sadhasivam } 159c2d405aaSManivannan Sadhasivam 160c2d405aaSManivannan Sadhasivam kfree(dmabuf); 161c2d405aaSManivannan Sadhasivam 162c2d405aaSManivannan Sadhasivam return ret; 163c2d405aaSManivannan Sadhasivam } 164c2d405aaSManivannan Sadhasivam 165c2d405aaSManivannan Sadhasivam static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u8 val) 166c2d405aaSManivannan Sadhasivam { 16723b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 16823b7998eSJohan Hovold u8 block; 16923b7998eSJohan Hovold 17023b7998eSJohan Hovold block = XR21V141X_UART_REG_BLOCK + data->channel; 17123b7998eSJohan Hovold 17223b7998eSJohan Hovold return xr_set_reg(port, block, reg, val); 173c2d405aaSManivannan Sadhasivam } 174c2d405aaSManivannan Sadhasivam 175c2d405aaSManivannan Sadhasivam static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u8 *val) 176c2d405aaSManivannan Sadhasivam { 17723b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 17823b7998eSJohan Hovold u8 block; 17923b7998eSJohan Hovold 18023b7998eSJohan Hovold block = XR21V141X_UART_REG_BLOCK + data->channel; 18123b7998eSJohan Hovold 18223b7998eSJohan Hovold return xr_get_reg(port, block, reg, val); 183c2d405aaSManivannan Sadhasivam } 184c2d405aaSManivannan Sadhasivam 18523b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 186c2d405aaSManivannan Sadhasivam { 18723b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 18823b7998eSJohan Hovold u8 reg; 18923b7998eSJohan Hovold 19023b7998eSJohan Hovold reg = reg_base + data->channel; 19123b7998eSJohan Hovold 192c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 193c2d405aaSManivannan Sadhasivam } 194c2d405aaSManivannan Sadhasivam 195c2d405aaSManivannan Sadhasivam /* 196c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 197c2d405aaSManivannan Sadhasivam * module in XR21V141X: 198c2d405aaSManivannan Sadhasivam * 199c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 200c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 201c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 202c2d405aaSManivannan Sadhasivam */ 203c2d405aaSManivannan Sadhasivam static int xr_uart_enable(struct usb_serial_port *port) 204c2d405aaSManivannan Sadhasivam { 205c2d405aaSManivannan Sadhasivam int ret; 206c2d405aaSManivannan Sadhasivam 207c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 208c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 209c2d405aaSManivannan Sadhasivam if (ret) 210c2d405aaSManivannan Sadhasivam return ret; 211c2d405aaSManivannan Sadhasivam 212c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 213c2d405aaSManivannan Sadhasivam XR21V141X_UART_ENABLE_TX | XR21V141X_UART_ENABLE_RX); 214c2d405aaSManivannan Sadhasivam if (ret) 215c2d405aaSManivannan Sadhasivam return ret; 216c2d405aaSManivannan Sadhasivam 217c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 218c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 219c2d405aaSManivannan Sadhasivam 220c2d405aaSManivannan Sadhasivam if (ret) 221c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); 222c2d405aaSManivannan Sadhasivam 223c2d405aaSManivannan Sadhasivam return ret; 224c2d405aaSManivannan Sadhasivam } 225c2d405aaSManivannan Sadhasivam 226c2d405aaSManivannan Sadhasivam static int xr_uart_disable(struct usb_serial_port *port) 227c2d405aaSManivannan Sadhasivam { 228c2d405aaSManivannan Sadhasivam int ret; 229c2d405aaSManivannan Sadhasivam 230c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); 231c2d405aaSManivannan Sadhasivam if (ret) 232c2d405aaSManivannan Sadhasivam return ret; 233c2d405aaSManivannan Sadhasivam 234c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 235c2d405aaSManivannan Sadhasivam 236c2d405aaSManivannan Sadhasivam return ret; 237c2d405aaSManivannan Sadhasivam } 238c2d405aaSManivannan Sadhasivam 239c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 240c2d405aaSManivannan Sadhasivam { 241c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 242c2d405aaSManivannan Sadhasivam u8 status; 243c2d405aaSManivannan Sadhasivam int ret; 244c2d405aaSManivannan Sadhasivam 245c2d405aaSManivannan Sadhasivam ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_STATUS, &status); 246c2d405aaSManivannan Sadhasivam if (ret) 247c2d405aaSManivannan Sadhasivam return ret; 248c2d405aaSManivannan Sadhasivam 249c2d405aaSManivannan Sadhasivam /* 250c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 251c2d405aaSManivannan Sadhasivam * and '1' means not active. 252c2d405aaSManivannan Sadhasivam */ 2535f70fe32SJohan Hovold ret = ((status & XR21V141X_GPIO_DTR) ? 0 : TIOCM_DTR) | 2545f70fe32SJohan Hovold ((status & XR21V141X_GPIO_RTS) ? 0 : TIOCM_RTS) | 2555f70fe32SJohan Hovold ((status & XR21V141X_GPIO_CTS) ? 0 : TIOCM_CTS) | 2565f70fe32SJohan Hovold ((status & XR21V141X_GPIO_DSR) ? 0 : TIOCM_DSR) | 2575f70fe32SJohan Hovold ((status & XR21V141X_GPIO_RI) ? 0 : TIOCM_RI) | 2585f70fe32SJohan Hovold ((status & XR21V141X_GPIO_CD) ? 0 : TIOCM_CD); 259c2d405aaSManivannan Sadhasivam 260c2d405aaSManivannan Sadhasivam return ret; 261c2d405aaSManivannan Sadhasivam } 262c2d405aaSManivannan Sadhasivam 263c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 264c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 265c2d405aaSManivannan Sadhasivam { 266c2d405aaSManivannan Sadhasivam u8 gpio_set = 0; 267c2d405aaSManivannan Sadhasivam u8 gpio_clr = 0; 268c2d405aaSManivannan Sadhasivam int ret = 0; 269c2d405aaSManivannan Sadhasivam 270c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 271c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 2725f70fe32SJohan Hovold gpio_clr |= XR21V141X_GPIO_RTS; 273c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 2745f70fe32SJohan Hovold gpio_clr |= XR21V141X_GPIO_DTR; 275c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 2765f70fe32SJohan Hovold gpio_set |= XR21V141X_GPIO_RTS; 277c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 2785f70fe32SJohan Hovold gpio_set |= XR21V141X_GPIO_DTR; 279c2d405aaSManivannan Sadhasivam 280c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 281c2d405aaSManivannan Sadhasivam if (gpio_clr) 282c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_CLR, gpio_clr); 283c2d405aaSManivannan Sadhasivam 284c2d405aaSManivannan Sadhasivam if (gpio_set) 285c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_SET, gpio_set); 286c2d405aaSManivannan Sadhasivam 287c2d405aaSManivannan Sadhasivam return ret; 288c2d405aaSManivannan Sadhasivam } 289c2d405aaSManivannan Sadhasivam 290c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 291c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 292c2d405aaSManivannan Sadhasivam { 293c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 294c2d405aaSManivannan Sadhasivam 295c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 296c2d405aaSManivannan Sadhasivam } 297c2d405aaSManivannan Sadhasivam 298c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 299c2d405aaSManivannan Sadhasivam { 300c2d405aaSManivannan Sadhasivam if (on) 301c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 302c2d405aaSManivannan Sadhasivam else 303c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 304c2d405aaSManivannan Sadhasivam } 305c2d405aaSManivannan Sadhasivam 306c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 307c2d405aaSManivannan Sadhasivam { 308c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 309c2d405aaSManivannan Sadhasivam u8 state; 310c2d405aaSManivannan Sadhasivam 311c2d405aaSManivannan Sadhasivam if (break_state == 0) 312c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_OFF; 313c2d405aaSManivannan Sadhasivam else 314c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_ON; 315c2d405aaSManivannan Sadhasivam 316c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Turning break %s\n", 317c2d405aaSManivannan Sadhasivam state == XR21V141X_UART_BREAK_OFF ? "off" : "on"); 318c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_TX_BREAK, state); 319c2d405aaSManivannan Sadhasivam } 320c2d405aaSManivannan Sadhasivam 321c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 322c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 323c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 324c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 325c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 326c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 327c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 328c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 329c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 330c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 331c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 332c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 333c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 334c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 335c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 336c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 337c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 338c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 339c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 340c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 341c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 342c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 343c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 344c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 345c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 346c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 347c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 348c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 349c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 350c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 351c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 352c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 353c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 354c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 355c2d405aaSManivannan Sadhasivam }; 356c2d405aaSManivannan Sadhasivam 357c2d405aaSManivannan Sadhasivam static int xr_set_baudrate(struct tty_struct *tty, 358c2d405aaSManivannan Sadhasivam struct usb_serial_port *port) 359c2d405aaSManivannan Sadhasivam { 360c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 361c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 362c2d405aaSManivannan Sadhasivam int ret; 363c2d405aaSManivannan Sadhasivam 36455317e22SJohan Hovold baud = tty->termios.c_ospeed; 36555317e22SJohan Hovold if (!baud) 36655317e22SJohan Hovold return 0; 36755317e22SJohan Hovold 36855317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 369c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 370c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 371c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 372c2d405aaSManivannan Sadhasivam 373c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 374c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 375c2d405aaSManivannan Sadhasivam else 376c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 377c2d405aaSManivannan Sadhasivam 378c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 379c2d405aaSManivannan Sadhasivam /* 380c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 381c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 382c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 383c2d405aaSManivannan Sadhasivam */ 384c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 385c2d405aaSManivannan Sadhasivam divisor & 0xff); 386c2d405aaSManivannan Sadhasivam if (ret) 387c2d405aaSManivannan Sadhasivam return ret; 388c2d405aaSManivannan Sadhasivam 389c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 390c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 391c2d405aaSManivannan Sadhasivam if (ret) 392c2d405aaSManivannan Sadhasivam return ret; 393c2d405aaSManivannan Sadhasivam 394c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 395c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 396c2d405aaSManivannan Sadhasivam if (ret) 397c2d405aaSManivannan Sadhasivam return ret; 398c2d405aaSManivannan Sadhasivam 399c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 400c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 401c2d405aaSManivannan Sadhasivam if (ret) 402c2d405aaSManivannan Sadhasivam return ret; 403c2d405aaSManivannan Sadhasivam 404c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 405c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 406c2d405aaSManivannan Sadhasivam if (ret) 407c2d405aaSManivannan Sadhasivam return ret; 408c2d405aaSManivannan Sadhasivam 409c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 410c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 411c2d405aaSManivannan Sadhasivam if (ret) 412c2d405aaSManivannan Sadhasivam return ret; 413c2d405aaSManivannan Sadhasivam 414c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 415c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 416c2d405aaSManivannan Sadhasivam if (ret) 417c2d405aaSManivannan Sadhasivam return ret; 418c2d405aaSManivannan Sadhasivam 419c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 420c2d405aaSManivannan Sadhasivam 421c2d405aaSManivannan Sadhasivam return 0; 422c2d405aaSManivannan Sadhasivam } 423c2d405aaSManivannan Sadhasivam 424c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 42555317e22SJohan Hovold struct usb_serial_port *port, 42655317e22SJohan Hovold struct ktermios *old_termios) 427c2d405aaSManivannan Sadhasivam { 428c2d405aaSManivannan Sadhasivam u8 flow, gpio_mode; 429c2d405aaSManivannan Sadhasivam int ret; 430c2d405aaSManivannan Sadhasivam 431c2d405aaSManivannan Sadhasivam ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_MODE, &gpio_mode); 432c2d405aaSManivannan Sadhasivam if (ret) 433c2d405aaSManivannan Sadhasivam return; 434c2d405aaSManivannan Sadhasivam 435465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 4363c369a85SJohan Hovold gpio_mode &= ~XR21V141X_GPIO_MODE_MASK; 437465d3b3aSJohan Hovold 43855317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 439c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 4403c369a85SJohan Hovold gpio_mode |= XR21V141X_GPIO_MODE_RTS_CTS; 441c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_HW; 442c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 443c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 444c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 445c2d405aaSManivannan Sadhasivam 446c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 447c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_SW; 448c2d405aaSManivannan Sadhasivam 449c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_XON_CHAR, start_char); 450c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_XOFF_CHAR, stop_char); 451c2d405aaSManivannan Sadhasivam } else { 452c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 453c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_NONE; 454c2d405aaSManivannan Sadhasivam } 455c2d405aaSManivannan Sadhasivam 456c2d405aaSManivannan Sadhasivam /* 457c2d405aaSManivannan Sadhasivam * As per the datasheet, UART needs to be disabled while writing to 458c2d405aaSManivannan Sadhasivam * FLOW_CONTROL register. 459c2d405aaSManivannan Sadhasivam */ 460c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 461c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_FLOW_CTRL, flow); 462c2d405aaSManivannan Sadhasivam xr_uart_enable(port); 463c2d405aaSManivannan Sadhasivam 464c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_GPIO_MODE, gpio_mode); 46555317e22SJohan Hovold 46655317e22SJohan Hovold if (C_BAUD(tty) == B0) 46755317e22SJohan Hovold xr_dtr_rts(port, 0); 46855317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 46955317e22SJohan Hovold xr_dtr_rts(port, 1); 470c2d405aaSManivannan Sadhasivam } 471c2d405aaSManivannan Sadhasivam 472c2d405aaSManivannan Sadhasivam static void xr_set_termios(struct tty_struct *tty, 473c2d405aaSManivannan Sadhasivam struct usb_serial_port *port, 474c2d405aaSManivannan Sadhasivam struct ktermios *old_termios) 475c2d405aaSManivannan Sadhasivam { 476c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 477c2d405aaSManivannan Sadhasivam u8 bits = 0; 478c2d405aaSManivannan Sadhasivam int ret; 479c2d405aaSManivannan Sadhasivam 480736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 481c2d405aaSManivannan Sadhasivam xr_set_baudrate(tty, port); 482c2d405aaSManivannan Sadhasivam 483c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 484c2d405aaSManivannan Sadhasivam case CS5: 485c2d405aaSManivannan Sadhasivam case CS6: 486c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 487c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 488c2d405aaSManivannan Sadhasivam if (old_termios) 489c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 490c2d405aaSManivannan Sadhasivam else 491ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 492ea7ada4dSJohan Hovold 493ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 494ea7ada4dSJohan Hovold bits |= XR21V141X_UART_DATA_7; 495ea7ada4dSJohan Hovold else 496c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_8; 497c2d405aaSManivannan Sadhasivam break; 498c2d405aaSManivannan Sadhasivam case CS7: 499c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_7; 500c2d405aaSManivannan Sadhasivam break; 501c2d405aaSManivannan Sadhasivam case CS8: 502c2d405aaSManivannan Sadhasivam default: 503c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_8; 504c2d405aaSManivannan Sadhasivam break; 505c2d405aaSManivannan Sadhasivam } 506c2d405aaSManivannan Sadhasivam 507c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 508c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 509c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 51035567511SJohan Hovold bits |= XR21V141X_UART_PARITY_MARK; 511c2d405aaSManivannan Sadhasivam else 51235567511SJohan Hovold bits |= XR21V141X_UART_PARITY_SPACE; 513c2d405aaSManivannan Sadhasivam } else { 514c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 51535567511SJohan Hovold bits |= XR21V141X_UART_PARITY_ODD; 516c2d405aaSManivannan Sadhasivam else 51735567511SJohan Hovold bits |= XR21V141X_UART_PARITY_EVEN; 518c2d405aaSManivannan Sadhasivam } 519c2d405aaSManivannan Sadhasivam } 520c2d405aaSManivannan Sadhasivam 521c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 52235567511SJohan Hovold bits |= XR21V141X_UART_STOP_2; 523c2d405aaSManivannan Sadhasivam else 52435567511SJohan Hovold bits |= XR21V141X_UART_STOP_1; 525c2d405aaSManivannan Sadhasivam 526c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 527c2d405aaSManivannan Sadhasivam if (ret) 528c2d405aaSManivannan Sadhasivam return; 529c2d405aaSManivannan Sadhasivam 53055317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 531c2d405aaSManivannan Sadhasivam } 532c2d405aaSManivannan Sadhasivam 533c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 534c2d405aaSManivannan Sadhasivam { 535c2d405aaSManivannan Sadhasivam int ret; 536c2d405aaSManivannan Sadhasivam 537c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 538c2d405aaSManivannan Sadhasivam if (ret) { 539c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 540c2d405aaSManivannan Sadhasivam return ret; 541c2d405aaSManivannan Sadhasivam } 542c2d405aaSManivannan Sadhasivam 543c2d405aaSManivannan Sadhasivam /* Setup termios */ 544c2d405aaSManivannan Sadhasivam if (tty) 545c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 546c2d405aaSManivannan Sadhasivam 547c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 548c2d405aaSManivannan Sadhasivam if (ret) { 549c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 550c2d405aaSManivannan Sadhasivam return ret; 551c2d405aaSManivannan Sadhasivam } 552c2d405aaSManivannan Sadhasivam 553c2d405aaSManivannan Sadhasivam return 0; 554c2d405aaSManivannan Sadhasivam } 555c2d405aaSManivannan Sadhasivam 556c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 557c2d405aaSManivannan Sadhasivam { 558c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 559c2d405aaSManivannan Sadhasivam 560c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 561c2d405aaSManivannan Sadhasivam } 562c2d405aaSManivannan Sadhasivam 563c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 564c2d405aaSManivannan Sadhasivam { 5655fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 5665fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 5675fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 5685fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 5695fec21e7SJohan Hovold struct usb_interface *data; 5705fec21e7SJohan Hovold int ret; 5715fec21e7SJohan Hovold 5725fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 5735fec21e7SJohan Hovold if (ret < 0) 574c2d405aaSManivannan Sadhasivam return -ENODEV; 575c2d405aaSManivannan Sadhasivam 5765fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 5775fec21e7SJohan Hovold if (!desc) 5785fec21e7SJohan Hovold return -ENODEV; 5795fec21e7SJohan Hovold 5805fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 5815fec21e7SJohan Hovold if (!data) 5825fec21e7SJohan Hovold return -ENODEV; 5835fec21e7SJohan Hovold 5845fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 5855fec21e7SJohan Hovold if (ret) 5865fec21e7SJohan Hovold return ret; 5875fec21e7SJohan Hovold 588c2d405aaSManivannan Sadhasivam return 0; 589c2d405aaSManivannan Sadhasivam } 590c2d405aaSManivannan Sadhasivam 591*49036fd0SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port) 592*49036fd0SJohan Hovold { 593*49036fd0SJohan Hovold u8 mask, mode; 594*49036fd0SJohan Hovold int ret; 595*49036fd0SJohan Hovold 596*49036fd0SJohan Hovold /* Configure all pins as GPIO. */ 597*49036fd0SJohan Hovold mode = 0; 598*49036fd0SJohan Hovold ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_MODE, mode); 599*49036fd0SJohan Hovold if (ret) 600*49036fd0SJohan Hovold return ret; 601*49036fd0SJohan Hovold 602*49036fd0SJohan Hovold /* 603*49036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 604*49036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 605*49036fd0SJohan Hovold */ 606*49036fd0SJohan Hovold mask = XR21V141X_GPIO_DTR | XR21V141X_GPIO_RTS; 607*49036fd0SJohan Hovold ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_DIR, mask); 608*49036fd0SJohan Hovold if (ret) 609*49036fd0SJohan Hovold return ret; 610*49036fd0SJohan Hovold 611*49036fd0SJohan Hovold ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_SET, mask); 612*49036fd0SJohan Hovold if (ret) 613*49036fd0SJohan Hovold return ret; 614*49036fd0SJohan Hovold 615*49036fd0SJohan Hovold return 0; 616*49036fd0SJohan Hovold } 617*49036fd0SJohan Hovold 61823b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 61923b7998eSJohan Hovold { 62023b7998eSJohan Hovold struct usb_interface_descriptor *desc; 62123b7998eSJohan Hovold struct xr_data *data; 622*49036fd0SJohan Hovold int ret; 62323b7998eSJohan Hovold 62423b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 62523b7998eSJohan Hovold if (!data) 62623b7998eSJohan Hovold return -ENOMEM; 62723b7998eSJohan Hovold 62823b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 62923b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 63023b7998eSJohan Hovold 63123b7998eSJohan Hovold usb_set_serial_port_data(port, data); 63223b7998eSJohan Hovold 633*49036fd0SJohan Hovold ret = xr_gpio_init(port); 634*49036fd0SJohan Hovold if (ret) 635*49036fd0SJohan Hovold goto err_free; 636*49036fd0SJohan Hovold 63723b7998eSJohan Hovold return 0; 638*49036fd0SJohan Hovold 639*49036fd0SJohan Hovold err_free: 640*49036fd0SJohan Hovold kfree(data); 641*49036fd0SJohan Hovold 642*49036fd0SJohan Hovold return ret; 64323b7998eSJohan Hovold } 64423b7998eSJohan Hovold 64523b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 64623b7998eSJohan Hovold { 64723b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 64823b7998eSJohan Hovold 64923b7998eSJohan Hovold kfree(data); 65023b7998eSJohan Hovold } 65123b7998eSJohan Hovold 652c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 65323b7998eSJohan Hovold { USB_DEVICE_INTERFACE_CLASS(0x04e2, 0x1410, USB_CLASS_COMM) }, /* XR21V1410 */ 65423b7998eSJohan Hovold { USB_DEVICE_INTERFACE_CLASS(0x04e2, 0x1412, USB_CLASS_COMM) }, /* XR21V1412 */ 65523b7998eSJohan Hovold { USB_DEVICE_INTERFACE_CLASS(0x04e2, 0x1414, USB_CLASS_COMM) }, /* XR21V1414 */ 656c2d405aaSManivannan Sadhasivam { } 657c2d405aaSManivannan Sadhasivam }; 658c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 659c2d405aaSManivannan Sadhasivam 660c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 661c2d405aaSManivannan Sadhasivam .driver = { 662c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 663c2d405aaSManivannan Sadhasivam .name = "xr_serial", 664c2d405aaSManivannan Sadhasivam }, 665c2d405aaSManivannan Sadhasivam .id_table = id_table, 666c2d405aaSManivannan Sadhasivam .num_ports = 1, 667c2d405aaSManivannan Sadhasivam .probe = xr_probe, 66823b7998eSJohan Hovold .port_probe = xr_port_probe, 66923b7998eSJohan Hovold .port_remove = xr_port_remove, 670c2d405aaSManivannan Sadhasivam .open = xr_open, 671c2d405aaSManivannan Sadhasivam .close = xr_close, 672c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 673c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 674c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 675c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 676c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 677c2d405aaSManivannan Sadhasivam }; 678c2d405aaSManivannan Sadhasivam 679c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 680c2d405aaSManivannan Sadhasivam &xr_device, NULL 681c2d405aaSManivannan Sadhasivam }; 682c2d405aaSManivannan Sadhasivam 683c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 684c2d405aaSManivannan Sadhasivam 685c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 686c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 687c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 688