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) 64*607f6718SJohan Hovold #define XR_GPIO_CLK BIT(6) 65*607f6718SJohan Hovold #define XR_GPIO_XEN BIT(7) 66*607f6718SJohan Hovold #define XR_GPIO_TXT BIT(8) 67*607f6718SJohan 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 90*607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_MASK GENMASK(2, 0) 91*607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RTS_CTS 0x1 92*607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_DTR_DSR 0x2 93*607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RS485 0x3 94*607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RS485_ADDR 0x4 95*607f6718SJohan Hovold #define XR_GPIO_MODE_TX_TOGGLE 0x100 96*607f6718SJohan Hovold #define XR_GPIO_MODE_RX_TOGGLE 0x200 97*607f6718SJohan Hovold 98*607f6718SJohan Hovold #define XR_CUSTOM_DRIVER_ACTIVE 0x1 99*607f6718SJohan Hovold 100*607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port); 101*607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port); 102*607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 103*607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios); 104c2d405aaSManivannan Sadhasivam 105f865e614SJohan Hovold struct xr_type { 106*607f6718SJohan Hovold int reg_width; 107*607f6718SJohan Hovold u8 reg_recipient; 108*607f6718SJohan Hovold u8 set_reg; 109*607f6718SJohan Hovold u8 get_reg; 110*607f6718SJohan Hovold 111f865e614SJohan Hovold u8 uart_enable; 112f865e614SJohan Hovold u8 flow_control; 113f865e614SJohan Hovold u8 xon_char; 114f865e614SJohan Hovold u8 xoff_char; 115f865e614SJohan Hovold u8 tx_break; 116f865e614SJohan Hovold u8 gpio_mode; 117f865e614SJohan Hovold u8 gpio_direction; 118f865e614SJohan Hovold u8 gpio_set; 119f865e614SJohan Hovold u8 gpio_clear; 120f865e614SJohan Hovold u8 gpio_status; 121*607f6718SJohan Hovold u8 custom_driver; 122*607f6718SJohan Hovold 123*607f6718SJohan Hovold bool have_xmit_toggle; 124*607f6718SJohan Hovold 125*607f6718SJohan Hovold int (*enable)(struct usb_serial_port *port); 126*607f6718SJohan Hovold int (*disable)(struct usb_serial_port *port); 127*607f6718SJohan Hovold void (*set_line_settings)(struct tty_struct *tty, 128*607f6718SJohan Hovold struct usb_serial_port *port, 129*607f6718SJohan Hovold struct ktermios *old_termios); 130f865e614SJohan Hovold }; 131f865e614SJohan Hovold 132f865e614SJohan Hovold enum xr_type_id { 133f865e614SJohan Hovold XR21V141X, 134*607f6718SJohan Hovold XR21B142X, 135f865e614SJohan Hovold XR_TYPE_COUNT, 136f865e614SJohan Hovold }; 137f865e614SJohan Hovold 138f865e614SJohan Hovold static const struct xr_type xr_types[] = { 139f865e614SJohan Hovold [XR21V141X] = { 140*607f6718SJohan Hovold .reg_width = 8, 141*607f6718SJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 142*607f6718SJohan Hovold .set_reg = 0x00, 143*607f6718SJohan Hovold .get_reg = 0x01, 144*607f6718SJohan Hovold 145f865e614SJohan Hovold .uart_enable = 0x03, 146f865e614SJohan Hovold .flow_control = 0x0c, 147f865e614SJohan Hovold .xon_char = 0x10, 148f865e614SJohan Hovold .xoff_char = 0x11, 149f865e614SJohan Hovold .tx_break = 0x14, 150f865e614SJohan Hovold .gpio_mode = 0x1a, 151f865e614SJohan Hovold .gpio_direction = 0x1b, 152f865e614SJohan Hovold .gpio_set = 0x1d, 153f865e614SJohan Hovold .gpio_clear = 0x1e, 154f865e614SJohan Hovold .gpio_status = 0x1f, 155*607f6718SJohan Hovold 156*607f6718SJohan Hovold .enable = xr21v141x_uart_enable, 157*607f6718SJohan Hovold .disable = xr21v141x_uart_disable, 158*607f6718SJohan Hovold .set_line_settings = xr21v141x_set_line_settings, 159*607f6718SJohan Hovold }, 160*607f6718SJohan Hovold [XR21B142X] = { 161*607f6718SJohan Hovold .reg_width = 16, 162*607f6718SJohan Hovold .reg_recipient = USB_RECIP_INTERFACE, 163*607f6718SJohan Hovold .set_reg = 0x00, 164*607f6718SJohan Hovold .get_reg = 0x00, 165*607f6718SJohan Hovold 166*607f6718SJohan Hovold .uart_enable = 0x00, 167*607f6718SJohan Hovold .flow_control = 0x06, 168*607f6718SJohan Hovold .xon_char = 0x07, 169*607f6718SJohan Hovold .xoff_char = 0x08, 170*607f6718SJohan Hovold .tx_break = 0x0a, 171*607f6718SJohan Hovold .gpio_mode = 0x0c, 172*607f6718SJohan Hovold .gpio_direction = 0x0d, 173*607f6718SJohan Hovold .gpio_set = 0x0e, 174*607f6718SJohan Hovold .gpio_clear = 0x0f, 175*607f6718SJohan Hovold .gpio_status = 0x10, 176*607f6718SJohan Hovold .custom_driver = 0x60, 177*607f6718SJohan Hovold 178*607f6718SJohan Hovold .have_xmit_toggle = true, 179f865e614SJohan Hovold }, 180f865e614SJohan Hovold }; 181c2d405aaSManivannan Sadhasivam 18223b7998eSJohan Hovold struct xr_data { 183f865e614SJohan Hovold const struct xr_type *type; 184*607f6718SJohan Hovold u8 channel; /* zero-based index or interface number */ 18523b7998eSJohan Hovold }; 18623b7998eSJohan Hovold 187*607f6718SJohan Hovold static int xr_set_reg(struct usb_serial_port *port, u8 channel, u8 reg, u16 val) 188c2d405aaSManivannan Sadhasivam { 189*607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 190*607f6718SJohan Hovold const struct xr_type *type = data->type; 191c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 192c2d405aaSManivannan Sadhasivam int ret; 193c2d405aaSManivannan Sadhasivam 194*607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 195*607f6718SJohan Hovold type->set_reg, 196*607f6718SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, 197*607f6718SJohan Hovold val, (channel << 8) | reg, NULL, 0, 198c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 199c2d405aaSManivannan Sadhasivam if (ret < 0) { 200c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 201c2d405aaSManivannan Sadhasivam return ret; 202c2d405aaSManivannan Sadhasivam } 203c2d405aaSManivannan Sadhasivam 204c2d405aaSManivannan Sadhasivam return 0; 205c2d405aaSManivannan Sadhasivam } 206c2d405aaSManivannan Sadhasivam 207*607f6718SJohan Hovold static int xr_get_reg(struct usb_serial_port *port, u8 channel, u8 reg, u16 *val) 208c2d405aaSManivannan Sadhasivam { 209*607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 210*607f6718SJohan Hovold const struct xr_type *type = data->type; 211c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 212c2d405aaSManivannan Sadhasivam u8 *dmabuf; 213*607f6718SJohan Hovold int ret, len; 214c2d405aaSManivannan Sadhasivam 215*607f6718SJohan Hovold if (type->reg_width == 8) 216*607f6718SJohan Hovold len = 1; 217*607f6718SJohan Hovold else 218*607f6718SJohan Hovold len = 2; 219*607f6718SJohan Hovold 220*607f6718SJohan Hovold dmabuf = kmalloc(len, GFP_KERNEL); 221c2d405aaSManivannan Sadhasivam if (!dmabuf) 222c2d405aaSManivannan Sadhasivam return -ENOMEM; 223c2d405aaSManivannan Sadhasivam 224*607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 225*607f6718SJohan Hovold type->get_reg, 226*607f6718SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, 227*607f6718SJohan Hovold 0, (channel << 8) | reg, dmabuf, len, 228c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 229*607f6718SJohan Hovold if (ret == len) { 230*607f6718SJohan Hovold if (len == 2) 231*607f6718SJohan Hovold *val = le16_to_cpup((__le16 *)dmabuf); 232*607f6718SJohan Hovold else 233c2d405aaSManivannan Sadhasivam *val = *dmabuf; 234c2d405aaSManivannan Sadhasivam ret = 0; 235c2d405aaSManivannan Sadhasivam } else { 236c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 237c2d405aaSManivannan Sadhasivam if (ret >= 0) 238c2d405aaSManivannan Sadhasivam ret = -EIO; 239c2d405aaSManivannan Sadhasivam } 240c2d405aaSManivannan Sadhasivam 241c2d405aaSManivannan Sadhasivam kfree(dmabuf); 242c2d405aaSManivannan Sadhasivam 243c2d405aaSManivannan Sadhasivam return ret; 244c2d405aaSManivannan Sadhasivam } 245c2d405aaSManivannan Sadhasivam 246*607f6718SJohan Hovold static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u16 val) 247c2d405aaSManivannan Sadhasivam { 24823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 24923b7998eSJohan Hovold 250*607f6718SJohan Hovold return xr_set_reg(port, data->channel, reg, val); 251c2d405aaSManivannan Sadhasivam } 252c2d405aaSManivannan Sadhasivam 253*607f6718SJohan Hovold static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u16 *val) 254c2d405aaSManivannan Sadhasivam { 25523b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 25623b7998eSJohan Hovold 257*607f6718SJohan Hovold return xr_get_reg(port, data->channel, reg, val); 258c2d405aaSManivannan Sadhasivam } 259c2d405aaSManivannan Sadhasivam 26023b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 261c2d405aaSManivannan Sadhasivam { 26223b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 26323b7998eSJohan Hovold u8 reg; 26423b7998eSJohan Hovold 26523b7998eSJohan Hovold reg = reg_base + data->channel; 26623b7998eSJohan Hovold 267c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 268c2d405aaSManivannan Sadhasivam } 269c2d405aaSManivannan Sadhasivam 270*607f6718SJohan Hovold static int __xr_uart_enable(struct usb_serial_port *port) 271*607f6718SJohan Hovold { 272*607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 273*607f6718SJohan Hovold 274*607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 275*607f6718SJohan Hovold XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); 276*607f6718SJohan Hovold } 277*607f6718SJohan Hovold 278*607f6718SJohan Hovold static int __xr_uart_disable(struct usb_serial_port *port) 279*607f6718SJohan Hovold { 280*607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 281*607f6718SJohan Hovold 282*607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 0); 283*607f6718SJohan Hovold } 284*607f6718SJohan Hovold 285c2d405aaSManivannan Sadhasivam /* 286c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 287c2d405aaSManivannan Sadhasivam * module in XR21V141X: 288c2d405aaSManivannan Sadhasivam * 289c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 290c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 291c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 292c2d405aaSManivannan Sadhasivam */ 293*607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port) 294c2d405aaSManivannan Sadhasivam { 295c2d405aaSManivannan Sadhasivam int ret; 296c2d405aaSManivannan Sadhasivam 297c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 298c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 299c2d405aaSManivannan Sadhasivam if (ret) 300c2d405aaSManivannan Sadhasivam return ret; 301c2d405aaSManivannan Sadhasivam 302*607f6718SJohan Hovold ret = __xr_uart_enable(port); 303c2d405aaSManivannan Sadhasivam if (ret) 304c2d405aaSManivannan Sadhasivam return ret; 305c2d405aaSManivannan Sadhasivam 306c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 307c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 308c2d405aaSManivannan Sadhasivam if (ret) 309*607f6718SJohan Hovold __xr_uart_disable(port); 310c2d405aaSManivannan Sadhasivam 311c2d405aaSManivannan Sadhasivam return ret; 312c2d405aaSManivannan Sadhasivam } 313c2d405aaSManivannan Sadhasivam 314*607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port) 315c2d405aaSManivannan Sadhasivam { 316c2d405aaSManivannan Sadhasivam int ret; 317c2d405aaSManivannan Sadhasivam 318*607f6718SJohan Hovold ret = __xr_uart_disable(port); 319c2d405aaSManivannan Sadhasivam if (ret) 320c2d405aaSManivannan Sadhasivam return ret; 321c2d405aaSManivannan Sadhasivam 322c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 323c2d405aaSManivannan Sadhasivam 324c2d405aaSManivannan Sadhasivam return ret; 325c2d405aaSManivannan Sadhasivam } 326c2d405aaSManivannan Sadhasivam 327*607f6718SJohan Hovold static int xr_uart_enable(struct usb_serial_port *port) 328*607f6718SJohan Hovold { 329*607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 330*607f6718SJohan Hovold 331*607f6718SJohan Hovold if (data->type->enable) 332*607f6718SJohan Hovold return data->type->enable(port); 333*607f6718SJohan Hovold 334*607f6718SJohan Hovold return __xr_uart_enable(port); 335*607f6718SJohan Hovold } 336*607f6718SJohan Hovold 337*607f6718SJohan Hovold static int xr_uart_disable(struct usb_serial_port *port) 338*607f6718SJohan Hovold { 339*607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 340*607f6718SJohan Hovold 341*607f6718SJohan Hovold if (data->type->disable) 342*607f6718SJohan Hovold return data->type->disable(port); 343*607f6718SJohan Hovold 344*607f6718SJohan Hovold return __xr_uart_disable(port); 345*607f6718SJohan Hovold } 346*607f6718SJohan Hovold 347c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 348c2d405aaSManivannan Sadhasivam { 349c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 350f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 351*607f6718SJohan Hovold u16 status; 352c2d405aaSManivannan Sadhasivam int ret; 353c2d405aaSManivannan Sadhasivam 354f865e614SJohan Hovold ret = xr_get_reg_uart(port, data->type->gpio_status, &status); 355c2d405aaSManivannan Sadhasivam if (ret) 356c2d405aaSManivannan Sadhasivam return ret; 357c2d405aaSManivannan Sadhasivam 358c2d405aaSManivannan Sadhasivam /* 359c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 360c2d405aaSManivannan Sadhasivam * and '1' means not active. 361c2d405aaSManivannan Sadhasivam */ 362958d6b95SJohan Hovold ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | 363958d6b95SJohan Hovold ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | 364958d6b95SJohan Hovold ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | 365958d6b95SJohan Hovold ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | 366958d6b95SJohan Hovold ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | 367958d6b95SJohan Hovold ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); 368c2d405aaSManivannan Sadhasivam 369c2d405aaSManivannan Sadhasivam return ret; 370c2d405aaSManivannan Sadhasivam } 371c2d405aaSManivannan Sadhasivam 372c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 373c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 374c2d405aaSManivannan Sadhasivam { 375f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 376f865e614SJohan Hovold const struct xr_type *type = data->type; 377*607f6718SJohan Hovold u16 gpio_set = 0; 378*607f6718SJohan Hovold u16 gpio_clr = 0; 379c2d405aaSManivannan Sadhasivam int ret = 0; 380c2d405aaSManivannan Sadhasivam 381c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 382c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 383958d6b95SJohan Hovold gpio_clr |= XR_GPIO_RTS; 384c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 385958d6b95SJohan Hovold gpio_clr |= XR_GPIO_DTR; 386c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 387958d6b95SJohan Hovold gpio_set |= XR_GPIO_RTS; 388c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 389958d6b95SJohan Hovold gpio_set |= XR_GPIO_DTR; 390c2d405aaSManivannan Sadhasivam 391c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 392c2d405aaSManivannan Sadhasivam if (gpio_clr) 393f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); 394c2d405aaSManivannan Sadhasivam 395c2d405aaSManivannan Sadhasivam if (gpio_set) 396f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); 397c2d405aaSManivannan Sadhasivam 398c2d405aaSManivannan Sadhasivam return ret; 399c2d405aaSManivannan Sadhasivam } 400c2d405aaSManivannan Sadhasivam 401c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 402c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 403c2d405aaSManivannan Sadhasivam { 404c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 405c2d405aaSManivannan Sadhasivam 406c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 407c2d405aaSManivannan Sadhasivam } 408c2d405aaSManivannan Sadhasivam 409c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 410c2d405aaSManivannan Sadhasivam { 411c2d405aaSManivannan Sadhasivam if (on) 412c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 413c2d405aaSManivannan Sadhasivam else 414c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 415c2d405aaSManivannan Sadhasivam } 416c2d405aaSManivannan Sadhasivam 417c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 418c2d405aaSManivannan Sadhasivam { 419c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 420f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 421f865e614SJohan Hovold const struct xr_type *type = data->type; 422*607f6718SJohan Hovold u16 state; 423c2d405aaSManivannan Sadhasivam 424c2d405aaSManivannan Sadhasivam if (break_state == 0) 425*607f6718SJohan Hovold state = 0; 426c2d405aaSManivannan Sadhasivam else 427*607f6718SJohan Hovold state = GENMASK(type->reg_width - 1, 0); 428c2d405aaSManivannan Sadhasivam 429*607f6718SJohan Hovold dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); 430*607f6718SJohan Hovold 431f865e614SJohan Hovold xr_set_reg_uart(port, type->tx_break, state); 432c2d405aaSManivannan Sadhasivam } 433c2d405aaSManivannan Sadhasivam 434c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 435c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 436c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 437c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 438c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 439c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 440c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 441c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 442c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 443c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 444c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 445c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 446c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 447c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 448c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 449c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 450c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 451c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 452c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 453c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 454c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 455c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 456c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 457c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 458c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 459c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 460c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 461c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 462c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 463c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 464c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 465c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 466c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 467c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 468c2d405aaSManivannan Sadhasivam }; 469c2d405aaSManivannan Sadhasivam 470*607f6718SJohan Hovold static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) 471c2d405aaSManivannan Sadhasivam { 472c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 473c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 474c2d405aaSManivannan Sadhasivam int ret; 475c2d405aaSManivannan Sadhasivam 47655317e22SJohan Hovold baud = tty->termios.c_ospeed; 47755317e22SJohan Hovold if (!baud) 47855317e22SJohan Hovold return 0; 47955317e22SJohan Hovold 48055317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 481c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 482c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 483c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 484c2d405aaSManivannan Sadhasivam 485c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 486c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 487c2d405aaSManivannan Sadhasivam else 488c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 489c2d405aaSManivannan Sadhasivam 490c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 491c2d405aaSManivannan Sadhasivam /* 492c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 493c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 494c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 495c2d405aaSManivannan Sadhasivam */ 496c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 497c2d405aaSManivannan Sadhasivam divisor & 0xff); 498c2d405aaSManivannan Sadhasivam if (ret) 499c2d405aaSManivannan Sadhasivam return ret; 500c2d405aaSManivannan Sadhasivam 501c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 502c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 503c2d405aaSManivannan Sadhasivam if (ret) 504c2d405aaSManivannan Sadhasivam return ret; 505c2d405aaSManivannan Sadhasivam 506c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 507c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 508c2d405aaSManivannan Sadhasivam if (ret) 509c2d405aaSManivannan Sadhasivam return ret; 510c2d405aaSManivannan Sadhasivam 511c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 512c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 513c2d405aaSManivannan Sadhasivam if (ret) 514c2d405aaSManivannan Sadhasivam return ret; 515c2d405aaSManivannan Sadhasivam 516c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 517c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 518c2d405aaSManivannan Sadhasivam if (ret) 519c2d405aaSManivannan Sadhasivam return ret; 520c2d405aaSManivannan Sadhasivam 521c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 522c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 523c2d405aaSManivannan Sadhasivam if (ret) 524c2d405aaSManivannan Sadhasivam return ret; 525c2d405aaSManivannan Sadhasivam 526c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 527c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 528c2d405aaSManivannan Sadhasivam if (ret) 529c2d405aaSManivannan Sadhasivam return ret; 530c2d405aaSManivannan Sadhasivam 531c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 532c2d405aaSManivannan Sadhasivam 533c2d405aaSManivannan Sadhasivam return 0; 534c2d405aaSManivannan Sadhasivam } 535c2d405aaSManivannan Sadhasivam 536c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 53755317e22SJohan Hovold struct usb_serial_port *port, 53855317e22SJohan Hovold struct ktermios *old_termios) 539c2d405aaSManivannan Sadhasivam { 540f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 541f865e614SJohan Hovold const struct xr_type *type = data->type; 542*607f6718SJohan Hovold u16 flow, gpio_mode; 543c2d405aaSManivannan Sadhasivam int ret; 544c2d405aaSManivannan Sadhasivam 545f865e614SJohan Hovold ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); 546c2d405aaSManivannan Sadhasivam if (ret) 547c2d405aaSManivannan Sadhasivam return; 548c2d405aaSManivannan Sadhasivam 549*607f6718SJohan Hovold /* 550*607f6718SJohan Hovold * According to the datasheets, the UART needs to be disabled while 551*607f6718SJohan Hovold * writing to the FLOW_CONTROL register (XR21V141X), or any register 552*607f6718SJohan Hovold * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). 553*607f6718SJohan Hovold */ 554*607f6718SJohan Hovold xr_uart_disable(port); 555*607f6718SJohan Hovold 556465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 557*607f6718SJohan Hovold gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; 558465d3b3aSJohan Hovold 55955317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 560c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 561*607f6718SJohan Hovold gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; 562958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_HW; 563c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 564c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 565c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 566c2d405aaSManivannan Sadhasivam 567c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 568958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_SW; 569c2d405aaSManivannan Sadhasivam 570f865e614SJohan Hovold xr_set_reg_uart(port, type->xon_char, start_char); 571f865e614SJohan Hovold xr_set_reg_uart(port, type->xoff_char, stop_char); 572c2d405aaSManivannan Sadhasivam } else { 573c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 574958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_NONE; 575c2d405aaSManivannan Sadhasivam } 576c2d405aaSManivannan Sadhasivam 577f865e614SJohan Hovold xr_set_reg_uart(port, type->flow_control, flow); 578f865e614SJohan Hovold xr_set_reg_uart(port, type->gpio_mode, gpio_mode); 57955317e22SJohan Hovold 580*607f6718SJohan Hovold xr_uart_enable(port); 581*607f6718SJohan Hovold 58255317e22SJohan Hovold if (C_BAUD(tty) == B0) 58355317e22SJohan Hovold xr_dtr_rts(port, 0); 58455317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 58555317e22SJohan Hovold xr_dtr_rts(port, 1); 586c2d405aaSManivannan Sadhasivam } 587c2d405aaSManivannan Sadhasivam 588*607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 589*607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 590c2d405aaSManivannan Sadhasivam { 591c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 592c2d405aaSManivannan Sadhasivam u8 bits = 0; 593c2d405aaSManivannan Sadhasivam int ret; 594c2d405aaSManivannan Sadhasivam 595736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 596*607f6718SJohan Hovold xr21v141x_set_baudrate(tty, port); 597c2d405aaSManivannan Sadhasivam 598c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 599c2d405aaSManivannan Sadhasivam case CS5: 600c2d405aaSManivannan Sadhasivam case CS6: 601c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 602c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 603c2d405aaSManivannan Sadhasivam if (old_termios) 604c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 605c2d405aaSManivannan Sadhasivam else 606ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 607ea7ada4dSJohan Hovold 608ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 609958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 610ea7ada4dSJohan Hovold else 611958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 612c2d405aaSManivannan Sadhasivam break; 613c2d405aaSManivannan Sadhasivam case CS7: 614958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 615c2d405aaSManivannan Sadhasivam break; 616c2d405aaSManivannan Sadhasivam case CS8: 617c2d405aaSManivannan Sadhasivam default: 618958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 619c2d405aaSManivannan Sadhasivam break; 620c2d405aaSManivannan Sadhasivam } 621c2d405aaSManivannan Sadhasivam 622c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 623c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 624c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 625958d6b95SJohan Hovold bits |= XR_UART_PARITY_MARK; 626c2d405aaSManivannan Sadhasivam else 627958d6b95SJohan Hovold bits |= XR_UART_PARITY_SPACE; 628c2d405aaSManivannan Sadhasivam } else { 629c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 630958d6b95SJohan Hovold bits |= XR_UART_PARITY_ODD; 631c2d405aaSManivannan Sadhasivam else 632958d6b95SJohan Hovold bits |= XR_UART_PARITY_EVEN; 633c2d405aaSManivannan Sadhasivam } 634c2d405aaSManivannan Sadhasivam } 635c2d405aaSManivannan Sadhasivam 636c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 637958d6b95SJohan Hovold bits |= XR_UART_STOP_2; 638c2d405aaSManivannan Sadhasivam else 639958d6b95SJohan Hovold bits |= XR_UART_STOP_1; 640c2d405aaSManivannan Sadhasivam 641c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 642c2d405aaSManivannan Sadhasivam if (ret) 643c2d405aaSManivannan Sadhasivam return; 644*607f6718SJohan Hovold } 645*607f6718SJohan Hovold 646*607f6718SJohan Hovold static void xr_cdc_set_line_coding(struct tty_struct *tty, 647*607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 648*607f6718SJohan Hovold { 649*607f6718SJohan Hovold struct usb_host_interface *alt = port->serial->interface->cur_altsetting; 650*607f6718SJohan Hovold struct usb_device *udev = port->serial->dev; 651*607f6718SJohan Hovold struct usb_cdc_line_coding *lc; 652*607f6718SJohan Hovold int ret; 653*607f6718SJohan Hovold 654*607f6718SJohan Hovold lc = kzalloc(sizeof(*lc), GFP_KERNEL); 655*607f6718SJohan Hovold if (!lc) 656*607f6718SJohan Hovold return; 657*607f6718SJohan Hovold 658*607f6718SJohan Hovold if (tty->termios.c_ospeed) 659*607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); 660*607f6718SJohan Hovold else if (old_termios) 661*607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(old_termios->c_ospeed); 662*607f6718SJohan Hovold else 663*607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(9600); 664*607f6718SJohan Hovold 665*607f6718SJohan Hovold if (C_CSTOPB(tty)) 666*607f6718SJohan Hovold lc->bCharFormat = USB_CDC_2_STOP_BITS; 667*607f6718SJohan Hovold else 668*607f6718SJohan Hovold lc->bCharFormat = USB_CDC_1_STOP_BITS; 669*607f6718SJohan Hovold 670*607f6718SJohan Hovold if (C_PARENB(tty)) { 671*607f6718SJohan Hovold if (C_CMSPAR(tty)) { 672*607f6718SJohan Hovold if (C_PARODD(tty)) 673*607f6718SJohan Hovold lc->bParityType = USB_CDC_MARK_PARITY; 674*607f6718SJohan Hovold else 675*607f6718SJohan Hovold lc->bParityType = USB_CDC_SPACE_PARITY; 676*607f6718SJohan Hovold } else { 677*607f6718SJohan Hovold if (C_PARODD(tty)) 678*607f6718SJohan Hovold lc->bParityType = USB_CDC_ODD_PARITY; 679*607f6718SJohan Hovold else 680*607f6718SJohan Hovold lc->bParityType = USB_CDC_EVEN_PARITY; 681*607f6718SJohan Hovold } 682*607f6718SJohan Hovold } else { 683*607f6718SJohan Hovold lc->bParityType = USB_CDC_NO_PARITY; 684*607f6718SJohan Hovold } 685*607f6718SJohan Hovold 686*607f6718SJohan Hovold switch (C_CSIZE(tty)) { 687*607f6718SJohan Hovold case CS5: 688*607f6718SJohan Hovold lc->bDataBits = 5; 689*607f6718SJohan Hovold break; 690*607f6718SJohan Hovold case CS6: 691*607f6718SJohan Hovold lc->bDataBits = 6; 692*607f6718SJohan Hovold break; 693*607f6718SJohan Hovold case CS7: 694*607f6718SJohan Hovold lc->bDataBits = 7; 695*607f6718SJohan Hovold break; 696*607f6718SJohan Hovold case CS8: 697*607f6718SJohan Hovold default: 698*607f6718SJohan Hovold lc->bDataBits = 8; 699*607f6718SJohan Hovold break; 700*607f6718SJohan Hovold } 701*607f6718SJohan Hovold 702*607f6718SJohan Hovold ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 703*607f6718SJohan Hovold USB_CDC_REQ_SET_LINE_CODING, 704*607f6718SJohan Hovold USB_TYPE_CLASS | USB_RECIP_INTERFACE, 705*607f6718SJohan Hovold 0, alt->desc.bInterfaceNumber, 706*607f6718SJohan Hovold lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); 707*607f6718SJohan Hovold if (ret < 0) 708*607f6718SJohan Hovold dev_err(&port->dev, "Failed to set line coding: %d\n", ret); 709*607f6718SJohan Hovold 710*607f6718SJohan Hovold kfree(lc); 711*607f6718SJohan Hovold } 712*607f6718SJohan Hovold 713*607f6718SJohan Hovold static void xr_set_termios(struct tty_struct *tty, 714*607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 715*607f6718SJohan Hovold { 716*607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 717*607f6718SJohan Hovold 718*607f6718SJohan Hovold /* 719*607f6718SJohan Hovold * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC 720*607f6718SJohan Hovold * mode upon receiving CDC requests. 721*607f6718SJohan Hovold */ 722*607f6718SJohan Hovold if (data->type->set_line_settings) 723*607f6718SJohan Hovold data->type->set_line_settings(tty, port, old_termios); 724*607f6718SJohan Hovold else 725*607f6718SJohan Hovold xr_cdc_set_line_coding(tty, port, old_termios); 726c2d405aaSManivannan Sadhasivam 72755317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 728c2d405aaSManivannan Sadhasivam } 729c2d405aaSManivannan Sadhasivam 730c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 731c2d405aaSManivannan Sadhasivam { 732c2d405aaSManivannan Sadhasivam int ret; 733c2d405aaSManivannan Sadhasivam 734c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 735c2d405aaSManivannan Sadhasivam if (ret) { 736c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 737c2d405aaSManivannan Sadhasivam return ret; 738c2d405aaSManivannan Sadhasivam } 739c2d405aaSManivannan Sadhasivam 740c2d405aaSManivannan Sadhasivam /* Setup termios */ 741c2d405aaSManivannan Sadhasivam if (tty) 742c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 743c2d405aaSManivannan Sadhasivam 744c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 745c2d405aaSManivannan Sadhasivam if (ret) { 746c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 747c2d405aaSManivannan Sadhasivam return ret; 748c2d405aaSManivannan Sadhasivam } 749c2d405aaSManivannan Sadhasivam 750c2d405aaSManivannan Sadhasivam return 0; 751c2d405aaSManivannan Sadhasivam } 752c2d405aaSManivannan Sadhasivam 753c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 754c2d405aaSManivannan Sadhasivam { 755c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 756c2d405aaSManivannan Sadhasivam 757c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 758c2d405aaSManivannan Sadhasivam } 759c2d405aaSManivannan Sadhasivam 760c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 761c2d405aaSManivannan Sadhasivam { 7625fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 7635fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 7645fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 7655fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 7665fec21e7SJohan Hovold struct usb_interface *data; 7675fec21e7SJohan Hovold int ret; 7685fec21e7SJohan Hovold 7695fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 7705fec21e7SJohan Hovold if (ret < 0) 771c2d405aaSManivannan Sadhasivam return -ENODEV; 772c2d405aaSManivannan Sadhasivam 7735fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 7745fec21e7SJohan Hovold if (!desc) 7755fec21e7SJohan Hovold return -ENODEV; 7765fec21e7SJohan Hovold 7775fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 7785fec21e7SJohan Hovold if (!data) 7795fec21e7SJohan Hovold return -ENODEV; 7805fec21e7SJohan Hovold 7815fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 7825fec21e7SJohan Hovold if (ret) 7835fec21e7SJohan Hovold return ret; 7845fec21e7SJohan Hovold 785f865e614SJohan Hovold usb_set_serial_data(serial, (void *)id->driver_info); 786f865e614SJohan Hovold 787c2d405aaSManivannan Sadhasivam return 0; 788c2d405aaSManivannan Sadhasivam } 789c2d405aaSManivannan Sadhasivam 790f865e614SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) 79149036fd0SJohan Hovold { 792*607f6718SJohan Hovold u16 mask, mode; 79349036fd0SJohan Hovold int ret; 79449036fd0SJohan Hovold 795*607f6718SJohan Hovold /* 796*607f6718SJohan Hovold * Configure all pins as GPIO except for Receive and Transmit Toggle. 797*607f6718SJohan Hovold */ 79849036fd0SJohan Hovold mode = 0; 799*607f6718SJohan Hovold if (type->have_xmit_toggle) 800*607f6718SJohan Hovold mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; 801*607f6718SJohan Hovold 802f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_mode, mode); 80349036fd0SJohan Hovold if (ret) 80449036fd0SJohan Hovold return ret; 80549036fd0SJohan Hovold 80649036fd0SJohan Hovold /* 80749036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 80849036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 80949036fd0SJohan Hovold */ 810958d6b95SJohan Hovold mask = XR_GPIO_DTR | XR_GPIO_RTS; 811f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_direction, mask); 81249036fd0SJohan Hovold if (ret) 81349036fd0SJohan Hovold return ret; 81449036fd0SJohan Hovold 815f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, mask); 81649036fd0SJohan Hovold if (ret) 81749036fd0SJohan Hovold return ret; 81849036fd0SJohan Hovold 81949036fd0SJohan Hovold return 0; 82049036fd0SJohan Hovold } 82149036fd0SJohan Hovold 82223b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 82323b7998eSJohan Hovold { 82423b7998eSJohan Hovold struct usb_interface_descriptor *desc; 825f865e614SJohan Hovold const struct xr_type *type; 82623b7998eSJohan Hovold struct xr_data *data; 827f865e614SJohan Hovold enum xr_type_id type_id; 82849036fd0SJohan Hovold int ret; 82923b7998eSJohan Hovold 830f865e614SJohan Hovold type_id = (int)(unsigned long)usb_get_serial_data(port->serial); 831f865e614SJohan Hovold type = &xr_types[type_id]; 832f865e614SJohan Hovold 83323b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 83423b7998eSJohan Hovold if (!data) 83523b7998eSJohan Hovold return -ENOMEM; 83623b7998eSJohan Hovold 837f865e614SJohan Hovold data->type = type; 838f865e614SJohan Hovold 83923b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 840*607f6718SJohan Hovold if (type_id == XR21V141X) 84123b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 842*607f6718SJohan Hovold else 843*607f6718SJohan Hovold data->channel = desc->bInterfaceNumber; 84423b7998eSJohan Hovold 84523b7998eSJohan Hovold usb_set_serial_port_data(port, data); 84623b7998eSJohan Hovold 847*607f6718SJohan Hovold if (type->custom_driver) { 848*607f6718SJohan Hovold ret = xr_set_reg_uart(port, type->custom_driver, 849*607f6718SJohan Hovold XR_CUSTOM_DRIVER_ACTIVE); 850*607f6718SJohan Hovold if (ret) 851*607f6718SJohan Hovold goto err_free; 852*607f6718SJohan Hovold } 853*607f6718SJohan Hovold 854f865e614SJohan Hovold ret = xr_gpio_init(port, type); 85549036fd0SJohan Hovold if (ret) 85649036fd0SJohan Hovold goto err_free; 85749036fd0SJohan Hovold 85823b7998eSJohan Hovold return 0; 85949036fd0SJohan Hovold 86049036fd0SJohan Hovold err_free: 86149036fd0SJohan Hovold kfree(data); 86249036fd0SJohan Hovold 86349036fd0SJohan Hovold return ret; 86423b7998eSJohan Hovold } 86523b7998eSJohan Hovold 86623b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 86723b7998eSJohan Hovold { 86823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 86923b7998eSJohan Hovold 87023b7998eSJohan Hovold kfree(data); 87123b7998eSJohan Hovold } 87223b7998eSJohan Hovold 873f865e614SJohan Hovold #define XR_DEVICE(vid, pid, type) \ 874f865e614SJohan Hovold USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ 875f865e614SJohan Hovold .driver_info = (type) 876f865e614SJohan Hovold 877c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 878f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, 879f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, 880f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, 881*607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, 882*607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, 883*607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, 884c2d405aaSManivannan Sadhasivam { } 885c2d405aaSManivannan Sadhasivam }; 886c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 887c2d405aaSManivannan Sadhasivam 888c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 889c2d405aaSManivannan Sadhasivam .driver = { 890c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 891c2d405aaSManivannan Sadhasivam .name = "xr_serial", 892c2d405aaSManivannan Sadhasivam }, 893c2d405aaSManivannan Sadhasivam .id_table = id_table, 894c2d405aaSManivannan Sadhasivam .num_ports = 1, 895c2d405aaSManivannan Sadhasivam .probe = xr_probe, 89623b7998eSJohan Hovold .port_probe = xr_port_probe, 89723b7998eSJohan Hovold .port_remove = xr_port_remove, 898c2d405aaSManivannan Sadhasivam .open = xr_open, 899c2d405aaSManivannan Sadhasivam .close = xr_close, 900c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 901c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 902c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 903c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 904c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 905c2d405aaSManivannan Sadhasivam }; 906c2d405aaSManivannan Sadhasivam 907c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 908c2d405aaSManivannan Sadhasivam &xr_device, NULL 909c2d405aaSManivannan Sadhasivam }; 910c2d405aaSManivannan Sadhasivam 911c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 912c2d405aaSManivannan Sadhasivam 913c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 914c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 915c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 916