1*c2d405aaSManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0+ 2*c2d405aaSManivannan Sadhasivam /* 3*c2d405aaSManivannan Sadhasivam * MaxLinear/Exar USB to Serial driver 4*c2d405aaSManivannan Sadhasivam * 5*c2d405aaSManivannan Sadhasivam * Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org> 6*c2d405aaSManivannan Sadhasivam * 7*c2d405aaSManivannan Sadhasivam * Based on the initial driver written by Patong Yang: 8*c2d405aaSManivannan Sadhasivam * 9*c2d405aaSManivannan Sadhasivam * https://lore.kernel.org/r/20180404070634.nhspvmxcjwfgjkcv@advantechmxl-desktop 10*c2d405aaSManivannan Sadhasivam * 11*c2d405aaSManivannan Sadhasivam * Copyright (c) 2018 Patong Yang <patong.mxl@gmail.com> 12*c2d405aaSManivannan Sadhasivam */ 13*c2d405aaSManivannan Sadhasivam 14*c2d405aaSManivannan Sadhasivam #include <linux/kernel.h> 15*c2d405aaSManivannan Sadhasivam #include <linux/module.h> 16*c2d405aaSManivannan Sadhasivam #include <linux/slab.h> 17*c2d405aaSManivannan Sadhasivam #include <linux/tty.h> 18*c2d405aaSManivannan Sadhasivam #include <linux/usb.h> 19*c2d405aaSManivannan Sadhasivam #include <linux/usb/serial.h> 20*c2d405aaSManivannan Sadhasivam 21*c2d405aaSManivannan Sadhasivam struct xr_txrx_clk_mask { 22*c2d405aaSManivannan Sadhasivam u16 tx; 23*c2d405aaSManivannan Sadhasivam u16 rx0; 24*c2d405aaSManivannan Sadhasivam u16 rx1; 25*c2d405aaSManivannan Sadhasivam }; 26*c2d405aaSManivannan Sadhasivam 27*c2d405aaSManivannan Sadhasivam #define XR_INT_OSC_HZ 48000000U 28*c2d405aaSManivannan Sadhasivam #define XR21V141X_MIN_SPEED 46U 29*c2d405aaSManivannan Sadhasivam #define XR21V141X_MAX_SPEED XR_INT_OSC_HZ 30*c2d405aaSManivannan Sadhasivam 31*c2d405aaSManivannan Sadhasivam /* USB Requests */ 32*c2d405aaSManivannan Sadhasivam #define XR21V141X_SET_REQ 0 33*c2d405aaSManivannan Sadhasivam #define XR21V141X_GET_REQ 1 34*c2d405aaSManivannan Sadhasivam 35*c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_0 0x04 36*c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_1 0x05 37*c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_2 0x06 38*c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_0 0x07 39*c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_1 0x08 40*c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_0 0x09 41*c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_1 0x0a 42*c2d405aaSManivannan Sadhasivam 43*c2d405aaSManivannan Sadhasivam /* XR21V141X register blocks */ 44*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_REG_BLOCK 0 45*c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_REG_BLOCK 4 46*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_CUSTOM_BLOCK 0x66 47*c2d405aaSManivannan Sadhasivam 48*c2d405aaSManivannan Sadhasivam /* XR21V141X UART Manager Registers */ 49*c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_FIFO_ENABLE_REG 0x10 50*c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_TX_FIFO 0x01 51*c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_RX_FIFO 0x02 52*c2d405aaSManivannan Sadhasivam 53*c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_RX_FIFO_RESET 0x18 54*c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_TX_FIFO_RESET 0x1c 55*c2d405aaSManivannan Sadhasivam 56*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_ENABLE_TX 0x1 57*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_ENABLE_RX 0x2 58*c2d405aaSManivannan Sadhasivam 59*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RI BIT(0) 60*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_CD BIT(1) 61*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_DSR BIT(2) 62*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_DTR BIT(3) 63*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_CTS BIT(4) 64*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RTS BIT(5) 65*c2d405aaSManivannan Sadhasivam 66*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_BREAK_ON 0xff 67*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_BREAK_OFF 0 68*c2d405aaSManivannan Sadhasivam 69*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_DATA_MASK GENMASK(3, 0) 70*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_DATA_7 0x7 71*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_DATA_8 0x8 72*c2d405aaSManivannan Sadhasivam 73*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_MASK GENMASK(6, 4) 74*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_SHIFT 0x4 75*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_NONE 0x0 76*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_ODD 0x1 77*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_EVEN 0x2 78*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_MARK 0x3 79*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_PARITY_SPACE 0x4 80*c2d405aaSManivannan Sadhasivam 81*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_STOP_MASK BIT(7) 82*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_STOP_SHIFT 0x7 83*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_STOP_1 0x0 84*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_STOP_2 0x1 85*c2d405aaSManivannan Sadhasivam 86*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_FLOW_MODE_NONE 0x0 87*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_FLOW_MODE_HW 0x1 88*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_FLOW_MODE_SW 0x2 89*c2d405aaSManivannan Sadhasivam 90*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_GPIO_MASK GENMASK(2, 0) 91*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RTS_CTS 0x1 92*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_DTR_DSR 0x2 93*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RS485 0x3 94*c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RS485_ADDR 0x4 95*c2d405aaSManivannan Sadhasivam 96*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_ENABLE 0x03 97*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_FORMAT 0x0b 98*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_FLOW_CTRL 0x0c 99*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_XON_CHAR 0x10 100*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_XOFF_CHAR 0x11 101*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_LOOPBACK 0x12 102*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_TX_BREAK 0x14 103*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_RS845_DELAY 0x15 104*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_MODE 0x1a 105*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_DIR 0x1b 106*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_INT_MASK 0x1c 107*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_SET 0x1d 108*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_CLR 0x1e 109*c2d405aaSManivannan Sadhasivam #define XR21V141X_REG_GPIO_STATUS 0x1f 110*c2d405aaSManivannan Sadhasivam 111*c2d405aaSManivannan Sadhasivam static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) 112*c2d405aaSManivannan Sadhasivam { 113*c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 114*c2d405aaSManivannan Sadhasivam int ret; 115*c2d405aaSManivannan Sadhasivam 116*c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 117*c2d405aaSManivannan Sadhasivam usb_sndctrlpipe(serial->dev, 0), 118*c2d405aaSManivannan Sadhasivam XR21V141X_SET_REQ, 119*c2d405aaSManivannan Sadhasivam USB_DIR_OUT | USB_TYPE_VENDOR, val, 120*c2d405aaSManivannan Sadhasivam reg | (block << 8), NULL, 0, 121*c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 122*c2d405aaSManivannan Sadhasivam if (ret < 0) { 123*c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 124*c2d405aaSManivannan Sadhasivam return ret; 125*c2d405aaSManivannan Sadhasivam } 126*c2d405aaSManivannan Sadhasivam 127*c2d405aaSManivannan Sadhasivam return 0; 128*c2d405aaSManivannan Sadhasivam } 129*c2d405aaSManivannan Sadhasivam 130*c2d405aaSManivannan Sadhasivam static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) 131*c2d405aaSManivannan Sadhasivam { 132*c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 133*c2d405aaSManivannan Sadhasivam u8 *dmabuf; 134*c2d405aaSManivannan Sadhasivam int ret; 135*c2d405aaSManivannan Sadhasivam 136*c2d405aaSManivannan Sadhasivam dmabuf = kmalloc(1, GFP_KERNEL); 137*c2d405aaSManivannan Sadhasivam if (!dmabuf) 138*c2d405aaSManivannan Sadhasivam return -ENOMEM; 139*c2d405aaSManivannan Sadhasivam 140*c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 141*c2d405aaSManivannan Sadhasivam usb_rcvctrlpipe(serial->dev, 0), 142*c2d405aaSManivannan Sadhasivam XR21V141X_GET_REQ, 143*c2d405aaSManivannan Sadhasivam USB_DIR_IN | USB_TYPE_VENDOR, 0, 144*c2d405aaSManivannan Sadhasivam reg | (block << 8), dmabuf, 1, 145*c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 146*c2d405aaSManivannan Sadhasivam if (ret == 1) { 147*c2d405aaSManivannan Sadhasivam *val = *dmabuf; 148*c2d405aaSManivannan Sadhasivam ret = 0; 149*c2d405aaSManivannan Sadhasivam } else { 150*c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 151*c2d405aaSManivannan Sadhasivam if (ret >= 0) 152*c2d405aaSManivannan Sadhasivam ret = -EIO; 153*c2d405aaSManivannan Sadhasivam } 154*c2d405aaSManivannan Sadhasivam 155*c2d405aaSManivannan Sadhasivam kfree(dmabuf); 156*c2d405aaSManivannan Sadhasivam 157*c2d405aaSManivannan Sadhasivam return ret; 158*c2d405aaSManivannan Sadhasivam } 159*c2d405aaSManivannan Sadhasivam 160*c2d405aaSManivannan Sadhasivam static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u8 val) 161*c2d405aaSManivannan Sadhasivam { 162*c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); 163*c2d405aaSManivannan Sadhasivam } 164*c2d405aaSManivannan Sadhasivam 165*c2d405aaSManivannan Sadhasivam static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u8 *val) 166*c2d405aaSManivannan Sadhasivam { 167*c2d405aaSManivannan Sadhasivam return xr_get_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); 168*c2d405aaSManivannan Sadhasivam } 169*c2d405aaSManivannan Sadhasivam 170*c2d405aaSManivannan Sadhasivam static int xr_set_reg_um(struct usb_serial_port *port, u8 reg, u8 val) 171*c2d405aaSManivannan Sadhasivam { 172*c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 173*c2d405aaSManivannan Sadhasivam } 174*c2d405aaSManivannan Sadhasivam 175*c2d405aaSManivannan Sadhasivam /* 176*c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 177*c2d405aaSManivannan Sadhasivam * module in XR21V141X: 178*c2d405aaSManivannan Sadhasivam * 179*c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 180*c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 181*c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 182*c2d405aaSManivannan Sadhasivam */ 183*c2d405aaSManivannan Sadhasivam static int xr_uart_enable(struct usb_serial_port *port) 184*c2d405aaSManivannan Sadhasivam { 185*c2d405aaSManivannan Sadhasivam int ret; 186*c2d405aaSManivannan Sadhasivam 187*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 188*c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 189*c2d405aaSManivannan Sadhasivam if (ret) 190*c2d405aaSManivannan Sadhasivam return ret; 191*c2d405aaSManivannan Sadhasivam 192*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 193*c2d405aaSManivannan Sadhasivam XR21V141X_UART_ENABLE_TX | XR21V141X_UART_ENABLE_RX); 194*c2d405aaSManivannan Sadhasivam if (ret) 195*c2d405aaSManivannan Sadhasivam return ret; 196*c2d405aaSManivannan Sadhasivam 197*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 198*c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 199*c2d405aaSManivannan Sadhasivam 200*c2d405aaSManivannan Sadhasivam if (ret) 201*c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); 202*c2d405aaSManivannan Sadhasivam 203*c2d405aaSManivannan Sadhasivam return ret; 204*c2d405aaSManivannan Sadhasivam } 205*c2d405aaSManivannan Sadhasivam 206*c2d405aaSManivannan Sadhasivam static int xr_uart_disable(struct usb_serial_port *port) 207*c2d405aaSManivannan Sadhasivam { 208*c2d405aaSManivannan Sadhasivam int ret; 209*c2d405aaSManivannan Sadhasivam 210*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); 211*c2d405aaSManivannan Sadhasivam if (ret) 212*c2d405aaSManivannan Sadhasivam return ret; 213*c2d405aaSManivannan Sadhasivam 214*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 215*c2d405aaSManivannan Sadhasivam 216*c2d405aaSManivannan Sadhasivam return ret; 217*c2d405aaSManivannan Sadhasivam } 218*c2d405aaSManivannan Sadhasivam 219*c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 220*c2d405aaSManivannan Sadhasivam { 221*c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 222*c2d405aaSManivannan Sadhasivam u8 status; 223*c2d405aaSManivannan Sadhasivam int ret; 224*c2d405aaSManivannan Sadhasivam 225*c2d405aaSManivannan Sadhasivam ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_STATUS, &status); 226*c2d405aaSManivannan Sadhasivam if (ret) 227*c2d405aaSManivannan Sadhasivam return ret; 228*c2d405aaSManivannan Sadhasivam 229*c2d405aaSManivannan Sadhasivam /* 230*c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 231*c2d405aaSManivannan Sadhasivam * and '1' means not active. 232*c2d405aaSManivannan Sadhasivam */ 233*c2d405aaSManivannan Sadhasivam ret = ((status & XR21V141X_UART_MODE_DTR) ? 0 : TIOCM_DTR) | 234*c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_RTS) ? 0 : TIOCM_RTS) | 235*c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_CTS) ? 0 : TIOCM_CTS) | 236*c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_DSR) ? 0 : TIOCM_DSR) | 237*c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_RI) ? 0 : TIOCM_RI) | 238*c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_CD) ? 0 : TIOCM_CD); 239*c2d405aaSManivannan Sadhasivam 240*c2d405aaSManivannan Sadhasivam return ret; 241*c2d405aaSManivannan Sadhasivam } 242*c2d405aaSManivannan Sadhasivam 243*c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 244*c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 245*c2d405aaSManivannan Sadhasivam { 246*c2d405aaSManivannan Sadhasivam u8 gpio_set = 0; 247*c2d405aaSManivannan Sadhasivam u8 gpio_clr = 0; 248*c2d405aaSManivannan Sadhasivam int ret = 0; 249*c2d405aaSManivannan Sadhasivam 250*c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 251*c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 252*c2d405aaSManivannan Sadhasivam gpio_clr |= XR21V141X_UART_MODE_RTS; 253*c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 254*c2d405aaSManivannan Sadhasivam gpio_clr |= XR21V141X_UART_MODE_DTR; 255*c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 256*c2d405aaSManivannan Sadhasivam gpio_set |= XR21V141X_UART_MODE_RTS; 257*c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 258*c2d405aaSManivannan Sadhasivam gpio_set |= XR21V141X_UART_MODE_DTR; 259*c2d405aaSManivannan Sadhasivam 260*c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 261*c2d405aaSManivannan Sadhasivam if (gpio_clr) 262*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_CLR, gpio_clr); 263*c2d405aaSManivannan Sadhasivam 264*c2d405aaSManivannan Sadhasivam if (gpio_set) 265*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_SET, gpio_set); 266*c2d405aaSManivannan Sadhasivam 267*c2d405aaSManivannan Sadhasivam return ret; 268*c2d405aaSManivannan Sadhasivam } 269*c2d405aaSManivannan Sadhasivam 270*c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 271*c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 272*c2d405aaSManivannan Sadhasivam { 273*c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 274*c2d405aaSManivannan Sadhasivam 275*c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 276*c2d405aaSManivannan Sadhasivam } 277*c2d405aaSManivannan Sadhasivam 278*c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 279*c2d405aaSManivannan Sadhasivam { 280*c2d405aaSManivannan Sadhasivam if (on) 281*c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 282*c2d405aaSManivannan Sadhasivam else 283*c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 284*c2d405aaSManivannan Sadhasivam } 285*c2d405aaSManivannan Sadhasivam 286*c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 287*c2d405aaSManivannan Sadhasivam { 288*c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 289*c2d405aaSManivannan Sadhasivam u8 state; 290*c2d405aaSManivannan Sadhasivam 291*c2d405aaSManivannan Sadhasivam if (break_state == 0) 292*c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_OFF; 293*c2d405aaSManivannan Sadhasivam else 294*c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_ON; 295*c2d405aaSManivannan Sadhasivam 296*c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Turning break %s\n", 297*c2d405aaSManivannan Sadhasivam state == XR21V141X_UART_BREAK_OFF ? "off" : "on"); 298*c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_TX_BREAK, state); 299*c2d405aaSManivannan Sadhasivam } 300*c2d405aaSManivannan Sadhasivam 301*c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 302*c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 303*c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 304*c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 305*c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 306*c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 307*c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 308*c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 309*c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 310*c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 311*c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 312*c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 313*c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 314*c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 315*c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 316*c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 317*c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 318*c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 319*c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 320*c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 321*c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 322*c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 323*c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 324*c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 325*c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 326*c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 327*c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 328*c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 329*c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 330*c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 331*c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 332*c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 333*c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 334*c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 335*c2d405aaSManivannan Sadhasivam }; 336*c2d405aaSManivannan Sadhasivam 337*c2d405aaSManivannan Sadhasivam static int xr_set_baudrate(struct tty_struct *tty, 338*c2d405aaSManivannan Sadhasivam struct usb_serial_port *port) 339*c2d405aaSManivannan Sadhasivam { 340*c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 341*c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 342*c2d405aaSManivannan Sadhasivam int ret; 343*c2d405aaSManivannan Sadhasivam 344*c2d405aaSManivannan Sadhasivam baud = clamp(tty->termios.c_ospeed, XR21V141X_MIN_SPEED, 345*c2d405aaSManivannan Sadhasivam XR21V141X_MAX_SPEED); 346*c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 347*c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 348*c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 349*c2d405aaSManivannan Sadhasivam 350*c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 351*c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 352*c2d405aaSManivannan Sadhasivam else 353*c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 354*c2d405aaSManivannan Sadhasivam 355*c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 356*c2d405aaSManivannan Sadhasivam /* 357*c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 358*c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 359*c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 360*c2d405aaSManivannan Sadhasivam */ 361*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 362*c2d405aaSManivannan Sadhasivam divisor & 0xff); 363*c2d405aaSManivannan Sadhasivam if (ret) 364*c2d405aaSManivannan Sadhasivam return ret; 365*c2d405aaSManivannan Sadhasivam 366*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 367*c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 368*c2d405aaSManivannan Sadhasivam if (ret) 369*c2d405aaSManivannan Sadhasivam return ret; 370*c2d405aaSManivannan Sadhasivam 371*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 372*c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 373*c2d405aaSManivannan Sadhasivam if (ret) 374*c2d405aaSManivannan Sadhasivam return ret; 375*c2d405aaSManivannan Sadhasivam 376*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 377*c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 378*c2d405aaSManivannan Sadhasivam if (ret) 379*c2d405aaSManivannan Sadhasivam return ret; 380*c2d405aaSManivannan Sadhasivam 381*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 382*c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 383*c2d405aaSManivannan Sadhasivam if (ret) 384*c2d405aaSManivannan Sadhasivam return ret; 385*c2d405aaSManivannan Sadhasivam 386*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 387*c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 388*c2d405aaSManivannan Sadhasivam if (ret) 389*c2d405aaSManivannan Sadhasivam return ret; 390*c2d405aaSManivannan Sadhasivam 391*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 392*c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 393*c2d405aaSManivannan Sadhasivam if (ret) 394*c2d405aaSManivannan Sadhasivam return ret; 395*c2d405aaSManivannan Sadhasivam 396*c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 397*c2d405aaSManivannan Sadhasivam 398*c2d405aaSManivannan Sadhasivam return 0; 399*c2d405aaSManivannan Sadhasivam } 400*c2d405aaSManivannan Sadhasivam 401*c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 402*c2d405aaSManivannan Sadhasivam struct usb_serial_port *port) 403*c2d405aaSManivannan Sadhasivam { 404*c2d405aaSManivannan Sadhasivam unsigned int cflag = tty->termios.c_cflag; 405*c2d405aaSManivannan Sadhasivam u8 flow, gpio_mode; 406*c2d405aaSManivannan Sadhasivam int ret; 407*c2d405aaSManivannan Sadhasivam 408*c2d405aaSManivannan Sadhasivam ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_MODE, &gpio_mode); 409*c2d405aaSManivannan Sadhasivam if (ret) 410*c2d405aaSManivannan Sadhasivam return; 411*c2d405aaSManivannan Sadhasivam 412*c2d405aaSManivannan Sadhasivam if (cflag & CRTSCTS) { 413*c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 414*c2d405aaSManivannan Sadhasivam 415*c2d405aaSManivannan Sadhasivam /* 416*c2d405aaSManivannan Sadhasivam * RTS/CTS is the default flow control mode, so set GPIO mode 417*c2d405aaSManivannan Sadhasivam * for controlling the pins manually by default. 418*c2d405aaSManivannan Sadhasivam */ 419*c2d405aaSManivannan Sadhasivam gpio_mode &= ~XR21V141X_UART_MODE_GPIO_MASK; 420*c2d405aaSManivannan Sadhasivam gpio_mode |= XR21V141X_UART_MODE_RTS_CTS; 421*c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_HW; 422*c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 423*c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 424*c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 425*c2d405aaSManivannan Sadhasivam 426*c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 427*c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_SW; 428*c2d405aaSManivannan Sadhasivam 429*c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_XON_CHAR, start_char); 430*c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_XOFF_CHAR, stop_char); 431*c2d405aaSManivannan Sadhasivam } else { 432*c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 433*c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_NONE; 434*c2d405aaSManivannan Sadhasivam } 435*c2d405aaSManivannan Sadhasivam 436*c2d405aaSManivannan Sadhasivam /* 437*c2d405aaSManivannan Sadhasivam * As per the datasheet, UART needs to be disabled while writing to 438*c2d405aaSManivannan Sadhasivam * FLOW_CONTROL register. 439*c2d405aaSManivannan Sadhasivam */ 440*c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 441*c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_FLOW_CTRL, flow); 442*c2d405aaSManivannan Sadhasivam xr_uart_enable(port); 443*c2d405aaSManivannan Sadhasivam 444*c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_GPIO_MODE, gpio_mode); 445*c2d405aaSManivannan Sadhasivam } 446*c2d405aaSManivannan Sadhasivam 447*c2d405aaSManivannan Sadhasivam static void xr_set_termios(struct tty_struct *tty, 448*c2d405aaSManivannan Sadhasivam struct usb_serial_port *port, 449*c2d405aaSManivannan Sadhasivam struct ktermios *old_termios) 450*c2d405aaSManivannan Sadhasivam { 451*c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 452*c2d405aaSManivannan Sadhasivam u8 bits = 0; 453*c2d405aaSManivannan Sadhasivam int ret; 454*c2d405aaSManivannan Sadhasivam 455*c2d405aaSManivannan Sadhasivam if ((old_termios && tty->termios.c_ospeed != old_termios->c_ospeed) || 456*c2d405aaSManivannan Sadhasivam !old_termios) 457*c2d405aaSManivannan Sadhasivam xr_set_baudrate(tty, port); 458*c2d405aaSManivannan Sadhasivam 459*c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 460*c2d405aaSManivannan Sadhasivam case CS5: 461*c2d405aaSManivannan Sadhasivam case CS6: 462*c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 463*c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 464*c2d405aaSManivannan Sadhasivam if (old_termios) 465*c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 466*c2d405aaSManivannan Sadhasivam else 467*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_8; 468*c2d405aaSManivannan Sadhasivam break; 469*c2d405aaSManivannan Sadhasivam case CS7: 470*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_7; 471*c2d405aaSManivannan Sadhasivam break; 472*c2d405aaSManivannan Sadhasivam case CS8: 473*c2d405aaSManivannan Sadhasivam default: 474*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_8; 475*c2d405aaSManivannan Sadhasivam break; 476*c2d405aaSManivannan Sadhasivam } 477*c2d405aaSManivannan Sadhasivam 478*c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 479*c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 480*c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 481*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_PARITY_MARK << 482*c2d405aaSManivannan Sadhasivam XR21V141X_UART_PARITY_SHIFT; 483*c2d405aaSManivannan Sadhasivam else 484*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_PARITY_SPACE << 485*c2d405aaSManivannan Sadhasivam XR21V141X_UART_PARITY_SHIFT; 486*c2d405aaSManivannan Sadhasivam } else { 487*c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 488*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_PARITY_ODD << 489*c2d405aaSManivannan Sadhasivam XR21V141X_UART_PARITY_SHIFT; 490*c2d405aaSManivannan Sadhasivam else 491*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_PARITY_EVEN << 492*c2d405aaSManivannan Sadhasivam XR21V141X_UART_PARITY_SHIFT; 493*c2d405aaSManivannan Sadhasivam } 494*c2d405aaSManivannan Sadhasivam } 495*c2d405aaSManivannan Sadhasivam 496*c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 497*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_STOP_2 << XR21V141X_UART_STOP_SHIFT; 498*c2d405aaSManivannan Sadhasivam else 499*c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_STOP_1 << XR21V141X_UART_STOP_SHIFT; 500*c2d405aaSManivannan Sadhasivam 501*c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 502*c2d405aaSManivannan Sadhasivam if (ret) 503*c2d405aaSManivannan Sadhasivam return; 504*c2d405aaSManivannan Sadhasivam 505*c2d405aaSManivannan Sadhasivam /* If baud rate is B0, clear DTR and RTS */ 506*c2d405aaSManivannan Sadhasivam if (C_BAUD(tty) == B0) 507*c2d405aaSManivannan Sadhasivam xr_dtr_rts(port, 0); 508*c2d405aaSManivannan Sadhasivam 509*c2d405aaSManivannan Sadhasivam xr_set_flow_mode(tty, port); 510*c2d405aaSManivannan Sadhasivam } 511*c2d405aaSManivannan Sadhasivam 512*c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 513*c2d405aaSManivannan Sadhasivam { 514*c2d405aaSManivannan Sadhasivam int ret; 515*c2d405aaSManivannan Sadhasivam 516*c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 517*c2d405aaSManivannan Sadhasivam if (ret) { 518*c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 519*c2d405aaSManivannan Sadhasivam return ret; 520*c2d405aaSManivannan Sadhasivam } 521*c2d405aaSManivannan Sadhasivam 522*c2d405aaSManivannan Sadhasivam /* Setup termios */ 523*c2d405aaSManivannan Sadhasivam if (tty) 524*c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 525*c2d405aaSManivannan Sadhasivam 526*c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 527*c2d405aaSManivannan Sadhasivam if (ret) { 528*c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 529*c2d405aaSManivannan Sadhasivam return ret; 530*c2d405aaSManivannan Sadhasivam } 531*c2d405aaSManivannan Sadhasivam 532*c2d405aaSManivannan Sadhasivam return 0; 533*c2d405aaSManivannan Sadhasivam } 534*c2d405aaSManivannan Sadhasivam 535*c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 536*c2d405aaSManivannan Sadhasivam { 537*c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 538*c2d405aaSManivannan Sadhasivam 539*c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 540*c2d405aaSManivannan Sadhasivam } 541*c2d405aaSManivannan Sadhasivam 542*c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 543*c2d405aaSManivannan Sadhasivam { 544*c2d405aaSManivannan Sadhasivam struct usb_device *usb_dev = interface_to_usbdev(serial->interface); 545*c2d405aaSManivannan Sadhasivam struct usb_driver *driver = serial->type->usb_driver; 546*c2d405aaSManivannan Sadhasivam struct usb_interface *control_interface; 547*c2d405aaSManivannan Sadhasivam int ret; 548*c2d405aaSManivannan Sadhasivam 549*c2d405aaSManivannan Sadhasivam /* Don't bind to control interface */ 550*c2d405aaSManivannan Sadhasivam if (serial->interface->cur_altsetting->desc.bInterfaceNumber == 0) 551*c2d405aaSManivannan Sadhasivam return -ENODEV; 552*c2d405aaSManivannan Sadhasivam 553*c2d405aaSManivannan Sadhasivam /* But claim the control interface during data interface probe */ 554*c2d405aaSManivannan Sadhasivam control_interface = usb_ifnum_to_if(usb_dev, 0); 555*c2d405aaSManivannan Sadhasivam ret = usb_driver_claim_interface(driver, control_interface, NULL); 556*c2d405aaSManivannan Sadhasivam if (ret) { 557*c2d405aaSManivannan Sadhasivam dev_err(&serial->interface->dev, "Failed to claim control interface\n"); 558*c2d405aaSManivannan Sadhasivam return ret; 559*c2d405aaSManivannan Sadhasivam } 560*c2d405aaSManivannan Sadhasivam 561*c2d405aaSManivannan Sadhasivam return 0; 562*c2d405aaSManivannan Sadhasivam } 563*c2d405aaSManivannan Sadhasivam 564*c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 565*c2d405aaSManivannan Sadhasivam { USB_DEVICE(0x04e2, 0x1410) }, /* XR21V141X */ 566*c2d405aaSManivannan Sadhasivam { } 567*c2d405aaSManivannan Sadhasivam }; 568*c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 569*c2d405aaSManivannan Sadhasivam 570*c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 571*c2d405aaSManivannan Sadhasivam .driver = { 572*c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 573*c2d405aaSManivannan Sadhasivam .name = "xr_serial", 574*c2d405aaSManivannan Sadhasivam }, 575*c2d405aaSManivannan Sadhasivam .id_table = id_table, 576*c2d405aaSManivannan Sadhasivam .num_ports = 1, 577*c2d405aaSManivannan Sadhasivam .probe = xr_probe, 578*c2d405aaSManivannan Sadhasivam .open = xr_open, 579*c2d405aaSManivannan Sadhasivam .close = xr_close, 580*c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 581*c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 582*c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 583*c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 584*c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 585*c2d405aaSManivannan Sadhasivam }; 586*c2d405aaSManivannan Sadhasivam 587*c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 588*c2d405aaSManivannan Sadhasivam &xr_device, NULL 589*c2d405aaSManivannan Sadhasivam }; 590*c2d405aaSManivannan Sadhasivam 591*c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 592*c2d405aaSManivannan Sadhasivam 593*c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 594*c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 595*c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 596