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 111*4099d4baSJohan Hovold u16 uart_enable; 112*4099d4baSJohan Hovold u16 flow_control; 113*4099d4baSJohan Hovold u16 xon_char; 114*4099d4baSJohan Hovold u16 xoff_char; 115*4099d4baSJohan Hovold u16 tx_break; 116*4099d4baSJohan Hovold u16 gpio_mode; 117*4099d4baSJohan Hovold u16 gpio_direction; 118*4099d4baSJohan Hovold u16 gpio_set; 119*4099d4baSJohan Hovold u16 gpio_clear; 120*4099d4baSJohan Hovold u16 gpio_status; 121*4099d4baSJohan Hovold u16 custom_driver; 122607f6718SJohan Hovold 123*4099d4baSJohan 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, 136*4099d4baSJohan Hovold XR21B1411, 137f865e614SJohan Hovold XR_TYPE_COUNT, 138f865e614SJohan Hovold }; 139f865e614SJohan Hovold 140f865e614SJohan Hovold static const struct xr_type xr_types[] = { 141f865e614SJohan Hovold [XR21V141X] = { 142607f6718SJohan Hovold .reg_width = 8, 143607f6718SJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 144607f6718SJohan Hovold .set_reg = 0x00, 145607f6718SJohan Hovold .get_reg = 0x01, 146607f6718SJohan Hovold 147f865e614SJohan Hovold .uart_enable = 0x03, 148f865e614SJohan Hovold .flow_control = 0x0c, 149f865e614SJohan Hovold .xon_char = 0x10, 150f865e614SJohan Hovold .xoff_char = 0x11, 151f865e614SJohan Hovold .tx_break = 0x14, 152f865e614SJohan Hovold .gpio_mode = 0x1a, 153f865e614SJohan Hovold .gpio_direction = 0x1b, 154f865e614SJohan Hovold .gpio_set = 0x1d, 155f865e614SJohan Hovold .gpio_clear = 0x1e, 156f865e614SJohan Hovold .gpio_status = 0x1f, 157607f6718SJohan Hovold 158607f6718SJohan Hovold .enable = xr21v141x_uart_enable, 159607f6718SJohan Hovold .disable = xr21v141x_uart_disable, 160607f6718SJohan Hovold .set_line_settings = xr21v141x_set_line_settings, 161607f6718SJohan Hovold }, 162607f6718SJohan Hovold [XR21B142X] = { 163607f6718SJohan Hovold .reg_width = 16, 164607f6718SJohan Hovold .reg_recipient = USB_RECIP_INTERFACE, 165607f6718SJohan Hovold .set_reg = 0x00, 166607f6718SJohan Hovold .get_reg = 0x00, 167607f6718SJohan Hovold 168607f6718SJohan Hovold .uart_enable = 0x00, 169607f6718SJohan Hovold .flow_control = 0x06, 170607f6718SJohan Hovold .xon_char = 0x07, 171607f6718SJohan Hovold .xoff_char = 0x08, 172607f6718SJohan Hovold .tx_break = 0x0a, 173607f6718SJohan Hovold .gpio_mode = 0x0c, 174607f6718SJohan Hovold .gpio_direction = 0x0d, 175607f6718SJohan Hovold .gpio_set = 0x0e, 176607f6718SJohan Hovold .gpio_clear = 0x0f, 177607f6718SJohan Hovold .gpio_status = 0x10, 178607f6718SJohan Hovold .custom_driver = 0x60, 179607f6718SJohan Hovold 180*4099d4baSJohan Hovold .have_5_6_bit_mode = true, 181607f6718SJohan Hovold .have_xmit_toggle = true, 182f865e614SJohan Hovold }, 183*4099d4baSJohan Hovold [XR21B1411] = { 184*4099d4baSJohan Hovold .reg_width = 12, 185*4099d4baSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 186*4099d4baSJohan Hovold .set_reg = 0x00, 187*4099d4baSJohan Hovold .get_reg = 0x01, 188*4099d4baSJohan Hovold 189*4099d4baSJohan Hovold .uart_enable = 0xc00, 190*4099d4baSJohan Hovold .flow_control = 0xc06, 191*4099d4baSJohan Hovold .xon_char = 0xc07, 192*4099d4baSJohan Hovold .xoff_char = 0xc08, 193*4099d4baSJohan Hovold .tx_break = 0xc0a, 194*4099d4baSJohan Hovold .gpio_mode = 0xc0c, 195*4099d4baSJohan Hovold .gpio_direction = 0xc0d, 196*4099d4baSJohan Hovold .gpio_set = 0xc0e, 197*4099d4baSJohan Hovold .gpio_clear = 0xc0f, 198*4099d4baSJohan Hovold .gpio_status = 0xc10, 199*4099d4baSJohan Hovold .custom_driver = 0x20d, 200*4099d4baSJohan Hovold }, 201f865e614SJohan Hovold }; 202c2d405aaSManivannan Sadhasivam 20323b7998eSJohan Hovold struct xr_data { 204f865e614SJohan Hovold const struct xr_type *type; 205607f6718SJohan Hovold u8 channel; /* zero-based index or interface number */ 20623b7998eSJohan Hovold }; 20723b7998eSJohan Hovold 208*4099d4baSJohan Hovold static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val) 209c2d405aaSManivannan Sadhasivam { 210607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 211607f6718SJohan Hovold const struct xr_type *type = data->type; 212c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 213c2d405aaSManivannan Sadhasivam int ret; 214c2d405aaSManivannan Sadhasivam 215607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 216607f6718SJohan Hovold type->set_reg, 217607f6718SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, 218607f6718SJohan Hovold val, (channel << 8) | reg, NULL, 0, 219c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 220c2d405aaSManivannan Sadhasivam if (ret < 0) { 221c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 222c2d405aaSManivannan Sadhasivam return ret; 223c2d405aaSManivannan Sadhasivam } 224c2d405aaSManivannan Sadhasivam 225c2d405aaSManivannan Sadhasivam return 0; 226c2d405aaSManivannan Sadhasivam } 227c2d405aaSManivannan Sadhasivam 228*4099d4baSJohan Hovold static int xr_get_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 *val) 229c2d405aaSManivannan Sadhasivam { 230607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 231607f6718SJohan Hovold const struct xr_type *type = data->type; 232c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 233c2d405aaSManivannan Sadhasivam u8 *dmabuf; 234607f6718SJohan Hovold int ret, len; 235c2d405aaSManivannan Sadhasivam 236607f6718SJohan Hovold if (type->reg_width == 8) 237607f6718SJohan Hovold len = 1; 238607f6718SJohan Hovold else 239607f6718SJohan Hovold len = 2; 240607f6718SJohan Hovold 241607f6718SJohan Hovold dmabuf = kmalloc(len, GFP_KERNEL); 242c2d405aaSManivannan Sadhasivam if (!dmabuf) 243c2d405aaSManivannan Sadhasivam return -ENOMEM; 244c2d405aaSManivannan Sadhasivam 245607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 246607f6718SJohan Hovold type->get_reg, 247607f6718SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, 248607f6718SJohan Hovold 0, (channel << 8) | reg, dmabuf, len, 249c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 250607f6718SJohan Hovold if (ret == len) { 251607f6718SJohan Hovold if (len == 2) 252607f6718SJohan Hovold *val = le16_to_cpup((__le16 *)dmabuf); 253607f6718SJohan Hovold else 254c2d405aaSManivannan Sadhasivam *val = *dmabuf; 255c2d405aaSManivannan Sadhasivam ret = 0; 256c2d405aaSManivannan Sadhasivam } else { 257c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 258c2d405aaSManivannan Sadhasivam if (ret >= 0) 259c2d405aaSManivannan Sadhasivam ret = -EIO; 260c2d405aaSManivannan Sadhasivam } 261c2d405aaSManivannan Sadhasivam 262c2d405aaSManivannan Sadhasivam kfree(dmabuf); 263c2d405aaSManivannan Sadhasivam 264c2d405aaSManivannan Sadhasivam return ret; 265c2d405aaSManivannan Sadhasivam } 266c2d405aaSManivannan Sadhasivam 267*4099d4baSJohan Hovold static int xr_set_reg_uart(struct usb_serial_port *port, u16 reg, u16 val) 268c2d405aaSManivannan Sadhasivam { 26923b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 27023b7998eSJohan Hovold 271607f6718SJohan Hovold return xr_set_reg(port, data->channel, reg, val); 272c2d405aaSManivannan Sadhasivam } 273c2d405aaSManivannan Sadhasivam 274*4099d4baSJohan Hovold static int xr_get_reg_uart(struct usb_serial_port *port, u16 reg, u16 *val) 275c2d405aaSManivannan Sadhasivam { 27623b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 27723b7998eSJohan Hovold 278607f6718SJohan Hovold return xr_get_reg(port, data->channel, reg, val); 279c2d405aaSManivannan Sadhasivam } 280c2d405aaSManivannan Sadhasivam 28123b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 282c2d405aaSManivannan Sadhasivam { 28323b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 28423b7998eSJohan Hovold u8 reg; 28523b7998eSJohan Hovold 28623b7998eSJohan Hovold reg = reg_base + data->channel; 28723b7998eSJohan Hovold 288c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 289c2d405aaSManivannan Sadhasivam } 290c2d405aaSManivannan Sadhasivam 291607f6718SJohan Hovold static int __xr_uart_enable(struct usb_serial_port *port) 292607f6718SJohan Hovold { 293607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 294607f6718SJohan Hovold 295607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 296607f6718SJohan Hovold XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); 297607f6718SJohan Hovold } 298607f6718SJohan Hovold 299607f6718SJohan Hovold static int __xr_uart_disable(struct usb_serial_port *port) 300607f6718SJohan Hovold { 301607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 302607f6718SJohan Hovold 303607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 0); 304607f6718SJohan Hovold } 305607f6718SJohan Hovold 306c2d405aaSManivannan Sadhasivam /* 307c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 308c2d405aaSManivannan Sadhasivam * module in XR21V141X: 309c2d405aaSManivannan Sadhasivam * 310c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 311c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 312c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 313c2d405aaSManivannan Sadhasivam */ 314607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port) 315c2d405aaSManivannan Sadhasivam { 316c2d405aaSManivannan Sadhasivam int ret; 317c2d405aaSManivannan Sadhasivam 318c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 319c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 320c2d405aaSManivannan Sadhasivam if (ret) 321c2d405aaSManivannan Sadhasivam return ret; 322c2d405aaSManivannan Sadhasivam 323607f6718SJohan Hovold ret = __xr_uart_enable(port); 324c2d405aaSManivannan Sadhasivam if (ret) 325c2d405aaSManivannan Sadhasivam return ret; 326c2d405aaSManivannan Sadhasivam 327c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 328c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 329c2d405aaSManivannan Sadhasivam if (ret) 330607f6718SJohan Hovold __xr_uart_disable(port); 331c2d405aaSManivannan Sadhasivam 332c2d405aaSManivannan Sadhasivam return ret; 333c2d405aaSManivannan Sadhasivam } 334c2d405aaSManivannan Sadhasivam 335607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port) 336c2d405aaSManivannan Sadhasivam { 337c2d405aaSManivannan Sadhasivam int ret; 338c2d405aaSManivannan Sadhasivam 339607f6718SJohan Hovold ret = __xr_uart_disable(port); 340c2d405aaSManivannan Sadhasivam if (ret) 341c2d405aaSManivannan Sadhasivam return ret; 342c2d405aaSManivannan Sadhasivam 343c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 344c2d405aaSManivannan Sadhasivam 345c2d405aaSManivannan Sadhasivam return ret; 346c2d405aaSManivannan Sadhasivam } 347c2d405aaSManivannan Sadhasivam 348607f6718SJohan Hovold static int xr_uart_enable(struct usb_serial_port *port) 349607f6718SJohan Hovold { 350607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 351607f6718SJohan Hovold 352607f6718SJohan Hovold if (data->type->enable) 353607f6718SJohan Hovold return data->type->enable(port); 354607f6718SJohan Hovold 355607f6718SJohan Hovold return __xr_uart_enable(port); 356607f6718SJohan Hovold } 357607f6718SJohan Hovold 358607f6718SJohan Hovold static int xr_uart_disable(struct usb_serial_port *port) 359607f6718SJohan Hovold { 360607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 361607f6718SJohan Hovold 362607f6718SJohan Hovold if (data->type->disable) 363607f6718SJohan Hovold return data->type->disable(port); 364607f6718SJohan Hovold 365607f6718SJohan Hovold return __xr_uart_disable(port); 366607f6718SJohan Hovold } 367607f6718SJohan Hovold 368c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 369c2d405aaSManivannan Sadhasivam { 370c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 371f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 372607f6718SJohan Hovold u16 status; 373c2d405aaSManivannan Sadhasivam int ret; 374c2d405aaSManivannan Sadhasivam 375f865e614SJohan Hovold ret = xr_get_reg_uart(port, data->type->gpio_status, &status); 376c2d405aaSManivannan Sadhasivam if (ret) 377c2d405aaSManivannan Sadhasivam return ret; 378c2d405aaSManivannan Sadhasivam 379c2d405aaSManivannan Sadhasivam /* 380c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 381c2d405aaSManivannan Sadhasivam * and '1' means not active. 382c2d405aaSManivannan Sadhasivam */ 383958d6b95SJohan Hovold ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | 384958d6b95SJohan Hovold ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | 385958d6b95SJohan Hovold ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | 386958d6b95SJohan Hovold ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | 387958d6b95SJohan Hovold ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | 388958d6b95SJohan Hovold ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); 389c2d405aaSManivannan Sadhasivam 390c2d405aaSManivannan Sadhasivam return ret; 391c2d405aaSManivannan Sadhasivam } 392c2d405aaSManivannan Sadhasivam 393c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 394c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 395c2d405aaSManivannan Sadhasivam { 396f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 397f865e614SJohan Hovold const struct xr_type *type = data->type; 398607f6718SJohan Hovold u16 gpio_set = 0; 399607f6718SJohan Hovold u16 gpio_clr = 0; 400c2d405aaSManivannan Sadhasivam int ret = 0; 401c2d405aaSManivannan Sadhasivam 402c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 403c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 404958d6b95SJohan Hovold gpio_clr |= XR_GPIO_RTS; 405c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 406958d6b95SJohan Hovold gpio_clr |= XR_GPIO_DTR; 407c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 408958d6b95SJohan Hovold gpio_set |= XR_GPIO_RTS; 409c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 410958d6b95SJohan Hovold gpio_set |= XR_GPIO_DTR; 411c2d405aaSManivannan Sadhasivam 412c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 413c2d405aaSManivannan Sadhasivam if (gpio_clr) 414f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); 415c2d405aaSManivannan Sadhasivam 416c2d405aaSManivannan Sadhasivam if (gpio_set) 417f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); 418c2d405aaSManivannan Sadhasivam 419c2d405aaSManivannan Sadhasivam return ret; 420c2d405aaSManivannan Sadhasivam } 421c2d405aaSManivannan Sadhasivam 422c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 423c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 424c2d405aaSManivannan Sadhasivam { 425c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 426c2d405aaSManivannan Sadhasivam 427c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 428c2d405aaSManivannan Sadhasivam } 429c2d405aaSManivannan Sadhasivam 430c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 431c2d405aaSManivannan Sadhasivam { 432c2d405aaSManivannan Sadhasivam if (on) 433c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 434c2d405aaSManivannan Sadhasivam else 435c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 436c2d405aaSManivannan Sadhasivam } 437c2d405aaSManivannan Sadhasivam 438c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 439c2d405aaSManivannan Sadhasivam { 440c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 441f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 442f865e614SJohan Hovold const struct xr_type *type = data->type; 443607f6718SJohan Hovold u16 state; 444c2d405aaSManivannan Sadhasivam 445c2d405aaSManivannan Sadhasivam if (break_state == 0) 446607f6718SJohan Hovold state = 0; 447c2d405aaSManivannan Sadhasivam else 448607f6718SJohan Hovold state = GENMASK(type->reg_width - 1, 0); 449c2d405aaSManivannan Sadhasivam 450607f6718SJohan Hovold dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); 451607f6718SJohan Hovold 452f865e614SJohan Hovold xr_set_reg_uart(port, type->tx_break, state); 453c2d405aaSManivannan Sadhasivam } 454c2d405aaSManivannan Sadhasivam 455c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 456c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 457c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 458c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 459c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 460c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 461c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 462c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 463c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 464c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 465c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 466c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 467c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 468c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 469c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 470c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 471c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 472c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 473c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 474c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 475c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 476c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 477c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 478c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 479c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 480c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 481c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 482c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 483c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 484c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 485c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 486c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 487c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 488c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 489c2d405aaSManivannan Sadhasivam }; 490c2d405aaSManivannan Sadhasivam 491607f6718SJohan Hovold static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) 492c2d405aaSManivannan Sadhasivam { 493c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 494c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 495c2d405aaSManivannan Sadhasivam int ret; 496c2d405aaSManivannan Sadhasivam 49755317e22SJohan Hovold baud = tty->termios.c_ospeed; 49855317e22SJohan Hovold if (!baud) 49955317e22SJohan Hovold return 0; 50055317e22SJohan Hovold 50155317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 502c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 503c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 504c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 505c2d405aaSManivannan Sadhasivam 506c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 507c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 508c2d405aaSManivannan Sadhasivam else 509c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 510c2d405aaSManivannan Sadhasivam 511c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 512c2d405aaSManivannan Sadhasivam /* 513c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 514c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 515c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 516c2d405aaSManivannan Sadhasivam */ 517c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 518c2d405aaSManivannan Sadhasivam divisor & 0xff); 519c2d405aaSManivannan Sadhasivam if (ret) 520c2d405aaSManivannan Sadhasivam return ret; 521c2d405aaSManivannan Sadhasivam 522c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 523c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 524c2d405aaSManivannan Sadhasivam if (ret) 525c2d405aaSManivannan Sadhasivam return ret; 526c2d405aaSManivannan Sadhasivam 527c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 528c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 529c2d405aaSManivannan Sadhasivam if (ret) 530c2d405aaSManivannan Sadhasivam return ret; 531c2d405aaSManivannan Sadhasivam 532c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 533c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 534c2d405aaSManivannan Sadhasivam if (ret) 535c2d405aaSManivannan Sadhasivam return ret; 536c2d405aaSManivannan Sadhasivam 537c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 538c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 539c2d405aaSManivannan Sadhasivam if (ret) 540c2d405aaSManivannan Sadhasivam return ret; 541c2d405aaSManivannan Sadhasivam 542c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 543c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 544c2d405aaSManivannan Sadhasivam if (ret) 545c2d405aaSManivannan Sadhasivam return ret; 546c2d405aaSManivannan Sadhasivam 547c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 548c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 549c2d405aaSManivannan Sadhasivam if (ret) 550c2d405aaSManivannan Sadhasivam return ret; 551c2d405aaSManivannan Sadhasivam 552c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 553c2d405aaSManivannan Sadhasivam 554c2d405aaSManivannan Sadhasivam return 0; 555c2d405aaSManivannan Sadhasivam } 556c2d405aaSManivannan Sadhasivam 557c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 55855317e22SJohan Hovold struct usb_serial_port *port, 55955317e22SJohan Hovold struct ktermios *old_termios) 560c2d405aaSManivannan Sadhasivam { 561f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 562f865e614SJohan Hovold const struct xr_type *type = data->type; 563607f6718SJohan Hovold u16 flow, gpio_mode; 564c2d405aaSManivannan Sadhasivam int ret; 565c2d405aaSManivannan Sadhasivam 566f865e614SJohan Hovold ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); 567c2d405aaSManivannan Sadhasivam if (ret) 568c2d405aaSManivannan Sadhasivam return; 569c2d405aaSManivannan Sadhasivam 570607f6718SJohan Hovold /* 571607f6718SJohan Hovold * According to the datasheets, the UART needs to be disabled while 572607f6718SJohan Hovold * writing to the FLOW_CONTROL register (XR21V141X), or any register 573607f6718SJohan Hovold * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). 574607f6718SJohan Hovold */ 575607f6718SJohan Hovold xr_uart_disable(port); 576607f6718SJohan Hovold 577465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 578607f6718SJohan Hovold gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; 579465d3b3aSJohan Hovold 58055317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 581c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 582607f6718SJohan Hovold gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; 583958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_HW; 584c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 585c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 586c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 587c2d405aaSManivannan Sadhasivam 588c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 589958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_SW; 590c2d405aaSManivannan Sadhasivam 591f865e614SJohan Hovold xr_set_reg_uart(port, type->xon_char, start_char); 592f865e614SJohan Hovold xr_set_reg_uart(port, type->xoff_char, stop_char); 593c2d405aaSManivannan Sadhasivam } else { 594c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 595958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_NONE; 596c2d405aaSManivannan Sadhasivam } 597c2d405aaSManivannan Sadhasivam 598f865e614SJohan Hovold xr_set_reg_uart(port, type->flow_control, flow); 599f865e614SJohan Hovold xr_set_reg_uart(port, type->gpio_mode, gpio_mode); 60055317e22SJohan Hovold 601607f6718SJohan Hovold xr_uart_enable(port); 602607f6718SJohan Hovold 60355317e22SJohan Hovold if (C_BAUD(tty) == B0) 60455317e22SJohan Hovold xr_dtr_rts(port, 0); 60555317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 60655317e22SJohan Hovold xr_dtr_rts(port, 1); 607c2d405aaSManivannan Sadhasivam } 608c2d405aaSManivannan Sadhasivam 609607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 610607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 611c2d405aaSManivannan Sadhasivam { 612c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 613c2d405aaSManivannan Sadhasivam u8 bits = 0; 614c2d405aaSManivannan Sadhasivam int ret; 615c2d405aaSManivannan Sadhasivam 616736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 617607f6718SJohan Hovold xr21v141x_set_baudrate(tty, port); 618c2d405aaSManivannan Sadhasivam 619c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 620c2d405aaSManivannan Sadhasivam case CS5: 621c2d405aaSManivannan Sadhasivam case CS6: 622c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 623c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 624c2d405aaSManivannan Sadhasivam if (old_termios) 625c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 626c2d405aaSManivannan Sadhasivam else 627ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 628ea7ada4dSJohan Hovold 629ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 630958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 631ea7ada4dSJohan Hovold else 632958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 633c2d405aaSManivannan Sadhasivam break; 634c2d405aaSManivannan Sadhasivam case CS7: 635958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 636c2d405aaSManivannan Sadhasivam break; 637c2d405aaSManivannan Sadhasivam case CS8: 638c2d405aaSManivannan Sadhasivam default: 639958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 640c2d405aaSManivannan Sadhasivam break; 641c2d405aaSManivannan Sadhasivam } 642c2d405aaSManivannan Sadhasivam 643c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 644c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 645c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 646958d6b95SJohan Hovold bits |= XR_UART_PARITY_MARK; 647c2d405aaSManivannan Sadhasivam else 648958d6b95SJohan Hovold bits |= XR_UART_PARITY_SPACE; 649c2d405aaSManivannan Sadhasivam } else { 650c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 651958d6b95SJohan Hovold bits |= XR_UART_PARITY_ODD; 652c2d405aaSManivannan Sadhasivam else 653958d6b95SJohan Hovold bits |= XR_UART_PARITY_EVEN; 654c2d405aaSManivannan Sadhasivam } 655c2d405aaSManivannan Sadhasivam } 656c2d405aaSManivannan Sadhasivam 657c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 658958d6b95SJohan Hovold bits |= XR_UART_STOP_2; 659c2d405aaSManivannan Sadhasivam else 660958d6b95SJohan Hovold bits |= XR_UART_STOP_1; 661c2d405aaSManivannan Sadhasivam 662c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 663c2d405aaSManivannan Sadhasivam if (ret) 664c2d405aaSManivannan Sadhasivam return; 665607f6718SJohan Hovold } 666607f6718SJohan Hovold 667607f6718SJohan Hovold static void xr_cdc_set_line_coding(struct tty_struct *tty, 668607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 669607f6718SJohan Hovold { 670*4099d4baSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 671607f6718SJohan Hovold struct usb_host_interface *alt = port->serial->interface->cur_altsetting; 672607f6718SJohan Hovold struct usb_device *udev = port->serial->dev; 673607f6718SJohan Hovold struct usb_cdc_line_coding *lc; 674607f6718SJohan Hovold int ret; 675607f6718SJohan Hovold 676607f6718SJohan Hovold lc = kzalloc(sizeof(*lc), GFP_KERNEL); 677607f6718SJohan Hovold if (!lc) 678607f6718SJohan Hovold return; 679607f6718SJohan Hovold 680607f6718SJohan Hovold if (tty->termios.c_ospeed) 681607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); 682607f6718SJohan Hovold else if (old_termios) 683607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(old_termios->c_ospeed); 684607f6718SJohan Hovold else 685607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(9600); 686607f6718SJohan Hovold 687607f6718SJohan Hovold if (C_CSTOPB(tty)) 688607f6718SJohan Hovold lc->bCharFormat = USB_CDC_2_STOP_BITS; 689607f6718SJohan Hovold else 690607f6718SJohan Hovold lc->bCharFormat = USB_CDC_1_STOP_BITS; 691607f6718SJohan Hovold 692607f6718SJohan Hovold if (C_PARENB(tty)) { 693607f6718SJohan Hovold if (C_CMSPAR(tty)) { 694607f6718SJohan Hovold if (C_PARODD(tty)) 695607f6718SJohan Hovold lc->bParityType = USB_CDC_MARK_PARITY; 696607f6718SJohan Hovold else 697607f6718SJohan Hovold lc->bParityType = USB_CDC_SPACE_PARITY; 698607f6718SJohan Hovold } else { 699607f6718SJohan Hovold if (C_PARODD(tty)) 700607f6718SJohan Hovold lc->bParityType = USB_CDC_ODD_PARITY; 701607f6718SJohan Hovold else 702607f6718SJohan Hovold lc->bParityType = USB_CDC_EVEN_PARITY; 703607f6718SJohan Hovold } 704607f6718SJohan Hovold } else { 705607f6718SJohan Hovold lc->bParityType = USB_CDC_NO_PARITY; 706607f6718SJohan Hovold } 707607f6718SJohan Hovold 708*4099d4baSJohan Hovold if (!data->type->have_5_6_bit_mode && 709*4099d4baSJohan Hovold (C_CSIZE(tty) == CS5 || C_CSIZE(tty) == CS6)) { 710*4099d4baSJohan Hovold tty->termios.c_cflag &= ~CSIZE; 711*4099d4baSJohan Hovold if (old_termios) 712*4099d4baSJohan Hovold tty->termios.c_cflag |= old_termios->c_cflag & CSIZE; 713*4099d4baSJohan Hovold else 714*4099d4baSJohan Hovold tty->termios.c_cflag |= CS8; 715*4099d4baSJohan Hovold } 716*4099d4baSJohan Hovold 717607f6718SJohan Hovold switch (C_CSIZE(tty)) { 718607f6718SJohan Hovold case CS5: 719607f6718SJohan Hovold lc->bDataBits = 5; 720607f6718SJohan Hovold break; 721607f6718SJohan Hovold case CS6: 722607f6718SJohan Hovold lc->bDataBits = 6; 723607f6718SJohan Hovold break; 724607f6718SJohan Hovold case CS7: 725607f6718SJohan Hovold lc->bDataBits = 7; 726607f6718SJohan Hovold break; 727607f6718SJohan Hovold case CS8: 728607f6718SJohan Hovold default: 729607f6718SJohan Hovold lc->bDataBits = 8; 730607f6718SJohan Hovold break; 731607f6718SJohan Hovold } 732607f6718SJohan Hovold 733607f6718SJohan Hovold ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 734607f6718SJohan Hovold USB_CDC_REQ_SET_LINE_CODING, 735607f6718SJohan Hovold USB_TYPE_CLASS | USB_RECIP_INTERFACE, 736607f6718SJohan Hovold 0, alt->desc.bInterfaceNumber, 737607f6718SJohan Hovold lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); 738607f6718SJohan Hovold if (ret < 0) 739607f6718SJohan Hovold dev_err(&port->dev, "Failed to set line coding: %d\n", ret); 740607f6718SJohan Hovold 741607f6718SJohan Hovold kfree(lc); 742607f6718SJohan Hovold } 743607f6718SJohan Hovold 744607f6718SJohan Hovold static void xr_set_termios(struct tty_struct *tty, 745607f6718SJohan Hovold struct usb_serial_port *port, struct ktermios *old_termios) 746607f6718SJohan Hovold { 747607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 748607f6718SJohan Hovold 749607f6718SJohan Hovold /* 750607f6718SJohan Hovold * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC 751607f6718SJohan Hovold * mode upon receiving CDC requests. 752607f6718SJohan Hovold */ 753607f6718SJohan Hovold if (data->type->set_line_settings) 754607f6718SJohan Hovold data->type->set_line_settings(tty, port, old_termios); 755607f6718SJohan Hovold else 756607f6718SJohan Hovold xr_cdc_set_line_coding(tty, port, old_termios); 757c2d405aaSManivannan Sadhasivam 75855317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 759c2d405aaSManivannan Sadhasivam } 760c2d405aaSManivannan Sadhasivam 761c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 762c2d405aaSManivannan Sadhasivam { 763c2d405aaSManivannan Sadhasivam int ret; 764c2d405aaSManivannan Sadhasivam 765c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 766c2d405aaSManivannan Sadhasivam if (ret) { 767c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 768c2d405aaSManivannan Sadhasivam return ret; 769c2d405aaSManivannan Sadhasivam } 770c2d405aaSManivannan Sadhasivam 771c2d405aaSManivannan Sadhasivam /* Setup termios */ 772c2d405aaSManivannan Sadhasivam if (tty) 773c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 774c2d405aaSManivannan Sadhasivam 775c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 776c2d405aaSManivannan Sadhasivam if (ret) { 777c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 778c2d405aaSManivannan Sadhasivam return ret; 779c2d405aaSManivannan Sadhasivam } 780c2d405aaSManivannan Sadhasivam 781c2d405aaSManivannan Sadhasivam return 0; 782c2d405aaSManivannan Sadhasivam } 783c2d405aaSManivannan Sadhasivam 784c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 785c2d405aaSManivannan Sadhasivam { 786c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 787c2d405aaSManivannan Sadhasivam 788c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 789c2d405aaSManivannan Sadhasivam } 790c2d405aaSManivannan Sadhasivam 791c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 792c2d405aaSManivannan Sadhasivam { 7935fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 7945fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 7955fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 7965fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 7975fec21e7SJohan Hovold struct usb_interface *data; 7985fec21e7SJohan Hovold int ret; 7995fec21e7SJohan Hovold 8005fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 8015fec21e7SJohan Hovold if (ret < 0) 802c2d405aaSManivannan Sadhasivam return -ENODEV; 803c2d405aaSManivannan Sadhasivam 8045fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 8055fec21e7SJohan Hovold if (!desc) 8065fec21e7SJohan Hovold return -ENODEV; 8075fec21e7SJohan Hovold 8085fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 8095fec21e7SJohan Hovold if (!data) 8105fec21e7SJohan Hovold return -ENODEV; 8115fec21e7SJohan Hovold 8125fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 8135fec21e7SJohan Hovold if (ret) 8145fec21e7SJohan Hovold return ret; 8155fec21e7SJohan Hovold 816f865e614SJohan Hovold usb_set_serial_data(serial, (void *)id->driver_info); 817f865e614SJohan Hovold 818c2d405aaSManivannan Sadhasivam return 0; 819c2d405aaSManivannan Sadhasivam } 820c2d405aaSManivannan Sadhasivam 821f865e614SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) 82249036fd0SJohan Hovold { 823607f6718SJohan Hovold u16 mask, mode; 82449036fd0SJohan Hovold int ret; 82549036fd0SJohan Hovold 826607f6718SJohan Hovold /* 827607f6718SJohan Hovold * Configure all pins as GPIO except for Receive and Transmit Toggle. 828607f6718SJohan Hovold */ 82949036fd0SJohan Hovold mode = 0; 830607f6718SJohan Hovold if (type->have_xmit_toggle) 831607f6718SJohan Hovold mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; 832607f6718SJohan Hovold 833f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_mode, mode); 83449036fd0SJohan Hovold if (ret) 83549036fd0SJohan Hovold return ret; 83649036fd0SJohan Hovold 83749036fd0SJohan Hovold /* 83849036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 83949036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 84049036fd0SJohan Hovold */ 841958d6b95SJohan Hovold mask = XR_GPIO_DTR | XR_GPIO_RTS; 842f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_direction, mask); 84349036fd0SJohan Hovold if (ret) 84449036fd0SJohan Hovold return ret; 84549036fd0SJohan Hovold 846f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, mask); 84749036fd0SJohan Hovold if (ret) 84849036fd0SJohan Hovold return ret; 84949036fd0SJohan Hovold 85049036fd0SJohan Hovold return 0; 85149036fd0SJohan Hovold } 85249036fd0SJohan Hovold 85323b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 85423b7998eSJohan Hovold { 85523b7998eSJohan Hovold struct usb_interface_descriptor *desc; 856f865e614SJohan Hovold const struct xr_type *type; 85723b7998eSJohan Hovold struct xr_data *data; 858f865e614SJohan Hovold enum xr_type_id type_id; 85949036fd0SJohan Hovold int ret; 86023b7998eSJohan Hovold 861f865e614SJohan Hovold type_id = (int)(unsigned long)usb_get_serial_data(port->serial); 862f865e614SJohan Hovold type = &xr_types[type_id]; 863f865e614SJohan Hovold 86423b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 86523b7998eSJohan Hovold if (!data) 86623b7998eSJohan Hovold return -ENOMEM; 86723b7998eSJohan Hovold 868f865e614SJohan Hovold data->type = type; 869f865e614SJohan Hovold 87023b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 871607f6718SJohan Hovold if (type_id == XR21V141X) 87223b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 873607f6718SJohan Hovold else 874607f6718SJohan Hovold data->channel = desc->bInterfaceNumber; 87523b7998eSJohan Hovold 87623b7998eSJohan Hovold usb_set_serial_port_data(port, data); 87723b7998eSJohan Hovold 878607f6718SJohan Hovold if (type->custom_driver) { 879607f6718SJohan Hovold ret = xr_set_reg_uart(port, type->custom_driver, 880607f6718SJohan Hovold XR_CUSTOM_DRIVER_ACTIVE); 881607f6718SJohan Hovold if (ret) 882607f6718SJohan Hovold goto err_free; 883607f6718SJohan Hovold } 884607f6718SJohan Hovold 885f865e614SJohan Hovold ret = xr_gpio_init(port, type); 88649036fd0SJohan Hovold if (ret) 88749036fd0SJohan Hovold goto err_free; 88849036fd0SJohan Hovold 88923b7998eSJohan Hovold return 0; 89049036fd0SJohan Hovold 89149036fd0SJohan Hovold err_free: 89249036fd0SJohan Hovold kfree(data); 89349036fd0SJohan Hovold 89449036fd0SJohan Hovold return ret; 89523b7998eSJohan Hovold } 89623b7998eSJohan Hovold 89723b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 89823b7998eSJohan Hovold { 89923b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 90023b7998eSJohan Hovold 90123b7998eSJohan Hovold kfree(data); 90223b7998eSJohan Hovold } 90323b7998eSJohan Hovold 904f865e614SJohan Hovold #define XR_DEVICE(vid, pid, type) \ 905f865e614SJohan Hovold USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ 906f865e614SJohan Hovold .driver_info = (type) 907f865e614SJohan Hovold 908c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 909f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, 910*4099d4baSJohan Hovold { XR_DEVICE(0x04e2, 0x1411, XR21B1411) }, 911f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, 912f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, 913607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, 914607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, 915607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, 916c2d405aaSManivannan Sadhasivam { } 917c2d405aaSManivannan Sadhasivam }; 918c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 919c2d405aaSManivannan Sadhasivam 920c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 921c2d405aaSManivannan Sadhasivam .driver = { 922c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 923c2d405aaSManivannan Sadhasivam .name = "xr_serial", 924c2d405aaSManivannan Sadhasivam }, 925c2d405aaSManivannan Sadhasivam .id_table = id_table, 926c2d405aaSManivannan Sadhasivam .num_ports = 1, 927c2d405aaSManivannan Sadhasivam .probe = xr_probe, 92823b7998eSJohan Hovold .port_probe = xr_port_probe, 92923b7998eSJohan Hovold .port_remove = xr_port_remove, 930c2d405aaSManivannan Sadhasivam .open = xr_open, 931c2d405aaSManivannan Sadhasivam .close = xr_close, 932c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 933c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 934c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 935c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 936c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 937c2d405aaSManivannan Sadhasivam }; 938c2d405aaSManivannan Sadhasivam 939c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 940c2d405aaSManivannan Sadhasivam &xr_device, NULL 941c2d405aaSManivannan Sadhasivam }; 942c2d405aaSManivannan Sadhasivam 943c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 944c2d405aaSManivannan Sadhasivam 945c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 946c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 947c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 948