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 98607f6718SJohan Hovold #define XR_CUSTOM_DRIVER_ACTIVE 0x1 99607f6718SJohan Hovold 100607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port); 101607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port); 102607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 103607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios); 104c2d405aaSManivannan Sadhasivam 105f865e614SJohan Hovold struct xr_type { 106607f6718SJohan Hovold int reg_width; 107607f6718SJohan Hovold u8 reg_recipient; 108607f6718SJohan Hovold u8 set_reg; 109607f6718SJohan Hovold u8 get_reg; 110607f6718SJohan Hovold 1114099d4baSJohan Hovold u16 uart_enable; 1124099d4baSJohan Hovold u16 flow_control; 1134099d4baSJohan Hovold u16 xon_char; 1144099d4baSJohan Hovold u16 xoff_char; 1154099d4baSJohan Hovold u16 tx_break; 1164099d4baSJohan Hovold u16 gpio_mode; 1174099d4baSJohan Hovold u16 gpio_direction; 1184099d4baSJohan Hovold u16 gpio_set; 1194099d4baSJohan Hovold u16 gpio_clear; 1204099d4baSJohan Hovold u16 gpio_status; 1214099d4baSJohan Hovold u16 custom_driver; 122607f6718SJohan Hovold 1234099d4baSJohan Hovold bool have_5_6_bit_mode; 124607f6718SJohan Hovold bool have_xmit_toggle; 125607f6718SJohan Hovold 126607f6718SJohan Hovold int (*enable)(struct usb_serial_port *port); 127607f6718SJohan Hovold int (*disable)(struct usb_serial_port *port); 128607f6718SJohan Hovold void (*set_line_settings)(struct tty_struct *tty, 129607f6718SJohan Hovold struct usb_serial_port *port, 130607f6718SJohan Hovold struct ktermios *old_termios); 131f865e614SJohan Hovold }; 132f865e614SJohan Hovold 133f865e614SJohan Hovold enum xr_type_id { 134f865e614SJohan Hovold XR21V141X, 135607f6718SJohan Hovold XR21B142X, 1364099d4baSJohan Hovold XR21B1411, 137*6da99f9dSJohan Hovold XR2280X, 138f865e614SJohan Hovold XR_TYPE_COUNT, 139f865e614SJohan Hovold }; 140f865e614SJohan Hovold 141f865e614SJohan Hovold static const struct xr_type xr_types[] = { 142f865e614SJohan Hovold [XR21V141X] = { 143607f6718SJohan Hovold .reg_width = 8, 144607f6718SJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 145607f6718SJohan Hovold .set_reg = 0x00, 146607f6718SJohan Hovold .get_reg = 0x01, 147607f6718SJohan Hovold 148f865e614SJohan Hovold .uart_enable = 0x03, 149f865e614SJohan Hovold .flow_control = 0x0c, 150f865e614SJohan Hovold .xon_char = 0x10, 151f865e614SJohan Hovold .xoff_char = 0x11, 152f865e614SJohan Hovold .tx_break = 0x14, 153f865e614SJohan Hovold .gpio_mode = 0x1a, 154f865e614SJohan Hovold .gpio_direction = 0x1b, 155f865e614SJohan Hovold .gpio_set = 0x1d, 156f865e614SJohan Hovold .gpio_clear = 0x1e, 157f865e614SJohan Hovold .gpio_status = 0x1f, 158607f6718SJohan Hovold 159607f6718SJohan Hovold .enable = xr21v141x_uart_enable, 160607f6718SJohan Hovold .disable = xr21v141x_uart_disable, 161607f6718SJohan Hovold .set_line_settings = xr21v141x_set_line_settings, 162607f6718SJohan Hovold }, 163607f6718SJohan Hovold [XR21B142X] = { 164607f6718SJohan Hovold .reg_width = 16, 165607f6718SJohan Hovold .reg_recipient = USB_RECIP_INTERFACE, 166607f6718SJohan Hovold .set_reg = 0x00, 167607f6718SJohan Hovold .get_reg = 0x00, 168607f6718SJohan Hovold 169607f6718SJohan Hovold .uart_enable = 0x00, 170607f6718SJohan Hovold .flow_control = 0x06, 171607f6718SJohan Hovold .xon_char = 0x07, 172607f6718SJohan Hovold .xoff_char = 0x08, 173607f6718SJohan Hovold .tx_break = 0x0a, 174607f6718SJohan Hovold .gpio_mode = 0x0c, 175607f6718SJohan Hovold .gpio_direction = 0x0d, 176607f6718SJohan Hovold .gpio_set = 0x0e, 177607f6718SJohan Hovold .gpio_clear = 0x0f, 178607f6718SJohan Hovold .gpio_status = 0x10, 179607f6718SJohan Hovold .custom_driver = 0x60, 180607f6718SJohan Hovold 1814099d4baSJohan Hovold .have_5_6_bit_mode = true, 182607f6718SJohan Hovold .have_xmit_toggle = true, 183f865e614SJohan Hovold }, 1844099d4baSJohan Hovold [XR21B1411] = { 1854099d4baSJohan Hovold .reg_width = 12, 1864099d4baSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 1874099d4baSJohan Hovold .set_reg = 0x00, 1884099d4baSJohan Hovold .get_reg = 0x01, 1894099d4baSJohan Hovold 1904099d4baSJohan Hovold .uart_enable = 0xc00, 1914099d4baSJohan Hovold .flow_control = 0xc06, 1924099d4baSJohan Hovold .xon_char = 0xc07, 1934099d4baSJohan Hovold .xoff_char = 0xc08, 1944099d4baSJohan Hovold .tx_break = 0xc0a, 1954099d4baSJohan Hovold .gpio_mode = 0xc0c, 1964099d4baSJohan Hovold .gpio_direction = 0xc0d, 1974099d4baSJohan Hovold .gpio_set = 0xc0e, 1984099d4baSJohan Hovold .gpio_clear = 0xc0f, 1994099d4baSJohan Hovold .gpio_status = 0xc10, 2004099d4baSJohan Hovold .custom_driver = 0x20d, 2014099d4baSJohan Hovold }, 202*6da99f9dSJohan Hovold [XR2280X] = { 203*6da99f9dSJohan Hovold .reg_width = 16, 204*6da99f9dSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 205*6da99f9dSJohan Hovold .set_reg = 0x05, 206*6da99f9dSJohan Hovold .get_reg = 0x05, 207*6da99f9dSJohan Hovold 208*6da99f9dSJohan Hovold .uart_enable = 0x40, 209*6da99f9dSJohan Hovold .flow_control = 0x46, 210*6da99f9dSJohan Hovold .xon_char = 0x47, 211*6da99f9dSJohan Hovold .xoff_char = 0x48, 212*6da99f9dSJohan Hovold .tx_break = 0x4a, 213*6da99f9dSJohan Hovold .gpio_mode = 0x4c, 214*6da99f9dSJohan Hovold .gpio_direction = 0x4d, 215*6da99f9dSJohan Hovold .gpio_set = 0x4e, 216*6da99f9dSJohan Hovold .gpio_clear = 0x4f, 217*6da99f9dSJohan Hovold .gpio_status = 0x50, 218*6da99f9dSJohan Hovold .custom_driver = 0x81, 219*6da99f9dSJohan Hovold }, 220f865e614SJohan Hovold }; 221c2d405aaSManivannan Sadhasivam 22223b7998eSJohan Hovold struct xr_data { 223f865e614SJohan Hovold const struct xr_type *type; 224607f6718SJohan Hovold u8 channel; /* zero-based index or interface number */ 22523b7998eSJohan Hovold }; 22623b7998eSJohan Hovold 2274099d4baSJohan Hovold static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val) 228c2d405aaSManivannan Sadhasivam { 229607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 230607f6718SJohan Hovold const struct xr_type *type = data->type; 231c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 232c2d405aaSManivannan Sadhasivam int ret; 233c2d405aaSManivannan Sadhasivam 234607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 235607f6718SJohan Hovold type->set_reg, 236607f6718SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, 237607f6718SJohan Hovold val, (channel << 8) | reg, NULL, 0, 238c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 239c2d405aaSManivannan Sadhasivam if (ret < 0) { 240c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 241c2d405aaSManivannan Sadhasivam return ret; 242c2d405aaSManivannan Sadhasivam } 243c2d405aaSManivannan Sadhasivam 244c2d405aaSManivannan Sadhasivam return 0; 245c2d405aaSManivannan Sadhasivam } 246c2d405aaSManivannan Sadhasivam 2474099d4baSJohan Hovold static int xr_get_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 *val) 248c2d405aaSManivannan Sadhasivam { 249607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 250607f6718SJohan Hovold const struct xr_type *type = data->type; 251c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 252c2d405aaSManivannan Sadhasivam u8 *dmabuf; 253607f6718SJohan Hovold int ret, len; 254c2d405aaSManivannan Sadhasivam 255607f6718SJohan Hovold if (type->reg_width == 8) 256607f6718SJohan Hovold len = 1; 257607f6718SJohan Hovold else 258607f6718SJohan Hovold len = 2; 259607f6718SJohan Hovold 260607f6718SJohan Hovold dmabuf = kmalloc(len, GFP_KERNEL); 261c2d405aaSManivannan Sadhasivam if (!dmabuf) 262c2d405aaSManivannan Sadhasivam return -ENOMEM; 263c2d405aaSManivannan Sadhasivam 264607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 265607f6718SJohan Hovold type->get_reg, 266607f6718SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, 267607f6718SJohan Hovold 0, (channel << 8) | reg, dmabuf, len, 268c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 269607f6718SJohan Hovold if (ret == len) { 270607f6718SJohan Hovold if (len == 2) 271607f6718SJohan Hovold *val = le16_to_cpup((__le16 *)dmabuf); 272607f6718SJohan Hovold else 273c2d405aaSManivannan Sadhasivam *val = *dmabuf; 274c2d405aaSManivannan Sadhasivam ret = 0; 275c2d405aaSManivannan Sadhasivam } else { 276c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 277c2d405aaSManivannan Sadhasivam if (ret >= 0) 278c2d405aaSManivannan Sadhasivam ret = -EIO; 279c2d405aaSManivannan Sadhasivam } 280c2d405aaSManivannan Sadhasivam 281c2d405aaSManivannan Sadhasivam kfree(dmabuf); 282c2d405aaSManivannan Sadhasivam 283c2d405aaSManivannan Sadhasivam return ret; 284c2d405aaSManivannan Sadhasivam } 285c2d405aaSManivannan Sadhasivam 2864099d4baSJohan Hovold static int xr_set_reg_uart(struct usb_serial_port *port, u16 reg, u16 val) 287c2d405aaSManivannan Sadhasivam { 28823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 28923b7998eSJohan Hovold 290607f6718SJohan Hovold return xr_set_reg(port, data->channel, reg, val); 291c2d405aaSManivannan Sadhasivam } 292c2d405aaSManivannan Sadhasivam 2934099d4baSJohan Hovold static int xr_get_reg_uart(struct usb_serial_port *port, u16 reg, u16 *val) 294c2d405aaSManivannan Sadhasivam { 29523b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 29623b7998eSJohan Hovold 297607f6718SJohan Hovold return xr_get_reg(port, data->channel, reg, val); 298c2d405aaSManivannan Sadhasivam } 299c2d405aaSManivannan Sadhasivam 30023b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 301c2d405aaSManivannan Sadhasivam { 30223b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 30323b7998eSJohan Hovold u8 reg; 30423b7998eSJohan Hovold 30523b7998eSJohan Hovold reg = reg_base + data->channel; 30623b7998eSJohan Hovold 307c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 308c2d405aaSManivannan Sadhasivam } 309c2d405aaSManivannan Sadhasivam 310607f6718SJohan Hovold static int __xr_uart_enable(struct usb_serial_port *port) 311607f6718SJohan Hovold { 312607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 313607f6718SJohan Hovold 314607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 315607f6718SJohan Hovold XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); 316607f6718SJohan Hovold } 317607f6718SJohan Hovold 318607f6718SJohan Hovold static int __xr_uart_disable(struct usb_serial_port *port) 319607f6718SJohan Hovold { 320607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 321607f6718SJohan Hovold 322607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 0); 323607f6718SJohan Hovold } 324607f6718SJohan Hovold 325c2d405aaSManivannan Sadhasivam /* 326c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 327c2d405aaSManivannan Sadhasivam * module in XR21V141X: 328c2d405aaSManivannan Sadhasivam * 329c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 330c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 331c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 332c2d405aaSManivannan Sadhasivam */ 333607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port) 334c2d405aaSManivannan Sadhasivam { 335c2d405aaSManivannan Sadhasivam int ret; 336c2d405aaSManivannan Sadhasivam 337c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 338c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 339c2d405aaSManivannan Sadhasivam if (ret) 340c2d405aaSManivannan Sadhasivam return ret; 341c2d405aaSManivannan Sadhasivam 342607f6718SJohan Hovold ret = __xr_uart_enable(port); 343c2d405aaSManivannan Sadhasivam if (ret) 344c2d405aaSManivannan Sadhasivam return ret; 345c2d405aaSManivannan Sadhasivam 346c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 347c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 348c2d405aaSManivannan Sadhasivam if (ret) 349607f6718SJohan Hovold __xr_uart_disable(port); 350c2d405aaSManivannan Sadhasivam 351c2d405aaSManivannan Sadhasivam return ret; 352c2d405aaSManivannan Sadhasivam } 353c2d405aaSManivannan Sadhasivam 354607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port) 355c2d405aaSManivannan Sadhasivam { 356c2d405aaSManivannan Sadhasivam int ret; 357c2d405aaSManivannan Sadhasivam 358607f6718SJohan Hovold ret = __xr_uart_disable(port); 359c2d405aaSManivannan Sadhasivam if (ret) 360c2d405aaSManivannan Sadhasivam return ret; 361c2d405aaSManivannan Sadhasivam 362c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 363c2d405aaSManivannan Sadhasivam 364c2d405aaSManivannan Sadhasivam return ret; 365c2d405aaSManivannan Sadhasivam } 366c2d405aaSManivannan Sadhasivam 367607f6718SJohan Hovold static int xr_uart_enable(struct usb_serial_port *port) 368607f6718SJohan Hovold { 369607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 370607f6718SJohan Hovold 371607f6718SJohan Hovold if (data->type->enable) 372607f6718SJohan Hovold return data->type->enable(port); 373607f6718SJohan Hovold 374607f6718SJohan Hovold return __xr_uart_enable(port); 375607f6718SJohan Hovold } 376607f6718SJohan Hovold 377607f6718SJohan Hovold static int xr_uart_disable(struct usb_serial_port *port) 378607f6718SJohan Hovold { 379607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 380607f6718SJohan Hovold 381607f6718SJohan Hovold if (data->type->disable) 382607f6718SJohan Hovold return data->type->disable(port); 383607f6718SJohan Hovold 384607f6718SJohan Hovold return __xr_uart_disable(port); 385607f6718SJohan Hovold } 386607f6718SJohan Hovold 387c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 388c2d405aaSManivannan Sadhasivam { 389c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 390f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 391607f6718SJohan Hovold u16 status; 392c2d405aaSManivannan Sadhasivam int ret; 393c2d405aaSManivannan Sadhasivam 394f865e614SJohan Hovold ret = xr_get_reg_uart(port, data->type->gpio_status, &status); 395c2d405aaSManivannan Sadhasivam if (ret) 396c2d405aaSManivannan Sadhasivam return ret; 397c2d405aaSManivannan Sadhasivam 398c2d405aaSManivannan Sadhasivam /* 399c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 400c2d405aaSManivannan Sadhasivam * and '1' means not active. 401c2d405aaSManivannan Sadhasivam */ 402958d6b95SJohan Hovold ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | 403958d6b95SJohan Hovold ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | 404958d6b95SJohan Hovold ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | 405958d6b95SJohan Hovold ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | 406958d6b95SJohan Hovold ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | 407958d6b95SJohan Hovold ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); 408c2d405aaSManivannan Sadhasivam 409c2d405aaSManivannan Sadhasivam return ret; 410c2d405aaSManivannan Sadhasivam } 411c2d405aaSManivannan Sadhasivam 412c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 413c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 414c2d405aaSManivannan Sadhasivam { 415f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 416f865e614SJohan Hovold const struct xr_type *type = data->type; 417607f6718SJohan Hovold u16 gpio_set = 0; 418607f6718SJohan Hovold u16 gpio_clr = 0; 419c2d405aaSManivannan Sadhasivam int ret = 0; 420c2d405aaSManivannan Sadhasivam 421c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 422c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 423958d6b95SJohan Hovold gpio_clr |= XR_GPIO_RTS; 424c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 425958d6b95SJohan Hovold gpio_clr |= XR_GPIO_DTR; 426c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 427958d6b95SJohan Hovold gpio_set |= XR_GPIO_RTS; 428c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 429958d6b95SJohan Hovold gpio_set |= XR_GPIO_DTR; 430c2d405aaSManivannan Sadhasivam 431c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 432c2d405aaSManivannan Sadhasivam if (gpio_clr) 433f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); 434c2d405aaSManivannan Sadhasivam 435c2d405aaSManivannan Sadhasivam if (gpio_set) 436f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); 437c2d405aaSManivannan Sadhasivam 438c2d405aaSManivannan Sadhasivam return ret; 439c2d405aaSManivannan Sadhasivam } 440c2d405aaSManivannan Sadhasivam 441c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 442c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 443c2d405aaSManivannan Sadhasivam { 444c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 445c2d405aaSManivannan Sadhasivam 446c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 447c2d405aaSManivannan Sadhasivam } 448c2d405aaSManivannan Sadhasivam 449c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 450c2d405aaSManivannan Sadhasivam { 451c2d405aaSManivannan Sadhasivam if (on) 452c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 453c2d405aaSManivannan Sadhasivam else 454c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 455c2d405aaSManivannan Sadhasivam } 456c2d405aaSManivannan Sadhasivam 457c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 458c2d405aaSManivannan Sadhasivam { 459c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 460f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 461f865e614SJohan Hovold const struct xr_type *type = data->type; 462607f6718SJohan Hovold u16 state; 463c2d405aaSManivannan Sadhasivam 464c2d405aaSManivannan Sadhasivam if (break_state == 0) 465607f6718SJohan Hovold state = 0; 466c2d405aaSManivannan Sadhasivam else 467607f6718SJohan Hovold state = GENMASK(type->reg_width - 1, 0); 468c2d405aaSManivannan Sadhasivam 469607f6718SJohan Hovold dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); 470607f6718SJohan Hovold 471f865e614SJohan Hovold xr_set_reg_uart(port, type->tx_break, state); 472c2d405aaSManivannan Sadhasivam } 473c2d405aaSManivannan Sadhasivam 474c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 475c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 476c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 477c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 478c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 479c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 480c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 481c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 482c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 483c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 484c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 485c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 486c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 487c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 488c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 489c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 490c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 491c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 492c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 493c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 494c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 495c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 496c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 497c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 498c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 499c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 500c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 501c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 502c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 503c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 504c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 505c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 506c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 507c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 508c2d405aaSManivannan Sadhasivam }; 509c2d405aaSManivannan Sadhasivam 510607f6718SJohan Hovold static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) 511c2d405aaSManivannan Sadhasivam { 512c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 513c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 514c2d405aaSManivannan Sadhasivam int ret; 515c2d405aaSManivannan Sadhasivam 51655317e22SJohan Hovold baud = tty->termios.c_ospeed; 51755317e22SJohan Hovold if (!baud) 51855317e22SJohan Hovold return 0; 51955317e22SJohan Hovold 52055317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 521c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 522c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 523c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 524c2d405aaSManivannan Sadhasivam 525c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 526c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 527c2d405aaSManivannan Sadhasivam else 528c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 529c2d405aaSManivannan Sadhasivam 530c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 531c2d405aaSManivannan Sadhasivam /* 532c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 533c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 534c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 535c2d405aaSManivannan Sadhasivam */ 536c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 537c2d405aaSManivannan Sadhasivam divisor & 0xff); 538c2d405aaSManivannan Sadhasivam if (ret) 539c2d405aaSManivannan Sadhasivam return ret; 540c2d405aaSManivannan Sadhasivam 541c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 542c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 543c2d405aaSManivannan Sadhasivam if (ret) 544c2d405aaSManivannan Sadhasivam return ret; 545c2d405aaSManivannan Sadhasivam 546c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 547c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 548c2d405aaSManivannan Sadhasivam if (ret) 549c2d405aaSManivannan Sadhasivam return ret; 550c2d405aaSManivannan Sadhasivam 551c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 552c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 553c2d405aaSManivannan Sadhasivam if (ret) 554c2d405aaSManivannan Sadhasivam return ret; 555c2d405aaSManivannan Sadhasivam 556c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 557c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 558c2d405aaSManivannan Sadhasivam if (ret) 559c2d405aaSManivannan Sadhasivam return ret; 560c2d405aaSManivannan Sadhasivam 561c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 562c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 563c2d405aaSManivannan Sadhasivam if (ret) 564c2d405aaSManivannan Sadhasivam return ret; 565c2d405aaSManivannan Sadhasivam 566c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 567c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 568c2d405aaSManivannan Sadhasivam if (ret) 569c2d405aaSManivannan Sadhasivam return ret; 570c2d405aaSManivannan Sadhasivam 571c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 572c2d405aaSManivannan Sadhasivam 573c2d405aaSManivannan Sadhasivam return 0; 574c2d405aaSManivannan Sadhasivam } 575c2d405aaSManivannan Sadhasivam 576c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 57755317e22SJohan Hovold struct usb_serial_port *port, 57855317e22SJohan Hovold struct ktermios *old_termios) 579c2d405aaSManivannan Sadhasivam { 580f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 581f865e614SJohan Hovold const struct xr_type *type = data->type; 582607f6718SJohan Hovold u16 flow, gpio_mode; 583c2d405aaSManivannan Sadhasivam int ret; 584c2d405aaSManivannan Sadhasivam 585f865e614SJohan Hovold ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); 586c2d405aaSManivannan Sadhasivam if (ret) 587c2d405aaSManivannan Sadhasivam return; 588c2d405aaSManivannan Sadhasivam 589607f6718SJohan Hovold /* 590607f6718SJohan Hovold * According to the datasheets, the UART needs to be disabled while 591607f6718SJohan Hovold * writing to the FLOW_CONTROL register (XR21V141X), or any register 592607f6718SJohan Hovold * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). 593607f6718SJohan Hovold */ 594607f6718SJohan Hovold xr_uart_disable(port); 595607f6718SJohan Hovold 596465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 597607f6718SJohan Hovold gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; 598465d3b3aSJohan Hovold 59955317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 600c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 601607f6718SJohan Hovold gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; 602958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_HW; 603c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 604c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 605c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 606c2d405aaSManivannan Sadhasivam 607c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 608958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_SW; 609c2d405aaSManivannan Sadhasivam 610f865e614SJohan Hovold xr_set_reg_uart(port, type->xon_char, start_char); 611f865e614SJohan Hovold xr_set_reg_uart(port, type->xoff_char, stop_char); 612c2d405aaSManivannan Sadhasivam } else { 613c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 614958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_NONE; 615c2d405aaSManivannan Sadhasivam } 616c2d405aaSManivannan Sadhasivam 617f865e614SJohan Hovold xr_set_reg_uart(port, type->flow_control, flow); 618f865e614SJohan Hovold xr_set_reg_uart(port, type->gpio_mode, gpio_mode); 61955317e22SJohan Hovold 620607f6718SJohan Hovold xr_uart_enable(port); 621607f6718SJohan Hovold 62255317e22SJohan Hovold if (C_BAUD(tty) == B0) 62355317e22SJohan Hovold xr_dtr_rts(port, 0); 62455317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 62555317e22SJohan Hovold xr_dtr_rts(port, 1); 626c2d405aaSManivannan Sadhasivam } 627c2d405aaSManivannan Sadhasivam 628607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 629607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 630c2d405aaSManivannan Sadhasivam { 631c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 632c2d405aaSManivannan Sadhasivam u8 bits = 0; 633c2d405aaSManivannan Sadhasivam int ret; 634c2d405aaSManivannan Sadhasivam 635736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 636607f6718SJohan Hovold xr21v141x_set_baudrate(tty, port); 637c2d405aaSManivannan Sadhasivam 638c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 639c2d405aaSManivannan Sadhasivam case CS5: 640c2d405aaSManivannan Sadhasivam case CS6: 641c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 642c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 643c2d405aaSManivannan Sadhasivam if (old_termios) 644c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 645c2d405aaSManivannan Sadhasivam else 646ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 647ea7ada4dSJohan Hovold 648ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 649958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 650ea7ada4dSJohan Hovold else 651958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 652c2d405aaSManivannan Sadhasivam break; 653c2d405aaSManivannan Sadhasivam case CS7: 654958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 655c2d405aaSManivannan Sadhasivam break; 656c2d405aaSManivannan Sadhasivam case CS8: 657c2d405aaSManivannan Sadhasivam default: 658958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 659c2d405aaSManivannan Sadhasivam break; 660c2d405aaSManivannan Sadhasivam } 661c2d405aaSManivannan Sadhasivam 662c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 663c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 664c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 665958d6b95SJohan Hovold bits |= XR_UART_PARITY_MARK; 666c2d405aaSManivannan Sadhasivam else 667958d6b95SJohan Hovold bits |= XR_UART_PARITY_SPACE; 668c2d405aaSManivannan Sadhasivam } else { 669c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 670958d6b95SJohan Hovold bits |= XR_UART_PARITY_ODD; 671c2d405aaSManivannan Sadhasivam else 672958d6b95SJohan Hovold bits |= XR_UART_PARITY_EVEN; 673c2d405aaSManivannan Sadhasivam } 674c2d405aaSManivannan Sadhasivam } 675c2d405aaSManivannan Sadhasivam 676c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 677958d6b95SJohan Hovold bits |= XR_UART_STOP_2; 678c2d405aaSManivannan Sadhasivam else 679958d6b95SJohan Hovold bits |= XR_UART_STOP_1; 680c2d405aaSManivannan Sadhasivam 681c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 682c2d405aaSManivannan Sadhasivam if (ret) 683c2d405aaSManivannan Sadhasivam return; 684607f6718SJohan Hovold } 685607f6718SJohan Hovold 686607f6718SJohan Hovold static void xr_cdc_set_line_coding(struct tty_struct *tty, 687607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 688607f6718SJohan Hovold { 6894099d4baSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 690607f6718SJohan Hovold struct usb_host_interface *alt = port->serial->interface->cur_altsetting; 691607f6718SJohan Hovold struct usb_device *udev = port->serial->dev; 692607f6718SJohan Hovold struct usb_cdc_line_coding *lc; 693607f6718SJohan Hovold int ret; 694607f6718SJohan Hovold 695607f6718SJohan Hovold lc = kzalloc(sizeof(*lc), GFP_KERNEL); 696607f6718SJohan Hovold if (!lc) 697607f6718SJohan Hovold return; 698607f6718SJohan Hovold 699607f6718SJohan Hovold if (tty->termios.c_ospeed) 700607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); 701607f6718SJohan Hovold else if (old_termios) 702607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(old_termios->c_ospeed); 703607f6718SJohan Hovold else 704607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(9600); 705607f6718SJohan Hovold 706607f6718SJohan Hovold if (C_CSTOPB(tty)) 707607f6718SJohan Hovold lc->bCharFormat = USB_CDC_2_STOP_BITS; 708607f6718SJohan Hovold else 709607f6718SJohan Hovold lc->bCharFormat = USB_CDC_1_STOP_BITS; 710607f6718SJohan Hovold 711607f6718SJohan Hovold if (C_PARENB(tty)) { 712607f6718SJohan Hovold if (C_CMSPAR(tty)) { 713607f6718SJohan Hovold if (C_PARODD(tty)) 714607f6718SJohan Hovold lc->bParityType = USB_CDC_MARK_PARITY; 715607f6718SJohan Hovold else 716607f6718SJohan Hovold lc->bParityType = USB_CDC_SPACE_PARITY; 717607f6718SJohan Hovold } else { 718607f6718SJohan Hovold if (C_PARODD(tty)) 719607f6718SJohan Hovold lc->bParityType = USB_CDC_ODD_PARITY; 720607f6718SJohan Hovold else 721607f6718SJohan Hovold lc->bParityType = USB_CDC_EVEN_PARITY; 722607f6718SJohan Hovold } 723607f6718SJohan Hovold } else { 724607f6718SJohan Hovold lc->bParityType = USB_CDC_NO_PARITY; 725607f6718SJohan Hovold } 726607f6718SJohan Hovold 7274099d4baSJohan Hovold if (!data->type->have_5_6_bit_mode && 7284099d4baSJohan Hovold (C_CSIZE(tty) == CS5 || C_CSIZE(tty) == CS6)) { 7294099d4baSJohan Hovold tty->termios.c_cflag &= ~CSIZE; 7304099d4baSJohan Hovold if (old_termios) 7314099d4baSJohan Hovold tty->termios.c_cflag |= old_termios->c_cflag & CSIZE; 7324099d4baSJohan Hovold else 7334099d4baSJohan Hovold tty->termios.c_cflag |= CS8; 7344099d4baSJohan Hovold } 7354099d4baSJohan Hovold 736607f6718SJohan Hovold switch (C_CSIZE(tty)) { 737607f6718SJohan Hovold case CS5: 738607f6718SJohan Hovold lc->bDataBits = 5; 739607f6718SJohan Hovold break; 740607f6718SJohan Hovold case CS6: 741607f6718SJohan Hovold lc->bDataBits = 6; 742607f6718SJohan Hovold break; 743607f6718SJohan Hovold case CS7: 744607f6718SJohan Hovold lc->bDataBits = 7; 745607f6718SJohan Hovold break; 746607f6718SJohan Hovold case CS8: 747607f6718SJohan Hovold default: 748607f6718SJohan Hovold lc->bDataBits = 8; 749607f6718SJohan Hovold break; 750607f6718SJohan Hovold } 751607f6718SJohan Hovold 752607f6718SJohan Hovold ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 753607f6718SJohan Hovold USB_CDC_REQ_SET_LINE_CODING, 754607f6718SJohan Hovold USB_TYPE_CLASS | USB_RECIP_INTERFACE, 755607f6718SJohan Hovold 0, alt->desc.bInterfaceNumber, 756607f6718SJohan Hovold lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); 757607f6718SJohan Hovold if (ret < 0) 758607f6718SJohan Hovold dev_err(&port->dev, "Failed to set line coding: %d\n", ret); 759607f6718SJohan Hovold 760607f6718SJohan Hovold kfree(lc); 761607f6718SJohan Hovold } 762607f6718SJohan Hovold 763607f6718SJohan Hovold static void xr_set_termios(struct tty_struct *tty, 764607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 765607f6718SJohan Hovold { 766607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 767607f6718SJohan Hovold 768607f6718SJohan Hovold /* 769607f6718SJohan Hovold * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC 770607f6718SJohan Hovold * mode upon receiving CDC requests. 771607f6718SJohan Hovold */ 772607f6718SJohan Hovold if (data->type->set_line_settings) 773607f6718SJohan Hovold data->type->set_line_settings(tty, port, old_termios); 774607f6718SJohan Hovold else 775607f6718SJohan Hovold xr_cdc_set_line_coding(tty, port, old_termios); 776c2d405aaSManivannan Sadhasivam 77755317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 778c2d405aaSManivannan Sadhasivam } 779c2d405aaSManivannan Sadhasivam 780c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 781c2d405aaSManivannan Sadhasivam { 782c2d405aaSManivannan Sadhasivam int ret; 783c2d405aaSManivannan Sadhasivam 784c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 785c2d405aaSManivannan Sadhasivam if (ret) { 786c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 787c2d405aaSManivannan Sadhasivam return ret; 788c2d405aaSManivannan Sadhasivam } 789c2d405aaSManivannan Sadhasivam 790c2d405aaSManivannan Sadhasivam /* Setup termios */ 791c2d405aaSManivannan Sadhasivam if (tty) 792c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 793c2d405aaSManivannan Sadhasivam 794c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 795c2d405aaSManivannan Sadhasivam if (ret) { 796c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 797c2d405aaSManivannan Sadhasivam return ret; 798c2d405aaSManivannan Sadhasivam } 799c2d405aaSManivannan Sadhasivam 800c2d405aaSManivannan Sadhasivam return 0; 801c2d405aaSManivannan Sadhasivam } 802c2d405aaSManivannan Sadhasivam 803c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 804c2d405aaSManivannan Sadhasivam { 805c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 806c2d405aaSManivannan Sadhasivam 807c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 808c2d405aaSManivannan Sadhasivam } 809c2d405aaSManivannan Sadhasivam 810c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 811c2d405aaSManivannan Sadhasivam { 8125fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 8135fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 8145fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 8155fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 8165fec21e7SJohan Hovold struct usb_interface *data; 8175fec21e7SJohan Hovold int ret; 8185fec21e7SJohan Hovold 8195fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 8205fec21e7SJohan Hovold if (ret < 0) 821c2d405aaSManivannan Sadhasivam return -ENODEV; 822c2d405aaSManivannan Sadhasivam 8235fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 8245fec21e7SJohan Hovold if (!desc) 8255fec21e7SJohan Hovold return -ENODEV; 8265fec21e7SJohan Hovold 8275fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 8285fec21e7SJohan Hovold if (!data) 8295fec21e7SJohan Hovold return -ENODEV; 8305fec21e7SJohan Hovold 8315fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 8325fec21e7SJohan Hovold if (ret) 8335fec21e7SJohan Hovold return ret; 8345fec21e7SJohan Hovold 835f865e614SJohan Hovold usb_set_serial_data(serial, (void *)id->driver_info); 836f865e614SJohan Hovold 837c2d405aaSManivannan Sadhasivam return 0; 838c2d405aaSManivannan Sadhasivam } 839c2d405aaSManivannan Sadhasivam 840f865e614SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) 84149036fd0SJohan Hovold { 842607f6718SJohan Hovold u16 mask, mode; 84349036fd0SJohan Hovold int ret; 84449036fd0SJohan Hovold 845607f6718SJohan Hovold /* 846607f6718SJohan Hovold * Configure all pins as GPIO except for Receive and Transmit Toggle. 847607f6718SJohan Hovold */ 84849036fd0SJohan Hovold mode = 0; 849607f6718SJohan Hovold if (type->have_xmit_toggle) 850607f6718SJohan Hovold mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; 851607f6718SJohan Hovold 852f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_mode, mode); 85349036fd0SJohan Hovold if (ret) 85449036fd0SJohan Hovold return ret; 85549036fd0SJohan Hovold 85649036fd0SJohan Hovold /* 85749036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 85849036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 85949036fd0SJohan Hovold */ 860958d6b95SJohan Hovold mask = XR_GPIO_DTR | XR_GPIO_RTS; 861f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_direction, mask); 86249036fd0SJohan Hovold if (ret) 86349036fd0SJohan Hovold return ret; 86449036fd0SJohan Hovold 865f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, mask); 86649036fd0SJohan Hovold if (ret) 86749036fd0SJohan Hovold return ret; 86849036fd0SJohan Hovold 86949036fd0SJohan Hovold return 0; 87049036fd0SJohan Hovold } 87149036fd0SJohan Hovold 87223b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 87323b7998eSJohan Hovold { 87423b7998eSJohan Hovold struct usb_interface_descriptor *desc; 875f865e614SJohan Hovold const struct xr_type *type; 87623b7998eSJohan Hovold struct xr_data *data; 877f865e614SJohan Hovold enum xr_type_id type_id; 87849036fd0SJohan Hovold int ret; 87923b7998eSJohan Hovold 880f865e614SJohan Hovold type_id = (int)(unsigned long)usb_get_serial_data(port->serial); 881f865e614SJohan Hovold type = &xr_types[type_id]; 882f865e614SJohan Hovold 88323b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 88423b7998eSJohan Hovold if (!data) 88523b7998eSJohan Hovold return -ENOMEM; 88623b7998eSJohan Hovold 887f865e614SJohan Hovold data->type = type; 888f865e614SJohan Hovold 88923b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 890607f6718SJohan Hovold if (type_id == XR21V141X) 89123b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 892607f6718SJohan Hovold else 893607f6718SJohan Hovold data->channel = desc->bInterfaceNumber; 89423b7998eSJohan Hovold 89523b7998eSJohan Hovold usb_set_serial_port_data(port, data); 89623b7998eSJohan Hovold 897607f6718SJohan Hovold if (type->custom_driver) { 898607f6718SJohan Hovold ret = xr_set_reg_uart(port, type->custom_driver, 899607f6718SJohan Hovold XR_CUSTOM_DRIVER_ACTIVE); 900607f6718SJohan Hovold if (ret) 901607f6718SJohan Hovold goto err_free; 902607f6718SJohan Hovold } 903607f6718SJohan Hovold 904f865e614SJohan Hovold ret = xr_gpio_init(port, type); 90549036fd0SJohan Hovold if (ret) 90649036fd0SJohan Hovold goto err_free; 90749036fd0SJohan Hovold 90823b7998eSJohan Hovold return 0; 90949036fd0SJohan Hovold 91049036fd0SJohan Hovold err_free: 91149036fd0SJohan Hovold kfree(data); 91249036fd0SJohan Hovold 91349036fd0SJohan Hovold return ret; 91423b7998eSJohan Hovold } 91523b7998eSJohan Hovold 91623b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 91723b7998eSJohan Hovold { 91823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 91923b7998eSJohan Hovold 92023b7998eSJohan Hovold kfree(data); 92123b7998eSJohan Hovold } 92223b7998eSJohan Hovold 923f865e614SJohan Hovold #define XR_DEVICE(vid, pid, type) \ 924f865e614SJohan Hovold USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ 925f865e614SJohan Hovold .driver_info = (type) 926f865e614SJohan Hovold 927c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 928*6da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1400, XR2280X) }, 929*6da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1401, XR2280X) }, 930*6da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1402, XR2280X) }, 931*6da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1403, XR2280X) }, 932f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, 9334099d4baSJohan Hovold { XR_DEVICE(0x04e2, 0x1411, XR21B1411) }, 934f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, 935f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, 936607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, 937607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, 938607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, 939c2d405aaSManivannan Sadhasivam { } 940c2d405aaSManivannan Sadhasivam }; 941c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 942c2d405aaSManivannan Sadhasivam 943c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 944c2d405aaSManivannan Sadhasivam .driver = { 945c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 946c2d405aaSManivannan Sadhasivam .name = "xr_serial", 947c2d405aaSManivannan Sadhasivam }, 948c2d405aaSManivannan Sadhasivam .id_table = id_table, 949c2d405aaSManivannan Sadhasivam .num_ports = 1, 950c2d405aaSManivannan Sadhasivam .probe = xr_probe, 95123b7998eSJohan Hovold .port_probe = xr_port_probe, 95223b7998eSJohan Hovold .port_remove = xr_port_remove, 953c2d405aaSManivannan Sadhasivam .open = xr_open, 954c2d405aaSManivannan Sadhasivam .close = xr_close, 955c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 956c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 957c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 958c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 959c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 960c2d405aaSManivannan Sadhasivam }; 961c2d405aaSManivannan Sadhasivam 962c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 963c2d405aaSManivannan Sadhasivam &xr_device, NULL 964c2d405aaSManivannan Sadhasivam }; 965c2d405aaSManivannan Sadhasivam 966c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 967c2d405aaSManivannan Sadhasivam 968c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 969c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 970c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 971