The same driver is used by a wide range of MaxLinear devices. Other models are close enough to use the same driver, but they use a different register set. So, instead of having the registers hardcoded at the driver, use a table. This will allow further patches to add support for other devices. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@xxxxxxxxxx> --- drivers/usb/serial/xr_serial.c | 151 ++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 38 deletions(-) diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 169c7ef11d73..518c4725431a 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -28,10 +28,6 @@ struct xr_txrx_clk_mask { #define MIN_SPEED 46U #define MAX_SPEED XR_INT_OSC_HZ -/* USB Requests */ -#define SET_REQ 0 -#define GET_REQ 1 - #define CLOCK_DIVISOR_0 0x04 #define CLOCK_DIVISOR_1 0x05 #define CLOCK_DIVISOR_2 0x06 @@ -93,29 +89,73 @@ struct xr_txrx_clk_mask { #define UART_MODE_RS485 0x3 #define UART_MODE_RS485_ADDR 0x4 -#define XR21V141X_REG_ENABLE 0x03 -#define XR21V141X_REG_FORMAT 0x0b -#define XR21V141X_REG_FLOW_CTRL 0x0c -#define XR21V141X_REG_XON_CHAR 0x10 -#define XR21V141X_REG_XOFF_CHAR 0x11 -#define XR21V141X_REG_LOOPBACK 0x12 -#define XR21V141X_REG_TX_BREAK 0x14 -#define XR21V141X_REG_RS845_DELAY 0x15 -#define XR21V141X_REG_GPIO_MODE 0x1a -#define XR21V141X_REG_GPIO_DIR 0x1b -#define XR21V141X_REG_GPIO_INT_MASK 0x1c -#define XR21V141X_REG_GPIO_SET 0x1d -#define XR21V141X_REG_GPIO_CLR 0x1e -#define XR21V141X_REG_GPIO_STATUS 0x1f +enum xr_model { + XR21V141X, + MAX_XR_MODELS +}; + +enum xr_hal_type { + REG_ENABLE, + REG_FORMAT, + REG_FLOW_CTRL, + REG_XON_CHAR, + REG_XOFF_CHAR, + REG_TX_BREAK, + REG_RS485_DELAY, + REG_GPIO_MODE, + REG_GPIO_DIR, + REG_GPIO_SET, + REG_GPIO_CLR, + REG_GPIO_STATUS, + REG_GPIO_INT_MASK, + REG_CUSTOMIZED_INT, + REG_GPIO_PULL_UP_ENABLE, + REG_GPIO_PULL_DOWN_ENABLE, + REG_LOOPBACK, + REG_LOW_LATENCY, + REG_CUSTOM_DRIVER, + + REQ_SET, + REQ_GET, + + MAX_XR_HAL_TYPE +}; + +static const int xr_hal_table[MAX_XR_MODELS][MAX_XR_HAL_TYPE] = { + [XR21V141X] = { + [REG_ENABLE] = 0x03, + [REG_FORMAT] = 0x0b, + [REG_FLOW_CTRL] = 0x0c, + [REG_XON_CHAR] = 0x10, + [REG_XOFF_CHAR] = 0x11, + [REG_LOOPBACK] = 0x12, + [REG_TX_BREAK] = 0x14, + [REG_RS485_DELAY] = 0x15, + [REG_GPIO_MODE] = 0x1a, + [REG_GPIO_DIR] = 0x1b, + [REG_GPIO_INT_MASK] = 0x1c, + [REG_GPIO_SET] = 0x1d, + [REG_GPIO_CLR] = 0x1e, + [REG_GPIO_STATUS] = 0x1f, + + [REQ_SET] = 0, + [REQ_GET] = 1, + } +}; + +struct xr_port_private { + enum xr_model model; +}; static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); struct usb_serial *serial = port->serial; int ret; ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - SET_REQ, + xr_hal_table[port_priv->model][REQ_SET], USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, val, reg | (block << 8), NULL, 0, USB_CTRL_SET_TIMEOUT); @@ -129,6 +169,7 @@ static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); struct usb_serial *serial = port->serial; u8 *dmabuf; int ret; @@ -139,7 +180,7 @@ static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - GET_REQ, + xr_hal_table[port_priv->model][REQ_GET], USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, reg | (block << 8), dmabuf, 1, USB_CTRL_GET_TIMEOUT); @@ -182,6 +223,7 @@ static int xr_set_reg_um(struct usb_serial_port *port, u8 reg, u8 val) */ static int xr_uart_enable(struct usb_serial_port *port) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); int ret; ret = xr_set_reg_um(port, UM_FIFO_ENABLE_REG, @@ -189,7 +231,7 @@ static int xr_uart_enable(struct usb_serial_port *port) if (ret) return ret; - ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, + ret = xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_ENABLE], UART_ENABLE_TX | UART_ENABLE_RX); if (ret) return ret; @@ -198,16 +240,18 @@ static int xr_uart_enable(struct usb_serial_port *port) UM_ENABLE_TX_FIFO | UM_ENABLE_RX_FIFO); if (ret) - xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); + xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_ENABLE], 0); return ret; } static int xr_uart_disable(struct usb_serial_port *port) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); int ret; - ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); + ret = xr_set_reg_uart(port, + xr_hal_table[port_priv->model][REG_ENABLE], 0); if (ret) return ret; @@ -219,10 +263,13 @@ static int xr_uart_disable(struct usb_serial_port *port) static int xr_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); u8 status; int ret; - ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_STATUS, &status); + ret = xr_get_reg_uart(port, + xr_hal_table[port_priv->model][REG_GPIO_STATUS], + &status); if (ret) return ret; @@ -243,6 +290,7 @@ static int xr_tiocmget(struct tty_struct *tty) static int xr_tiocmset_port(struct usb_serial_port *port, unsigned int set, unsigned int clear) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); u8 gpio_set = 0; u8 gpio_clr = 0; int ret = 0; @@ -259,10 +307,14 @@ static int xr_tiocmset_port(struct usb_serial_port *port, /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ if (gpio_clr) - ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_CLR, gpio_clr); + ret = xr_set_reg_uart(port, + xr_hal_table[port_priv->model][REG_GPIO_CLR], + gpio_clr); if (gpio_set) - ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_SET, gpio_set); + ret = xr_set_reg_uart(port, + xr_hal_table[port_priv->model][REG_GPIO_SET], + gpio_set); return ret; } @@ -286,6 +338,7 @@ static void xr_dtr_rts(struct usb_serial_port *port, int on) static void xr_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); u8 state; if (break_state == 0) @@ -295,7 +348,8 @@ static void xr_break_ctl(struct tty_struct *tty, int break_state) dev_dbg(&port->dev, "Turning break %s\n", state == UART_BREAK_OFF ? "off" : "on"); - xr_set_reg_uart(port, XR21V141X_REG_TX_BREAK, state); + xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_TX_BREAK], + state); } /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ @@ -405,10 +459,11 @@ static void xr_set_flow_mode(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); u8 flow, gpio_mode; int ret; - ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_MODE, &gpio_mode); + ret = xr_get_reg_uart(port, xr_hal_table[port_priv->model][REG_GPIO_MODE], &gpio_mode); if (ret) return; @@ -426,8 +481,8 @@ static void xr_set_flow_mode(struct tty_struct *tty, dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); flow = UART_FLOW_MODE_SW; - xr_set_reg_uart(port, XR21V141X_REG_XON_CHAR, start_char); - xr_set_reg_uart(port, XR21V141X_REG_XOFF_CHAR, stop_char); + xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_XON_CHAR], start_char); + xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_XOFF_CHAR], stop_char); } else { dev_dbg(&port->dev, "Disabling flow ctrl\n"); flow = UART_FLOW_MODE_NONE; @@ -438,10 +493,10 @@ static void xr_set_flow_mode(struct tty_struct *tty, * FLOW_CONTROL register. */ xr_uart_disable(port); - xr_set_reg_uart(port, XR21V141X_REG_FLOW_CTRL, flow); + xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_FLOW_CTRL], flow); xr_uart_enable(port); - xr_set_reg_uart(port, XR21V141X_REG_GPIO_MODE, gpio_mode); + xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_GPIO_MODE], gpio_mode); if (C_BAUD(tty) == B0) xr_dtr_rts(port, 0); @@ -453,9 +508,9 @@ static void xr_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); struct ktermios *termios = &tty->termios; u8 bits = 0; - int ret; if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) xr_set_baudrate(tty, port); @@ -498,15 +553,16 @@ static void xr_set_termios(struct tty_struct *tty, else bits |= UART_STOP_1; - ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); - if (ret) - return; + xr_set_reg_uart(port, + xr_hal_table[port_priv->model][REG_FORMAT], + bits); xr_set_flow_mode(tty, port, old_termios); } static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) { + struct xr_port_private *port_priv = usb_get_serial_data(port->serial); u8 gpio_dir; int ret; @@ -521,7 +577,7 @@ static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) * inputs. */ gpio_dir = UART_MODE_DTR | UART_MODE_RTS; - xr_set_reg_uart(port, XR21V141X_REG_GPIO_DIR, gpio_dir); + xr_set_reg_uart(port, xr_hal_table[port_priv->model][REG_GPIO_DIR], gpio_dir); /* Setup termios */ if (tty) @@ -545,15 +601,33 @@ static void xr_close(struct usb_serial_port *port) static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) { + struct xr_port_private *port_priv; + /* Don't bind to control interface */ if (serial->interface->cur_altsetting->desc.bInterfaceNumber == 0) return -ENODEV; + port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL); + if (!port_priv) + return -ENOMEM; + + port_priv->model = id->driver_info; + + usb_set_serial_data(serial, port_priv); + return 0; } +static void xr_disconnect(struct usb_serial *serial) +{ + struct xr_port_private *port_priv = usb_get_serial_data(serial); + + usb_set_serial_data(serial, 0); + kfree(port_priv); +} + static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x04e2, 0x1410) }, /* XR21V141X */ + { USB_DEVICE(0x04e2, 0x1410), .driver_info = XR21V141X}, { } }; MODULE_DEVICE_TABLE(usb, id_table); @@ -566,6 +640,7 @@ static struct usb_serial_driver xr_device = { .id_table = id_table, .num_ports = 1, .probe = xr_probe, + .disconnect = xr_disconnect, .open = xr_open, .close = xr_close, .break_ctl = xr_break_ctl, -- 2.30.2