// SPDX-License-Identifier: GPL-2.0+
/*
* MaxLinear/Exar USB to Serial driver
*
* Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org>
* Copyright (c) 2021 Johan Hovold <johan@kernel.org>
*
* Based on the initial driver written by Patong Yang:
*
* https://lore.kernel.org/r/20180404070634.nhspvmxcjwfgjkcv@advantechmxl-desktop
*
* Copyright (c) 2018 Patong Yang <patong.mxl@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/usb/serial.h>
struct xr_txrx_clk_mask {
u16 tx;
u16 rx0;
u16 rx1;
};
#define XR_INT_OSC_HZ 48000000U
#define XR21V141X_MIN_SPEED 46U
#define XR21V141X_MAX_SPEED XR_INT_OSC_HZ
/* XR21V141X register blocks */
#define XR21V141X_UART_REG_BLOCK 0
#define XR21V141X_UM_REG_BLOCK 4
#define XR21V141X_UART_CUSTOM_BLOCK 0x66
/* XR21V141X UART registers */
#define XR21V141X_CLOCK_DIVISOR_0 0x04
#define XR21V141X_CLOCK_DIVISOR_1 0x05
#define XR21V141X_CLOCK_DIVISOR_2 0x06
#define XR21V141X_TX_CLOCK_MASK_0 0x07
#define XR21V141X_TX_CLOCK_MASK_1 0x08
#define XR21V141X_RX_CLOCK_MASK_0 0x09
#define XR21V141X_RX_CLOCK_MASK_1 0x0a
#define XR21V141X_REG_FORMAT 0x0b
/* XR21V141X UART Manager registers */
#define XR21V141X_UM_FIFO_ENABLE_REG 0x10
#define XR21V141X_UM_ENABLE_TX_FIFO 0x01
#define XR21V141X_UM_ENABLE_RX_FIFO 0x02
#define XR21V141X_UM_RX_FIFO_RESET 0x18
#define XR21V141X_UM_TX_FIFO_RESET 0x1c
#define XR_UART_ENABLE_TX 0x1
#define XR_UART_ENABLE_RX 0x2
#define XR_GPIO_RI BIT(0)
#define XR_GPIO_CD BIT(1)
#define XR_GPIO_DSR BIT(2)
#define XR_GPIO_DTR BIT(3)
#define XR_GPIO_CTS BIT(4)
#define XR_GPIO_RTS BIT(5)
#define XR_GPIO_CLK BIT(6)
#define XR_GPIO_XEN BIT(7)
#define XR_GPIO_TXT BIT(8)
#define XR_GPIO_RXT BIT(9)
#define XR_UART_DATA_MASK GENMASK(3, 0)
#define XR_UART_DATA_7 0x7
#define XR_UART_DATA_8 0x8
#define XR_UART_PARITY_MASK GENMASK(6, 4)
#define XR_UART_PARITY_SHIFT 4
#define XR_UART_PARITY_NONE (0x0 << XR_UART_PARITY_SHIFT)
#define XR_UART_PARITY_ODD (0x1 << XR_UART_PARITY_SHIFT)
#define XR_UART_PARITY_EVEN (0x2 << XR_UART_PARITY_SHIFT)
#define XR_UART_PARITY_MARK (0x3 << XR_UART_PARITY_SHIFT)
#define XR_UART_PARITY_SPACE (0x4 << XR_UART_PARITY_SHIFT)
#define XR_UART_STOP_MASK BIT(7)
#define XR_UART_STOP_SHIFT 7
#define XR_UART_STOP_1 (0x0 << XR_UART_STOP_SHIFT)
#define XR_UART_STOP_2 (0x1 << XR_UART_STOP_SHIFT)
#define XR_UART_FLOW_MODE_NONE 0x0
#define XR_UART_FLOW_MODE_HW 0x1
#define XR_UART_FLOW_MODE_SW 0x2
#define XR_GPIO_MODE_SEL_MASK GENMASK(2, 0)
#define XR_GPIO_MODE_SEL_RTS_CTS 0x1
#define XR_GPIO_MODE_SEL_DTR_DSR 0x2
#define XR_GPIO_MODE_SEL_RS485 0x3
#define XR_GPIO_MODE_SEL_RS485_ADDR 0x4
#define XR_GPIO_MODE_RS485_TX_H 0x8
#define XR_GPIO_MODE_TX_TOGGLE 0x100
#define XR_GPIO_MODE_RX_TOGGLE 0x200
#define XR_FIFO_RESET 0x1
#define XR_CUSTOM_DRIVER_ACTIVE 0x1
static int xr21v141x_uart_enable(struct usb_serial_port *port);
static int xr21v141x_uart_disable(struct usb_serial_port *port);
static int xr21v141x_fifo_reset(struct usb_serial_port *port);
static void xr21v141x_set_line_settings(struct tty_struct *tty,
struct usb_serial_port *port,
const struct ktermios *old_termios);
struct xr_type {
int reg_width;
u8 reg_recipient;
u8 set_reg;
u8 get_reg;
u16 uart_enable;
u16 flow_control;
u16 xon_char;
u16 xoff_char;
u16 tx_break;
u16 gpio_mode;
u16 gpio_direction;
u16 gpio_set;
u16 gpio_clear;
u16 gpio_status;
u16 tx_fifo_reset;
u16 rx_fifo_reset;
u16 custom_driver;
bool have_5_6_bit_mode;
bool have_xmit_toggle;
int (*enable)(struct usb_serial_port *port);
int (*disable)(struct usb_serial_port *port);
int (*fifo_reset)(struct usb_serial_port *port);
void (*set_line_settings)(struct tty_struct *tty,
struct usb_serial_port *port,
const struct ktermios *old_termios);
};
enum xr_type_id {
XR21V141X,
XR21B142X,
XR21B1411,
XR2280X,
XR_TYPE_COUNT,
};
static const struct xr_type xr_types[] = {
[XR21V141X] = {
.reg_width = 8,
.reg_recipient = USB_RECIP_DEVICE,
.set_reg = 0x00,
.get_reg = 0x01,
.uart_enable = 0x03,
.flow_control = 0x0c,
.xon_char = 0x10,
.xoff_char = 0x11,
.tx_break = 0x14,
.gpio_mode = 0x1a,
.gpio_direction = 0x1b,
.gpio_set = 0x1d,
.gpio_clear = 0x1e,
.gpio_status = 0x1f,
.enable = xr21v141x_uart_enable,
.disable = xr21v141x_uart_disable,
.fifo_reset = xr21v141x_fifo_reset,
.set_line_settings = xr21v141x_set_line_settings,
},
[XR21B142X] = {
.reg_width = 16,
.reg_recipient = USB_RECIP_INTERFACE,
.set_reg = 0x00,
.get_reg = 0x00,
.uart_enable = 0x00,
.flow_control = 0x06,
.xon_char = 0x07,
.xoff_char = 0x08,
.tx_break = 0x0a,
.gpio_mode = 0x0c,
.gpio_direction = 0x0d,
.gpio_set = 0x0e,
.gpio_clear = 0x0f,
.gpio_status = 0x10,
.tx_fifo_reset = 0x40,
.rx_fifo_reset = 0x43,
.custom_driver = 0x60,
.have_5_6_bit_mode = true,
.have_xmit_toggle = true,
},
[XR21B1411] = {
.reg_width = 12,
.reg_recipient = USB_RECIP_DEVICE,
.set_reg = 0x00,
.get_reg = 0x01,
.uart_enable = 0xc00,
.flow_control = 0xc06,
.xon_char = 0xc07,
.xoff_char = 0xc08,
.tx_break = 0xc0a,
.gpio_mode = 0xc0c,
.gpio_direction = 0xc0d,
.gpio_set = 0xc0e,
.gpio_clear = 0xc0f,
.gpio_status = 0xc10,
.tx_fifo_reset = 0xc80,
.rx_fifo_reset = 0xcc0,
.custom_driver = 0x20d,
},