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