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> 19*5fec21e7SJohan 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 60c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RI BIT(0) 61c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_CD BIT(1) 62c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_DSR BIT(2) 63c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_DTR BIT(3) 64c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_CTS BIT(4) 65c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_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 91c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_GPIO_MASK GENMASK(2, 0) 92c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RTS_CTS 0x1 93c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_DTR_DSR 0x2 94c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_MODE_RS485 0x3 95c2d405aaSManivannan Sadhasivam #define XR21V141X_UART_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 112c2d405aaSManivannan Sadhasivam static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) 113c2d405aaSManivannan Sadhasivam { 114c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 115c2d405aaSManivannan Sadhasivam int ret; 116c2d405aaSManivannan Sadhasivam 117c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 118c2d405aaSManivannan Sadhasivam usb_sndctrlpipe(serial->dev, 0), 119c2d405aaSManivannan Sadhasivam XR21V141X_SET_REQ, 12072fc7fc7SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 12172fc7fc7SJohan Hovold val, reg | (block << 8), NULL, 0, 122c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 123c2d405aaSManivannan Sadhasivam if (ret < 0) { 124c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 125c2d405aaSManivannan Sadhasivam return ret; 126c2d405aaSManivannan Sadhasivam } 127c2d405aaSManivannan Sadhasivam 128c2d405aaSManivannan Sadhasivam return 0; 129c2d405aaSManivannan Sadhasivam } 130c2d405aaSManivannan Sadhasivam 131c2d405aaSManivannan Sadhasivam static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) 132c2d405aaSManivannan Sadhasivam { 133c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 134c2d405aaSManivannan Sadhasivam u8 *dmabuf; 135c2d405aaSManivannan Sadhasivam int ret; 136c2d405aaSManivannan Sadhasivam 137c2d405aaSManivannan Sadhasivam dmabuf = kmalloc(1, GFP_KERNEL); 138c2d405aaSManivannan Sadhasivam if (!dmabuf) 139c2d405aaSManivannan Sadhasivam return -ENOMEM; 140c2d405aaSManivannan Sadhasivam 141c2d405aaSManivannan Sadhasivam ret = usb_control_msg(serial->dev, 142c2d405aaSManivannan Sadhasivam usb_rcvctrlpipe(serial->dev, 0), 143c2d405aaSManivannan Sadhasivam XR21V141X_GET_REQ, 14472fc7fc7SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 14572fc7fc7SJohan Hovold 0, reg | (block << 8), dmabuf, 1, 146c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 147c2d405aaSManivannan Sadhasivam if (ret == 1) { 148c2d405aaSManivannan Sadhasivam *val = *dmabuf; 149c2d405aaSManivannan Sadhasivam ret = 0; 150c2d405aaSManivannan Sadhasivam } else { 151c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 152c2d405aaSManivannan Sadhasivam if (ret >= 0) 153c2d405aaSManivannan Sadhasivam ret = -EIO; 154c2d405aaSManivannan Sadhasivam } 155c2d405aaSManivannan Sadhasivam 156c2d405aaSManivannan Sadhasivam kfree(dmabuf); 157c2d405aaSManivannan Sadhasivam 158c2d405aaSManivannan Sadhasivam return ret; 159c2d405aaSManivannan Sadhasivam } 160c2d405aaSManivannan Sadhasivam 161c2d405aaSManivannan Sadhasivam static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u8 val) 162c2d405aaSManivannan Sadhasivam { 163c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); 164c2d405aaSManivannan Sadhasivam } 165c2d405aaSManivannan Sadhasivam 166c2d405aaSManivannan Sadhasivam static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u8 *val) 167c2d405aaSManivannan Sadhasivam { 168c2d405aaSManivannan Sadhasivam return xr_get_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); 169c2d405aaSManivannan Sadhasivam } 170c2d405aaSManivannan Sadhasivam 171c2d405aaSManivannan Sadhasivam static int xr_set_reg_um(struct usb_serial_port *port, u8 reg, u8 val) 172c2d405aaSManivannan Sadhasivam { 173c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 174c2d405aaSManivannan Sadhasivam } 175c2d405aaSManivannan Sadhasivam 176c2d405aaSManivannan Sadhasivam /* 177c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 178c2d405aaSManivannan Sadhasivam * module in XR21V141X: 179c2d405aaSManivannan Sadhasivam * 180c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 181c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 182c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 183c2d405aaSManivannan Sadhasivam */ 184c2d405aaSManivannan Sadhasivam static int xr_uart_enable(struct usb_serial_port *port) 185c2d405aaSManivannan Sadhasivam { 186c2d405aaSManivannan Sadhasivam int ret; 187c2d405aaSManivannan Sadhasivam 188c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 189c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 190c2d405aaSManivannan Sadhasivam if (ret) 191c2d405aaSManivannan Sadhasivam return ret; 192c2d405aaSManivannan Sadhasivam 193c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 194c2d405aaSManivannan Sadhasivam XR21V141X_UART_ENABLE_TX | XR21V141X_UART_ENABLE_RX); 195c2d405aaSManivannan Sadhasivam if (ret) 196c2d405aaSManivannan Sadhasivam return ret; 197c2d405aaSManivannan Sadhasivam 198c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 199c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 200c2d405aaSManivannan Sadhasivam 201c2d405aaSManivannan Sadhasivam if (ret) 202c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); 203c2d405aaSManivannan Sadhasivam 204c2d405aaSManivannan Sadhasivam return ret; 205c2d405aaSManivannan Sadhasivam } 206c2d405aaSManivannan Sadhasivam 207c2d405aaSManivannan Sadhasivam static int xr_uart_disable(struct usb_serial_port *port) 208c2d405aaSManivannan Sadhasivam { 209c2d405aaSManivannan Sadhasivam int ret; 210c2d405aaSManivannan Sadhasivam 211c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); 212c2d405aaSManivannan Sadhasivam if (ret) 213c2d405aaSManivannan Sadhasivam return ret; 214c2d405aaSManivannan Sadhasivam 215c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 216c2d405aaSManivannan Sadhasivam 217c2d405aaSManivannan Sadhasivam return ret; 218c2d405aaSManivannan Sadhasivam } 219c2d405aaSManivannan Sadhasivam 220c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 221c2d405aaSManivannan Sadhasivam { 222c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 223c2d405aaSManivannan Sadhasivam u8 status; 224c2d405aaSManivannan Sadhasivam int ret; 225c2d405aaSManivannan Sadhasivam 226c2d405aaSManivannan Sadhasivam ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_STATUS, &status); 227c2d405aaSManivannan Sadhasivam if (ret) 228c2d405aaSManivannan Sadhasivam return ret; 229c2d405aaSManivannan Sadhasivam 230c2d405aaSManivannan Sadhasivam /* 231c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 232c2d405aaSManivannan Sadhasivam * and '1' means not active. 233c2d405aaSManivannan Sadhasivam */ 234c2d405aaSManivannan Sadhasivam ret = ((status & XR21V141X_UART_MODE_DTR) ? 0 : TIOCM_DTR) | 235c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_RTS) ? 0 : TIOCM_RTS) | 236c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_CTS) ? 0 : TIOCM_CTS) | 237c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_DSR) ? 0 : TIOCM_DSR) | 238c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_RI) ? 0 : TIOCM_RI) | 239c2d405aaSManivannan Sadhasivam ((status & XR21V141X_UART_MODE_CD) ? 0 : TIOCM_CD); 240c2d405aaSManivannan Sadhasivam 241c2d405aaSManivannan Sadhasivam return ret; 242c2d405aaSManivannan Sadhasivam } 243c2d405aaSManivannan Sadhasivam 244c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 245c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 246c2d405aaSManivannan Sadhasivam { 247c2d405aaSManivannan Sadhasivam u8 gpio_set = 0; 248c2d405aaSManivannan Sadhasivam u8 gpio_clr = 0; 249c2d405aaSManivannan Sadhasivam int ret = 0; 250c2d405aaSManivannan Sadhasivam 251c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 252c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 253c2d405aaSManivannan Sadhasivam gpio_clr |= XR21V141X_UART_MODE_RTS; 254c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 255c2d405aaSManivannan Sadhasivam gpio_clr |= XR21V141X_UART_MODE_DTR; 256c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 257c2d405aaSManivannan Sadhasivam gpio_set |= XR21V141X_UART_MODE_RTS; 258c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 259c2d405aaSManivannan Sadhasivam gpio_set |= XR21V141X_UART_MODE_DTR; 260c2d405aaSManivannan Sadhasivam 261c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 262c2d405aaSManivannan Sadhasivam if (gpio_clr) 263c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_CLR, gpio_clr); 264c2d405aaSManivannan Sadhasivam 265c2d405aaSManivannan Sadhasivam if (gpio_set) 266c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_SET, gpio_set); 267c2d405aaSManivannan Sadhasivam 268c2d405aaSManivannan Sadhasivam return ret; 269c2d405aaSManivannan Sadhasivam } 270c2d405aaSManivannan Sadhasivam 271c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 272c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 273c2d405aaSManivannan Sadhasivam { 274c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 275c2d405aaSManivannan Sadhasivam 276c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 277c2d405aaSManivannan Sadhasivam } 278c2d405aaSManivannan Sadhasivam 279c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 280c2d405aaSManivannan Sadhasivam { 281c2d405aaSManivannan Sadhasivam if (on) 282c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 283c2d405aaSManivannan Sadhasivam else 284c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 285c2d405aaSManivannan Sadhasivam } 286c2d405aaSManivannan Sadhasivam 287c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 288c2d405aaSManivannan Sadhasivam { 289c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 290c2d405aaSManivannan Sadhasivam u8 state; 291c2d405aaSManivannan Sadhasivam 292c2d405aaSManivannan Sadhasivam if (break_state == 0) 293c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_OFF; 294c2d405aaSManivannan Sadhasivam else 295c2d405aaSManivannan Sadhasivam state = XR21V141X_UART_BREAK_ON; 296c2d405aaSManivannan Sadhasivam 297c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Turning break %s\n", 298c2d405aaSManivannan Sadhasivam state == XR21V141X_UART_BREAK_OFF ? "off" : "on"); 299c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_TX_BREAK, state); 300c2d405aaSManivannan Sadhasivam } 301c2d405aaSManivannan Sadhasivam 302c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 303c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 304c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 305c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 306c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 307c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 308c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 309c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 310c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 311c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 312c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 313c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 314c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 315c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 316c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 317c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 318c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 319c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 320c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 321c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 322c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 323c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 324c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 325c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 326c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 327c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 328c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 329c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 330c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 331c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 332c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 333c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 334c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 335c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 336c2d405aaSManivannan Sadhasivam }; 337c2d405aaSManivannan Sadhasivam 338c2d405aaSManivannan Sadhasivam static int xr_set_baudrate(struct tty_struct *tty, 339c2d405aaSManivannan Sadhasivam struct usb_serial_port *port) 340c2d405aaSManivannan Sadhasivam { 341c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 342c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 343c2d405aaSManivannan Sadhasivam int ret; 344c2d405aaSManivannan Sadhasivam 34555317e22SJohan Hovold baud = tty->termios.c_ospeed; 34655317e22SJohan Hovold if (!baud) 34755317e22SJohan Hovold return 0; 34855317e22SJohan Hovold 34955317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 350c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 351c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 352c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 353c2d405aaSManivannan Sadhasivam 354c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 355c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 356c2d405aaSManivannan Sadhasivam else 357c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 358c2d405aaSManivannan Sadhasivam 359c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 360c2d405aaSManivannan Sadhasivam /* 361c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 362c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 363c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 364c2d405aaSManivannan Sadhasivam */ 365c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 366c2d405aaSManivannan Sadhasivam divisor & 0xff); 367c2d405aaSManivannan Sadhasivam if (ret) 368c2d405aaSManivannan Sadhasivam return ret; 369c2d405aaSManivannan Sadhasivam 370c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 371c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 372c2d405aaSManivannan Sadhasivam if (ret) 373c2d405aaSManivannan Sadhasivam return ret; 374c2d405aaSManivannan Sadhasivam 375c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 376c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 377c2d405aaSManivannan Sadhasivam if (ret) 378c2d405aaSManivannan Sadhasivam return ret; 379c2d405aaSManivannan Sadhasivam 380c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 381c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 382c2d405aaSManivannan Sadhasivam if (ret) 383c2d405aaSManivannan Sadhasivam return ret; 384c2d405aaSManivannan Sadhasivam 385c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 386c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 387c2d405aaSManivannan Sadhasivam if (ret) 388c2d405aaSManivannan Sadhasivam return ret; 389c2d405aaSManivannan Sadhasivam 390c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 391c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 392c2d405aaSManivannan Sadhasivam if (ret) 393c2d405aaSManivannan Sadhasivam return ret; 394c2d405aaSManivannan Sadhasivam 395c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 396c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 397c2d405aaSManivannan Sadhasivam if (ret) 398c2d405aaSManivannan Sadhasivam return ret; 399c2d405aaSManivannan Sadhasivam 400c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 401c2d405aaSManivannan Sadhasivam 402c2d405aaSManivannan Sadhasivam return 0; 403c2d405aaSManivannan Sadhasivam } 404c2d405aaSManivannan Sadhasivam 405c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 40655317e22SJohan Hovold struct usb_serial_port *port, 40755317e22SJohan Hovold struct ktermios *old_termios) 408c2d405aaSManivannan Sadhasivam { 409c2d405aaSManivannan Sadhasivam u8 flow, gpio_mode; 410c2d405aaSManivannan Sadhasivam int ret; 411c2d405aaSManivannan Sadhasivam 412c2d405aaSManivannan Sadhasivam ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_MODE, &gpio_mode); 413c2d405aaSManivannan Sadhasivam if (ret) 414c2d405aaSManivannan Sadhasivam return; 415c2d405aaSManivannan Sadhasivam 416465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 417465d3b3aSJohan Hovold gpio_mode &= ~XR21V141X_UART_MODE_GPIO_MASK; 418465d3b3aSJohan Hovold 41955317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 420c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 421c2d405aaSManivannan Sadhasivam gpio_mode |= XR21V141X_UART_MODE_RTS_CTS; 422c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_HW; 423c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 424c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 425c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 426c2d405aaSManivannan Sadhasivam 427c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 428c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_SW; 429c2d405aaSManivannan Sadhasivam 430c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_XON_CHAR, start_char); 431c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_XOFF_CHAR, stop_char); 432c2d405aaSManivannan Sadhasivam } else { 433c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 434c2d405aaSManivannan Sadhasivam flow = XR21V141X_UART_FLOW_MODE_NONE; 435c2d405aaSManivannan Sadhasivam } 436c2d405aaSManivannan Sadhasivam 437c2d405aaSManivannan Sadhasivam /* 438c2d405aaSManivannan Sadhasivam * As per the datasheet, UART needs to be disabled while writing to 439c2d405aaSManivannan Sadhasivam * FLOW_CONTROL register. 440c2d405aaSManivannan Sadhasivam */ 441c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 442c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_FLOW_CTRL, flow); 443c2d405aaSManivannan Sadhasivam xr_uart_enable(port); 444c2d405aaSManivannan Sadhasivam 445c2d405aaSManivannan Sadhasivam xr_set_reg_uart(port, XR21V141X_REG_GPIO_MODE, gpio_mode); 44655317e22SJohan Hovold 44755317e22SJohan Hovold if (C_BAUD(tty) == B0) 44855317e22SJohan Hovold xr_dtr_rts(port, 0); 44955317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 45055317e22SJohan Hovold xr_dtr_rts(port, 1); 451c2d405aaSManivannan Sadhasivam } 452c2d405aaSManivannan Sadhasivam 453c2d405aaSManivannan Sadhasivam static void xr_set_termios(struct tty_struct *tty, 454c2d405aaSManivannan Sadhasivam struct usb_serial_port *port, 455c2d405aaSManivannan Sadhasivam struct ktermios *old_termios) 456c2d405aaSManivannan Sadhasivam { 457c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 458c2d405aaSManivannan Sadhasivam u8 bits = 0; 459c2d405aaSManivannan Sadhasivam int ret; 460c2d405aaSManivannan Sadhasivam 461736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 462c2d405aaSManivannan Sadhasivam xr_set_baudrate(tty, port); 463c2d405aaSManivannan Sadhasivam 464c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 465c2d405aaSManivannan Sadhasivam case CS5: 466c2d405aaSManivannan Sadhasivam case CS6: 467c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 468c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 469c2d405aaSManivannan Sadhasivam if (old_termios) 470c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 471c2d405aaSManivannan Sadhasivam else 472ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 473ea7ada4dSJohan Hovold 474ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 475ea7ada4dSJohan Hovold bits |= XR21V141X_UART_DATA_7; 476ea7ada4dSJohan Hovold else 477c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_8; 478c2d405aaSManivannan Sadhasivam break; 479c2d405aaSManivannan Sadhasivam case CS7: 480c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_7; 481c2d405aaSManivannan Sadhasivam break; 482c2d405aaSManivannan Sadhasivam case CS8: 483c2d405aaSManivannan Sadhasivam default: 484c2d405aaSManivannan Sadhasivam bits |= XR21V141X_UART_DATA_8; 485c2d405aaSManivannan Sadhasivam break; 486c2d405aaSManivannan Sadhasivam } 487c2d405aaSManivannan Sadhasivam 488c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 489c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 490c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 49135567511SJohan Hovold bits |= XR21V141X_UART_PARITY_MARK; 492c2d405aaSManivannan Sadhasivam else 49335567511SJohan Hovold bits |= XR21V141X_UART_PARITY_SPACE; 494c2d405aaSManivannan Sadhasivam } else { 495c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 49635567511SJohan Hovold bits |= XR21V141X_UART_PARITY_ODD; 497c2d405aaSManivannan Sadhasivam else 49835567511SJohan Hovold bits |= XR21V141X_UART_PARITY_EVEN; 499c2d405aaSManivannan Sadhasivam } 500c2d405aaSManivannan Sadhasivam } 501c2d405aaSManivannan Sadhasivam 502c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 50335567511SJohan Hovold bits |= XR21V141X_UART_STOP_2; 504c2d405aaSManivannan Sadhasivam else 50535567511SJohan Hovold bits |= XR21V141X_UART_STOP_1; 506c2d405aaSManivannan Sadhasivam 507c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 508c2d405aaSManivannan Sadhasivam if (ret) 509c2d405aaSManivannan Sadhasivam return; 510c2d405aaSManivannan Sadhasivam 51155317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 512c2d405aaSManivannan Sadhasivam } 513c2d405aaSManivannan Sadhasivam 514c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 515c2d405aaSManivannan Sadhasivam { 5160d05d7d9SJohan Hovold u8 gpio_dir; 517c2d405aaSManivannan Sadhasivam int ret; 518c2d405aaSManivannan Sadhasivam 519c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 520c2d405aaSManivannan Sadhasivam if (ret) { 521c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 522c2d405aaSManivannan Sadhasivam return ret; 523c2d405aaSManivannan Sadhasivam } 524c2d405aaSManivannan Sadhasivam 5250d05d7d9SJohan Hovold /* 5260d05d7d9SJohan Hovold * Configure DTR and RTS as outputs and RI, CD, DSR and CTS as 5270d05d7d9SJohan Hovold * inputs. 5280d05d7d9SJohan Hovold */ 5290d05d7d9SJohan Hovold gpio_dir = XR21V141X_UART_MODE_DTR | XR21V141X_UART_MODE_RTS; 5300d05d7d9SJohan Hovold xr_set_reg_uart(port, XR21V141X_REG_GPIO_DIR, gpio_dir); 5310d05d7d9SJohan Hovold 532c2d405aaSManivannan Sadhasivam /* Setup termios */ 533c2d405aaSManivannan Sadhasivam if (tty) 534c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 535c2d405aaSManivannan Sadhasivam 536c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 537c2d405aaSManivannan Sadhasivam if (ret) { 538c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 539c2d405aaSManivannan Sadhasivam return ret; 540c2d405aaSManivannan Sadhasivam } 541c2d405aaSManivannan Sadhasivam 542c2d405aaSManivannan Sadhasivam return 0; 543c2d405aaSManivannan Sadhasivam } 544c2d405aaSManivannan Sadhasivam 545c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 546c2d405aaSManivannan Sadhasivam { 547c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 548c2d405aaSManivannan Sadhasivam 549c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 550c2d405aaSManivannan Sadhasivam } 551c2d405aaSManivannan Sadhasivam 552c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 553c2d405aaSManivannan Sadhasivam { 554*5fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 555*5fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 556*5fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 557*5fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 558*5fec21e7SJohan Hovold struct usb_interface *data; 559*5fec21e7SJohan Hovold int ret; 560*5fec21e7SJohan Hovold 561*5fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 562*5fec21e7SJohan Hovold if (ret < 0) 563c2d405aaSManivannan Sadhasivam return -ENODEV; 564c2d405aaSManivannan Sadhasivam 565*5fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 566*5fec21e7SJohan Hovold if (!desc) 567*5fec21e7SJohan Hovold return -ENODEV; 568*5fec21e7SJohan Hovold 569*5fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 570*5fec21e7SJohan Hovold if (!data) 571*5fec21e7SJohan Hovold return -ENODEV; 572*5fec21e7SJohan Hovold 573*5fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 574*5fec21e7SJohan Hovold if (ret) 575*5fec21e7SJohan Hovold return ret; 576*5fec21e7SJohan Hovold 577c2d405aaSManivannan Sadhasivam return 0; 578c2d405aaSManivannan Sadhasivam } 579c2d405aaSManivannan Sadhasivam 580c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 581*5fec21e7SJohan Hovold { USB_DEVICE_INTERFACE_CLASS(0x04e2, 0x1410, USB_CLASS_COMM) }, /* XR21V141X */ 582c2d405aaSManivannan Sadhasivam { } 583c2d405aaSManivannan Sadhasivam }; 584c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 585c2d405aaSManivannan Sadhasivam 586c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 587c2d405aaSManivannan Sadhasivam .driver = { 588c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 589c2d405aaSManivannan Sadhasivam .name = "xr_serial", 590c2d405aaSManivannan Sadhasivam }, 591c2d405aaSManivannan Sadhasivam .id_table = id_table, 592c2d405aaSManivannan Sadhasivam .num_ports = 1, 593c2d405aaSManivannan Sadhasivam .probe = xr_probe, 594c2d405aaSManivannan Sadhasivam .open = xr_open, 595c2d405aaSManivannan Sadhasivam .close = xr_close, 596c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 597c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 598c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 599c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 600c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 601c2d405aaSManivannan Sadhasivam }; 602c2d405aaSManivannan Sadhasivam 603c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 604c2d405aaSManivannan Sadhasivam &xr_device, NULL 605c2d405aaSManivannan Sadhasivam }; 606c2d405aaSManivannan Sadhasivam 607c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 608c2d405aaSManivannan Sadhasivam 609c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 610c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 611c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 612