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 96607f6718SJohan Hovold #define XR_GPIO_MODE_TX_TOGGLE 0x100 97607f6718SJohan Hovold #define XR_GPIO_MODE_RX_TOGGLE 0x200 98607f6718SJohan Hovold 9906f79d57SJohan Hovold #define XR_FIFO_RESET 0x1 10006f79d57SJohan Hovold 101607f6718SJohan Hovold #define XR_CUSTOM_DRIVER_ACTIVE 0x1 102607f6718SJohan Hovold 103607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port); 104607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port); 10506f79d57SJohan Hovold static int xr21v141x_fifo_reset(struct usb_serial_port *port); 106607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 107*f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 108*f6d47fe5SIlpo Järvinen const struct ktermios *old_termios); 109c2d405aaSManivannan Sadhasivam 110f865e614SJohan Hovold struct xr_type { 111607f6718SJohan Hovold int reg_width; 112607f6718SJohan Hovold u8 reg_recipient; 113607f6718SJohan Hovold u8 set_reg; 114607f6718SJohan Hovold u8 get_reg; 115607f6718SJohan Hovold 1164099d4baSJohan Hovold u16 uart_enable; 1174099d4baSJohan Hovold u16 flow_control; 1184099d4baSJohan Hovold u16 xon_char; 1194099d4baSJohan Hovold u16 xoff_char; 1204099d4baSJohan Hovold u16 tx_break; 1214099d4baSJohan Hovold u16 gpio_mode; 1224099d4baSJohan Hovold u16 gpio_direction; 1234099d4baSJohan Hovold u16 gpio_set; 1244099d4baSJohan Hovold u16 gpio_clear; 1254099d4baSJohan Hovold u16 gpio_status; 12606f79d57SJohan Hovold u16 tx_fifo_reset; 12706f79d57SJohan Hovold u16 rx_fifo_reset; 1284099d4baSJohan Hovold u16 custom_driver; 129607f6718SJohan Hovold 1304099d4baSJohan Hovold bool have_5_6_bit_mode; 131607f6718SJohan Hovold bool have_xmit_toggle; 132607f6718SJohan Hovold 133607f6718SJohan Hovold int (*enable)(struct usb_serial_port *port); 134607f6718SJohan Hovold int (*disable)(struct usb_serial_port *port); 13506f79d57SJohan Hovold int (*fifo_reset)(struct usb_serial_port *port); 136607f6718SJohan Hovold void (*set_line_settings)(struct tty_struct *tty, 137607f6718SJohan Hovold struct usb_serial_port *port, 138*f6d47fe5SIlpo Järvinen const struct ktermios *old_termios); 139f865e614SJohan Hovold }; 140f865e614SJohan Hovold 141f865e614SJohan Hovold enum xr_type_id { 142f865e614SJohan Hovold XR21V141X, 143607f6718SJohan Hovold XR21B142X, 1444099d4baSJohan Hovold XR21B1411, 1456da99f9dSJohan Hovold XR2280X, 146f865e614SJohan Hovold XR_TYPE_COUNT, 147f865e614SJohan Hovold }; 148f865e614SJohan Hovold 149f865e614SJohan Hovold static const struct xr_type xr_types[] = { 150f865e614SJohan Hovold [XR21V141X] = { 151607f6718SJohan Hovold .reg_width = 8, 152607f6718SJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 153607f6718SJohan Hovold .set_reg = 0x00, 154607f6718SJohan Hovold .get_reg = 0x01, 155607f6718SJohan Hovold 156f865e614SJohan Hovold .uart_enable = 0x03, 157f865e614SJohan Hovold .flow_control = 0x0c, 158f865e614SJohan Hovold .xon_char = 0x10, 159f865e614SJohan Hovold .xoff_char = 0x11, 160f865e614SJohan Hovold .tx_break = 0x14, 161f865e614SJohan Hovold .gpio_mode = 0x1a, 162f865e614SJohan Hovold .gpio_direction = 0x1b, 163f865e614SJohan Hovold .gpio_set = 0x1d, 164f865e614SJohan Hovold .gpio_clear = 0x1e, 165f865e614SJohan Hovold .gpio_status = 0x1f, 166607f6718SJohan Hovold 167607f6718SJohan Hovold .enable = xr21v141x_uart_enable, 168607f6718SJohan Hovold .disable = xr21v141x_uart_disable, 16906f79d57SJohan Hovold .fifo_reset = xr21v141x_fifo_reset, 170607f6718SJohan Hovold .set_line_settings = xr21v141x_set_line_settings, 171607f6718SJohan Hovold }, 172607f6718SJohan Hovold [XR21B142X] = { 173607f6718SJohan Hovold .reg_width = 16, 174607f6718SJohan Hovold .reg_recipient = USB_RECIP_INTERFACE, 175607f6718SJohan Hovold .set_reg = 0x00, 176607f6718SJohan Hovold .get_reg = 0x00, 177607f6718SJohan Hovold 178607f6718SJohan Hovold .uart_enable = 0x00, 179607f6718SJohan Hovold .flow_control = 0x06, 180607f6718SJohan Hovold .xon_char = 0x07, 181607f6718SJohan Hovold .xoff_char = 0x08, 182607f6718SJohan Hovold .tx_break = 0x0a, 183607f6718SJohan Hovold .gpio_mode = 0x0c, 184607f6718SJohan Hovold .gpio_direction = 0x0d, 185607f6718SJohan Hovold .gpio_set = 0x0e, 186607f6718SJohan Hovold .gpio_clear = 0x0f, 187607f6718SJohan Hovold .gpio_status = 0x10, 18806f79d57SJohan Hovold .tx_fifo_reset = 0x40, 18906f79d57SJohan Hovold .rx_fifo_reset = 0x43, 190607f6718SJohan Hovold .custom_driver = 0x60, 191607f6718SJohan Hovold 1924099d4baSJohan Hovold .have_5_6_bit_mode = true, 193607f6718SJohan Hovold .have_xmit_toggle = true, 194f865e614SJohan Hovold }, 1954099d4baSJohan Hovold [XR21B1411] = { 1964099d4baSJohan Hovold .reg_width = 12, 1974099d4baSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 1984099d4baSJohan Hovold .set_reg = 0x00, 1994099d4baSJohan Hovold .get_reg = 0x01, 2004099d4baSJohan Hovold 2014099d4baSJohan Hovold .uart_enable = 0xc00, 2024099d4baSJohan Hovold .flow_control = 0xc06, 2034099d4baSJohan Hovold .xon_char = 0xc07, 2044099d4baSJohan Hovold .xoff_char = 0xc08, 2054099d4baSJohan Hovold .tx_break = 0xc0a, 2064099d4baSJohan Hovold .gpio_mode = 0xc0c, 2074099d4baSJohan Hovold .gpio_direction = 0xc0d, 2084099d4baSJohan Hovold .gpio_set = 0xc0e, 2094099d4baSJohan Hovold .gpio_clear = 0xc0f, 2104099d4baSJohan Hovold .gpio_status = 0xc10, 21106f79d57SJohan Hovold .tx_fifo_reset = 0xc80, 21206f79d57SJohan Hovold .rx_fifo_reset = 0xcc0, 2134099d4baSJohan Hovold .custom_driver = 0x20d, 2144099d4baSJohan Hovold }, 2156da99f9dSJohan Hovold [XR2280X] = { 2166da99f9dSJohan Hovold .reg_width = 16, 2176da99f9dSJohan Hovold .reg_recipient = USB_RECIP_DEVICE, 2186da99f9dSJohan Hovold .set_reg = 0x05, 2196da99f9dSJohan Hovold .get_reg = 0x05, 2206da99f9dSJohan Hovold 2216da99f9dSJohan Hovold .uart_enable = 0x40, 2226da99f9dSJohan Hovold .flow_control = 0x46, 2236da99f9dSJohan Hovold .xon_char = 0x47, 2246da99f9dSJohan Hovold .xoff_char = 0x48, 2256da99f9dSJohan Hovold .tx_break = 0x4a, 2266da99f9dSJohan Hovold .gpio_mode = 0x4c, 2276da99f9dSJohan Hovold .gpio_direction = 0x4d, 2286da99f9dSJohan Hovold .gpio_set = 0x4e, 2296da99f9dSJohan Hovold .gpio_clear = 0x4f, 2306da99f9dSJohan Hovold .gpio_status = 0x50, 23106f79d57SJohan Hovold .tx_fifo_reset = 0x60, 23206f79d57SJohan Hovold .rx_fifo_reset = 0x63, 2336da99f9dSJohan Hovold .custom_driver = 0x81, 2346da99f9dSJohan Hovold }, 235f865e614SJohan Hovold }; 236c2d405aaSManivannan Sadhasivam 23723b7998eSJohan Hovold struct xr_data { 238f865e614SJohan Hovold const struct xr_type *type; 239607f6718SJohan Hovold u8 channel; /* zero-based index or interface number */ 24023b7998eSJohan Hovold }; 24123b7998eSJohan Hovold 2424099d4baSJohan Hovold static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val) 243c2d405aaSManivannan Sadhasivam { 244607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 245607f6718SJohan Hovold const struct xr_type *type = data->type; 246c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 247c2d405aaSManivannan Sadhasivam int ret; 248c2d405aaSManivannan Sadhasivam 249607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 250607f6718SJohan Hovold type->set_reg, 251607f6718SJohan Hovold USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, 252607f6718SJohan Hovold val, (channel << 8) | reg, NULL, 0, 253c2d405aaSManivannan Sadhasivam USB_CTRL_SET_TIMEOUT); 254c2d405aaSManivannan Sadhasivam if (ret < 0) { 255c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); 256c2d405aaSManivannan Sadhasivam return ret; 257c2d405aaSManivannan Sadhasivam } 258c2d405aaSManivannan Sadhasivam 259c2d405aaSManivannan Sadhasivam return 0; 260c2d405aaSManivannan Sadhasivam } 261c2d405aaSManivannan Sadhasivam 2624099d4baSJohan Hovold static int xr_get_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 *val) 263c2d405aaSManivannan Sadhasivam { 264607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 265607f6718SJohan Hovold const struct xr_type *type = data->type; 266c2d405aaSManivannan Sadhasivam struct usb_serial *serial = port->serial; 267c2d405aaSManivannan Sadhasivam u8 *dmabuf; 268607f6718SJohan Hovold int ret, len; 269c2d405aaSManivannan Sadhasivam 270607f6718SJohan Hovold if (type->reg_width == 8) 271607f6718SJohan Hovold len = 1; 272607f6718SJohan Hovold else 273607f6718SJohan Hovold len = 2; 274607f6718SJohan Hovold 275607f6718SJohan Hovold dmabuf = kmalloc(len, GFP_KERNEL); 276c2d405aaSManivannan Sadhasivam if (!dmabuf) 277c2d405aaSManivannan Sadhasivam return -ENOMEM; 278c2d405aaSManivannan Sadhasivam 279607f6718SJohan Hovold ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 280607f6718SJohan Hovold type->get_reg, 281607f6718SJohan Hovold USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, 282607f6718SJohan Hovold 0, (channel << 8) | reg, dmabuf, len, 283c2d405aaSManivannan Sadhasivam USB_CTRL_GET_TIMEOUT); 284607f6718SJohan Hovold if (ret == len) { 285607f6718SJohan Hovold if (len == 2) 286607f6718SJohan Hovold *val = le16_to_cpup((__le16 *)dmabuf); 287607f6718SJohan Hovold else 288c2d405aaSManivannan Sadhasivam *val = *dmabuf; 289c2d405aaSManivannan Sadhasivam ret = 0; 290c2d405aaSManivannan Sadhasivam } else { 291c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); 292c2d405aaSManivannan Sadhasivam if (ret >= 0) 293c2d405aaSManivannan Sadhasivam ret = -EIO; 294c2d405aaSManivannan Sadhasivam } 295c2d405aaSManivannan Sadhasivam 296c2d405aaSManivannan Sadhasivam kfree(dmabuf); 297c2d405aaSManivannan Sadhasivam 298c2d405aaSManivannan Sadhasivam return ret; 299c2d405aaSManivannan Sadhasivam } 300c2d405aaSManivannan Sadhasivam 3014099d4baSJohan Hovold static int xr_set_reg_uart(struct usb_serial_port *port, u16 reg, u16 val) 302c2d405aaSManivannan Sadhasivam { 30323b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 30423b7998eSJohan Hovold 305607f6718SJohan Hovold return xr_set_reg(port, data->channel, reg, val); 306c2d405aaSManivannan Sadhasivam } 307c2d405aaSManivannan Sadhasivam 3084099d4baSJohan Hovold static int xr_get_reg_uart(struct usb_serial_port *port, u16 reg, u16 *val) 309c2d405aaSManivannan Sadhasivam { 31023b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 31123b7998eSJohan Hovold 312607f6718SJohan Hovold return xr_get_reg(port, data->channel, reg, val); 313c2d405aaSManivannan Sadhasivam } 314c2d405aaSManivannan Sadhasivam 31523b7998eSJohan Hovold static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) 316c2d405aaSManivannan Sadhasivam { 31723b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 31823b7998eSJohan Hovold u8 reg; 31923b7998eSJohan Hovold 32023b7998eSJohan Hovold reg = reg_base + data->channel; 32123b7998eSJohan Hovold 322c2d405aaSManivannan Sadhasivam return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); 323c2d405aaSManivannan Sadhasivam } 324c2d405aaSManivannan Sadhasivam 325607f6718SJohan Hovold static int __xr_uart_enable(struct usb_serial_port *port) 326607f6718SJohan Hovold { 327607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 328607f6718SJohan Hovold 329607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 330607f6718SJohan Hovold XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); 331607f6718SJohan Hovold } 332607f6718SJohan Hovold 333607f6718SJohan Hovold static int __xr_uart_disable(struct usb_serial_port *port) 334607f6718SJohan Hovold { 335607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 336607f6718SJohan Hovold 337607f6718SJohan Hovold return xr_set_reg_uart(port, data->type->uart_enable, 0); 338607f6718SJohan Hovold } 339607f6718SJohan Hovold 340c2d405aaSManivannan Sadhasivam /* 341c2d405aaSManivannan Sadhasivam * According to datasheet, below is the recommended sequence for enabling UART 342c2d405aaSManivannan Sadhasivam * module in XR21V141X: 343c2d405aaSManivannan Sadhasivam * 344c2d405aaSManivannan Sadhasivam * Enable Tx FIFO 345c2d405aaSManivannan Sadhasivam * Enable Tx and Rx 346c2d405aaSManivannan Sadhasivam * Enable Rx FIFO 347c2d405aaSManivannan Sadhasivam */ 348607f6718SJohan Hovold static int xr21v141x_uart_enable(struct usb_serial_port *port) 349c2d405aaSManivannan Sadhasivam { 350c2d405aaSManivannan Sadhasivam int ret; 351c2d405aaSManivannan Sadhasivam 352c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 353c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO); 354c2d405aaSManivannan Sadhasivam if (ret) 355c2d405aaSManivannan Sadhasivam return ret; 356c2d405aaSManivannan Sadhasivam 357607f6718SJohan Hovold ret = __xr_uart_enable(port); 358c2d405aaSManivannan Sadhasivam if (ret) 359c2d405aaSManivannan Sadhasivam return ret; 360c2d405aaSManivannan Sadhasivam 361c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 362c2d405aaSManivannan Sadhasivam XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); 363c2d405aaSManivannan Sadhasivam if (ret) 364607f6718SJohan Hovold __xr_uart_disable(port); 365c2d405aaSManivannan Sadhasivam 366c2d405aaSManivannan Sadhasivam return ret; 367c2d405aaSManivannan Sadhasivam } 368c2d405aaSManivannan Sadhasivam 369607f6718SJohan Hovold static int xr21v141x_uart_disable(struct usb_serial_port *port) 370c2d405aaSManivannan Sadhasivam { 371c2d405aaSManivannan Sadhasivam int ret; 372c2d405aaSManivannan Sadhasivam 373607f6718SJohan Hovold ret = __xr_uart_disable(port); 374c2d405aaSManivannan Sadhasivam if (ret) 375c2d405aaSManivannan Sadhasivam return ret; 376c2d405aaSManivannan Sadhasivam 377c2d405aaSManivannan Sadhasivam ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); 378c2d405aaSManivannan Sadhasivam 379c2d405aaSManivannan Sadhasivam return ret; 380c2d405aaSManivannan Sadhasivam } 381c2d405aaSManivannan Sadhasivam 382607f6718SJohan Hovold static int xr_uart_enable(struct usb_serial_port *port) 383607f6718SJohan Hovold { 384607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 385607f6718SJohan Hovold 386607f6718SJohan Hovold if (data->type->enable) 387607f6718SJohan Hovold return data->type->enable(port); 388607f6718SJohan Hovold 389607f6718SJohan Hovold return __xr_uart_enable(port); 390607f6718SJohan Hovold } 391607f6718SJohan Hovold 392607f6718SJohan Hovold static int xr_uart_disable(struct usb_serial_port *port) 393607f6718SJohan Hovold { 394607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 395607f6718SJohan Hovold 396607f6718SJohan Hovold if (data->type->disable) 397607f6718SJohan Hovold return data->type->disable(port); 398607f6718SJohan Hovold 399607f6718SJohan Hovold return __xr_uart_disable(port); 400607f6718SJohan Hovold } 401607f6718SJohan Hovold 40206f79d57SJohan Hovold static int xr21v141x_fifo_reset(struct usb_serial_port *port) 40306f79d57SJohan Hovold { 40406f79d57SJohan Hovold int ret; 40506f79d57SJohan Hovold 40606f79d57SJohan Hovold ret = xr_set_reg_um(port, XR21V141X_UM_TX_FIFO_RESET, XR_FIFO_RESET); 40706f79d57SJohan Hovold if (ret) 40806f79d57SJohan Hovold return ret; 40906f79d57SJohan Hovold 41006f79d57SJohan Hovold ret = xr_set_reg_um(port, XR21V141X_UM_RX_FIFO_RESET, XR_FIFO_RESET); 41106f79d57SJohan Hovold if (ret) 41206f79d57SJohan Hovold return ret; 41306f79d57SJohan Hovold 41406f79d57SJohan Hovold return 0; 41506f79d57SJohan Hovold } 41606f79d57SJohan Hovold 41706f79d57SJohan Hovold static int xr_fifo_reset(struct usb_serial_port *port) 41806f79d57SJohan Hovold { 41906f79d57SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 42006f79d57SJohan Hovold int ret; 42106f79d57SJohan Hovold 42206f79d57SJohan Hovold if (data->type->fifo_reset) 42306f79d57SJohan Hovold return data->type->fifo_reset(port); 42406f79d57SJohan Hovold 42506f79d57SJohan Hovold ret = xr_set_reg_uart(port, data->type->tx_fifo_reset, XR_FIFO_RESET); 42606f79d57SJohan Hovold if (ret) 42706f79d57SJohan Hovold return ret; 42806f79d57SJohan Hovold 42906f79d57SJohan Hovold ret = xr_set_reg_uart(port, data->type->rx_fifo_reset, XR_FIFO_RESET); 43006f79d57SJohan Hovold if (ret) 43106f79d57SJohan Hovold return ret; 43206f79d57SJohan Hovold 43306f79d57SJohan Hovold return 0; 43406f79d57SJohan Hovold } 43506f79d57SJohan Hovold 436c2d405aaSManivannan Sadhasivam static int xr_tiocmget(struct tty_struct *tty) 437c2d405aaSManivannan Sadhasivam { 438c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 439f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 440607f6718SJohan Hovold u16 status; 441c2d405aaSManivannan Sadhasivam int ret; 442c2d405aaSManivannan Sadhasivam 443f865e614SJohan Hovold ret = xr_get_reg_uart(port, data->type->gpio_status, &status); 444c2d405aaSManivannan Sadhasivam if (ret) 445c2d405aaSManivannan Sadhasivam return ret; 446c2d405aaSManivannan Sadhasivam 447c2d405aaSManivannan Sadhasivam /* 448c2d405aaSManivannan Sadhasivam * Modem control pins are active low, so reading '0' means it is active 449c2d405aaSManivannan Sadhasivam * and '1' means not active. 450c2d405aaSManivannan Sadhasivam */ 451958d6b95SJohan Hovold ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | 452958d6b95SJohan Hovold ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | 453958d6b95SJohan Hovold ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | 454958d6b95SJohan Hovold ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | 455958d6b95SJohan Hovold ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | 456958d6b95SJohan Hovold ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); 457c2d405aaSManivannan Sadhasivam 458c2d405aaSManivannan Sadhasivam return ret; 459c2d405aaSManivannan Sadhasivam } 460c2d405aaSManivannan Sadhasivam 461c2d405aaSManivannan Sadhasivam static int xr_tiocmset_port(struct usb_serial_port *port, 462c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 463c2d405aaSManivannan Sadhasivam { 464f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 465f865e614SJohan Hovold const struct xr_type *type = data->type; 466607f6718SJohan Hovold u16 gpio_set = 0; 467607f6718SJohan Hovold u16 gpio_clr = 0; 468c2d405aaSManivannan Sadhasivam int ret = 0; 469c2d405aaSManivannan Sadhasivam 470c2d405aaSManivannan Sadhasivam /* Modem control pins are active low, so set & clr are swapped */ 471c2d405aaSManivannan Sadhasivam if (set & TIOCM_RTS) 472958d6b95SJohan Hovold gpio_clr |= XR_GPIO_RTS; 473c2d405aaSManivannan Sadhasivam if (set & TIOCM_DTR) 474958d6b95SJohan Hovold gpio_clr |= XR_GPIO_DTR; 475c2d405aaSManivannan Sadhasivam if (clear & TIOCM_RTS) 476958d6b95SJohan Hovold gpio_set |= XR_GPIO_RTS; 477c2d405aaSManivannan Sadhasivam if (clear & TIOCM_DTR) 478958d6b95SJohan Hovold gpio_set |= XR_GPIO_DTR; 479c2d405aaSManivannan Sadhasivam 480c2d405aaSManivannan Sadhasivam /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ 481c2d405aaSManivannan Sadhasivam if (gpio_clr) 482f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); 483c2d405aaSManivannan Sadhasivam 484c2d405aaSManivannan Sadhasivam if (gpio_set) 485f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); 486c2d405aaSManivannan Sadhasivam 487c2d405aaSManivannan Sadhasivam return ret; 488c2d405aaSManivannan Sadhasivam } 489c2d405aaSManivannan Sadhasivam 490c2d405aaSManivannan Sadhasivam static int xr_tiocmset(struct tty_struct *tty, 491c2d405aaSManivannan Sadhasivam unsigned int set, unsigned int clear) 492c2d405aaSManivannan Sadhasivam { 493c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 494c2d405aaSManivannan Sadhasivam 495c2d405aaSManivannan Sadhasivam return xr_tiocmset_port(port, set, clear); 496c2d405aaSManivannan Sadhasivam } 497c2d405aaSManivannan Sadhasivam 498c2d405aaSManivannan Sadhasivam static void xr_dtr_rts(struct usb_serial_port *port, int on) 499c2d405aaSManivannan Sadhasivam { 500c2d405aaSManivannan Sadhasivam if (on) 501c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); 502c2d405aaSManivannan Sadhasivam else 503c2d405aaSManivannan Sadhasivam xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); 504c2d405aaSManivannan Sadhasivam } 505c2d405aaSManivannan Sadhasivam 506c2d405aaSManivannan Sadhasivam static void xr_break_ctl(struct tty_struct *tty, int break_state) 507c2d405aaSManivannan Sadhasivam { 508c2d405aaSManivannan Sadhasivam struct usb_serial_port *port = tty->driver_data; 509f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 510f865e614SJohan Hovold const struct xr_type *type = data->type; 511607f6718SJohan Hovold u16 state; 512c2d405aaSManivannan Sadhasivam 513c2d405aaSManivannan Sadhasivam if (break_state == 0) 514607f6718SJohan Hovold state = 0; 515c2d405aaSManivannan Sadhasivam else 516607f6718SJohan Hovold state = GENMASK(type->reg_width - 1, 0); 517c2d405aaSManivannan Sadhasivam 518607f6718SJohan Hovold dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); 519607f6718SJohan Hovold 520f865e614SJohan Hovold xr_set_reg_uart(port, type->tx_break, state); 521c2d405aaSManivannan Sadhasivam } 522c2d405aaSManivannan Sadhasivam 523c2d405aaSManivannan Sadhasivam /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ 524c2d405aaSManivannan Sadhasivam static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { 525c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 526c2d405aaSManivannan Sadhasivam { 0x000, 0x000, 0x000 }, 527c2d405aaSManivannan Sadhasivam { 0x100, 0x000, 0x100 }, 528c2d405aaSManivannan Sadhasivam { 0x020, 0x400, 0x020 }, 529c2d405aaSManivannan Sadhasivam { 0x010, 0x100, 0x010 }, 530c2d405aaSManivannan Sadhasivam { 0x208, 0x040, 0x208 }, 531c2d405aaSManivannan Sadhasivam { 0x104, 0x820, 0x108 }, 532c2d405aaSManivannan Sadhasivam { 0x844, 0x210, 0x884 }, 533c2d405aaSManivannan Sadhasivam { 0x444, 0x110, 0x444 }, 534c2d405aaSManivannan Sadhasivam { 0x122, 0x888, 0x224 }, 535c2d405aaSManivannan Sadhasivam { 0x912, 0x448, 0x924 }, 536c2d405aaSManivannan Sadhasivam { 0x492, 0x248, 0x492 }, 537c2d405aaSManivannan Sadhasivam { 0x252, 0x928, 0x292 }, 538c2d405aaSManivannan Sadhasivam { 0x94a, 0x4a4, 0xa52 }, 539c2d405aaSManivannan Sadhasivam { 0x52a, 0xaa4, 0x54a }, 540c2d405aaSManivannan Sadhasivam { 0xaaa, 0x954, 0x4aa }, 541c2d405aaSManivannan Sadhasivam { 0xaaa, 0x554, 0xaaa }, 542c2d405aaSManivannan Sadhasivam { 0x555, 0xad4, 0x5aa }, 543c2d405aaSManivannan Sadhasivam { 0xb55, 0xab4, 0x55a }, 544c2d405aaSManivannan Sadhasivam { 0x6b5, 0x5ac, 0xb56 }, 545c2d405aaSManivannan Sadhasivam { 0x5b5, 0xd6c, 0x6d6 }, 546c2d405aaSManivannan Sadhasivam { 0xb6d, 0xb6a, 0xdb6 }, 547c2d405aaSManivannan Sadhasivam { 0x76d, 0x6da, 0xbb6 }, 548c2d405aaSManivannan Sadhasivam { 0xedd, 0xdda, 0x76e }, 549c2d405aaSManivannan Sadhasivam { 0xddd, 0xbba, 0xeee }, 550c2d405aaSManivannan Sadhasivam { 0x7bb, 0xf7a, 0xdde }, 551c2d405aaSManivannan Sadhasivam { 0xf7b, 0xef6, 0x7de }, 552c2d405aaSManivannan Sadhasivam { 0xdf7, 0xbf6, 0xf7e }, 553c2d405aaSManivannan Sadhasivam { 0x7f7, 0xfee, 0xefe }, 554c2d405aaSManivannan Sadhasivam { 0xfdf, 0xfbe, 0x7fe }, 555c2d405aaSManivannan Sadhasivam { 0xf7f, 0xefe, 0xffe }, 556c2d405aaSManivannan Sadhasivam { 0xfff, 0xffe, 0xffd }, 557c2d405aaSManivannan Sadhasivam }; 558c2d405aaSManivannan Sadhasivam 559607f6718SJohan Hovold static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) 560c2d405aaSManivannan Sadhasivam { 561c2d405aaSManivannan Sadhasivam u32 divisor, baud, idx; 562c2d405aaSManivannan Sadhasivam u16 tx_mask, rx_mask; 563c2d405aaSManivannan Sadhasivam int ret; 564c2d405aaSManivannan Sadhasivam 56555317e22SJohan Hovold baud = tty->termios.c_ospeed; 56655317e22SJohan Hovold if (!baud) 56755317e22SJohan Hovold return 0; 56855317e22SJohan Hovold 56955317e22SJohan Hovold baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); 570c2d405aaSManivannan Sadhasivam divisor = XR_INT_OSC_HZ / baud; 571c2d405aaSManivannan Sadhasivam idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; 572c2d405aaSManivannan Sadhasivam tx_mask = xr21v141x_txrx_clk_masks[idx].tx; 573c2d405aaSManivannan Sadhasivam 574c2d405aaSManivannan Sadhasivam if (divisor & 0x01) 575c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; 576c2d405aaSManivannan Sadhasivam else 577c2d405aaSManivannan Sadhasivam rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; 578c2d405aaSManivannan Sadhasivam 579c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); 580c2d405aaSManivannan Sadhasivam /* 581c2d405aaSManivannan Sadhasivam * XR21V141X uses fractional baud rate generator with 48MHz internal 582c2d405aaSManivannan Sadhasivam * oscillator and 19-bit programmable divisor. So theoretically it can 583c2d405aaSManivannan Sadhasivam * generate most commonly used baud rates with high accuracy. 584c2d405aaSManivannan Sadhasivam */ 585c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, 586c2d405aaSManivannan Sadhasivam divisor & 0xff); 587c2d405aaSManivannan Sadhasivam if (ret) 588c2d405aaSManivannan Sadhasivam return ret; 589c2d405aaSManivannan Sadhasivam 590c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, 591c2d405aaSManivannan Sadhasivam (divisor >> 8) & 0xff); 592c2d405aaSManivannan Sadhasivam if (ret) 593c2d405aaSManivannan Sadhasivam return ret; 594c2d405aaSManivannan Sadhasivam 595c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, 596c2d405aaSManivannan Sadhasivam (divisor >> 16) & 0xff); 597c2d405aaSManivannan Sadhasivam if (ret) 598c2d405aaSManivannan Sadhasivam return ret; 599c2d405aaSManivannan Sadhasivam 600c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, 601c2d405aaSManivannan Sadhasivam tx_mask & 0xff); 602c2d405aaSManivannan Sadhasivam if (ret) 603c2d405aaSManivannan Sadhasivam return ret; 604c2d405aaSManivannan Sadhasivam 605c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, 606c2d405aaSManivannan Sadhasivam (tx_mask >> 8) & 0xff); 607c2d405aaSManivannan Sadhasivam if (ret) 608c2d405aaSManivannan Sadhasivam return ret; 609c2d405aaSManivannan Sadhasivam 610c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, 611c2d405aaSManivannan Sadhasivam rx_mask & 0xff); 612c2d405aaSManivannan Sadhasivam if (ret) 613c2d405aaSManivannan Sadhasivam return ret; 614c2d405aaSManivannan Sadhasivam 615c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, 616c2d405aaSManivannan Sadhasivam (rx_mask >> 8) & 0xff); 617c2d405aaSManivannan Sadhasivam if (ret) 618c2d405aaSManivannan Sadhasivam return ret; 619c2d405aaSManivannan Sadhasivam 620c2d405aaSManivannan Sadhasivam tty_encode_baud_rate(tty, baud, baud); 621c2d405aaSManivannan Sadhasivam 622c2d405aaSManivannan Sadhasivam return 0; 623c2d405aaSManivannan Sadhasivam } 624c2d405aaSManivannan Sadhasivam 625c2d405aaSManivannan Sadhasivam static void xr_set_flow_mode(struct tty_struct *tty, 62655317e22SJohan Hovold struct usb_serial_port *port, 627*f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 628c2d405aaSManivannan Sadhasivam { 629f865e614SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 630f865e614SJohan Hovold const struct xr_type *type = data->type; 631607f6718SJohan Hovold u16 flow, gpio_mode; 632c2d405aaSManivannan Sadhasivam int ret; 633c2d405aaSManivannan Sadhasivam 634f865e614SJohan Hovold ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); 635c2d405aaSManivannan Sadhasivam if (ret) 636c2d405aaSManivannan Sadhasivam return; 637c2d405aaSManivannan Sadhasivam 638607f6718SJohan Hovold /* 639607f6718SJohan Hovold * According to the datasheets, the UART needs to be disabled while 640607f6718SJohan Hovold * writing to the FLOW_CONTROL register (XR21V141X), or any register 641607f6718SJohan Hovold * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). 642607f6718SJohan Hovold */ 643607f6718SJohan Hovold xr_uart_disable(port); 644607f6718SJohan Hovold 645465d3b3aSJohan Hovold /* Set GPIO mode for controlling the pins manually by default. */ 646607f6718SJohan Hovold gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; 647465d3b3aSJohan Hovold 64855317e22SJohan Hovold if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { 649c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); 650607f6718SJohan Hovold gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; 651958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_HW; 652c2d405aaSManivannan Sadhasivam } else if (I_IXON(tty)) { 653c2d405aaSManivannan Sadhasivam u8 start_char = START_CHAR(tty); 654c2d405aaSManivannan Sadhasivam u8 stop_char = STOP_CHAR(tty); 655c2d405aaSManivannan Sadhasivam 656c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); 657958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_SW; 658c2d405aaSManivannan Sadhasivam 659f865e614SJohan Hovold xr_set_reg_uart(port, type->xon_char, start_char); 660f865e614SJohan Hovold xr_set_reg_uart(port, type->xoff_char, stop_char); 661c2d405aaSManivannan Sadhasivam } else { 662c2d405aaSManivannan Sadhasivam dev_dbg(&port->dev, "Disabling flow ctrl\n"); 663958d6b95SJohan Hovold flow = XR_UART_FLOW_MODE_NONE; 664c2d405aaSManivannan Sadhasivam } 665c2d405aaSManivannan Sadhasivam 666f865e614SJohan Hovold xr_set_reg_uart(port, type->flow_control, flow); 667f865e614SJohan Hovold xr_set_reg_uart(port, type->gpio_mode, gpio_mode); 66855317e22SJohan Hovold 669607f6718SJohan Hovold xr_uart_enable(port); 670607f6718SJohan Hovold 67155317e22SJohan Hovold if (C_BAUD(tty) == B0) 67255317e22SJohan Hovold xr_dtr_rts(port, 0); 67355317e22SJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 67455317e22SJohan Hovold xr_dtr_rts(port, 1); 675c2d405aaSManivannan Sadhasivam } 676c2d405aaSManivannan Sadhasivam 677607f6718SJohan Hovold static void xr21v141x_set_line_settings(struct tty_struct *tty, 678*f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 679*f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 680c2d405aaSManivannan Sadhasivam { 681c2d405aaSManivannan Sadhasivam struct ktermios *termios = &tty->termios; 682c2d405aaSManivannan Sadhasivam u8 bits = 0; 683c2d405aaSManivannan Sadhasivam int ret; 684c2d405aaSManivannan Sadhasivam 685736c0931SJohan Hovold if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) 686607f6718SJohan Hovold xr21v141x_set_baudrate(tty, port); 687c2d405aaSManivannan Sadhasivam 688c2d405aaSManivannan Sadhasivam switch (C_CSIZE(tty)) { 689c2d405aaSManivannan Sadhasivam case CS5: 690c2d405aaSManivannan Sadhasivam case CS6: 691c2d405aaSManivannan Sadhasivam /* CS5 and CS6 are not supported, so just restore old setting */ 692c2d405aaSManivannan Sadhasivam termios->c_cflag &= ~CSIZE; 693c2d405aaSManivannan Sadhasivam if (old_termios) 694c2d405aaSManivannan Sadhasivam termios->c_cflag |= old_termios->c_cflag & CSIZE; 695c2d405aaSManivannan Sadhasivam else 696ea7ada4dSJohan Hovold termios->c_cflag |= CS8; 697ea7ada4dSJohan Hovold 698ea7ada4dSJohan Hovold if (C_CSIZE(tty) == CS7) 699958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 700ea7ada4dSJohan Hovold else 701958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 702c2d405aaSManivannan Sadhasivam break; 703c2d405aaSManivannan Sadhasivam case CS7: 704958d6b95SJohan Hovold bits |= XR_UART_DATA_7; 705c2d405aaSManivannan Sadhasivam break; 706c2d405aaSManivannan Sadhasivam case CS8: 707c2d405aaSManivannan Sadhasivam default: 708958d6b95SJohan Hovold bits |= XR_UART_DATA_8; 709c2d405aaSManivannan Sadhasivam break; 710c2d405aaSManivannan Sadhasivam } 711c2d405aaSManivannan Sadhasivam 712c2d405aaSManivannan Sadhasivam if (C_PARENB(tty)) { 713c2d405aaSManivannan Sadhasivam if (C_CMSPAR(tty)) { 714c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 715958d6b95SJohan Hovold bits |= XR_UART_PARITY_MARK; 716c2d405aaSManivannan Sadhasivam else 717958d6b95SJohan Hovold bits |= XR_UART_PARITY_SPACE; 718c2d405aaSManivannan Sadhasivam } else { 719c2d405aaSManivannan Sadhasivam if (C_PARODD(tty)) 720958d6b95SJohan Hovold bits |= XR_UART_PARITY_ODD; 721c2d405aaSManivannan Sadhasivam else 722958d6b95SJohan Hovold bits |= XR_UART_PARITY_EVEN; 723c2d405aaSManivannan Sadhasivam } 724c2d405aaSManivannan Sadhasivam } 725c2d405aaSManivannan Sadhasivam 726c2d405aaSManivannan Sadhasivam if (C_CSTOPB(tty)) 727958d6b95SJohan Hovold bits |= XR_UART_STOP_2; 728c2d405aaSManivannan Sadhasivam else 729958d6b95SJohan Hovold bits |= XR_UART_STOP_1; 730c2d405aaSManivannan Sadhasivam 731c2d405aaSManivannan Sadhasivam ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); 732c2d405aaSManivannan Sadhasivam if (ret) 733c2d405aaSManivannan Sadhasivam return; 734607f6718SJohan Hovold } 735607f6718SJohan Hovold 736607f6718SJohan Hovold static void xr_cdc_set_line_coding(struct tty_struct *tty, 737*f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 738*f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 739607f6718SJohan Hovold { 7404099d4baSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 741607f6718SJohan Hovold struct usb_host_interface *alt = port->serial->interface->cur_altsetting; 742607f6718SJohan Hovold struct usb_device *udev = port->serial->dev; 743607f6718SJohan Hovold struct usb_cdc_line_coding *lc; 744607f6718SJohan Hovold int ret; 745607f6718SJohan Hovold 746607f6718SJohan Hovold lc = kzalloc(sizeof(*lc), GFP_KERNEL); 747607f6718SJohan Hovold if (!lc) 748607f6718SJohan Hovold return; 749607f6718SJohan Hovold 750607f6718SJohan Hovold if (tty->termios.c_ospeed) 751607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); 752607f6718SJohan Hovold else if (old_termios) 753607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(old_termios->c_ospeed); 754607f6718SJohan Hovold else 755607f6718SJohan Hovold lc->dwDTERate = cpu_to_le32(9600); 756607f6718SJohan Hovold 757607f6718SJohan Hovold if (C_CSTOPB(tty)) 758607f6718SJohan Hovold lc->bCharFormat = USB_CDC_2_STOP_BITS; 759607f6718SJohan Hovold else 760607f6718SJohan Hovold lc->bCharFormat = USB_CDC_1_STOP_BITS; 761607f6718SJohan Hovold 762607f6718SJohan Hovold if (C_PARENB(tty)) { 763607f6718SJohan Hovold if (C_CMSPAR(tty)) { 764607f6718SJohan Hovold if (C_PARODD(tty)) 765607f6718SJohan Hovold lc->bParityType = USB_CDC_MARK_PARITY; 766607f6718SJohan Hovold else 767607f6718SJohan Hovold lc->bParityType = USB_CDC_SPACE_PARITY; 768607f6718SJohan Hovold } else { 769607f6718SJohan Hovold if (C_PARODD(tty)) 770607f6718SJohan Hovold lc->bParityType = USB_CDC_ODD_PARITY; 771607f6718SJohan Hovold else 772607f6718SJohan Hovold lc->bParityType = USB_CDC_EVEN_PARITY; 773607f6718SJohan Hovold } 774607f6718SJohan Hovold } else { 775607f6718SJohan Hovold lc->bParityType = USB_CDC_NO_PARITY; 776607f6718SJohan Hovold } 777607f6718SJohan Hovold 7784099d4baSJohan Hovold if (!data->type->have_5_6_bit_mode && 7794099d4baSJohan Hovold (C_CSIZE(tty) == CS5 || C_CSIZE(tty) == CS6)) { 7804099d4baSJohan Hovold tty->termios.c_cflag &= ~CSIZE; 7814099d4baSJohan Hovold if (old_termios) 7824099d4baSJohan Hovold tty->termios.c_cflag |= old_termios->c_cflag & CSIZE; 7834099d4baSJohan Hovold else 7844099d4baSJohan Hovold tty->termios.c_cflag |= CS8; 7854099d4baSJohan Hovold } 7864099d4baSJohan Hovold 787607f6718SJohan Hovold switch (C_CSIZE(tty)) { 788607f6718SJohan Hovold case CS5: 789607f6718SJohan Hovold lc->bDataBits = 5; 790607f6718SJohan Hovold break; 791607f6718SJohan Hovold case CS6: 792607f6718SJohan Hovold lc->bDataBits = 6; 793607f6718SJohan Hovold break; 794607f6718SJohan Hovold case CS7: 795607f6718SJohan Hovold lc->bDataBits = 7; 796607f6718SJohan Hovold break; 797607f6718SJohan Hovold case CS8: 798607f6718SJohan Hovold default: 799607f6718SJohan Hovold lc->bDataBits = 8; 800607f6718SJohan Hovold break; 801607f6718SJohan Hovold } 802607f6718SJohan Hovold 803607f6718SJohan Hovold ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 804607f6718SJohan Hovold USB_CDC_REQ_SET_LINE_CODING, 805607f6718SJohan Hovold USB_TYPE_CLASS | USB_RECIP_INTERFACE, 806607f6718SJohan Hovold 0, alt->desc.bInterfaceNumber, 807607f6718SJohan Hovold lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); 808607f6718SJohan Hovold if (ret < 0) 809607f6718SJohan Hovold dev_err(&port->dev, "Failed to set line coding: %d\n", ret); 810607f6718SJohan Hovold 811607f6718SJohan Hovold kfree(lc); 812607f6718SJohan Hovold } 813607f6718SJohan Hovold 814607f6718SJohan Hovold static void xr_set_termios(struct tty_struct *tty, 815*f6d47fe5SIlpo Järvinen struct usb_serial_port *port, 816*f6d47fe5SIlpo Järvinen const struct ktermios *old_termios) 817607f6718SJohan Hovold { 818607f6718SJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 819607f6718SJohan Hovold 820607f6718SJohan Hovold /* 821607f6718SJohan Hovold * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC 822607f6718SJohan Hovold * mode upon receiving CDC requests. 823607f6718SJohan Hovold */ 824607f6718SJohan Hovold if (data->type->set_line_settings) 825607f6718SJohan Hovold data->type->set_line_settings(tty, port, old_termios); 826607f6718SJohan Hovold else 827607f6718SJohan Hovold xr_cdc_set_line_coding(tty, port, old_termios); 828c2d405aaSManivannan Sadhasivam 82955317e22SJohan Hovold xr_set_flow_mode(tty, port, old_termios); 830c2d405aaSManivannan Sadhasivam } 831c2d405aaSManivannan Sadhasivam 832c2d405aaSManivannan Sadhasivam static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) 833c2d405aaSManivannan Sadhasivam { 834c2d405aaSManivannan Sadhasivam int ret; 835c2d405aaSManivannan Sadhasivam 83606f79d57SJohan Hovold ret = xr_fifo_reset(port); 83706f79d57SJohan Hovold if (ret) 83806f79d57SJohan Hovold return ret; 83906f79d57SJohan Hovold 840c2d405aaSManivannan Sadhasivam ret = xr_uart_enable(port); 841c2d405aaSManivannan Sadhasivam if (ret) { 842c2d405aaSManivannan Sadhasivam dev_err(&port->dev, "Failed to enable UART\n"); 843c2d405aaSManivannan Sadhasivam return ret; 844c2d405aaSManivannan Sadhasivam } 845c2d405aaSManivannan Sadhasivam 846c2d405aaSManivannan Sadhasivam /* Setup termios */ 847c2d405aaSManivannan Sadhasivam if (tty) 848c2d405aaSManivannan Sadhasivam xr_set_termios(tty, port, NULL); 849c2d405aaSManivannan Sadhasivam 850c2d405aaSManivannan Sadhasivam ret = usb_serial_generic_open(tty, port); 851c2d405aaSManivannan Sadhasivam if (ret) { 852c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 853c2d405aaSManivannan Sadhasivam return ret; 854c2d405aaSManivannan Sadhasivam } 855c2d405aaSManivannan Sadhasivam 856c2d405aaSManivannan Sadhasivam return 0; 857c2d405aaSManivannan Sadhasivam } 858c2d405aaSManivannan Sadhasivam 859c2d405aaSManivannan Sadhasivam static void xr_close(struct usb_serial_port *port) 860c2d405aaSManivannan Sadhasivam { 861c2d405aaSManivannan Sadhasivam usb_serial_generic_close(port); 862c2d405aaSManivannan Sadhasivam 863c2d405aaSManivannan Sadhasivam xr_uart_disable(port); 864c2d405aaSManivannan Sadhasivam } 865c2d405aaSManivannan Sadhasivam 866c2d405aaSManivannan Sadhasivam static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) 867c2d405aaSManivannan Sadhasivam { 8685fec21e7SJohan Hovold struct usb_interface *control = serial->interface; 8695fec21e7SJohan Hovold struct usb_host_interface *alt = control->cur_altsetting; 8705fec21e7SJohan Hovold struct usb_cdc_parsed_header hdrs; 8715fec21e7SJohan Hovold struct usb_cdc_union_desc *desc; 8725fec21e7SJohan Hovold struct usb_interface *data; 8735fec21e7SJohan Hovold int ret; 8745fec21e7SJohan Hovold 8755fec21e7SJohan Hovold ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); 8765fec21e7SJohan Hovold if (ret < 0) 877c2d405aaSManivannan Sadhasivam return -ENODEV; 878c2d405aaSManivannan Sadhasivam 8795fec21e7SJohan Hovold desc = hdrs.usb_cdc_union_desc; 8805fec21e7SJohan Hovold if (!desc) 8815fec21e7SJohan Hovold return -ENODEV; 8825fec21e7SJohan Hovold 8835fec21e7SJohan Hovold data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); 8845fec21e7SJohan Hovold if (!data) 8855fec21e7SJohan Hovold return -ENODEV; 8865fec21e7SJohan Hovold 8875fec21e7SJohan Hovold ret = usb_serial_claim_interface(serial, data); 8885fec21e7SJohan Hovold if (ret) 8895fec21e7SJohan Hovold return ret; 8905fec21e7SJohan Hovold 891f865e614SJohan Hovold usb_set_serial_data(serial, (void *)id->driver_info); 892f865e614SJohan Hovold 893c2d405aaSManivannan Sadhasivam return 0; 894c2d405aaSManivannan Sadhasivam } 895c2d405aaSManivannan Sadhasivam 896f865e614SJohan Hovold static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) 89749036fd0SJohan Hovold { 898607f6718SJohan Hovold u16 mask, mode; 89949036fd0SJohan Hovold int ret; 90049036fd0SJohan Hovold 901607f6718SJohan Hovold /* 902607f6718SJohan Hovold * Configure all pins as GPIO except for Receive and Transmit Toggle. 903607f6718SJohan Hovold */ 90449036fd0SJohan Hovold mode = 0; 905607f6718SJohan Hovold if (type->have_xmit_toggle) 906607f6718SJohan Hovold mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; 907607f6718SJohan Hovold 908f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_mode, mode); 90949036fd0SJohan Hovold if (ret) 91049036fd0SJohan Hovold return ret; 91149036fd0SJohan Hovold 91249036fd0SJohan Hovold /* 91349036fd0SJohan Hovold * Configure DTR and RTS as outputs and make sure they are deasserted 91449036fd0SJohan Hovold * (active low), and configure RI, CD, DSR and CTS as inputs. 91549036fd0SJohan Hovold */ 916958d6b95SJohan Hovold mask = XR_GPIO_DTR | XR_GPIO_RTS; 917f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_direction, mask); 91849036fd0SJohan Hovold if (ret) 91949036fd0SJohan Hovold return ret; 92049036fd0SJohan Hovold 921f865e614SJohan Hovold ret = xr_set_reg_uart(port, type->gpio_set, mask); 92249036fd0SJohan Hovold if (ret) 92349036fd0SJohan Hovold return ret; 92449036fd0SJohan Hovold 92549036fd0SJohan Hovold return 0; 92649036fd0SJohan Hovold } 92749036fd0SJohan Hovold 92823b7998eSJohan Hovold static int xr_port_probe(struct usb_serial_port *port) 92923b7998eSJohan Hovold { 93023b7998eSJohan Hovold struct usb_interface_descriptor *desc; 931f865e614SJohan Hovold const struct xr_type *type; 93223b7998eSJohan Hovold struct xr_data *data; 933f865e614SJohan Hovold enum xr_type_id type_id; 93449036fd0SJohan Hovold int ret; 93523b7998eSJohan Hovold 936f865e614SJohan Hovold type_id = (int)(unsigned long)usb_get_serial_data(port->serial); 937f865e614SJohan Hovold type = &xr_types[type_id]; 938f865e614SJohan Hovold 93923b7998eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL); 94023b7998eSJohan Hovold if (!data) 94123b7998eSJohan Hovold return -ENOMEM; 94223b7998eSJohan Hovold 943f865e614SJohan Hovold data->type = type; 944f865e614SJohan Hovold 94523b7998eSJohan Hovold desc = &port->serial->interface->cur_altsetting->desc; 946607f6718SJohan Hovold if (type_id == XR21V141X) 94723b7998eSJohan Hovold data->channel = desc->bInterfaceNumber / 2; 948607f6718SJohan Hovold else 949607f6718SJohan Hovold data->channel = desc->bInterfaceNumber; 95023b7998eSJohan Hovold 95123b7998eSJohan Hovold usb_set_serial_port_data(port, data); 95223b7998eSJohan Hovold 953607f6718SJohan Hovold if (type->custom_driver) { 954607f6718SJohan Hovold ret = xr_set_reg_uart(port, type->custom_driver, 955607f6718SJohan Hovold XR_CUSTOM_DRIVER_ACTIVE); 956607f6718SJohan Hovold if (ret) 957607f6718SJohan Hovold goto err_free; 958607f6718SJohan Hovold } 959607f6718SJohan Hovold 960f865e614SJohan Hovold ret = xr_gpio_init(port, type); 96149036fd0SJohan Hovold if (ret) 96249036fd0SJohan Hovold goto err_free; 96349036fd0SJohan Hovold 96423b7998eSJohan Hovold return 0; 96549036fd0SJohan Hovold 96649036fd0SJohan Hovold err_free: 96749036fd0SJohan Hovold kfree(data); 96849036fd0SJohan Hovold 96949036fd0SJohan Hovold return ret; 97023b7998eSJohan Hovold } 97123b7998eSJohan Hovold 97223b7998eSJohan Hovold static void xr_port_remove(struct usb_serial_port *port) 97323b7998eSJohan Hovold { 97423b7998eSJohan Hovold struct xr_data *data = usb_get_serial_port_data(port); 97523b7998eSJohan Hovold 97623b7998eSJohan Hovold kfree(data); 97723b7998eSJohan Hovold } 97823b7998eSJohan Hovold 979f865e614SJohan Hovold #define XR_DEVICE(vid, pid, type) \ 980f865e614SJohan Hovold USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ 981f865e614SJohan Hovold .driver_info = (type) 982f865e614SJohan Hovold 983c2d405aaSManivannan Sadhasivam static const struct usb_device_id id_table[] = { 9846da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1400, XR2280X) }, 9856da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1401, XR2280X) }, 9866da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1402, XR2280X) }, 9876da99f9dSJohan Hovold { XR_DEVICE(0x04e2, 0x1403, XR2280X) }, 988f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, 9894099d4baSJohan Hovold { XR_DEVICE(0x04e2, 0x1411, XR21B1411) }, 990f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, 991f865e614SJohan Hovold { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, 992607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, 993607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, 994607f6718SJohan Hovold { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, 995c2d405aaSManivannan Sadhasivam { } 996c2d405aaSManivannan Sadhasivam }; 997c2d405aaSManivannan Sadhasivam MODULE_DEVICE_TABLE(usb, id_table); 998c2d405aaSManivannan Sadhasivam 999c2d405aaSManivannan Sadhasivam static struct usb_serial_driver xr_device = { 1000c2d405aaSManivannan Sadhasivam .driver = { 1001c2d405aaSManivannan Sadhasivam .owner = THIS_MODULE, 1002c2d405aaSManivannan Sadhasivam .name = "xr_serial", 1003c2d405aaSManivannan Sadhasivam }, 1004c2d405aaSManivannan Sadhasivam .id_table = id_table, 1005c2d405aaSManivannan Sadhasivam .num_ports = 1, 1006c2d405aaSManivannan Sadhasivam .probe = xr_probe, 100723b7998eSJohan Hovold .port_probe = xr_port_probe, 100823b7998eSJohan Hovold .port_remove = xr_port_remove, 1009c2d405aaSManivannan Sadhasivam .open = xr_open, 1010c2d405aaSManivannan Sadhasivam .close = xr_close, 1011c2d405aaSManivannan Sadhasivam .break_ctl = xr_break_ctl, 1012c2d405aaSManivannan Sadhasivam .set_termios = xr_set_termios, 1013c2d405aaSManivannan Sadhasivam .tiocmget = xr_tiocmget, 1014c2d405aaSManivannan Sadhasivam .tiocmset = xr_tiocmset, 1015c2d405aaSManivannan Sadhasivam .dtr_rts = xr_dtr_rts 1016c2d405aaSManivannan Sadhasivam }; 1017c2d405aaSManivannan Sadhasivam 1018c2d405aaSManivannan Sadhasivam static struct usb_serial_driver * const serial_drivers[] = { 1019c2d405aaSManivannan Sadhasivam &xr_device, NULL 1020c2d405aaSManivannan Sadhasivam }; 1021c2d405aaSManivannan Sadhasivam 1022c2d405aaSManivannan Sadhasivam module_usb_serial_driver(serial_drivers, id_table); 1023c2d405aaSManivannan Sadhasivam 1024c2d405aaSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); 1025c2d405aaSManivannan Sadhasivam MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); 1026c2d405aaSManivannan Sadhasivam MODULE_LICENSE("GPL"); 1027