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> 6d801c8d4SJohan Hovold * Copyright (c) 2021 Johan Hovold <johan@kernel.org> 7c2d405aaSManivannan Sadhasivam * 8c2d405aaSManivannan Sadhasivam * Based on the initial driver written by Patong Yang: 9c2d405aaSManivannan Sadhasivam * 10c2d405aaSManivannan Sadhasivam * https://lore.kernel.org/r/20180404070634.nhspvmxcjwfgjkcv@advantechmxl-desktop 11c2d405aaSManivannan Sadhasivam * 12c2d405aaSManivannan Sadhasivam * Copyright (c) 2018 Patong Yang <patong.mxl@gmail.com> 13c2d405aaSManivannan Sadhasivam */ 14c2d405aaSManivannan Sadhasivam 15c2d405aaSManivannan Sadhasivam #include <linux/kernel.h> 16c2d405aaSManivannan Sadhasivam #include <linux/module.h> 17c2d405aaSManivannan Sadhasivam #include <linux/slab.h> 18c2d405aaSManivannan Sadhasivam #include <linux/tty.h> 19c2d405aaSManivannan Sadhasivam #include <linux/usb.h> 205fec21e7SJohan Hovold #include <linux/usb/cdc.h> 21c2d405aaSManivannan Sadhasivam #include <linux/usb/serial.h> 22c2d405aaSManivannan Sadhasivam 23c2d405aaSManivannan Sadhasivam struct xr_txrx_clk_mask { 24c2d405aaSManivannan Sadhasivam u16 tx; 25c2d405aaSManivannan Sadhasivam u16 rx0; 26c2d405aaSManivannan Sadhasivam u16 rx1; 27c2d405aaSManivannan Sadhasivam }; 28c2d405aaSManivannan Sadhasivam 29c2d405aaSManivannan Sadhasivam #define XR_INT_OSC_HZ 48000000U 30c2d405aaSManivannan Sadhasivam #define XR21V141X_MIN_SPEED 46U 31c2d405aaSManivannan Sadhasivam #define XR21V141X_MAX_SPEED XR_INT_OSC_HZ 32c2d405aaSManivannan Sadhasivam 33f865e614SJohan Hovold /* XR21V141X register blocks */ 34f865e614SJohan Hovold #define XR21V141X_UART_REG_BLOCK 0 35f865e614SJohan Hovold #define XR21V141X_UM_REG_BLOCK 4 36f865e614SJohan Hovold #define XR21V141X_UART_CUSTOM_BLOCK 0x66 37f865e614SJohan Hovold 38f865e614SJohan Hovold /* XR21V141X UART registers */ 39c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_0 0x04 40c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_1 0x05 41c2d405aaSManivannan Sadhasivam #define XR21V141X_CLOCK_DIVISOR_2 0x06 42c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_0 0x07 43c2d405aaSManivannan Sadhasivam #define XR21V141X_TX_CLOCK_MASK_1 0x08 44c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_0 0x09 45c2d405aaSManivannan Sadhasivam #define XR21V141X_RX_CLOCK_MASK_1 0x0a 46f865e614SJohan Hovold #define XR21V141X_REG_FORMAT 0x0b 47c2d405aaSManivannan Sadhasivam 48f865e614SJohan Hovold /* XR21V141X UART Manager registers */ 49c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_FIFO_ENABLE_REG 0x10 50c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_TX_FIFO 0x01 51c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_ENABLE_RX_FIFO 0x02 52c2d405aaSManivannan Sadhasivam 53c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_RX_FIFO_RESET 0x18 54c2d405aaSManivannan Sadhasivam #define XR21V141X_UM_TX_FIFO_RESET 0x1c 55c2d405aaSManivannan Sadhasivam 56958d6b95SJohan Hovold #define XR_UART_ENABLE_TX 0x1 57958d6b95SJohan Hovold #define XR_UART_ENABLE_RX 0x2 58c2d405aaSManivannan Sadhasivam 59958d6b95SJohan Hovold #define XR_GPIO_RI BIT(0) 60958d6b95SJohan Hovold #define XR_GPIO_CD BIT(1) 61958d6b95SJohan Hovold #define XR_GPIO_DSR BIT(2) 62958d6b95SJohan Hovold #define XR_GPIO_DTR BIT(3) 63958d6b95SJohan Hovold #define XR_GPIO_CTS BIT(4) 64958d6b95SJohan Hovold #define XR_GPIO_RTS BIT(5) 65607f6718SJohan Hovold #define XR_GPIO_CLK BIT(6) 66607f6718SJohan Hovold #define XR_GPIO_XEN BIT(7) 67607f6718SJohan Hovold #define XR_GPIO_TXT BIT(8) 68607f6718SJohan Hovold #define XR_GPIO_RXT BIT(9) 69c2d405aaSManivannan Sadhasivam 70958d6b95SJohan Hovold #define XR_UART_DATA_MASK GENMASK(3, 0) 71958d6b95SJohan Hovold #define XR_UART_DATA_7 0x7 72958d6b95SJohan Hovold #define XR_UART_DATA_8 0x8 73c2d405aaSManivannan Sadhasivam 74958d6b95SJohan Hovold #define XR_UART_PARITY_MASK GENMASK(6, 4) 75958d6b95SJohan Hovold #define XR_UART_PARITY_SHIFT 4 76958d6b95SJohan Hovold #define XR_UART_PARITY_NONE (0x0 << XR_UART_PARITY_SHIFT) 77958d6b95SJohan Hovold #define XR_UART_PARITY_ODD (0x1 << XR_UART_PARITY_SHIFT) 78958d6b95SJohan Hovold #define XR_UART_PARITY_EVEN (0x2 << XR_UART_PARITY_SHIFT) 79958d6b95SJohan Hovold #define XR_UART_PARITY_MARK (0x3 << XR_UART_PARITY_SHIFT) 80958d6b95SJohan Hovold #define XR_UART_PARITY_SPACE (0x4 << XR_UART_PARITY_SHIFT) 81c2d405aaSManivannan Sadhasivam 82958d6b95SJohan Hovold #define XR_UART_STOP_MASK BIT(7) 83958d6b95SJohan Hovold #define XR_UART_STOP_SHIFT 7 84958d6b95SJohan Hovold #define XR_UART_STOP_1 (0x0 << XR_UART_STOP_SHIFT) 85958d6b95SJohan Hovold #define XR_UART_STOP_2 (0x1 << XR_UART_STOP_SHIFT) 86c2d405aaSManivannan Sadhasivam 87958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_NONE 0x0 88958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_HW 0x1 89958d6b95SJohan Hovold #define XR_UART_FLOW_MODE_SW 0x2 90c2d405aaSManivannan Sadhasivam 91607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_MASK GENMASK(2, 0) 92607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RTS_CTS 0x1 93607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_DTR_DSR 0x2 94607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RS485 0x3 95607f6718SJohan Hovold #define XR_GPIO_MODE_SEL_RS485_ADDR 0x4 96*974e2f6aSJarkko Sonninen #define XR_GPIO_MODE_RS485_TX_H 0x8 97607f6718SJohan Hovold #define XR_GPIO_MODE_TX_TOGGLE 0x100 98607f6718SJohan Hovold #define XR_GPIO_MODE_RX_TOGGLE 0x200 99607f6718SJohan Hovold 10006f79d57SJohan Hovold #define XR_FIFO_RESET 0x1 10106f79d57SJohan Hovold 102607f6718SJohan Hovold #define XR_CUSTOM_DRIVER_ACTIVE 0x1 103607f6718SJohan Hovold 104607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port); 105607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port); 10606f79d57SJohan Hovold static int xr21v141x_fifo_reset(struct usb_serial_port *port); 107607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 108f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 109f6d47fe5SIlpo Järvinen const struct ktermios *old_termios); 110c2d405aaSManivannan Sadhasivam 111f865e614SJohan Hovold struct xr_type { 112607f6718SJohan Hovold int reg_width; 113607f6718SJohan Hovold u8 reg_recipient; 114607f6718SJohan Hovold u8 set_reg; 115607f6718SJohan Hovold u8 get_reg; 116607f6718SJohan Hovold 1174099d4baSJohan Hovold u16 uart_enable; 1184099d4baSJohan Hovold u16 flow_control; 1194099d4baSJohan Hovold u16 xon_char; 1204099d4baSJohan Hovold u16 xoff_char; 1214099d4baSJohan Hovold u16 tx_break; 1224099d4baSJohan Hovold u16 gpio_mode; 1234099d4baSJohan Hovold u16 gpio_direction; 1244099d4baSJohan Hovold u16 gpio_set; 1254099d4baSJohan Hovold u16 gpio_clear; 1264099d4baSJohan Hovold u16 gpio_status; 12706f79d57SJohan Hovold u16 tx_fifo_reset; 12806f79d57SJohan Hovold u16 rx_fifo_reset; 1294099d4baSJohan Hovold u16 custom_driver; 130607f6718SJohan Hovold 1314099d4baSJohan Hovold bool have_5_6_bit_mode; 132607f6718SJohan Hovold bool have_xmit_toggle; 133607f6718SJohan Hovold 134607f6718SJohan Hovold int (*enable)(struct usb_serial_port *port); 135607f6718SJohan Hovold int (*disable)(struct usb_serial_port *port); 13606f79d57SJohan Hovold int (*fifo_reset)(struct usb_serial_port *port); 137607f6718SJohan Hovold void (*set_line_settings)(struct tty_struct *tty, 138607f6718SJohan Hovold struct usb_serial_port *port, 139f6d47fe5SIlpo Järvinen const struct ktermios *old_termios); 140f865e614SJohan Hovold }; 141f865e614SJohan Hovold 142f865e614SJohan Hovold enum xr_type_id { 143f865e614SJohan Hovold XR21V141X, 144607f6718SJohan Hovold XR21B142X, 1454099d4baSJohan Hovold XR21B1411, 1466da99f9dSJohan Hovold XR2280X, 147f865e614SJohan Hovold XR_TYPE_COUNT, 148f865e614SJohan Hovold }; 149f865e614SJohan Hovold 150f865e614SJohan Hovold static const struct xr_type xr_types[] = { 151f865e614SJohan Hovold [XR21V141X] = { 152607f6718SJohan Hovold .reg_width = 8, 153607f6718SJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 154607f6718SJohan Hovold .set_reg = 0x00, 155607f6718SJohan Hovold .get_reg = 0x01, 156607f6718SJohan Hovold 157f865e614SJohan Hovold .uart_enable = 0x03, 158f865e614SJohan Hovold .flow_control = 0x0c, 159f865e614SJohan Hovold .xon_char = 0x10, 160f865e614SJohan Hovold .xoff_char = 0x11, 161f865e614SJohan Hovold .tx_break = 0x14, 162f865e614SJohan Hovold .gpio_mode = 0x1a, 163f865e614SJohan Hovold .gpio_direction = 0x1b, 164f865e614SJohan Hovold .gpio_set = 0x1d, 165f865e614SJohan Hovold .gpio_clear = 0x1e, 166f865e614SJohan Hovold .gpio_status = 0x1f, 167607f6718SJohan Hovold 168607f6718SJohan Hovold .enable = xr21v141x_uart_enable, 169607f6718SJohan Hovold .disable = xr21v141x_uart_disable, 17006f79d57SJohan Hovold .fifo_reset = xr21v141x_fifo_reset, 171607f6718SJohan Hovold .set_line_settings = xr21v141x_set_line_settings, 172607f6718SJohan Hovold }, 173607f6718SJohan Hovold [XR21B142X] = { 174607f6718SJohan Hovold .reg_width = 16, 175607f6718SJohan Hovold .reg_recipient = USB_RECIP_INTERFACE, 176607f6718SJohan Hovold .set_reg = 0x00, 177607f6718SJohan Hovold .get_reg = 0x00, 178607f6718SJohan Hovold 179607f6718SJohan Hovold .uart_enable = 0x00, 180607f6718SJohan Hovold .flow_control = 0x06, 181607f6718SJohan Hovold .xon_char = 0x07, 182607f6718SJohan Hovold .xoff_char = 0x08, 183607f6718SJohan Hovold .tx_break = 0x0a, 184607f6718SJohan Hovold .gpio_mode = 0x0c, 185607f6718SJohan Hovold .gpio_direction = 0x0d, 186607f6718SJohan Hovold .gpio_set = 0x0e, 187607f6718SJohan Hovold .gpio_clear = 0x0f, 188607f6718SJohan Hovold .gpio_status = 0x10, 18906f79d57SJohan Hovold .tx_fifo_reset = 0x40, 19006f79d57SJohan Hovold .rx_fifo_reset = 0x43, 191607f6718SJohan Hovold .custom_driver = 0x60, 192607f6718SJohan Hovold 1934099d4baSJohan Hovold .have_5_6_bit_mode = true, 194607f6718SJohan Hovold .have_xmit_toggle = true, 195f865e614SJohan Hovold }, 1964099d4baSJohan Hovold [XR21B1411] = { 1974099d4baSJohan Hovold .reg_width = 12, 1984099d4baSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 1994099d4baSJohan Hovold .set_reg = 0x00, 2004099d4baSJohan Hovold .get_reg = 0x01, 2014099d4baSJohan Hovold 2024099d4baSJohan Hovold .uart_enable = 0xc00, 2034099d4baSJohan Hovold .flow_control = 0xc06, 2044099d4baSJohan Hovold .xon_char = 0xc07, 2054099d4baSJohan Hovold .xoff_char = 0xc08, 2064099d4baSJohan Hovold .tx_break = 0xc0a, 2074099d4baSJohan Hovold .gpio_mode = 0xc0c, 2084099d4baSJohan Hovold .gpio_direction = 0xc0d, 2094099d4baSJohan Hovold .gpio_set = 0xc0e, 2104099d4baSJohan Hovold .gpio_clear = 0xc0f, 2114099d4baSJohan Hovold .gpio_status = 0xc10, 21206f79d57SJohan Hovold .tx_fifo_reset = 0xc80, 21306f79d57SJohan Hovold .rx_fifo_reset = 0xcc0, 2144099d4baSJohan Hovold .custom_driver = 0x20d, 2154099d4baSJohan Hovold }, 2166da99f9dSJohan Hovold [XR2280X] = { 2176da99f9dSJohan Hovold .reg_width = 16, 2186da99f9dSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 2196da99f9dSJohan Hovold .set_reg = 0x05, 2206da99f9dSJohan Hovold .get_reg = 0x05, 2216da99f9dSJohan Hovold 2226da99f9dSJohan Hovold .uart_enable = 0x40, 2236da99f9dSJohan Hovold .flow_control = 0x46, 2246da99f9dSJohan Hovold .xon_char = 0x47, 2256da99f9dSJohan Hovold .xoff_char = 0x48, 2266da99f9dSJohan Hovold .tx_break = 0x4a, 2276da99f9dSJohan Hovold .gpio_mode = 0x4c, 2286da99f9dSJohan Hovold .gpio_direction = 0x4d, 2296da99f9dSJohan Hovold .gpio_set = 0x4e, 2306da99f9dSJohan Hovold .gpio_clear = 0x4f, 2316da99f9dSJohan Hovold .gpio_status = 0x50, 23206f79d57SJohan Hovold .tx_fifo_reset = 0x60, 23306f79d57SJohan Hovold .rx_fifo_reset = 0x63, 2346da99f9dSJohan Hovold .custom_driver = 0x81, 2356da99f9dSJohan Hovold }, 236f865e614SJohan Hovold }; 237c2d405aaSManivannan Sadhasivam 23823b7998eSJohan Hovold struct xr_data { 239f865e614SJohan Hovold const struct xr_type *type; 240607f6718SJohan Hovold u8 channel; /* zero-based index or interface number */ 241*974e2f6aSJarkko Sonninen struct serial_rs485 rs485; 24223b7998eSJohan Hovold }; 24323b7998eSJohan Hovold 2444099d4baSJohan Hovold static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val) 245c2d405aaSManivannan Sadhasivam { 246607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 247607f6718SJohan Hovold const struct xr_type *type = data->type; 248c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 249c2d405aaSManivannan Sadhasivam int ret; 250c2d405aaSManivannan Sadhasivam 251607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 252607f6718SJohan Hovold type->set_reg, 253607f6718SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, 254607f6718SJohan Hovold val, (channel << 8) | reg, NULL, 0, 255c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 256c2d405aaSManivannan Sadhasivam if (ret < 0) { 257c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 258c2d405aaSManivannan Sadhasivam return ret; 259c2d405aaSManivannan Sadhasivam } 260c2d405aaSManivannan Sadhasivam 261c2d405aaSManivannan Sadhasivam return 0; 262c2d405aaSManivannan Sadhasivam } 263c2d405aaSManivannan Sadhasivam 2644099d4baSJohan Hovold static int xr_get_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 *val) 265c2d405aaSManivannan Sadhasivam { 266607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 267607f6718SJohan Hovold const struct xr_type *type = data->type; 268c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 269c2d405aaSManivannan Sadhasivam u8 *dmabuf; 270607f6718SJohan Hovold int ret, len; 271c2d405aaSManivannan Sadhasivam 272607f6718SJohan Hovold if (type->reg_width == 8) 273607f6718SJohan Hovold len = 1; 274607f6718SJohan Hovold else 275607f6718SJohan Hovold len = 2; 276607f6718SJohan Hovold 277607f6718SJohan Hovold dmabuf = kmalloc(len, GFP_KERNEL); 278c2d405aaSManivannan Sadhasivam if (!dmabuf) 279c2d405aaSManivannan Sadhasivam return -ENOMEM; 280c2d405aaSManivannan Sadhasivam 281607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 282607f6718SJohan Hovold type->get_reg, 283607f6718SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, 284607f6718SJohan Hovold 0, (channel << 8) | reg, dmabuf, len, 285c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 286607f6718SJohan Hovold if (ret == len) { 287607f6718SJohan Hovold if (len == 2) 288607f6718SJohan Hovold *val = le16_to_cpup((__le16 *)dmabuf); 289607f6718SJohan Hovold else 290c2d405aaSManivannan Sadhasivam *val = *dmabuf; 291c2d405aaSManivannan Sadhasivam ret = 0; 292c2d405aaSManivannan Sadhasivam } else { 293c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 294c2d405aaSManivannan Sadhasivam if (ret >= 0) 295c2d405aaSManivannan Sadhasivam ret = -EIO; 296c2d405aaSManivannan Sadhasivam } 297c2d405aaSManivannan Sadhasivam 298c2d405aaSManivannan Sadhasivam kfree(dmabuf); 299c2d405aaSManivannan Sadhasivam 300c2d405aaSManivannan Sadhasivam return ret; 301c2d405aaSManivannan Sadhasivam } 302c2d405aaSManivannan Sadhasivam 3034099d4baSJohan Hovold static int xr_set_reg_uart(struct usb_serial_port *port, u16 reg, u16 val) 304c2d405aaSManivannan Sadhasivam { 30523b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 30623b7998eSJohan Hovold 307607f6718SJohan Hovold return xr_set_reg(port, data->channel, reg, val); 308c2d405aaSManivannan Sadhasivam } 309c2d405aaSManivannan Sadhasivam 3104099d4baSJohan Hovold static int xr_get_reg_uart(struct usb_serial_port *port, u16 reg, u16 *val) 311c2d405aaSManivannan Sadhasivam { 31223b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 31323b7998eSJohan Hovold 314607f6718SJohan Hovold return xr_get_reg(port, data->channel, reg, val); 315c2d405aaSManivannan Sadhasivam } 316c2d405aaSManivannan Sadhasivam 31723b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 318c2d405aaSManivannan Sadhasivam { 31923b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 32023b7998eSJohan Hovold u8 reg; 32123b7998eSJohan Hovold 32223b7998eSJohan Hovold reg = reg_base + data->channel; 32323b7998eSJohan Hovold 324c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 325c2d405aaSManivannan Sadhasivam } 326c2d405aaSManivannan Sadhasivam 327607f6718SJohan Hovold static int __xr_uart_enable(struct usb_serial_port *port) 328607f6718SJohan Hovold { 329607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 330607f6718SJohan Hovold 331607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 332607f6718SJohan Hovold XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); 333607f6718SJohan Hovold } 334607f6718SJohan Hovold 335607f6718SJohan Hovold static int __xr_uart_disable(struct usb_serial_port *port) 336607f6718SJohan Hovold { 337607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 338607f6718SJohan Hovold 339607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 0); 340607f6718SJohan Hovold } 341607f6718SJohan Hovold 342c2d405aaSManivannan Sadhasivam /* 343c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 344c2d405aaSManivannan Sadhasivam * module in XR21V141X: 345c2d405aaSManivannan Sadhasivam * 346c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 347c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 348c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 349c2d405aaSManivannan Sadhasivam */ 350607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port) 351c2d405aaSManivannan Sadhasivam { 352c2d405aaSManivannan Sadhasivam int ret; 353c2d405aaSManivannan Sadhasivam 354c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 355c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 356c2d405aaSManivannan Sadhasivam if (ret) 357c2d405aaSManivannan Sadhasivam return ret; 358c2d405aaSManivannan Sadhasivam 359607f6718SJohan Hovold ret = __xr_uart_enable(port); 360c2d405aaSManivannan Sadhasivam if (ret) 361c2d405aaSManivannan Sadhasivam return ret; 362c2d405aaSManivannan Sadhasivam 363c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 364c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 365c2d405aaSManivannan Sadhasivam if (ret) 366607f6718SJohan Hovold __xr_uart_disable(port); 367c2d405aaSManivannan Sadhasivam 368c2d405aaSManivannan Sadhasivam return ret; 369c2d405aaSManivannan Sadhasivam } 370c2d405aaSManivannan Sadhasivam 371607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port) 372c2d405aaSManivannan Sadhasivam { 373c2d405aaSManivannan Sadhasivam int ret; 374c2d405aaSManivannan Sadhasivam 375607f6718SJohan Hovold ret = __xr_uart_disable(port); 376c2d405aaSManivannan Sadhasivam if (ret) 377c2d405aaSManivannan Sadhasivam return ret; 378c2d405aaSManivannan Sadhasivam 379c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 380c2d405aaSManivannan Sadhasivam 381c2d405aaSManivannan Sadhasivam return ret; 382c2d405aaSManivannan Sadhasivam } 383c2d405aaSManivannan Sadhasivam 384607f6718SJohan Hovold static int xr_uart_enable(struct usb_serial_port *port) 385607f6718SJohan Hovold { 386607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 387607f6718SJohan Hovold 388607f6718SJohan Hovold if (data->type->enable) 389607f6718SJohan Hovold return data->type->enable(port); 390607f6718SJohan Hovold 391607f6718SJohan Hovold return __xr_uart_enable(port); 392607f6718SJohan Hovold } 393607f6718SJohan Hovold 394607f6718SJohan Hovold static int xr_uart_disable(struct usb_serial_port *port) 395607f6718SJohan Hovold { 396607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 397607f6718SJohan Hovold 398607f6718SJohan Hovold if (data->type->disable) 399607f6718SJohan Hovold return data->type->disable(port); 400607f6718SJohan Hovold 401607f6718SJohan Hovold return __xr_uart_disable(port); 402607f6718SJohan Hovold } 403607f6718SJohan Hovold 40406f79d57SJohan Hovold static int xr21v141x_fifo_reset(struct usb_serial_port *port) 40506f79d57SJohan Hovold { 40606f79d57SJohan Hovold int ret; 40706f79d57SJohan Hovold 40806f79d57SJohan Hovold ret = xr_set_reg_um(port, XR21V141X_UM_TX_FIFO_RESET, XR_FIFO_RESET); 40906f79d57SJohan Hovold if (ret) 41006f79d57SJohan Hovold return ret; 41106f79d57SJohan Hovold 41206f79d57SJohan Hovold ret = xr_set_reg_um(port, XR21V141X_UM_RX_FIFO_RESET, XR_FIFO_RESET); 41306f79d57SJohan Hovold if (ret) 41406f79d57SJohan Hovold return ret; 41506f79d57SJohan Hovold 41606f79d57SJohan Hovold return 0; 41706f79d57SJohan Hovold } 41806f79d57SJohan Hovold 41906f79d57SJohan Hovold static int xr_fifo_reset(struct usb_serial_port *port) 42006f79d57SJohan Hovold { 42106f79d57SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 42206f79d57SJohan Hovold int ret; 42306f79d57SJohan Hovold 42406f79d57SJohan Hovold if (data->type->fifo_reset) 42506f79d57SJohan Hovold return data->type->fifo_reset(port); 42606f79d57SJohan Hovold 42706f79d57SJohan Hovold ret = xr_set_reg_uart(port, data->type->tx_fifo_reset, XR_FIFO_RESET); 42806f79d57SJohan Hovold if (ret) 42906f79d57SJohan Hovold return ret; 43006f79d57SJohan Hovold 43106f79d57SJohan Hovold ret = xr_set_reg_uart(port, data->type->rx_fifo_reset, XR_FIFO_RESET); 43206f79d57SJohan Hovold if (ret) 43306f79d57SJohan Hovold return ret; 43406f79d57SJohan Hovold 43506f79d57SJohan Hovold return 0; 43606f79d57SJohan Hovold } 43706f79d57SJohan Hovold 438c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 439c2d405aaSManivannan Sadhasivam { 440c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 441f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 442607f6718SJohan Hovold u16 status; 443c2d405aaSManivannan Sadhasivam int ret; 444c2d405aaSManivannan Sadhasivam 445f865e614SJohan Hovold ret = xr_get_reg_uart(port, data->type->gpio_status, &status); 446c2d405aaSManivannan Sadhasivam if (ret) 447c2d405aaSManivannan Sadhasivam return ret; 448c2d405aaSManivannan Sadhasivam 449c2d405aaSManivannan Sadhasivam /* 450c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 451c2d405aaSManivannan Sadhasivam * and '1' means not active. 452c2d405aaSManivannan Sadhasivam */ 453958d6b95SJohan Hovold ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | 454958d6b95SJohan Hovold ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | 455958d6b95SJohan Hovold ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | 456958d6b95SJohan Hovold ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | 457958d6b95SJohan Hovold ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | 458958d6b95SJohan Hovold ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); 459c2d405aaSManivannan Sadhasivam 460c2d405aaSManivannan Sadhasivam return ret; 461c2d405aaSManivannan Sadhasivam } 462c2d405aaSManivannan Sadhasivam 463c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 464c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 465c2d405aaSManivannan Sadhasivam { 466f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 467f865e614SJohan Hovold const struct xr_type *type = data->type; 468607f6718SJohan Hovold u16 gpio_set = 0; 469607f6718SJohan Hovold u16 gpio_clr = 0; 470c2d405aaSManivannan Sadhasivam int ret = 0; 471c2d405aaSManivannan Sadhasivam 472c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 473c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 474958d6b95SJohan Hovold gpio_clr |= XR_GPIO_RTS; 475c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 476958d6b95SJohan Hovold gpio_clr |= XR_GPIO_DTR; 477c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 478958d6b95SJohan Hovold gpio_set |= XR_GPIO_RTS; 479c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 480958d6b95SJohan Hovold gpio_set |= XR_GPIO_DTR; 481c2d405aaSManivannan Sadhasivam 482c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 483c2d405aaSManivannan Sadhasivam if (gpio_clr) 484f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); 485c2d405aaSManivannan Sadhasivam 486c2d405aaSManivannan Sadhasivam if (gpio_set) 487f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); 488c2d405aaSManivannan Sadhasivam 489c2d405aaSManivannan Sadhasivam return ret; 490c2d405aaSManivannan Sadhasivam } 491c2d405aaSManivannan Sadhasivam 492c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 493c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 494c2d405aaSManivannan Sadhasivam { 495c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 496c2d405aaSManivannan Sadhasivam 497c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 498c2d405aaSManivannan Sadhasivam } 499c2d405aaSManivannan Sadhasivam 500c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 501c2d405aaSManivannan Sadhasivam { 502c2d405aaSManivannan Sadhasivam if (on) 503c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 504c2d405aaSManivannan Sadhasivam else 505c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 506c2d405aaSManivannan Sadhasivam } 507c2d405aaSManivannan Sadhasivam 5086ff58ae1SJohan Hovold static int xr_break_ctl(struct tty_struct *tty, int break_state) 509c2d405aaSManivannan Sadhasivam { 510c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 511f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 512f865e614SJohan Hovold const struct xr_type *type = data->type; 513607f6718SJohan Hovold u16 state; 514c2d405aaSManivannan Sadhasivam 515c2d405aaSManivannan Sadhasivam if (break_state == 0) 516607f6718SJohan Hovold state = 0; 517c2d405aaSManivannan Sadhasivam else 518607f6718SJohan Hovold state = GENMASK(type->reg_width - 1, 0); 519c2d405aaSManivannan Sadhasivam 520607f6718SJohan Hovold dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); 521607f6718SJohan Hovold 5226ff58ae1SJohan Hovold return xr_set_reg_uart(port, type->tx_break, state); 523c2d405aaSManivannan Sadhasivam } 524c2d405aaSManivannan Sadhasivam 525c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 526c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 527c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 528c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 529c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 530c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 531c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 532c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 533c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 534c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 535c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 536c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 537c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 538c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 539c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 540c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 541c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 542c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 543c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 544c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 545c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 546c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 547c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 548c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 549c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 550c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 551c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 552c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 553c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 554c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 555c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 556c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 557c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 558c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 559c2d405aaSManivannan Sadhasivam }; 560c2d405aaSManivannan Sadhasivam 561607f6718SJohan Hovold static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) 562c2d405aaSManivannan Sadhasivam { 563c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 564c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 565c2d405aaSManivannan Sadhasivam int ret; 566c2d405aaSManivannan Sadhasivam 56755317e22SJohan Hovold baud = tty->termios.c_ospeed; 56855317e22SJohan Hovold if (!baud) 56955317e22SJohan Hovold return 0; 57055317e22SJohan Hovold 57155317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 572c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 573c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 574c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 575c2d405aaSManivannan Sadhasivam 576c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 577c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 578c2d405aaSManivannan Sadhasivam else 579c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 580c2d405aaSManivannan Sadhasivam 581c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 582c2d405aaSManivannan Sadhasivam /* 583c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 584c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 585c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 586c2d405aaSManivannan Sadhasivam */ 587c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 588c2d405aaSManivannan Sadhasivam divisor & 0xff); 589c2d405aaSManivannan Sadhasivam if (ret) 590c2d405aaSManivannan Sadhasivam return ret; 591c2d405aaSManivannan Sadhasivam 592c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 593c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 594c2d405aaSManivannan Sadhasivam if (ret) 595c2d405aaSManivannan Sadhasivam return ret; 596c2d405aaSManivannan Sadhasivam 597c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 598c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 599c2d405aaSManivannan Sadhasivam if (ret) 600c2d405aaSManivannan Sadhasivam return ret; 601c2d405aaSManivannan Sadhasivam 602c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 603c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 604c2d405aaSManivannan Sadhasivam if (ret) 605c2d405aaSManivannan Sadhasivam return ret; 606c2d405aaSManivannan Sadhasivam 607c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 608c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 609c2d405aaSManivannan Sadhasivam if (ret) 610c2d405aaSManivannan Sadhasivam return ret; 611c2d405aaSManivannan Sadhasivam 612c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 613c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 614c2d405aaSManivannan Sadhasivam if (ret) 615c2d405aaSManivannan Sadhasivam return ret; 616c2d405aaSManivannan Sadhasivam 617c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 618c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 619c2d405aaSManivannan Sadhasivam if (ret) 620c2d405aaSManivannan Sadhasivam return ret; 621c2d405aaSManivannan Sadhasivam 622c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 623c2d405aaSManivannan Sadhasivam 624c2d405aaSManivannan Sadhasivam return 0; 625c2d405aaSManivannan Sadhasivam } 626c2d405aaSManivannan Sadhasivam 627c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 62855317e22SJohan Hovold struct usb_serial_port *port, 629f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 630c2d405aaSManivannan Sadhasivam { 631f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 632f865e614SJohan Hovold const struct xr_type *type = data->type; 633607f6718SJohan Hovold u16 flow, gpio_mode; 634*974e2f6aSJarkko Sonninen bool rs485_enabled; 635c2d405aaSManivannan Sadhasivam int ret; 636c2d405aaSManivannan Sadhasivam 637f865e614SJohan Hovold ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); 638c2d405aaSManivannan Sadhasivam if (ret) 639c2d405aaSManivannan Sadhasivam return; 640c2d405aaSManivannan Sadhasivam 641607f6718SJohan Hovold /* 642607f6718SJohan Hovold * According to the datasheets, the UART needs to be disabled while 643607f6718SJohan Hovold * writing to the FLOW_CONTROL register (XR21V141X), or any register 644607f6718SJohan Hovold * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). 645607f6718SJohan Hovold */ 646607f6718SJohan Hovold xr_uart_disable(port); 647607f6718SJohan Hovold 648465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 649607f6718SJohan Hovold gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; 650465d3b3aSJohan Hovold 651*974e2f6aSJarkko Sonninen rs485_enabled = !!(data->rs485.flags & SER_RS485_ENABLED); 652*974e2f6aSJarkko Sonninen if (rs485_enabled) { 653*974e2f6aSJarkko Sonninen dev_dbg(&port->dev, "Enabling RS-485\n"); 654*974e2f6aSJarkko Sonninen gpio_mode |= XR_GPIO_MODE_SEL_RS485; 655*974e2f6aSJarkko Sonninen if (data->rs485.flags & SER_RS485_RTS_ON_SEND) 656*974e2f6aSJarkko Sonninen gpio_mode &= ~XR_GPIO_MODE_RS485_TX_H; 657*974e2f6aSJarkko Sonninen else 658*974e2f6aSJarkko Sonninen gpio_mode |= XR_GPIO_MODE_RS485_TX_H; 659*974e2f6aSJarkko Sonninen } 660*974e2f6aSJarkko Sonninen 661*974e2f6aSJarkko Sonninen if (C_CRTSCTS(tty) && C_BAUD(tty) != B0 && !rs485_enabled) { 662c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 663607f6718SJohan Hovold gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; 664958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_HW; 665c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 666c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 667c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 668c2d405aaSManivannan Sadhasivam 669c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 670958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_SW; 671c2d405aaSManivannan Sadhasivam 672f865e614SJohan Hovold xr_set_reg_uart(port, type->xon_char, start_char); 673f865e614SJohan Hovold xr_set_reg_uart(port, type->xoff_char, stop_char); 674c2d405aaSManivannan Sadhasivam } else { 675c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 676958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_NONE; 677c2d405aaSManivannan Sadhasivam } 678c2d405aaSManivannan Sadhasivam 679f865e614SJohan Hovold xr_set_reg_uart(port, type->flow_control, flow); 680f865e614SJohan Hovold xr_set_reg_uart(port, type->gpio_mode, gpio_mode); 68155317e22SJohan Hovold 682607f6718SJohan Hovold xr_uart_enable(port); 683607f6718SJohan Hovold 68455317e22SJohan Hovold if (C_BAUD(tty) == B0) 68555317e22SJohan Hovold xr_dtr_rts(port, 0); 68655317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 68755317e22SJohan Hovold xr_dtr_rts(port, 1); 688c2d405aaSManivannan Sadhasivam } 689c2d405aaSManivannan Sadhasivam 690607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 691f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 692f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 693c2d405aaSManivannan Sadhasivam { 694c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 695c2d405aaSManivannan Sadhasivam u8 bits = 0; 696c2d405aaSManivannan Sadhasivam int ret; 697c2d405aaSManivannan Sadhasivam 698736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 699607f6718SJohan Hovold xr21v141x_set_baudrate(tty, port); 700c2d405aaSManivannan Sadhasivam 701c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 702c2d405aaSManivannan Sadhasivam case CS5: 703c2d405aaSManivannan Sadhasivam case CS6: 704c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 705c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 706c2d405aaSManivannan Sadhasivam if (old_termios) 707c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 708c2d405aaSManivannan Sadhasivam else 709ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 710ea7ada4dSJohan Hovold 711ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 712958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 713ea7ada4dSJohan Hovold else 714958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 715c2d405aaSManivannan Sadhasivam break; 716c2d405aaSManivannan Sadhasivam case CS7: 717958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 718c2d405aaSManivannan Sadhasivam break; 719c2d405aaSManivannan Sadhasivam case CS8: 720c2d405aaSManivannan Sadhasivam default: 721958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 722c2d405aaSManivannan Sadhasivam break; 723c2d405aaSManivannan Sadhasivam } 724c2d405aaSManivannan Sadhasivam 725c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 726c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 727c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 728958d6b95SJohan Hovold bits |= XR_UART_PARITY_MARK; 729c2d405aaSManivannan Sadhasivam else 730958d6b95SJohan Hovold bits |= XR_UART_PARITY_SPACE; 731c2d405aaSManivannan Sadhasivam } else { 732c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 733958d6b95SJohan Hovold bits |= XR_UART_PARITY_ODD; 734c2d405aaSManivannan Sadhasivam else 735958d6b95SJohan Hovold bits |= XR_UART_PARITY_EVEN; 736c2d405aaSManivannan Sadhasivam } 737c2d405aaSManivannan Sadhasivam } 738c2d405aaSManivannan Sadhasivam 739c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 740958d6b95SJohan Hovold bits |= XR_UART_STOP_2; 741c2d405aaSManivannan Sadhasivam else 742958d6b95SJohan Hovold bits |= XR_UART_STOP_1; 743c2d405aaSManivannan Sadhasivam 744c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 745c2d405aaSManivannan Sadhasivam if (ret) 746c2d405aaSManivannan Sadhasivam return; 747607f6718SJohan Hovold } 748607f6718SJohan Hovold 749607f6718SJohan Hovold static void xr_cdc_set_line_coding(struct tty_struct *tty, 750f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 751f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 752607f6718SJohan Hovold { 7534099d4baSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 754607f6718SJohan Hovold struct usb_host_interface *alt = port->serial->interface->cur_altsetting; 755607f6718SJohan Hovold struct usb_device *udev = port->serial->dev; 756607f6718SJohan Hovold struct usb_cdc_line_coding *lc; 757607f6718SJohan Hovold int ret; 758607f6718SJohan Hovold 759607f6718SJohan Hovold lc = kzalloc(sizeof(*lc), GFP_KERNEL); 760607f6718SJohan Hovold if (!lc) 761607f6718SJohan Hovold return; 762607f6718SJohan Hovold 763607f6718SJohan Hovold if (tty->termios.c_ospeed) 764607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); 765607f6718SJohan Hovold else 766607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(9600); 767607f6718SJohan Hovold 768607f6718SJohan Hovold if (C_CSTOPB(tty)) 769607f6718SJohan Hovold lc->bCharFormat = USB_CDC_2_STOP_BITS; 770607f6718SJohan Hovold else 771607f6718SJohan Hovold lc->bCharFormat = USB_CDC_1_STOP_BITS; 772607f6718SJohan Hovold 773607f6718SJohan Hovold if (C_PARENB(tty)) { 774607f6718SJohan Hovold if (C_CMSPAR(tty)) { 775607f6718SJohan Hovold if (C_PARODD(tty)) 776607f6718SJohan Hovold lc->bParityType = USB_CDC_MARK_PARITY; 777607f6718SJohan Hovold else 778607f6718SJohan Hovold lc->bParityType = USB_CDC_SPACE_PARITY; 779607f6718SJohan Hovold } else { 780607f6718SJohan Hovold if (C_PARODD(tty)) 781607f6718SJohan Hovold lc->bParityType = USB_CDC_ODD_PARITY; 782607f6718SJohan Hovold else 783607f6718SJohan Hovold lc->bParityType = USB_CDC_EVEN_PARITY; 784607f6718SJohan Hovold } 785607f6718SJohan Hovold } else { 786607f6718SJohan Hovold lc->bParityType = USB_CDC_NO_PARITY; 787607f6718SJohan Hovold } 788607f6718SJohan Hovold 7894099d4baSJohan Hovold if (!data->type->have_5_6_bit_mode && 7904099d4baSJohan Hovold (C_CSIZE(tty) == CS5 || C_CSIZE(tty) == CS6)) { 7914099d4baSJohan Hovold tty->termios.c_cflag &= ~CSIZE; 7924099d4baSJohan Hovold if (old_termios) 7934099d4baSJohan Hovold tty->termios.c_cflag |= old_termios->c_cflag & CSIZE; 7944099d4baSJohan Hovold else 7954099d4baSJohan Hovold tty->termios.c_cflag |= CS8; 7964099d4baSJohan Hovold } 7974099d4baSJohan Hovold 798607f6718SJohan Hovold switch (C_CSIZE(tty)) { 799607f6718SJohan Hovold case CS5: 800607f6718SJohan Hovold lc->bDataBits = 5; 801607f6718SJohan Hovold break; 802607f6718SJohan Hovold case CS6: 803607f6718SJohan Hovold lc->bDataBits = 6; 804607f6718SJohan Hovold break; 805607f6718SJohan Hovold case CS7: 806607f6718SJohan Hovold lc->bDataBits = 7; 807607f6718SJohan Hovold break; 808607f6718SJohan Hovold case CS8: 809607f6718SJohan Hovold default: 810607f6718SJohan Hovold lc->bDataBits = 8; 811607f6718SJohan Hovold break; 812607f6718SJohan Hovold } 813607f6718SJohan Hovold 814607f6718SJohan Hovold ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 815607f6718SJohan Hovold USB_CDC_REQ_SET_LINE_CODING, 816607f6718SJohan Hovold USB_TYPE_CLASS | USB_RECIP_INTERFACE, 817607f6718SJohan Hovold 0, alt->desc.bInterfaceNumber, 818607f6718SJohan Hovold lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); 819607f6718SJohan Hovold if (ret < 0) 820607f6718SJohan Hovold dev_err(&port->dev, "Failed to set line coding: %d\n", ret); 821607f6718SJohan Hovold 822607f6718SJohan Hovold kfree(lc); 823607f6718SJohan Hovold } 824607f6718SJohan Hovold 825*974e2f6aSJarkko Sonninen static void xr_sanitize_serial_rs485(struct serial_rs485 *rs485) 826*974e2f6aSJarkko Sonninen { 827*974e2f6aSJarkko Sonninen if (!(rs485->flags & SER_RS485_ENABLED)) { 828*974e2f6aSJarkko Sonninen memset(rs485, 0, sizeof(*rs485)); 829*974e2f6aSJarkko Sonninen return; 830*974e2f6aSJarkko Sonninen } 831*974e2f6aSJarkko Sonninen 832*974e2f6aSJarkko Sonninen /* RTS always toggles after TX */ 833*974e2f6aSJarkko Sonninen if (rs485->flags & SER_RS485_RTS_ON_SEND) 834*974e2f6aSJarkko Sonninen rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; 835*974e2f6aSJarkko Sonninen else 836*974e2f6aSJarkko Sonninen rs485->flags |= SER_RS485_RTS_AFTER_SEND; 837*974e2f6aSJarkko Sonninen 838*974e2f6aSJarkko Sonninen /* Only the flags are implemented at the moment */ 839*974e2f6aSJarkko Sonninen rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | 840*974e2f6aSJarkko Sonninen SER_RS485_RTS_AFTER_SEND; 841*974e2f6aSJarkko Sonninen rs485->delay_rts_before_send = 0; 842*974e2f6aSJarkko Sonninen rs485->delay_rts_after_send = 0; 843*974e2f6aSJarkko Sonninen memset(rs485->padding, 0, sizeof(rs485->padding)); 844*974e2f6aSJarkko Sonninen } 845*974e2f6aSJarkko Sonninen 846*974e2f6aSJarkko Sonninen static int xr_get_rs485_config(struct tty_struct *tty, 847*974e2f6aSJarkko Sonninen struct serial_rs485 __user *argp) 848*974e2f6aSJarkko Sonninen { 849*974e2f6aSJarkko Sonninen struct usb_serial_port *port = tty->driver_data; 850*974e2f6aSJarkko Sonninen struct xr_data *data = usb_get_serial_port_data(port); 851*974e2f6aSJarkko Sonninen 852*974e2f6aSJarkko Sonninen down_read(&tty->termios_rwsem); 853*974e2f6aSJarkko Sonninen if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) { 854*974e2f6aSJarkko Sonninen up_read(&tty->termios_rwsem); 855*974e2f6aSJarkko Sonninen return -EFAULT; 856*974e2f6aSJarkko Sonninen } 857*974e2f6aSJarkko Sonninen up_read(&tty->termios_rwsem); 858*974e2f6aSJarkko Sonninen 859*974e2f6aSJarkko Sonninen return 0; 860*974e2f6aSJarkko Sonninen } 861*974e2f6aSJarkko Sonninen 862*974e2f6aSJarkko Sonninen static int xr_set_rs485_config(struct tty_struct *tty, 863*974e2f6aSJarkko Sonninen struct serial_rs485 __user *argp) 864*974e2f6aSJarkko Sonninen { 865*974e2f6aSJarkko Sonninen struct usb_serial_port *port = tty->driver_data; 866*974e2f6aSJarkko Sonninen struct xr_data *data = usb_get_serial_port_data(port); 867*974e2f6aSJarkko Sonninen struct serial_rs485 rs485; 868*974e2f6aSJarkko Sonninen 869*974e2f6aSJarkko Sonninen if (copy_from_user(&rs485, argp, sizeof(rs485))) 870*974e2f6aSJarkko Sonninen return -EFAULT; 871*974e2f6aSJarkko Sonninen xr_sanitize_serial_rs485(&rs485); 872*974e2f6aSJarkko Sonninen 873*974e2f6aSJarkko Sonninen down_write(&tty->termios_rwsem); 874*974e2f6aSJarkko Sonninen data->rs485 = rs485; 875*974e2f6aSJarkko Sonninen xr_set_flow_mode(tty, port, NULL); 876*974e2f6aSJarkko Sonninen up_write(&tty->termios_rwsem); 877*974e2f6aSJarkko Sonninen 878*974e2f6aSJarkko Sonninen if (copy_to_user(argp, &rs485, sizeof(rs485))) 879*974e2f6aSJarkko Sonninen return -EFAULT; 880*974e2f6aSJarkko Sonninen 881*974e2f6aSJarkko Sonninen return 0; 882*974e2f6aSJarkko Sonninen } 883*974e2f6aSJarkko Sonninen 884*974e2f6aSJarkko Sonninen static int xr_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) 885*974e2f6aSJarkko Sonninen { 886*974e2f6aSJarkko Sonninen void __user *argp = (void __user *)arg; 887*974e2f6aSJarkko Sonninen 888*974e2f6aSJarkko Sonninen switch (cmd) { 889*974e2f6aSJarkko Sonninen case TIOCGRS485: 890*974e2f6aSJarkko Sonninen return xr_get_rs485_config(tty, argp); 891*974e2f6aSJarkko Sonninen case TIOCSRS485: 892*974e2f6aSJarkko Sonninen return xr_set_rs485_config(tty, argp); 893*974e2f6aSJarkko Sonninen } 894*974e2f6aSJarkko Sonninen 895*974e2f6aSJarkko Sonninen return -ENOIOCTLCMD; 896*974e2f6aSJarkko Sonninen } 897*974e2f6aSJarkko Sonninen 898607f6718SJohan Hovold static void xr_set_termios(struct tty_struct *tty, 899f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 900f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 901607f6718SJohan Hovold { 902607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 903607f6718SJohan Hovold 904607f6718SJohan Hovold /* 905607f6718SJohan Hovold * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC 906607f6718SJohan Hovold * mode upon receiving CDC requests. 907607f6718SJohan Hovold */ 908607f6718SJohan Hovold if (data->type->set_line_settings) 909607f6718SJohan Hovold data->type->set_line_settings(tty, port, old_termios); 910607f6718SJohan Hovold else 911607f6718SJohan Hovold xr_cdc_set_line_coding(tty, port, old_termios); 912c2d405aaSManivannan Sadhasivam 91355317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 914c2d405aaSManivannan Sadhasivam } 915c2d405aaSManivannan Sadhasivam 916c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 917c2d405aaSManivannan Sadhasivam { 918c2d405aaSManivannan Sadhasivam int ret; 919c2d405aaSManivannan Sadhasivam 92006f79d57SJohan Hovold ret = xr_fifo_reset(port); 92106f79d57SJohan Hovold if (ret) 92206f79d57SJohan Hovold return ret; 92306f79d57SJohan Hovold 924c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 925c2d405aaSManivannan Sadhasivam if (ret) { 926c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 927c2d405aaSManivannan Sadhasivam return ret; 928c2d405aaSManivannan Sadhasivam } 929c2d405aaSManivannan Sadhasivam 930c2d405aaSManivannan Sadhasivam /* Setup termios */ 931c2d405aaSManivannan Sadhasivam if (tty) 932c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 933c2d405aaSManivannan Sadhasivam 934c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 935c2d405aaSManivannan Sadhasivam if (ret) { 936c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 937c2d405aaSManivannan Sadhasivam return ret; 938c2d405aaSManivannan Sadhasivam } 939c2d405aaSManivannan Sadhasivam 940c2d405aaSManivannan Sadhasivam return 0; 941c2d405aaSManivannan Sadhasivam } 942c2d405aaSManivannan Sadhasivam 943c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 944c2d405aaSManivannan Sadhasivam { 945c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 946c2d405aaSManivannan Sadhasivam 947c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 948c2d405aaSManivannan Sadhasivam } 949c2d405aaSManivannan Sadhasivam 950c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 951c2d405aaSManivannan Sadhasivam { 9525fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 9535fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 9545fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 9555fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 9565fec21e7SJohan Hovold struct usb_interface *data; 9575fec21e7SJohan Hovold int ret; 9585fec21e7SJohan Hovold 9595fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 9605fec21e7SJohan Hovold if (ret < 0) 961c2d405aaSManivannan Sadhasivam return -ENODEV; 962c2d405aaSManivannan Sadhasivam 9635fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 9645fec21e7SJohan Hovold if (!desc) 9655fec21e7SJohan Hovold return -ENODEV; 9665fec21e7SJohan Hovold 9675fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 9685fec21e7SJohan Hovold if (!data) 9695fec21e7SJohan Hovold return -ENODEV; 9705fec21e7SJohan Hovold 9715fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 9725fec21e7SJohan Hovold if (ret) 9735fec21e7SJohan Hovold return ret; 9745fec21e7SJohan Hovold 975f865e614SJohan Hovold usb_set_serial_data(serial, (void *)id->driver_info); 976f865e614SJohan Hovold 977c2d405aaSManivannan Sadhasivam return 0; 978c2d405aaSManivannan Sadhasivam } 979c2d405aaSManivannan Sadhasivam 980f865e614SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) 98149036fd0SJohan Hovold { 982607f6718SJohan Hovold u16 mask, mode; 98349036fd0SJohan Hovold int ret; 98449036fd0SJohan Hovold 985607f6718SJohan Hovold /* 986607f6718SJohan Hovold * Configure all pins as GPIO except for Receive and Transmit Toggle. 987607f6718SJohan Hovold */ 98849036fd0SJohan Hovold mode = 0; 989607f6718SJohan Hovold if (type->have_xmit_toggle) 990607f6718SJohan Hovold mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; 991607f6718SJohan Hovold 992f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_mode, mode); 99349036fd0SJohan Hovold if (ret) 99449036fd0SJohan Hovold return ret; 99549036fd0SJohan Hovold 99649036fd0SJohan Hovold /* 99749036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 99849036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 99949036fd0SJohan Hovold */ 1000958d6b95SJohan Hovold mask = XR_GPIO_DTR | XR_GPIO_RTS; 1001f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_direction, mask); 100249036fd0SJohan Hovold if (ret) 100349036fd0SJohan Hovold return ret; 100449036fd0SJohan Hovold 1005f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, mask); 100649036fd0SJohan Hovold if (ret) 100749036fd0SJohan Hovold return ret; 100849036fd0SJohan Hovold 100949036fd0SJohan Hovold return 0; 101049036fd0SJohan Hovold } 101149036fd0SJohan Hovold 101223b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 101323b7998eSJohan Hovold { 101423b7998eSJohan Hovold struct usb_interface_descriptor *desc; 1015f865e614SJohan Hovold const struct xr_type *type; 101623b7998eSJohan Hovold struct xr_data *data; 1017f865e614SJohan Hovold enum xr_type_id type_id; 101849036fd0SJohan Hovold int ret; 101923b7998eSJohan Hovold 1020f865e614SJohan Hovold type_id = (int)(unsigned long)usb_get_serial_data(port->serial); 1021f865e614SJohan Hovold type = &xr_types[type_id]; 1022f865e614SJohan Hovold 102323b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 102423b7998eSJohan Hovold if (!data) 102523b7998eSJohan Hovold return -ENOMEM; 102623b7998eSJohan Hovold 1027f865e614SJohan Hovold data->type = type; 1028f865e614SJohan Hovold 102923b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 1030607f6718SJohan Hovold if (type_id == XR21V141X) 103123b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 1032607f6718SJohan Hovold else 1033607f6718SJohan Hovold data->channel = desc->bInterfaceNumber; 103423b7998eSJohan Hovold 103523b7998eSJohan Hovold usb_set_serial_port_data(port, data); 103623b7998eSJohan Hovold 1037607f6718SJohan Hovold if (type->custom_driver) { 1038607f6718SJohan Hovold ret = xr_set_reg_uart(port, type->custom_driver, 1039607f6718SJohan Hovold XR_CUSTOM_DRIVER_ACTIVE); 1040607f6718SJohan Hovold if (ret) 1041607f6718SJohan Hovold goto err_free; 1042607f6718SJohan Hovold } 1043607f6718SJohan Hovold 1044f865e614SJohan Hovold ret = xr_gpio_init(port, type); 104549036fd0SJohan Hovold if (ret) 104649036fd0SJohan Hovold goto err_free; 104749036fd0SJohan Hovold 104823b7998eSJohan Hovold return 0; 104949036fd0SJohan Hovold 105049036fd0SJohan Hovold err_free: 105149036fd0SJohan Hovold kfree(data); 105249036fd0SJohan Hovold 105349036fd0SJohan Hovold return ret; 105423b7998eSJohan Hovold } 105523b7998eSJohan Hovold 105623b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 105723b7998eSJohan Hovold { 105823b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 105923b7998eSJohan Hovold 106023b7998eSJohan Hovold kfree(data); 106123b7998eSJohan Hovold } 106223b7998eSJohan Hovold 1063f865e614SJohan Hovold #define XR_DEVICE(vid, pid, type) \ 1064f865e614SJohan Hovold USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ 1065f865e614SJohan Hovold .driver_info = (type) 1066f865e614SJohan Hovold 1067c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 10686da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1400, XR2280X) }, 10696da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1401, XR2280X) }, 10706da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1402, XR2280X) }, 10716da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1403, XR2280X) }, 1072f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, 10734099d4baSJohan Hovold { XR_DEVICE(0x04e2, 0x1411, XR21B1411) }, 1074f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, 1075f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, 1076607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, 1077607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, 1078607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, 1079c2d405aaSManivannan Sadhasivam { } 1080c2d405aaSManivannan Sadhasivam }; 1081c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 1082c2d405aaSManivannan Sadhasivam 1083c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 1084c2d405aaSManivannan Sadhasivam .driver = { 1085c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 1086c2d405aaSManivannan Sadhasivam .name = "xr_serial", 1087c2d405aaSManivannan Sadhasivam }, 1088c2d405aaSManivannan Sadhasivam .id_table = id_table, 1089c2d405aaSManivannan Sadhasivam .num_ports = 1, 1090c2d405aaSManivannan Sadhasivam .probe = xr_probe, 109123b7998eSJohan Hovold .port_probe = xr_port_probe, 109223b7998eSJohan Hovold .port_remove = xr_port_remove, 1093c2d405aaSManivannan Sadhasivam .open = xr_open, 1094c2d405aaSManivannan Sadhasivam .close = xr_close, 1095c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 1096c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 1097c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 1098c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 1099*974e2f6aSJarkko Sonninen .ioctl = xr_ioctl, 1100c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 1101c2d405aaSManivannan Sadhasivam }; 1102c2d405aaSManivannan Sadhasivam 1103c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 1104c2d405aaSManivannan Sadhasivam &xr_device, NULL 1105c2d405aaSManivannan Sadhasivam }; 1106c2d405aaSManivannan Sadhasivam 1107c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 1108c2d405aaSManivannan Sadhasivam 1109c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 1110c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 1111c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 1112