1c2d405aaSManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0+ 2c2d405aaSManivannan Sadhasivam /* 3c2d405aaSManivannan Sadhasivam * MaxLinear/Exar USB to Serial driver 4c2d405aaSManivannan Sadhasivam * 5c2d405aaSManivannan Sadhasivam * Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org> 6c2d405aaSManivannan Sadhasivam * 7c2d405aaSManivannan Sadhasivam * Based on the initial driver written by Patong Yang: 8c2d405aaSManivannan Sadhasivam * 9c2d405aaSManivannan Sadhasivam * https://lore.kernel.org/r/20180404070634.nhspvmxcjwfgjkcv@advantechmxl-desktop 10c2d405aaSManivannan Sadhasivam * 11c2d405aaSManivannan Sadhasivam * Copyright (c) 2018 Patong Yang <patong.mxl@gmail.com> 12c2d405aaSManivannan Sadhasivam */ 13c2d405aaSManivannan Sadhasivam 14c2d405aaSManivannan Sadhasivam #include <linux/kernel.h> 15c2d405aaSManivannan Sadhasivam #include <linux/module.h> 16c2d405aaSManivannan Sadhasivam #include <linux/slab.h> 17c2d405aaSManivannan Sadhasivam #include <linux/tty.h> 18c2d405aaSManivannan Sadhasivam #include <linux/usb.h> 195fec21e7SJohan Hovold #include <linux/usb/cdc.h> 20c2d405aaSManivannan Sadhasivam #include <linux/usb/serial.h> 21c2d405aaSManivannan Sadhasivam 22c2d405aaSManivannan Sadhasivam struct xr_txrx_clk_mask { 23c2d405aaSManivannan Sadhasivam u16 tx; 24c2d405aaSManivannan Sadhasivam u16 rx0; 25c2d405aaSManivannan Sadhasivam u16 rx1; 26c2d405aaSManivannan Sadhasivam }; 27c2d405aaSManivannan Sadhasivam 28c2d405aaSManivannan Sadhasivam #define XR_INT_OSC_HZ 48000000U 29c2d405aaSManivannan Sadhasivam #define XR21V141X_MIN_SPEED 46U 30c2d405aaSManivannan Sadhasivam #define XR21V141X_MAX_SPEED XR_INT_OSC_HZ 31c2d405aaSManivannan Sadhasivam 32f865e614SJohan Hovold /* XR21V141X register blocks */ 33f865e614SJohan Hovold #define XR21V141X_UART_REG_BLOCK 0 34f865e614SJohan Hovold #define XR21V141X_UM_REG_BLOCK 4 35f865e614SJohan Hovold #define XR21V141X_UART_CUSTOM_BLOCK 0x66 36f865e614SJohan Hovold 37f865e614SJohan Hovold /* XR21V141X UART registers */ 38c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_0 0x04 39c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_1 0x05 40c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_2 0x06 41c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_0 0x07 42c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_1 0x08 43c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_0 0x09 44c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_1 0x0a 45f865e614SJohan Hovold #define XR21V141X_REG_FORMAT 0x0b 46c2d405aaSManivannan Sadhasivam 47f865e614SJohan Hovold /* XR21V141X UART Manager registers */ 48c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_FIFO_ENABLE_REG 0x10 49c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_TX_FIFO 0x01 50c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_RX_FIFO 0x02 51c2d405aaSManivannan Sadhasivam 52c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_RX_FIFO_RESET 0x18 53c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_TX_FIFO_RESET 0x1c 54c2d405aaSManivannan Sadhasivam 55958d6b95SJohan Hovold #define XR_UART_ENABLE_TX 0x1 56958d6b95SJohan Hovold #define XR_UART_ENABLE_RX 0x2 57c2d405aaSManivannan Sadhasivam 58958d6b95SJohan Hovold #define XR_GPIO_RI BIT(0) 59958d6b95SJohan Hovold #define XR_GPIO_CD BIT(1) 60958d6b95SJohan Hovold #define XR_GPIO_DSR BIT(2) 61958d6b95SJohan Hovold #define XR_GPIO_DTR BIT(3) 62958d6b95SJohan Hovold #define XR_GPIO_CTS BIT(4) 63958d6b95SJohan Hovold #define XR_GPIO_RTS BIT(5) 64607f6718SJohan Hovold #define XR_GPIO_CLK BIT(6) 65607f6718SJohan Hovold #define XR_GPIO_XEN BIT(7) 66607f6718SJohan Hovold #define XR_GPIO_TXT BIT(8) 67607f6718SJohan Hovold #define XR_GPIO_RXT BIT(9) 68c2d405aaSManivannan Sadhasivam 69958d6b95SJohan Hovold #define XR_UART_DATA_MASK GENMASK(3, 0) 70958d6b95SJohan Hovold #define XR_UART_DATA_7 0x7 71958d6b95SJohan Hovold #define XR_UART_DATA_8 0x8 72c2d405aaSManivannan Sadhasivam 73958d6b95SJohan Hovold #define XR_UART_PARITY_MASK GENMASK(6, 4) 74958d6b95SJohan Hovold #define XR_UART_PARITY_SHIFT 4 75958d6b95SJohan Hovold #define XR_UART_PARITY_NONE (0x0 << XR_UART_PARITY_SHIFT) 76958d6b95SJohan Hovold #define XR_UART_PARITY_ODD (0x1 << XR_UART_PARITY_SHIFT) 77958d6b95SJohan Hovold #define XR_UART_PARITY_EVEN (0x2 << XR_UART_PARITY_SHIFT) 78958d6b95SJohan Hovold #define XR_UART_PARITY_MARK (0x3 << XR_UART_PARITY_SHIFT) 79958d6b95SJohan Hovold #define XR_UART_PARITY_SPACE (0x4 << XR_UART_PARITY_SHIFT) 80c2d405aaSManivannan Sadhasivam 81958d6b95SJohan Hovold #define XR_UART_STOP_MASK BIT(7) 82958d6b95SJohan Hovold #define XR_UART_STOP_SHIFT 7 83958d6b95SJohan Hovold #define XR_UART_STOP_1 (0x0 << XR_UART_STOP_SHIFT) 84958d6b95SJohan Hovold #define XR_UART_STOP_2 (0x1 << XR_UART_STOP_SHIFT) 85c2d405aaSManivannan Sadhasivam 86958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_NONE 0x0 87958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_HW 0x1 88958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_SW 0x2 89c2d405aaSManivannan Sadhasivam 90607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_MASK GENMASK(2, 0) 91607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RTS_CTS 0x1 92607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_DTR_DSR 0x2 93607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RS485 0x3 94607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RS485_ADDR 0x4 95607f6718SJohan Hovold #define XR_GPIO_MODE_TX_TOGGLE 0x100 96607f6718SJohan Hovold #define XR_GPIO_MODE_RX_TOGGLE 0x200 97607f6718SJohan Hovold 98*06f79d57SJohan Hovold #define XR_FIFO_RESET 0x1 99*06f79d57SJohan Hovold 100607f6718SJohan Hovold #define XR_CUSTOM_DRIVER_ACTIVE 0x1 101607f6718SJohan Hovold 102607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port); 103607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port); 104*06f79d57SJohan Hovold static int xr21v141x_fifo_reset(struct usb_serial_port *port); 105607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 106607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios); 107c2d405aaSManivannan Sadhasivam 108f865e614SJohan Hovold struct xr_type { 109607f6718SJohan Hovold int reg_width; 110607f6718SJohan Hovold u8 reg_recipient; 111607f6718SJohan Hovold u8 set_reg; 112607f6718SJohan Hovold u8 get_reg; 113607f6718SJohan Hovold 1144099d4baSJohan Hovold u16 uart_enable; 1154099d4baSJohan Hovold u16 flow_control; 1164099d4baSJohan Hovold u16 xon_char; 1174099d4baSJohan Hovold u16 xoff_char; 1184099d4baSJohan Hovold u16 tx_break; 1194099d4baSJohan Hovold u16 gpio_mode; 1204099d4baSJohan Hovold u16 gpio_direction; 1214099d4baSJohan Hovold u16 gpio_set; 1224099d4baSJohan Hovold u16 gpio_clear; 1234099d4baSJohan Hovold u16 gpio_status; 124*06f79d57SJohan Hovold u16 tx_fifo_reset; 125*06f79d57SJohan Hovold u16 rx_fifo_reset; 1264099d4baSJohan Hovold u16 custom_driver; 127607f6718SJohan Hovold 1284099d4baSJohan Hovold bool have_5_6_bit_mode; 129607f6718SJohan Hovold bool have_xmit_toggle; 130607f6718SJohan Hovold 131607f6718SJohan Hovold int (*enable)(struct usb_serial_port *port); 132607f6718SJohan Hovold int (*disable)(struct usb_serial_port *port); 133*06f79d57SJohan Hovold int (*fifo_reset)(struct usb_serial_port *port); 134607f6718SJohan Hovold void (*set_line_settings)(struct tty_struct *tty, 135607f6718SJohan Hovold struct usb_serial_port *port, 136607f6718SJohan Hovold struct ktermios *old_termios); 137f865e614SJohan Hovold }; 138f865e614SJohan Hovold 139f865e614SJohan Hovold enum xr_type_id { 140f865e614SJohan Hovold XR21V141X, 141607f6718SJohan Hovold XR21B142X, 1424099d4baSJohan Hovold XR21B1411, 1436da99f9dSJohan Hovold XR2280X, 144f865e614SJohan Hovold XR_TYPE_COUNT, 145f865e614SJohan Hovold }; 146f865e614SJohan Hovold 147f865e614SJohan Hovold static const struct xr_type xr_types[] = { 148f865e614SJohan Hovold [XR21V141X] = { 149607f6718SJohan Hovold .reg_width = 8, 150607f6718SJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 151607f6718SJohan Hovold .set_reg = 0x00, 152607f6718SJohan Hovold .get_reg = 0x01, 153607f6718SJohan Hovold 154f865e614SJohan Hovold .uart_enable = 0x03, 155f865e614SJohan Hovold .flow_control = 0x0c, 156f865e614SJohan Hovold .xon_char = 0x10, 157f865e614SJohan Hovold .xoff_char = 0x11, 158f865e614SJohan Hovold .tx_break = 0x14, 159f865e614SJohan Hovold .gpio_mode = 0x1a, 160f865e614SJohan Hovold .gpio_direction = 0x1b, 161f865e614SJohan Hovold .gpio_set = 0x1d, 162f865e614SJohan Hovold .gpio_clear = 0x1e, 163f865e614SJohan Hovold .gpio_status = 0x1f, 164607f6718SJohan Hovold 165607f6718SJohan Hovold .enable = xr21v141x_uart_enable, 166607f6718SJohan Hovold .disable = xr21v141x_uart_disable, 167*06f79d57SJohan Hovold .fifo_reset = xr21v141x_fifo_reset, 168607f6718SJohan Hovold .set_line_settings = xr21v141x_set_line_settings, 169607f6718SJohan Hovold }, 170607f6718SJohan Hovold [XR21B142X] = { 171607f6718SJohan Hovold .reg_width = 16, 172607f6718SJohan Hovold .reg_recipient = USB_RECIP_INTERFACE, 173607f6718SJohan Hovold .set_reg = 0x00, 174607f6718SJohan Hovold .get_reg = 0x00, 175607f6718SJohan Hovold 176607f6718SJohan Hovold .uart_enable = 0x00, 177607f6718SJohan Hovold .flow_control = 0x06, 178607f6718SJohan Hovold .xon_char = 0x07, 179607f6718SJohan Hovold .xoff_char = 0x08, 180607f6718SJohan Hovold .tx_break = 0x0a, 181607f6718SJohan Hovold .gpio_mode = 0x0c, 182607f6718SJohan Hovold .gpio_direction = 0x0d, 183607f6718SJohan Hovold .gpio_set = 0x0e, 184607f6718SJohan Hovold .gpio_clear = 0x0f, 185607f6718SJohan Hovold .gpio_status = 0x10, 186*06f79d57SJohan Hovold .tx_fifo_reset = 0x40, 187*06f79d57SJohan Hovold .rx_fifo_reset = 0x43, 188607f6718SJohan Hovold .custom_driver = 0x60, 189607f6718SJohan Hovold 1904099d4baSJohan Hovold .have_5_6_bit_mode = true, 191607f6718SJohan Hovold .have_xmit_toggle = true, 192f865e614SJohan Hovold }, 1934099d4baSJohan Hovold [XR21B1411] = { 1944099d4baSJohan Hovold .reg_width = 12, 1954099d4baSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 1964099d4baSJohan Hovold .set_reg = 0x00, 1974099d4baSJohan Hovold .get_reg = 0x01, 1984099d4baSJohan Hovold 1994099d4baSJohan Hovold .uart_enable = 0xc00, 2004099d4baSJohan Hovold .flow_control = 0xc06, 2014099d4baSJohan Hovold .xon_char = 0xc07, 2024099d4baSJohan Hovold .xoff_char = 0xc08, 2034099d4baSJohan Hovold .tx_break = 0xc0a, 2044099d4baSJohan Hovold .gpio_mode = 0xc0c, 2054099d4baSJohan Hovold .gpio_direction = 0xc0d, 2064099d4baSJohan Hovold .gpio_set = 0xc0e, 2074099d4baSJohan Hovold .gpio_clear = 0xc0f, 2084099d4baSJohan Hovold .gpio_status = 0xc10, 209*06f79d57SJohan Hovold .tx_fifo_reset = 0xc80, 210*06f79d57SJohan Hovold .rx_fifo_reset = 0xcc0, 2114099d4baSJohan Hovold .custom_driver = 0x20d, 2124099d4baSJohan Hovold }, 2136da99f9dSJohan Hovold [XR2280X] = { 2146da99f9dSJohan Hovold .reg_width = 16, 2156da99f9dSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 2166da99f9dSJohan Hovold .set_reg = 0x05, 2176da99f9dSJohan Hovold .get_reg = 0x05, 2186da99f9dSJohan Hovold 2196da99f9dSJohan Hovold .uart_enable = 0x40, 2206da99f9dSJohan Hovold .flow_control = 0x46, 2216da99f9dSJohan Hovold .xon_char = 0x47, 2226da99f9dSJohan Hovold .xoff_char = 0x48, 2236da99f9dSJohan Hovold .tx_break = 0x4a, 2246da99f9dSJohan Hovold .gpio_mode = 0x4c, 2256da99f9dSJohan Hovold .gpio_direction = 0x4d, 2266da99f9dSJohan Hovold .gpio_set = 0x4e, 2276da99f9dSJohan Hovold .gpio_clear = 0x4f, 2286da99f9dSJohan Hovold .gpio_status = 0x50, 229*06f79d57SJohan Hovold .tx_fifo_reset = 0x60, 230*06f79d57SJohan Hovold .rx_fifo_reset = 0x63, 2316da99f9dSJohan Hovold .custom_driver = 0x81, 2326da99f9dSJohan Hovold }, 233f865e614SJohan Hovold }; 234c2d405aaSManivannan Sadhasivam 23523b7998eSJohan Hovold struct xr_data { 236f865e614SJohan Hovold const struct xr_type *type; 237607f6718SJohan Hovold u8 channel; /* zero-based index or interface number */ 23823b7998eSJohan Hovold }; 23923b7998eSJohan Hovold 2404099d4baSJohan Hovold static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val) 241c2d405aaSManivannan Sadhasivam { 242607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 243607f6718SJohan Hovold const struct xr_type *type = data->type; 244c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 245c2d405aaSManivannan Sadhasivam int ret; 246c2d405aaSManivannan Sadhasivam 247607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 248607f6718SJohan Hovold type->set_reg, 249607f6718SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, 250607f6718SJohan Hovold val, (channel << 8) | reg, NULL, 0, 251c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 252c2d405aaSManivannan Sadhasivam if (ret < 0) { 253c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 254c2d405aaSManivannan Sadhasivam return ret; 255c2d405aaSManivannan Sadhasivam } 256c2d405aaSManivannan Sadhasivam 257c2d405aaSManivannan Sadhasivam return 0; 258c2d405aaSManivannan Sadhasivam } 259c2d405aaSManivannan Sadhasivam 2604099d4baSJohan Hovold static int xr_get_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 *val) 261c2d405aaSManivannan Sadhasivam { 262607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 263607f6718SJohan Hovold const struct xr_type *type = data->type; 264c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 265c2d405aaSManivannan Sadhasivam u8 *dmabuf; 266607f6718SJohan Hovold int ret, len; 267c2d405aaSManivannan Sadhasivam 268607f6718SJohan Hovold if (type->reg_width == 8) 269607f6718SJohan Hovold len = 1; 270607f6718SJohan Hovold else 271607f6718SJohan Hovold len = 2; 272607f6718SJohan Hovold 273607f6718SJohan Hovold dmabuf = kmalloc(len, GFP_KERNEL); 274c2d405aaSManivannan Sadhasivam if (!dmabuf) 275c2d405aaSManivannan Sadhasivam return -ENOMEM; 276c2d405aaSManivannan Sadhasivam 277607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 278607f6718SJohan Hovold type->get_reg, 279607f6718SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, 280607f6718SJohan Hovold 0, (channel << 8) | reg, dmabuf, len, 281c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 282607f6718SJohan Hovold if (ret == len) { 283607f6718SJohan Hovold if (len == 2) 284607f6718SJohan Hovold *val = le16_to_cpup((__le16 *)dmabuf); 285607f6718SJohan Hovold else 286c2d405aaSManivannan Sadhasivam *val = *dmabuf; 287c2d405aaSManivannan Sadhasivam ret = 0; 288c2d405aaSManivannan Sadhasivam } else { 289c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 290c2d405aaSManivannan Sadhasivam if (ret >= 0) 291c2d405aaSManivannan Sadhasivam ret = -EIO; 292c2d405aaSManivannan Sadhasivam } 293c2d405aaSManivannan Sadhasivam 294c2d405aaSManivannan Sadhasivam kfree(dmabuf); 295c2d405aaSManivannan Sadhasivam 296c2d405aaSManivannan Sadhasivam return ret; 297c2d405aaSManivannan Sadhasivam } 298c2d405aaSManivannan Sadhasivam 2994099d4baSJohan Hovold static int xr_set_reg_uart(struct usb_serial_port *port, u16 reg, u16 val) 300c2d405aaSManivannan Sadhasivam { 30123b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 30223b7998eSJohan Hovold 303607f6718SJohan Hovold return xr_set_reg(port, data->channel, reg, val); 304c2d405aaSManivannan Sadhasivam } 305c2d405aaSManivannan Sadhasivam 3064099d4baSJohan Hovold static int xr_get_reg_uart(struct usb_serial_port *port, u16 reg, u16 *val) 307c2d405aaSManivannan Sadhasivam { 30823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 30923b7998eSJohan Hovold 310607f6718SJohan Hovold return xr_get_reg(port, data->channel, reg, val); 311c2d405aaSManivannan Sadhasivam } 312c2d405aaSManivannan Sadhasivam 31323b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 314c2d405aaSManivannan Sadhasivam { 31523b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 31623b7998eSJohan Hovold u8 reg; 31723b7998eSJohan Hovold 31823b7998eSJohan Hovold reg = reg_base + data->channel; 31923b7998eSJohan Hovold 320c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 321c2d405aaSManivannan Sadhasivam } 322c2d405aaSManivannan Sadhasivam 323607f6718SJohan Hovold static int __xr_uart_enable(struct usb_serial_port *port) 324607f6718SJohan Hovold { 325607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 326607f6718SJohan Hovold 327607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 328607f6718SJohan Hovold XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); 329607f6718SJohan Hovold } 330607f6718SJohan Hovold 331607f6718SJohan Hovold static int __xr_uart_disable(struct usb_serial_port *port) 332607f6718SJohan Hovold { 333607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 334607f6718SJohan Hovold 335607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 0); 336607f6718SJohan Hovold } 337607f6718SJohan Hovold 338c2d405aaSManivannan Sadhasivam /* 339c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 340c2d405aaSManivannan Sadhasivam * module in XR21V141X: 341c2d405aaSManivannan Sadhasivam * 342c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 343c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 344c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 345c2d405aaSManivannan Sadhasivam */ 346607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port) 347c2d405aaSManivannan Sadhasivam { 348c2d405aaSManivannan Sadhasivam int ret; 349c2d405aaSManivannan Sadhasivam 350c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 351c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 352c2d405aaSManivannan Sadhasivam if (ret) 353c2d405aaSManivannan Sadhasivam return ret; 354c2d405aaSManivannan Sadhasivam 355607f6718SJohan Hovold ret = __xr_uart_enable(port); 356c2d405aaSManivannan Sadhasivam if (ret) 357c2d405aaSManivannan Sadhasivam return ret; 358c2d405aaSManivannan Sadhasivam 359c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 360c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 361c2d405aaSManivannan Sadhasivam if (ret) 362607f6718SJohan Hovold __xr_uart_disable(port); 363c2d405aaSManivannan Sadhasivam 364c2d405aaSManivannan Sadhasivam return ret; 365c2d405aaSManivannan Sadhasivam } 366c2d405aaSManivannan Sadhasivam 367607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port) 368c2d405aaSManivannan Sadhasivam { 369c2d405aaSManivannan Sadhasivam int ret; 370c2d405aaSManivannan Sadhasivam 371607f6718SJohan Hovold ret = __xr_uart_disable(port); 372c2d405aaSManivannan Sadhasivam if (ret) 373c2d405aaSManivannan Sadhasivam return ret; 374c2d405aaSManivannan Sadhasivam 375c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 376c2d405aaSManivannan Sadhasivam 377c2d405aaSManivannan Sadhasivam return ret; 378c2d405aaSManivannan Sadhasivam } 379c2d405aaSManivannan Sadhasivam 380607f6718SJohan Hovold static int xr_uart_enable(struct usb_serial_port *port) 381607f6718SJohan Hovold { 382607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 383607f6718SJohan Hovold 384607f6718SJohan Hovold if (data->type->enable) 385607f6718SJohan Hovold return data->type->enable(port); 386607f6718SJohan Hovold 387607f6718SJohan Hovold return __xr_uart_enable(port); 388607f6718SJohan Hovold } 389607f6718SJohan Hovold 390607f6718SJohan Hovold static int xr_uart_disable(struct usb_serial_port *port) 391607f6718SJohan Hovold { 392607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 393607f6718SJohan Hovold 394607f6718SJohan Hovold if (data->type->disable) 395607f6718SJohan Hovold return data->type->disable(port); 396607f6718SJohan Hovold 397607f6718SJohan Hovold return __xr_uart_disable(port); 398607f6718SJohan Hovold } 399607f6718SJohan Hovold 400*06f79d57SJohan Hovold static int xr21v141x_fifo_reset(struct usb_serial_port *port) 401*06f79d57SJohan Hovold { 402*06f79d57SJohan Hovold int ret; 403*06f79d57SJohan Hovold 404*06f79d57SJohan Hovold ret = xr_set_reg_um(port, XR21V141X_UM_TX_FIFO_RESET, XR_FIFO_RESET); 405*06f79d57SJohan Hovold if (ret) 406*06f79d57SJohan Hovold return ret; 407*06f79d57SJohan Hovold 408*06f79d57SJohan Hovold ret = xr_set_reg_um(port, XR21V141X_UM_RX_FIFO_RESET, XR_FIFO_RESET); 409*06f79d57SJohan Hovold if (ret) 410*06f79d57SJohan Hovold return ret; 411*06f79d57SJohan Hovold 412*06f79d57SJohan Hovold return 0; 413*06f79d57SJohan Hovold } 414*06f79d57SJohan Hovold 415*06f79d57SJohan Hovold static int xr_fifo_reset(struct usb_serial_port *port) 416*06f79d57SJohan Hovold { 417*06f79d57SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 418*06f79d57SJohan Hovold int ret; 419*06f79d57SJohan Hovold 420*06f79d57SJohan Hovold if (data->type->fifo_reset) 421*06f79d57SJohan Hovold return data->type->fifo_reset(port); 422*06f79d57SJohan Hovold 423*06f79d57SJohan Hovold ret = xr_set_reg_uart(port, data->type->tx_fifo_reset, XR_FIFO_RESET); 424*06f79d57SJohan Hovold if (ret) 425*06f79d57SJohan Hovold return ret; 426*06f79d57SJohan Hovold 427*06f79d57SJohan Hovold ret = xr_set_reg_uart(port, data->type->rx_fifo_reset, XR_FIFO_RESET); 428*06f79d57SJohan Hovold if (ret) 429*06f79d57SJohan Hovold return ret; 430*06f79d57SJohan Hovold 431*06f79d57SJohan Hovold return 0; 432*06f79d57SJohan Hovold } 433*06f79d57SJohan Hovold 434c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 435c2d405aaSManivannan Sadhasivam { 436c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 437f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 438607f6718SJohan Hovold u16 status; 439c2d405aaSManivannan Sadhasivam int ret; 440c2d405aaSManivannan Sadhasivam 441f865e614SJohan Hovold ret = xr_get_reg_uart(port, data->type->gpio_status, &status); 442c2d405aaSManivannan Sadhasivam if (ret) 443c2d405aaSManivannan Sadhasivam return ret; 444c2d405aaSManivannan Sadhasivam 445c2d405aaSManivannan Sadhasivam /* 446c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 447c2d405aaSManivannan Sadhasivam * and '1' means not active. 448c2d405aaSManivannan Sadhasivam */ 449958d6b95SJohan Hovold ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | 450958d6b95SJohan Hovold ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | 451958d6b95SJohan Hovold ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | 452958d6b95SJohan Hovold ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | 453958d6b95SJohan Hovold ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | 454958d6b95SJohan Hovold ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); 455c2d405aaSManivannan Sadhasivam 456c2d405aaSManivannan Sadhasivam return ret; 457c2d405aaSManivannan Sadhasivam } 458c2d405aaSManivannan Sadhasivam 459c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 460c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 461c2d405aaSManivannan Sadhasivam { 462f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 463f865e614SJohan Hovold const struct xr_type *type = data->type; 464607f6718SJohan Hovold u16 gpio_set = 0; 465607f6718SJohan Hovold u16 gpio_clr = 0; 466c2d405aaSManivannan Sadhasivam int ret = 0; 467c2d405aaSManivannan Sadhasivam 468c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 469c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 470958d6b95SJohan Hovold gpio_clr |= XR_GPIO_RTS; 471c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 472958d6b95SJohan Hovold gpio_clr |= XR_GPIO_DTR; 473c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 474958d6b95SJohan Hovold gpio_set |= XR_GPIO_RTS; 475c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 476958d6b95SJohan Hovold gpio_set |= XR_GPIO_DTR; 477c2d405aaSManivannan Sadhasivam 478c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 479c2d405aaSManivannan Sadhasivam if (gpio_clr) 480f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); 481c2d405aaSManivannan Sadhasivam 482c2d405aaSManivannan Sadhasivam if (gpio_set) 483f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); 484c2d405aaSManivannan Sadhasivam 485c2d405aaSManivannan Sadhasivam return ret; 486c2d405aaSManivannan Sadhasivam } 487c2d405aaSManivannan Sadhasivam 488c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 489c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 490c2d405aaSManivannan Sadhasivam { 491c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 492c2d405aaSManivannan Sadhasivam 493c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 494c2d405aaSManivannan Sadhasivam } 495c2d405aaSManivannan Sadhasivam 496c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 497c2d405aaSManivannan Sadhasivam { 498c2d405aaSManivannan Sadhasivam if (on) 499c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 500c2d405aaSManivannan Sadhasivam else 501c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 502c2d405aaSManivannan Sadhasivam } 503c2d405aaSManivannan Sadhasivam 504c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 505c2d405aaSManivannan Sadhasivam { 506c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 507f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 508f865e614SJohan Hovold const struct xr_type *type = data->type; 509607f6718SJohan Hovold u16 state; 510c2d405aaSManivannan Sadhasivam 511c2d405aaSManivannan Sadhasivam if (break_state == 0) 512607f6718SJohan Hovold state = 0; 513c2d405aaSManivannan Sadhasivam else 514607f6718SJohan Hovold state = GENMASK(type->reg_width - 1, 0); 515c2d405aaSManivannan Sadhasivam 516607f6718SJohan Hovold dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); 517607f6718SJohan Hovold 518f865e614SJohan Hovold xr_set_reg_uart(port, type->tx_break, state); 519c2d405aaSManivannan Sadhasivam } 520c2d405aaSManivannan Sadhasivam 521c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 522c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 523c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 524c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 525c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 526c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 527c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 528c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 529c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 530c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 531c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 532c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 533c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 534c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 535c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 536c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 537c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 538c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 539c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 540c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 541c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 542c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 543c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 544c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 545c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 546c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 547c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 548c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 549c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 550c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 551c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 552c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 553c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 554c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 555c2d405aaSManivannan Sadhasivam }; 556c2d405aaSManivannan Sadhasivam 557607f6718SJohan Hovold static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) 558c2d405aaSManivannan Sadhasivam { 559c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 560c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 561c2d405aaSManivannan Sadhasivam int ret; 562c2d405aaSManivannan Sadhasivam 56355317e22SJohan Hovold baud = tty->termios.c_ospeed; 56455317e22SJohan Hovold if (!baud) 56555317e22SJohan Hovold return 0; 56655317e22SJohan Hovold 56755317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 568c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 569c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 570c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 571c2d405aaSManivannan Sadhasivam 572c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 573c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 574c2d405aaSManivannan Sadhasivam else 575c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 576c2d405aaSManivannan Sadhasivam 577c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 578c2d405aaSManivannan Sadhasivam /* 579c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 580c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 581c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 582c2d405aaSManivannan Sadhasivam */ 583c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 584c2d405aaSManivannan Sadhasivam divisor & 0xff); 585c2d405aaSManivannan Sadhasivam if (ret) 586c2d405aaSManivannan Sadhasivam return ret; 587c2d405aaSManivannan Sadhasivam 588c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 589c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 590c2d405aaSManivannan Sadhasivam if (ret) 591c2d405aaSManivannan Sadhasivam return ret; 592c2d405aaSManivannan Sadhasivam 593c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 594c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 595c2d405aaSManivannan Sadhasivam if (ret) 596c2d405aaSManivannan Sadhasivam return ret; 597c2d405aaSManivannan Sadhasivam 598c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 599c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 600c2d405aaSManivannan Sadhasivam if (ret) 601c2d405aaSManivannan Sadhasivam return ret; 602c2d405aaSManivannan Sadhasivam 603c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 604c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 605c2d405aaSManivannan Sadhasivam if (ret) 606c2d405aaSManivannan Sadhasivam return ret; 607c2d405aaSManivannan Sadhasivam 608c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 609c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 610c2d405aaSManivannan Sadhasivam if (ret) 611c2d405aaSManivannan Sadhasivam return ret; 612c2d405aaSManivannan Sadhasivam 613c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 614c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 615c2d405aaSManivannan Sadhasivam if (ret) 616c2d405aaSManivannan Sadhasivam return ret; 617c2d405aaSManivannan Sadhasivam 618c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 619c2d405aaSManivannan Sadhasivam 620c2d405aaSManivannan Sadhasivam return 0; 621c2d405aaSManivannan Sadhasivam } 622c2d405aaSManivannan Sadhasivam 623c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 62455317e22SJohan Hovold struct usb_serial_port *port, 62555317e22SJohan Hovold struct ktermios *old_termios) 626c2d405aaSManivannan Sadhasivam { 627f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 628f865e614SJohan Hovold const struct xr_type *type = data->type; 629607f6718SJohan Hovold u16 flow, gpio_mode; 630c2d405aaSManivannan Sadhasivam int ret; 631c2d405aaSManivannan Sadhasivam 632f865e614SJohan Hovold ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); 633c2d405aaSManivannan Sadhasivam if (ret) 634c2d405aaSManivannan Sadhasivam return; 635c2d405aaSManivannan Sadhasivam 636607f6718SJohan Hovold /* 637607f6718SJohan Hovold * According to the datasheets, the UART needs to be disabled while 638607f6718SJohan Hovold * writing to the FLOW_CONTROL register (XR21V141X), or any register 639607f6718SJohan Hovold * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). 640607f6718SJohan Hovold */ 641607f6718SJohan Hovold xr_uart_disable(port); 642607f6718SJohan Hovold 643465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 644607f6718SJohan Hovold gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; 645465d3b3aSJohan Hovold 64655317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 647c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 648607f6718SJohan Hovold gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; 649958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_HW; 650c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 651c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 652c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 653c2d405aaSManivannan Sadhasivam 654c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 655958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_SW; 656c2d405aaSManivannan Sadhasivam 657f865e614SJohan Hovold xr_set_reg_uart(port, type->xon_char, start_char); 658f865e614SJohan Hovold xr_set_reg_uart(port, type->xoff_char, stop_char); 659c2d405aaSManivannan Sadhasivam } else { 660c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 661958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_NONE; 662c2d405aaSManivannan Sadhasivam } 663c2d405aaSManivannan Sadhasivam 664f865e614SJohan Hovold xr_set_reg_uart(port, type->flow_control, flow); 665f865e614SJohan Hovold xr_set_reg_uart(port, type->gpio_mode, gpio_mode); 66655317e22SJohan Hovold 667607f6718SJohan Hovold xr_uart_enable(port); 668607f6718SJohan Hovold 66955317e22SJohan Hovold if (C_BAUD(tty) == B0) 67055317e22SJohan Hovold xr_dtr_rts(port, 0); 67155317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 67255317e22SJohan Hovold xr_dtr_rts(port, 1); 673c2d405aaSManivannan Sadhasivam } 674c2d405aaSManivannan Sadhasivam 675607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 676607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 677c2d405aaSManivannan Sadhasivam { 678c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 679c2d405aaSManivannan Sadhasivam u8 bits = 0; 680c2d405aaSManivannan Sadhasivam int ret; 681c2d405aaSManivannan Sadhasivam 682736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 683607f6718SJohan Hovold xr21v141x_set_baudrate(tty, port); 684c2d405aaSManivannan Sadhasivam 685c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 686c2d405aaSManivannan Sadhasivam case CS5: 687c2d405aaSManivannan Sadhasivam case CS6: 688c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 689c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 690c2d405aaSManivannan Sadhasivam if (old_termios) 691c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 692c2d405aaSManivannan Sadhasivam else 693ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 694ea7ada4dSJohan Hovold 695ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 696958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 697ea7ada4dSJohan Hovold else 698958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 699c2d405aaSManivannan Sadhasivam break; 700c2d405aaSManivannan Sadhasivam case CS7: 701958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 702c2d405aaSManivannan Sadhasivam break; 703c2d405aaSManivannan Sadhasivam case CS8: 704c2d405aaSManivannan Sadhasivam default: 705958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 706c2d405aaSManivannan Sadhasivam break; 707c2d405aaSManivannan Sadhasivam } 708c2d405aaSManivannan Sadhasivam 709c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 710c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 711c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 712958d6b95SJohan Hovold bits |= XR_UART_PARITY_MARK; 713c2d405aaSManivannan Sadhasivam else 714958d6b95SJohan Hovold bits |= XR_UART_PARITY_SPACE; 715c2d405aaSManivannan Sadhasivam } else { 716c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 717958d6b95SJohan Hovold bits |= XR_UART_PARITY_ODD; 718c2d405aaSManivannan Sadhasivam else 719958d6b95SJohan Hovold bits |= XR_UART_PARITY_EVEN; 720c2d405aaSManivannan Sadhasivam } 721c2d405aaSManivannan Sadhasivam } 722c2d405aaSManivannan Sadhasivam 723c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 724958d6b95SJohan Hovold bits |= XR_UART_STOP_2; 725c2d405aaSManivannan Sadhasivam else 726958d6b95SJohan Hovold bits |= XR_UART_STOP_1; 727c2d405aaSManivannan Sadhasivam 728c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 729c2d405aaSManivannan Sadhasivam if (ret) 730c2d405aaSManivannan Sadhasivam return; 731607f6718SJohan Hovold } 732607f6718SJohan Hovold 733607f6718SJohan Hovold static void xr_cdc_set_line_coding(struct tty_struct *tty, 734607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 735607f6718SJohan Hovold { 7364099d4baSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 737607f6718SJohan Hovold struct usb_host_interface *alt = port->serial->interface->cur_altsetting; 738607f6718SJohan Hovold struct usb_device *udev = port->serial->dev; 739607f6718SJohan Hovold struct usb_cdc_line_coding *lc; 740607f6718SJohan Hovold int ret; 741607f6718SJohan Hovold 742607f6718SJohan Hovold lc = kzalloc(sizeof(*lc), GFP_KERNEL); 743607f6718SJohan Hovold if (!lc) 744607f6718SJohan Hovold return; 745607f6718SJohan Hovold 746607f6718SJohan Hovold if (tty->termios.c_ospeed) 747607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); 748607f6718SJohan Hovold else if (old_termios) 749607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(old_termios->c_ospeed); 750607f6718SJohan Hovold else 751607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(9600); 752607f6718SJohan Hovold 753607f6718SJohan Hovold if (C_CSTOPB(tty)) 754607f6718SJohan Hovold lc->bCharFormat = USB_CDC_2_STOP_BITS; 755607f6718SJohan Hovold else 756607f6718SJohan Hovold lc->bCharFormat = USB_CDC_1_STOP_BITS; 757607f6718SJohan Hovold 758607f6718SJohan Hovold if (C_PARENB(tty)) { 759607f6718SJohan Hovold if (C_CMSPAR(tty)) { 760607f6718SJohan Hovold if (C_PARODD(tty)) 761607f6718SJohan Hovold lc->bParityType = USB_CDC_MARK_PARITY; 762607f6718SJohan Hovold else 763607f6718SJohan Hovold lc->bParityType = USB_CDC_SPACE_PARITY; 764607f6718SJohan Hovold } else { 765607f6718SJohan Hovold if (C_PARODD(tty)) 766607f6718SJohan Hovold lc->bParityType = USB_CDC_ODD_PARITY; 767607f6718SJohan Hovold else 768607f6718SJohan Hovold lc->bParityType = USB_CDC_EVEN_PARITY; 769607f6718SJohan Hovold } 770607f6718SJohan Hovold } else { 771607f6718SJohan Hovold lc->bParityType = USB_CDC_NO_PARITY; 772607f6718SJohan Hovold } 773607f6718SJohan Hovold 7744099d4baSJohan Hovold if (!data->type->have_5_6_bit_mode && 7754099d4baSJohan Hovold (C_CSIZE(tty) == CS5 || C_CSIZE(tty) == CS6)) { 7764099d4baSJohan Hovold tty->termios.c_cflag &= ~CSIZE; 7774099d4baSJohan Hovold if (old_termios) 7784099d4baSJohan Hovold tty->termios.c_cflag |= old_termios->c_cflag & CSIZE; 7794099d4baSJohan Hovold else 7804099d4baSJohan Hovold tty->termios.c_cflag |= CS8; 7814099d4baSJohan Hovold } 7824099d4baSJohan Hovold 783607f6718SJohan Hovold switch (C_CSIZE(tty)) { 784607f6718SJohan Hovold case CS5: 785607f6718SJohan Hovold lc->bDataBits = 5; 786607f6718SJohan Hovold break; 787607f6718SJohan Hovold case CS6: 788607f6718SJohan Hovold lc->bDataBits = 6; 789607f6718SJohan Hovold break; 790607f6718SJohan Hovold case CS7: 791607f6718SJohan Hovold lc->bDataBits = 7; 792607f6718SJohan Hovold break; 793607f6718SJohan Hovold case CS8: 794607f6718SJohan Hovold default: 795607f6718SJohan Hovold lc->bDataBits = 8; 796607f6718SJohan Hovold break; 797607f6718SJohan Hovold } 798607f6718SJohan Hovold 799607f6718SJohan Hovold ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 800607f6718SJohan Hovold USB_CDC_REQ_SET_LINE_CODING, 801607f6718SJohan Hovold USB_TYPE_CLASS | USB_RECIP_INTERFACE, 802607f6718SJohan Hovold 0, alt->desc.bInterfaceNumber, 803607f6718SJohan Hovold lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); 804607f6718SJohan Hovold if (ret < 0) 805607f6718SJohan Hovold dev_err(&port->dev, "Failed to set line coding: %d\n", ret); 806607f6718SJohan Hovold 807607f6718SJohan Hovold kfree(lc); 808607f6718SJohan Hovold } 809607f6718SJohan Hovold 810607f6718SJohan Hovold static void xr_set_termios(struct tty_struct *tty, 811607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 812607f6718SJohan Hovold { 813607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 814607f6718SJohan Hovold 815607f6718SJohan Hovold /* 816607f6718SJohan Hovold * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC 817607f6718SJohan Hovold * mode upon receiving CDC requests. 818607f6718SJohan Hovold */ 819607f6718SJohan Hovold if (data->type->set_line_settings) 820607f6718SJohan Hovold data->type->set_line_settings(tty, port, old_termios); 821607f6718SJohan Hovold else 822607f6718SJohan Hovold xr_cdc_set_line_coding(tty, port, old_termios); 823c2d405aaSManivannan Sadhasivam 82455317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 825c2d405aaSManivannan Sadhasivam } 826c2d405aaSManivannan Sadhasivam 827c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 828c2d405aaSManivannan Sadhasivam { 829c2d405aaSManivannan Sadhasivam int ret; 830c2d405aaSManivannan Sadhasivam 831*06f79d57SJohan Hovold ret = xr_fifo_reset(port); 832*06f79d57SJohan Hovold if (ret) 833*06f79d57SJohan Hovold return ret; 834*06f79d57SJohan Hovold 835c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 836c2d405aaSManivannan Sadhasivam if (ret) { 837c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 838c2d405aaSManivannan Sadhasivam return ret; 839c2d405aaSManivannan Sadhasivam } 840c2d405aaSManivannan Sadhasivam 841c2d405aaSManivannan Sadhasivam /* Setup termios */ 842c2d405aaSManivannan Sadhasivam if (tty) 843c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 844c2d405aaSManivannan Sadhasivam 845c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 846c2d405aaSManivannan Sadhasivam if (ret) { 847c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 848c2d405aaSManivannan Sadhasivam return ret; 849c2d405aaSManivannan Sadhasivam } 850c2d405aaSManivannan Sadhasivam 851c2d405aaSManivannan Sadhasivam return 0; 852c2d405aaSManivannan Sadhasivam } 853c2d405aaSManivannan Sadhasivam 854c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 855c2d405aaSManivannan Sadhasivam { 856c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 857c2d405aaSManivannan Sadhasivam 858c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 859c2d405aaSManivannan Sadhasivam } 860c2d405aaSManivannan Sadhasivam 861c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 862c2d405aaSManivannan Sadhasivam { 8635fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 8645fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 8655fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 8665fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 8675fec21e7SJohan Hovold struct usb_interface *data; 8685fec21e7SJohan Hovold int ret; 8695fec21e7SJohan Hovold 8705fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 8715fec21e7SJohan Hovold if (ret < 0) 872c2d405aaSManivannan Sadhasivam return -ENODEV; 873c2d405aaSManivannan Sadhasivam 8745fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 8755fec21e7SJohan Hovold if (!desc) 8765fec21e7SJohan Hovold return -ENODEV; 8775fec21e7SJohan Hovold 8785fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 8795fec21e7SJohan Hovold if (!data) 8805fec21e7SJohan Hovold return -ENODEV; 8815fec21e7SJohan Hovold 8825fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 8835fec21e7SJohan Hovold if (ret) 8845fec21e7SJohan Hovold return ret; 8855fec21e7SJohan Hovold 886f865e614SJohan Hovold usb_set_serial_data(serial, (void *)id->driver_info); 887f865e614SJohan Hovold 888c2d405aaSManivannan Sadhasivam return 0; 889c2d405aaSManivannan Sadhasivam } 890c2d405aaSManivannan Sadhasivam 891f865e614SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) 89249036fd0SJohan Hovold { 893607f6718SJohan Hovold u16 mask, mode; 89449036fd0SJohan Hovold int ret; 89549036fd0SJohan Hovold 896607f6718SJohan Hovold /* 897607f6718SJohan Hovold * Configure all pins as GPIO except for Receive and Transmit Toggle. 898607f6718SJohan Hovold */ 89949036fd0SJohan Hovold mode = 0; 900607f6718SJohan Hovold if (type->have_xmit_toggle) 901607f6718SJohan Hovold mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; 902607f6718SJohan Hovold 903f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_mode, mode); 90449036fd0SJohan Hovold if (ret) 90549036fd0SJohan Hovold return ret; 90649036fd0SJohan Hovold 90749036fd0SJohan Hovold /* 90849036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 90949036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 91049036fd0SJohan Hovold */ 911958d6b95SJohan Hovold mask = XR_GPIO_DTR | XR_GPIO_RTS; 912f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_direction, mask); 91349036fd0SJohan Hovold if (ret) 91449036fd0SJohan Hovold return ret; 91549036fd0SJohan Hovold 916f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, mask); 91749036fd0SJohan Hovold if (ret) 91849036fd0SJohan Hovold return ret; 91949036fd0SJohan Hovold 92049036fd0SJohan Hovold return 0; 92149036fd0SJohan Hovold } 92249036fd0SJohan Hovold 92323b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 92423b7998eSJohan Hovold { 92523b7998eSJohan Hovold struct usb_interface_descriptor *desc; 926f865e614SJohan Hovold const struct xr_type *type; 92723b7998eSJohan Hovold struct xr_data *data; 928f865e614SJohan Hovold enum xr_type_id type_id; 92949036fd0SJohan Hovold int ret; 93023b7998eSJohan Hovold 931f865e614SJohan Hovold type_id = (int)(unsigned long)usb_get_serial_data(port->serial); 932f865e614SJohan Hovold type = &xr_types[type_id]; 933f865e614SJohan Hovold 93423b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 93523b7998eSJohan Hovold if (!data) 93623b7998eSJohan Hovold return -ENOMEM; 93723b7998eSJohan Hovold 938f865e614SJohan Hovold data->type = type; 939f865e614SJohan Hovold 94023b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 941607f6718SJohan Hovold if (type_id == XR21V141X) 94223b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 943607f6718SJohan Hovold else 944607f6718SJohan Hovold data->channel = desc->bInterfaceNumber; 94523b7998eSJohan Hovold 94623b7998eSJohan Hovold usb_set_serial_port_data(port, data); 94723b7998eSJohan Hovold 948607f6718SJohan Hovold if (type->custom_driver) { 949607f6718SJohan Hovold ret = xr_set_reg_uart(port, type->custom_driver, 950607f6718SJohan Hovold XR_CUSTOM_DRIVER_ACTIVE); 951607f6718SJohan Hovold if (ret) 952607f6718SJohan Hovold goto err_free; 953607f6718SJohan Hovold } 954607f6718SJohan Hovold 955f865e614SJohan Hovold ret = xr_gpio_init(port, type); 95649036fd0SJohan Hovold if (ret) 95749036fd0SJohan Hovold goto err_free; 95849036fd0SJohan Hovold 95923b7998eSJohan Hovold return 0; 96049036fd0SJohan Hovold 96149036fd0SJohan Hovold err_free: 96249036fd0SJohan Hovold kfree(data); 96349036fd0SJohan Hovold 96449036fd0SJohan Hovold return ret; 96523b7998eSJohan Hovold } 96623b7998eSJohan Hovold 96723b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 96823b7998eSJohan Hovold { 96923b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 97023b7998eSJohan Hovold 97123b7998eSJohan Hovold kfree(data); 97223b7998eSJohan Hovold } 97323b7998eSJohan Hovold 974f865e614SJohan Hovold #define XR_DEVICE(vid, pid, type) \ 975f865e614SJohan Hovold USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ 976f865e614SJohan Hovold .driver_info = (type) 977f865e614SJohan Hovold 978c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 9796da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1400, XR2280X) }, 9806da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1401, XR2280X) }, 9816da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1402, XR2280X) }, 9826da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1403, XR2280X) }, 983f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, 9844099d4baSJohan Hovold { XR_DEVICE(0x04e2, 0x1411, XR21B1411) }, 985f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, 986f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, 987607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, 988607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, 989607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, 990c2d405aaSManivannan Sadhasivam { } 991c2d405aaSManivannan Sadhasivam }; 992c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 993c2d405aaSManivannan Sadhasivam 994c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 995c2d405aaSManivannan Sadhasivam .driver = { 996c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 997c2d405aaSManivannan Sadhasivam .name = "xr_serial", 998c2d405aaSManivannan Sadhasivam }, 999c2d405aaSManivannan Sadhasivam .id_table = id_table, 1000c2d405aaSManivannan Sadhasivam .num_ports = 1, 1001c2d405aaSManivannan Sadhasivam .probe = xr_probe, 100223b7998eSJohan Hovold .port_probe = xr_port_probe, 100323b7998eSJohan Hovold .port_remove = xr_port_remove, 1004c2d405aaSManivannan Sadhasivam .open = xr_open, 1005c2d405aaSManivannan Sadhasivam .close = xr_close, 1006c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 1007c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 1008c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 1009c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 1010c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 1011c2d405aaSManivannan Sadhasivam }; 1012c2d405aaSManivannan Sadhasivam 1013c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 1014c2d405aaSManivannan Sadhasivam &xr_device, NULL 1015c2d405aaSManivannan Sadhasivam }; 1016c2d405aaSManivannan Sadhasivam 1017c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 1018c2d405aaSManivannan Sadhasivam 1019c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 1020c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 1021c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 1022