162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Fintek F81232 USB to serial adaptor driver 462306a36Sopenharmony_ci * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org) 762306a36Sopenharmony_ci * Copyright (C) 2012 Linux Foundation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/tty.h> 1462306a36Sopenharmony_ci#include <linux/tty_driver.h> 1562306a36Sopenharmony_ci#include <linux/tty_flip.h> 1662306a36Sopenharmony_ci#include <linux/serial.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/moduleparam.h> 1962306a36Sopenharmony_ci#include <linux/mutex.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci#include <linux/usb.h> 2262306a36Sopenharmony_ci#include <linux/usb/serial.h> 2362306a36Sopenharmony_ci#include <linux/serial_reg.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define F81232_ID \ 2662306a36Sopenharmony_ci { USB_DEVICE(0x1934, 0x0706) } /* 1 port UART device */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define F81534A_SERIES_ID \ 2962306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \ 3062306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \ 3162306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \ 3262306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \ 3362306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \ 3462306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \ 3562306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \ 3662306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \ 3762306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define F81534A_CTRL_ID \ 4062306a36Sopenharmony_ci { USB_DEVICE(0x2c42, 0x16f8) } /* Global control device */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic const struct usb_device_id f81232_id_table[] = { 4362306a36Sopenharmony_ci F81232_ID, 4462306a36Sopenharmony_ci { } /* Terminating entry */ 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct usb_device_id f81534a_id_table[] = { 4862306a36Sopenharmony_ci F81534A_SERIES_ID, 4962306a36Sopenharmony_ci { } /* Terminating entry */ 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic const struct usb_device_id f81534a_ctrl_id_table[] = { 5362306a36Sopenharmony_ci F81534A_CTRL_ID, 5462306a36Sopenharmony_ci { } /* Terminating entry */ 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct usb_device_id combined_id_table[] = { 5862306a36Sopenharmony_ci F81232_ID, 5962306a36Sopenharmony_ci F81534A_SERIES_ID, 6062306a36Sopenharmony_ci F81534A_CTRL_ID, 6162306a36Sopenharmony_ci { } /* Terminating entry */ 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, combined_id_table); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Maximum baudrate for F81232 */ 6662306a36Sopenharmony_ci#define F81232_MAX_BAUDRATE 1500000 6762306a36Sopenharmony_ci#define F81232_DEF_BAUDRATE 9600 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* USB Control EP parameter */ 7062306a36Sopenharmony_ci#define F81232_REGISTER_REQUEST 0xa0 7162306a36Sopenharmony_ci#define F81232_GET_REGISTER 0xc0 7262306a36Sopenharmony_ci#define F81232_SET_REGISTER 0x40 7362306a36Sopenharmony_ci#define F81534A_ACCESS_REG_RETRY 2 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define SERIAL_BASE_ADDRESS 0x0120 7662306a36Sopenharmony_ci#define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS) 7762306a36Sopenharmony_ci#define INTERRUPT_ENABLE_REGISTER (0x01 + SERIAL_BASE_ADDRESS) 7862306a36Sopenharmony_ci#define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS) 7962306a36Sopenharmony_ci#define LINE_CONTROL_REGISTER (0x03 + SERIAL_BASE_ADDRESS) 8062306a36Sopenharmony_ci#define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS) 8162306a36Sopenharmony_ci#define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) 8262306a36Sopenharmony_ci#define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * F81232 Clock registers (106h) 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Bit1-0: Clock source selector 8862306a36Sopenharmony_ci * 00: 1.846MHz. 8962306a36Sopenharmony_ci * 01: 18.46MHz. 9062306a36Sopenharmony_ci * 10: 24MHz. 9162306a36Sopenharmony_ci * 11: 14.77MHz. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci#define F81232_CLK_REGISTER 0x106 9462306a36Sopenharmony_ci#define F81232_CLK_1_846_MHZ 0 9562306a36Sopenharmony_ci#define F81232_CLK_18_46_MHZ BIT(0) 9662306a36Sopenharmony_ci#define F81232_CLK_24_MHZ BIT(1) 9762306a36Sopenharmony_ci#define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) 9862306a36Sopenharmony_ci#define F81232_CLK_MASK GENMASK(1, 0) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define F81534A_MODE_REG 0x107 10162306a36Sopenharmony_ci#define F81534A_TRIGGER_MASK GENMASK(3, 2) 10262306a36Sopenharmony_ci#define F81534A_TRIGGER_MULTIPLE_4X BIT(3) 10362306a36Sopenharmony_ci#define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Serial port self GPIO control, 2bytes [control&output data][input data] */ 10662306a36Sopenharmony_ci#define F81534A_GPIO_REG 0x10e 10762306a36Sopenharmony_ci#define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */ 10862306a36Sopenharmony_ci#define F81534A_GPIO_MODE1_DIR BIT(5) 10962306a36Sopenharmony_ci#define F81534A_GPIO_MODE0_DIR BIT(4) 11062306a36Sopenharmony_ci#define F81534A_GPIO_MODE2_OUTPUT BIT(2) 11162306a36Sopenharmony_ci#define F81534A_GPIO_MODE1_OUTPUT BIT(1) 11262306a36Sopenharmony_ci#define F81534A_GPIO_MODE0_OUTPUT BIT(0) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define F81534A_CTRL_CMD_ENABLE_PORT 0x116 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct f81232_private { 11762306a36Sopenharmony_ci struct mutex lock; 11862306a36Sopenharmony_ci u8 modem_control; 11962306a36Sopenharmony_ci u8 modem_status; 12062306a36Sopenharmony_ci u8 shadow_lcr; 12162306a36Sopenharmony_ci speed_t baud_base; 12262306a36Sopenharmony_ci struct work_struct lsr_work; 12362306a36Sopenharmony_ci struct work_struct interrupt_work; 12462306a36Sopenharmony_ci struct usb_serial_port *port; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 }; 12862306a36Sopenharmony_cistatic u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ, 12962306a36Sopenharmony_ci F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ }; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int calc_baud_divisor(speed_t baudrate, speed_t clockrate) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return DIV_ROUND_CLOSEST(clockrate, baudrate); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int status; 13962306a36Sopenharmony_ci struct usb_device *dev = port->serial->dev; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci status = usb_control_msg_recv(dev, 14262306a36Sopenharmony_ci 0, 14362306a36Sopenharmony_ci F81232_REGISTER_REQUEST, 14462306a36Sopenharmony_ci F81232_GET_REGISTER, 14562306a36Sopenharmony_ci reg, 14662306a36Sopenharmony_ci 0, 14762306a36Sopenharmony_ci val, 14862306a36Sopenharmony_ci sizeof(*val), 14962306a36Sopenharmony_ci USB_CTRL_GET_TIMEOUT, 15062306a36Sopenharmony_ci GFP_KERNEL); 15162306a36Sopenharmony_ci if (status) { 15262306a36Sopenharmony_ci dev_err(&port->dev, "%s failed status: %d\n", __func__, status); 15362306a36Sopenharmony_ci status = usb_translate_errors(status); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return status; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int status; 16262306a36Sopenharmony_ci struct usb_device *dev = port->serial->dev; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci status = usb_control_msg_send(dev, 16562306a36Sopenharmony_ci 0, 16662306a36Sopenharmony_ci F81232_REGISTER_REQUEST, 16762306a36Sopenharmony_ci F81232_SET_REGISTER, 16862306a36Sopenharmony_ci reg, 16962306a36Sopenharmony_ci 0, 17062306a36Sopenharmony_ci &val, 17162306a36Sopenharmony_ci sizeof(val), 17262306a36Sopenharmony_ci USB_CTRL_SET_TIMEOUT, 17362306a36Sopenharmony_ci GFP_KERNEL); 17462306a36Sopenharmony_ci if (status) { 17562306a36Sopenharmony_ci dev_err(&port->dev, "%s failed status: %d\n", __func__, status); 17662306a36Sopenharmony_ci status = usb_translate_errors(status); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return status; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int f81232_set_mask_register(struct usb_serial_port *port, u16 reg, 18362306a36Sopenharmony_ci u8 mask, u8 val) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int status; 18662306a36Sopenharmony_ci u8 tmp; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci status = f81232_get_register(port, reg, &tmp); 18962306a36Sopenharmony_ci if (status) 19062306a36Sopenharmony_ci return status; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci tmp = (tmp & ~mask) | (val & mask); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return f81232_set_register(port, reg, tmp); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void f81232_read_msr(struct usb_serial_port *port) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int status; 20062306a36Sopenharmony_ci u8 current_msr; 20162306a36Sopenharmony_ci struct tty_struct *tty; 20262306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci mutex_lock(&priv->lock); 20562306a36Sopenharmony_ci status = f81232_get_register(port, MODEM_STATUS_REGISTER, 20662306a36Sopenharmony_ci ¤t_msr); 20762306a36Sopenharmony_ci if (status) { 20862306a36Sopenharmony_ci dev_err(&port->dev, "%s fail, status: %d\n", __func__, status); 20962306a36Sopenharmony_ci mutex_unlock(&priv->lock); 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!(current_msr & UART_MSR_ANY_DELTA)) { 21462306a36Sopenharmony_ci mutex_unlock(&priv->lock); 21562306a36Sopenharmony_ci return; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci priv->modem_status = current_msr; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (current_msr & UART_MSR_DCTS) 22162306a36Sopenharmony_ci port->icount.cts++; 22262306a36Sopenharmony_ci if (current_msr & UART_MSR_DDSR) 22362306a36Sopenharmony_ci port->icount.dsr++; 22462306a36Sopenharmony_ci if (current_msr & UART_MSR_TERI) 22562306a36Sopenharmony_ci port->icount.rng++; 22662306a36Sopenharmony_ci if (current_msr & UART_MSR_DDCD) { 22762306a36Sopenharmony_ci port->icount.dcd++; 22862306a36Sopenharmony_ci tty = tty_port_tty_get(&port->port); 22962306a36Sopenharmony_ci if (tty) { 23062306a36Sopenharmony_ci usb_serial_handle_dcd_change(port, tty, 23162306a36Sopenharmony_ci current_msr & UART_MSR_DCD); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci tty_kref_put(tty); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci wake_up_interruptible(&port->port.delta_msr_wait); 23862306a36Sopenharmony_ci mutex_unlock(&priv->lock); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int f81232_set_mctrl(struct usb_serial_port *port, 24262306a36Sopenharmony_ci unsigned int set, unsigned int clear) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci u8 val; 24562306a36Sopenharmony_ci int status; 24662306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) 24962306a36Sopenharmony_ci return 0; /* no change */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* 'set' takes precedence over 'clear' */ 25262306a36Sopenharmony_ci clear &= ~set; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* force enable interrupt with OUT2 */ 25562306a36Sopenharmony_ci mutex_lock(&priv->lock); 25662306a36Sopenharmony_ci val = UART_MCR_OUT2 | priv->modem_control; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (clear & TIOCM_DTR) 25962306a36Sopenharmony_ci val &= ~UART_MCR_DTR; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (clear & TIOCM_RTS) 26262306a36Sopenharmony_ci val &= ~UART_MCR_RTS; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (set & TIOCM_DTR) 26562306a36Sopenharmony_ci val |= UART_MCR_DTR; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (set & TIOCM_RTS) 26862306a36Sopenharmony_ci val |= UART_MCR_RTS; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s new:%02x old:%02x\n", __func__, 27162306a36Sopenharmony_ci val, priv->modem_control); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci status = f81232_set_register(port, MODEM_CONTROL_REGISTER, val); 27462306a36Sopenharmony_ci if (status) { 27562306a36Sopenharmony_ci dev_err(&port->dev, "%s set MCR status < 0\n", __func__); 27662306a36Sopenharmony_ci mutex_unlock(&priv->lock); 27762306a36Sopenharmony_ci return status; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci priv->modem_control = val; 28162306a36Sopenharmony_ci mutex_unlock(&priv->lock); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void f81232_update_line_status(struct usb_serial_port *port, 28762306a36Sopenharmony_ci unsigned char *data, 28862306a36Sopenharmony_ci size_t actual_length) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!actual_length) 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci switch (data[0] & 0x07) { 29662306a36Sopenharmony_ci case 0x00: /* msr change */ 29762306a36Sopenharmony_ci dev_dbg(&port->dev, "IIR: MSR Change: %02x\n", data[0]); 29862306a36Sopenharmony_ci schedule_work(&priv->interrupt_work); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case 0x02: /* tx-empty */ 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case 0x04: /* rx data available */ 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case 0x06: /* lsr change */ 30562306a36Sopenharmony_ci /* we can forget it. the LSR will read from bulk-in */ 30662306a36Sopenharmony_ci dev_dbg(&port->dev, "IIR: LSR Change: %02x\n", data[0]); 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic void f81232_read_int_callback(struct urb *urb) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 31462306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 31562306a36Sopenharmony_ci unsigned int actual_length = urb->actual_length; 31662306a36Sopenharmony_ci int status = urb->status; 31762306a36Sopenharmony_ci int retval; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci switch (status) { 32062306a36Sopenharmony_ci case 0: 32162306a36Sopenharmony_ci /* success */ 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case -ECONNRESET: 32462306a36Sopenharmony_ci case -ENOENT: 32562306a36Sopenharmony_ci case -ESHUTDOWN: 32662306a36Sopenharmony_ci /* this urb is terminated, clean up */ 32762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n", 32862306a36Sopenharmony_ci __func__, status); 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n", 33262306a36Sopenharmony_ci __func__, status); 33362306a36Sopenharmony_ci goto exit; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, 33762306a36Sopenharmony_ci urb->actual_length, urb->transfer_buffer); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci f81232_update_line_status(port, data, actual_length); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ciexit: 34262306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 34362306a36Sopenharmony_ci if (retval) 34462306a36Sopenharmony_ci dev_err(&urb->dev->dev, 34562306a36Sopenharmony_ci "%s - usb_submit_urb failed with result %d\n", 34662306a36Sopenharmony_ci __func__, retval); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic char f81232_handle_lsr(struct usb_serial_port *port, u8 lsr) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 35262306a36Sopenharmony_ci char tty_flag = TTY_NORMAL; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (!(lsr & UART_LSR_BRK_ERROR_BITS)) 35562306a36Sopenharmony_ci return tty_flag; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (lsr & UART_LSR_BI) { 35862306a36Sopenharmony_ci tty_flag = TTY_BREAK; 35962306a36Sopenharmony_ci port->icount.brk++; 36062306a36Sopenharmony_ci usb_serial_handle_break(port); 36162306a36Sopenharmony_ci } else if (lsr & UART_LSR_PE) { 36262306a36Sopenharmony_ci tty_flag = TTY_PARITY; 36362306a36Sopenharmony_ci port->icount.parity++; 36462306a36Sopenharmony_ci } else if (lsr & UART_LSR_FE) { 36562306a36Sopenharmony_ci tty_flag = TTY_FRAME; 36662306a36Sopenharmony_ci port->icount.frame++; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (lsr & UART_LSR_OE) { 37062306a36Sopenharmony_ci port->icount.overrun++; 37162306a36Sopenharmony_ci schedule_work(&priv->lsr_work); 37262306a36Sopenharmony_ci tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return tty_flag; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void f81232_process_read_urb(struct urb *urb) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 38162306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 38262306a36Sopenharmony_ci char tty_flag; 38362306a36Sopenharmony_ci unsigned int i; 38462306a36Sopenharmony_ci u8 lsr; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * When opening the port we get a 1-byte packet with the current LSR, 38862306a36Sopenharmony_ci * which we discard. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci if ((urb->actual_length < 2) || (urb->actual_length % 2)) 39162306a36Sopenharmony_ci return; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */ 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci for (i = 0; i < urb->actual_length; i += 2) { 39662306a36Sopenharmony_ci lsr = data[i]; 39762306a36Sopenharmony_ci tty_flag = f81232_handle_lsr(port, lsr); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (port->sysrq) { 40062306a36Sopenharmony_ci if (usb_serial_handle_sysrq_char(port, data[i + 1])) 40162306a36Sopenharmony_ci continue; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci tty_insert_flip_char(&port->port, data[i + 1], tty_flag); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void f81534a_process_read_urb(struct urb *urb) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 41362306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 41462306a36Sopenharmony_ci char tty_flag; 41562306a36Sopenharmony_ci unsigned int i; 41662306a36Sopenharmony_ci u8 lsr; 41762306a36Sopenharmony_ci u8 len; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (urb->actual_length < 3) { 42062306a36Sopenharmony_ci dev_err(&port->dev, "short message received: %d\n", 42162306a36Sopenharmony_ci urb->actual_length); 42262306a36Sopenharmony_ci return; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci len = data[0]; 42662306a36Sopenharmony_ci if (len != urb->actual_length) { 42762306a36Sopenharmony_ci dev_err(&port->dev, "malformed message received: %d (%d)\n", 42862306a36Sopenharmony_ci urb->actual_length, len); 42962306a36Sopenharmony_ci return; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* bulk-in data: [LEN][Data.....][LSR] */ 43362306a36Sopenharmony_ci lsr = data[len - 1]; 43462306a36Sopenharmony_ci tty_flag = f81232_handle_lsr(port, lsr); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (port->sysrq) { 43762306a36Sopenharmony_ci for (i = 1; i < len - 1; ++i) { 43862306a36Sopenharmony_ci if (!usb_serial_handle_sysrq_char(port, data[i])) { 43962306a36Sopenharmony_ci tty_insert_flip_char(&port->port, data[i], 44062306a36Sopenharmony_ci tty_flag); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci } else { 44462306a36Sopenharmony_ci tty_insert_flip_string_fixed_flag(&port->port, &data[1], 44562306a36Sopenharmony_ci tty_flag, len - 2); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int f81232_break_ctl(struct tty_struct *tty, int break_state) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 45462306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 45562306a36Sopenharmony_ci int status; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mutex_lock(&priv->lock); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (break_state) 46062306a36Sopenharmony_ci priv->shadow_lcr |= UART_LCR_SBC; 46162306a36Sopenharmony_ci else 46262306a36Sopenharmony_ci priv->shadow_lcr &= ~UART_LCR_SBC; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci status = f81232_set_register(port, LINE_CONTROL_REGISTER, 46562306a36Sopenharmony_ci priv->shadow_lcr); 46662306a36Sopenharmony_ci if (status) 46762306a36Sopenharmony_ci dev_err(&port->dev, "set break failed: %d\n", status); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci mutex_unlock(&priv->lock); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return status; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int f81232_find_clk(speed_t baudrate) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci int idx; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { 47962306a36Sopenharmony_ci if (baudrate <= baudrate_table[idx] && 48062306a36Sopenharmony_ci baudrate_table[idx] % baudrate == 0) 48162306a36Sopenharmony_ci return idx; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic void f81232_set_baudrate(struct tty_struct *tty, 48862306a36Sopenharmony_ci struct usb_serial_port *port, speed_t baudrate, 48962306a36Sopenharmony_ci speed_t old_baudrate) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 49262306a36Sopenharmony_ci u8 lcr; 49362306a36Sopenharmony_ci int divisor; 49462306a36Sopenharmony_ci int status = 0; 49562306a36Sopenharmony_ci int i; 49662306a36Sopenharmony_ci int idx; 49762306a36Sopenharmony_ci speed_t baud_list[] = { baudrate, old_baudrate, F81232_DEF_BAUDRATE }; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(baud_list); ++i) { 50062306a36Sopenharmony_ci baudrate = baud_list[i]; 50162306a36Sopenharmony_ci if (baudrate == 0) { 50262306a36Sopenharmony_ci tty_encode_baud_rate(tty, 0, 0); 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci idx = f81232_find_clk(baudrate); 50762306a36Sopenharmony_ci if (idx >= 0) { 50862306a36Sopenharmony_ci tty_encode_baud_rate(tty, baudrate, baudrate); 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (idx < 0) 51462306a36Sopenharmony_ci return; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci priv->baud_base = baudrate_table[idx]; 51762306a36Sopenharmony_ci divisor = calc_baud_divisor(baudrate, priv->baud_base); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci status = f81232_set_mask_register(port, F81232_CLK_REGISTER, 52062306a36Sopenharmony_ci F81232_CLK_MASK, clock_table[idx]); 52162306a36Sopenharmony_ci if (status) { 52262306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set CLK_REG: %d\n", 52362306a36Sopenharmony_ci __func__, status); 52462306a36Sopenharmony_ci return; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci status = f81232_get_register(port, LINE_CONTROL_REGISTER, 52862306a36Sopenharmony_ci &lcr); /* get LCR */ 52962306a36Sopenharmony_ci if (status) { 53062306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to get LCR: %d\n", 53162306a36Sopenharmony_ci __func__, status); 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci status = f81232_set_register(port, LINE_CONTROL_REGISTER, 53662306a36Sopenharmony_ci lcr | UART_LCR_DLAB); /* Enable DLAB */ 53762306a36Sopenharmony_ci if (status) { 53862306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set DLAB: %d\n", 53962306a36Sopenharmony_ci __func__, status); 54062306a36Sopenharmony_ci return; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci status = f81232_set_register(port, RECEIVE_BUFFER_REGISTER, 54462306a36Sopenharmony_ci divisor & 0x00ff); /* low */ 54562306a36Sopenharmony_ci if (status) { 54662306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set baudrate MSB: %d\n", 54762306a36Sopenharmony_ci __func__, status); 54862306a36Sopenharmony_ci goto reapply_lcr; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER, 55262306a36Sopenharmony_ci (divisor & 0xff00) >> 8); /* high */ 55362306a36Sopenharmony_ci if (status) { 55462306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set baudrate LSB: %d\n", 55562306a36Sopenharmony_ci __func__, status); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cireapply_lcr: 55962306a36Sopenharmony_ci status = f81232_set_register(port, LINE_CONTROL_REGISTER, 56062306a36Sopenharmony_ci lcr & ~UART_LCR_DLAB); 56162306a36Sopenharmony_ci if (status) { 56262306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set DLAB: %d\n", 56362306a36Sopenharmony_ci __func__, status); 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int f81232_port_enable(struct usb_serial_port *port) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci u8 val; 57062306a36Sopenharmony_ci int status; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* fifo on, trigger8, clear TX/RX*/ 57362306a36Sopenharmony_ci val = UART_FCR_TRIGGER_8 | UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | 57462306a36Sopenharmony_ci UART_FCR_CLEAR_XMIT; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci status = f81232_set_register(port, FIFO_CONTROL_REGISTER, val); 57762306a36Sopenharmony_ci if (status) { 57862306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set FCR: %d\n", 57962306a36Sopenharmony_ci __func__, status); 58062306a36Sopenharmony_ci return status; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* MSR Interrupt only, LSR will read from Bulk-in odd byte */ 58462306a36Sopenharmony_ci status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER, 58562306a36Sopenharmony_ci UART_IER_MSI); 58662306a36Sopenharmony_ci if (status) { 58762306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set IER: %d\n", 58862306a36Sopenharmony_ci __func__, status); 58962306a36Sopenharmony_ci return status; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int f81232_port_disable(struct usb_serial_port *port) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci int status; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER, 0); 60062306a36Sopenharmony_ci if (status) { 60162306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set IER: %d\n", 60262306a36Sopenharmony_ci __func__, status); 60362306a36Sopenharmony_ci return status; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void f81232_set_termios(struct tty_struct *tty, 61062306a36Sopenharmony_ci struct usb_serial_port *port, 61162306a36Sopenharmony_ci const struct ktermios *old_termios) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 61462306a36Sopenharmony_ci u8 new_lcr = 0; 61562306a36Sopenharmony_ci int status = 0; 61662306a36Sopenharmony_ci speed_t baudrate; 61762306a36Sopenharmony_ci speed_t old_baud; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Don't change anything if nothing has changed */ 62062306a36Sopenharmony_ci if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) 62162306a36Sopenharmony_ci return; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (C_BAUD(tty) == B0) 62462306a36Sopenharmony_ci f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); 62562306a36Sopenharmony_ci else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 62662306a36Sopenharmony_ci f81232_set_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci baudrate = tty_get_baud_rate(tty); 62962306a36Sopenharmony_ci if (baudrate > 0) { 63062306a36Sopenharmony_ci if (old_termios) 63162306a36Sopenharmony_ci old_baud = tty_termios_baud_rate(old_termios); 63262306a36Sopenharmony_ci else 63362306a36Sopenharmony_ci old_baud = F81232_DEF_BAUDRATE; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci f81232_set_baudrate(tty, port, baudrate, old_baud); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (C_PARENB(tty)) { 63962306a36Sopenharmony_ci new_lcr |= UART_LCR_PARITY; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (!C_PARODD(tty)) 64262306a36Sopenharmony_ci new_lcr |= UART_LCR_EPAR; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (C_CMSPAR(tty)) 64562306a36Sopenharmony_ci new_lcr |= UART_LCR_SPAR; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (C_CSTOPB(tty)) 64962306a36Sopenharmony_ci new_lcr |= UART_LCR_STOP; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci new_lcr |= UART_LCR_WLEN(tty_get_char_size(tty->termios.c_cflag)); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci mutex_lock(&priv->lock); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci new_lcr |= (priv->shadow_lcr & UART_LCR_SBC); 65662306a36Sopenharmony_ci status = f81232_set_register(port, LINE_CONTROL_REGISTER, new_lcr); 65762306a36Sopenharmony_ci if (status) { 65862306a36Sopenharmony_ci dev_err(&port->dev, "%s failed to set LCR: %d\n", 65962306a36Sopenharmony_ci __func__, status); 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci priv->shadow_lcr = new_lcr; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci mutex_unlock(&priv->lock); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic int f81232_tiocmget(struct tty_struct *tty) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci int r; 67062306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 67162306a36Sopenharmony_ci struct f81232_private *port_priv = usb_get_serial_port_data(port); 67262306a36Sopenharmony_ci u8 mcr, msr; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* force get current MSR changed state */ 67562306a36Sopenharmony_ci f81232_read_msr(port); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci mutex_lock(&port_priv->lock); 67862306a36Sopenharmony_ci mcr = port_priv->modem_control; 67962306a36Sopenharmony_ci msr = port_priv->modem_status; 68062306a36Sopenharmony_ci mutex_unlock(&port_priv->lock); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci r = (mcr & UART_MCR_DTR ? TIOCM_DTR : 0) | 68362306a36Sopenharmony_ci (mcr & UART_MCR_RTS ? TIOCM_RTS : 0) | 68462306a36Sopenharmony_ci (msr & UART_MSR_CTS ? TIOCM_CTS : 0) | 68562306a36Sopenharmony_ci (msr & UART_MSR_DCD ? TIOCM_CAR : 0) | 68662306a36Sopenharmony_ci (msr & UART_MSR_RI ? TIOCM_RI : 0) | 68762306a36Sopenharmony_ci (msr & UART_MSR_DSR ? TIOCM_DSR : 0); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return r; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int f81232_tiocmset(struct tty_struct *tty, 69362306a36Sopenharmony_ci unsigned int set, unsigned int clear) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return f81232_set_mctrl(port, set, clear); 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int f81232_open(struct tty_struct *tty, struct usb_serial_port *port) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci int result; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci result = f81232_port_enable(port); 70562306a36Sopenharmony_ci if (result) 70662306a36Sopenharmony_ci return result; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Setup termios */ 70962306a36Sopenharmony_ci if (tty) 71062306a36Sopenharmony_ci f81232_set_termios(tty, port, NULL); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 71362306a36Sopenharmony_ci if (result) { 71462306a36Sopenharmony_ci dev_err(&port->dev, "%s - failed submitting interrupt urb," 71562306a36Sopenharmony_ci " error %d\n", __func__, result); 71662306a36Sopenharmony_ci return result; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci result = usb_serial_generic_open(tty, port); 72062306a36Sopenharmony_ci if (result) { 72162306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 72262306a36Sopenharmony_ci return result; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic int f81534a_open(struct tty_struct *tty, struct usb_serial_port *port) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci int status; 73162306a36Sopenharmony_ci u8 mask; 73262306a36Sopenharmony_ci u8 val; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci val = F81534A_TRIGGER_MULTIPLE_4X | F81534A_FIFO_128BYTE; 73562306a36Sopenharmony_ci mask = F81534A_TRIGGER_MASK | F81534A_FIFO_128BYTE; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci status = f81232_set_mask_register(port, F81534A_MODE_REG, mask, val); 73862306a36Sopenharmony_ci if (status) { 73962306a36Sopenharmony_ci dev_err(&port->dev, "failed to set MODE_REG: %d\n", status); 74062306a36Sopenharmony_ci return status; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return f81232_open(tty, port); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic void f81232_close(struct usb_serial_port *port) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct f81232_private *port_priv = usb_get_serial_port_data(port); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci f81232_port_disable(port); 75162306a36Sopenharmony_ci usb_serial_generic_close(port); 75262306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 75362306a36Sopenharmony_ci flush_work(&port_priv->interrupt_work); 75462306a36Sopenharmony_ci flush_work(&port_priv->lsr_work); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic void f81232_dtr_rts(struct usb_serial_port *port, int on) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci if (on) 76062306a36Sopenharmony_ci f81232_set_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0); 76162306a36Sopenharmony_ci else 76262306a36Sopenharmony_ci f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic bool f81232_tx_empty(struct usb_serial_port *port) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci int status; 76862306a36Sopenharmony_ci u8 tmp; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp); 77162306a36Sopenharmony_ci if (!status) { 77262306a36Sopenharmony_ci if ((tmp & UART_LSR_TEMT) != UART_LSR_TEMT) 77362306a36Sopenharmony_ci return false; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return true; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int f81232_carrier_raised(struct usb_serial_port *port) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci u8 msr; 78262306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci mutex_lock(&priv->lock); 78562306a36Sopenharmony_ci msr = priv->modem_status; 78662306a36Sopenharmony_ci mutex_unlock(&priv->lock); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (msr & UART_MSR_DCD) 78962306a36Sopenharmony_ci return 1; 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void f81232_get_serial(struct tty_struct *tty, struct serial_struct *ss) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 79662306a36Sopenharmony_ci struct f81232_private *priv = usb_get_serial_port_data(port); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci ss->baud_base = priv->baud_base; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic void f81232_interrupt_work(struct work_struct *work) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct f81232_private *priv = 80462306a36Sopenharmony_ci container_of(work, struct f81232_private, interrupt_work); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci f81232_read_msr(priv->port); 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic void f81232_lsr_worker(struct work_struct *work) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct f81232_private *priv; 81262306a36Sopenharmony_ci struct usb_serial_port *port; 81362306a36Sopenharmony_ci int status; 81462306a36Sopenharmony_ci u8 tmp; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci priv = container_of(work, struct f81232_private, lsr_work); 81762306a36Sopenharmony_ci port = priv->port; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp); 82062306a36Sopenharmony_ci if (status) 82162306a36Sopenharmony_ci dev_warn(&port->dev, "read LSR failed: %d\n", status); 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg, 82562306a36Sopenharmony_ci u16 size, void *val) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(intf); 82862306a36Sopenharmony_ci int retry = F81534A_ACCESS_REG_RETRY; 82962306a36Sopenharmony_ci int status; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci while (retry--) { 83262306a36Sopenharmony_ci status = usb_control_msg_send(dev, 83362306a36Sopenharmony_ci 0, 83462306a36Sopenharmony_ci F81232_REGISTER_REQUEST, 83562306a36Sopenharmony_ci F81232_SET_REGISTER, 83662306a36Sopenharmony_ci reg, 83762306a36Sopenharmony_ci 0, 83862306a36Sopenharmony_ci val, 83962306a36Sopenharmony_ci size, 84062306a36Sopenharmony_ci USB_CTRL_SET_TIMEOUT, 84162306a36Sopenharmony_ci GFP_KERNEL); 84262306a36Sopenharmony_ci if (status) { 84362306a36Sopenharmony_ci status = usb_translate_errors(status); 84462306a36Sopenharmony_ci if (status == -EIO) 84562306a36Sopenharmony_ci continue; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (status) { 85262306a36Sopenharmony_ci dev_err(&intf->dev, "failed to set register 0x%x: %d\n", 85362306a36Sopenharmony_ci reg, status); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return status; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci unsigned char enable[2] = {0}; 86262306a36Sopenharmony_ci int status; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* 86562306a36Sopenharmony_ci * Enable all available serial ports, define as following: 86662306a36Sopenharmony_ci * bit 15 : Reset behavior (when HUB got soft reset) 86762306a36Sopenharmony_ci * 0: maintain all serial port enabled state. 86862306a36Sopenharmony_ci * 1: disable all serial port. 86962306a36Sopenharmony_ci * bit 0~11 : Serial port enable bit. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_ci if (en) { 87262306a36Sopenharmony_ci enable[0] = 0xff; 87362306a36Sopenharmony_ci enable[1] = 0x8f; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci status = f81534a_ctrl_set_register(intf, F81534A_CTRL_CMD_ENABLE_PORT, 87762306a36Sopenharmony_ci sizeof(enable), enable); 87862306a36Sopenharmony_ci if (status) 87962306a36Sopenharmony_ci dev_err(&intf->dev, "failed to enable ports: %d\n", status); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return status; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int f81534a_ctrl_probe(struct usb_interface *intf, 88562306a36Sopenharmony_ci const struct usb_device_id *id) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci return f81534a_ctrl_enable_all_ports(intf, true); 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic void f81534a_ctrl_disconnect(struct usb_interface *intf) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci f81534a_ctrl_enable_all_ports(intf, false); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic int f81534a_ctrl_resume(struct usb_interface *intf) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci return f81534a_ctrl_enable_all_ports(intf, true); 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic int f81232_port_probe(struct usb_serial_port *port) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct f81232_private *priv; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci priv = devm_kzalloc(&port->dev, sizeof(*priv), GFP_KERNEL); 90562306a36Sopenharmony_ci if (!priv) 90662306a36Sopenharmony_ci return -ENOMEM; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci mutex_init(&priv->lock); 90962306a36Sopenharmony_ci INIT_WORK(&priv->interrupt_work, f81232_interrupt_work); 91062306a36Sopenharmony_ci INIT_WORK(&priv->lsr_work, f81232_lsr_worker); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci usb_set_serial_port_data(port, priv); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci priv->port = port; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci return 0; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic int f81534a_port_probe(struct usb_serial_port *port) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci int status; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* tri-state with pull-high, default RS232 Mode */ 92462306a36Sopenharmony_ci status = f81232_set_register(port, F81534A_GPIO_REG, 92562306a36Sopenharmony_ci F81534A_GPIO_MODE2_DIR); 92662306a36Sopenharmony_ci if (status) 92762306a36Sopenharmony_ci return status; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return f81232_port_probe(port); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic int f81232_suspend(struct usb_serial *serial, pm_message_t message) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct usb_serial_port *port = serial->port[0]; 93562306a36Sopenharmony_ci struct f81232_private *port_priv = usb_get_serial_port_data(port); 93662306a36Sopenharmony_ci int i; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) 93962306a36Sopenharmony_ci usb_kill_urb(port->read_urbs[i]); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (port_priv) { 94462306a36Sopenharmony_ci flush_work(&port_priv->interrupt_work); 94562306a36Sopenharmony_ci flush_work(&port_priv->lsr_work); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int f81232_resume(struct usb_serial *serial) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct usb_serial_port *port = serial->port[0]; 95462306a36Sopenharmony_ci int result; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (tty_port_initialized(&port->port)) { 95762306a36Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); 95862306a36Sopenharmony_ci if (result) { 95962306a36Sopenharmony_ci dev_err(&port->dev, "submit interrupt urb failed: %d\n", 96062306a36Sopenharmony_ci result); 96162306a36Sopenharmony_ci return result; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return usb_serial_generic_resume(serial); 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic struct usb_serial_driver f81232_device = { 96962306a36Sopenharmony_ci .driver = { 97062306a36Sopenharmony_ci .owner = THIS_MODULE, 97162306a36Sopenharmony_ci .name = "f81232", 97262306a36Sopenharmony_ci }, 97362306a36Sopenharmony_ci .id_table = f81232_id_table, 97462306a36Sopenharmony_ci .num_ports = 1, 97562306a36Sopenharmony_ci .bulk_in_size = 256, 97662306a36Sopenharmony_ci .bulk_out_size = 256, 97762306a36Sopenharmony_ci .open = f81232_open, 97862306a36Sopenharmony_ci .close = f81232_close, 97962306a36Sopenharmony_ci .dtr_rts = f81232_dtr_rts, 98062306a36Sopenharmony_ci .carrier_raised = f81232_carrier_raised, 98162306a36Sopenharmony_ci .get_serial = f81232_get_serial, 98262306a36Sopenharmony_ci .break_ctl = f81232_break_ctl, 98362306a36Sopenharmony_ci .set_termios = f81232_set_termios, 98462306a36Sopenharmony_ci .tiocmget = f81232_tiocmget, 98562306a36Sopenharmony_ci .tiocmset = f81232_tiocmset, 98662306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 98762306a36Sopenharmony_ci .tx_empty = f81232_tx_empty, 98862306a36Sopenharmony_ci .process_read_urb = f81232_process_read_urb, 98962306a36Sopenharmony_ci .read_int_callback = f81232_read_int_callback, 99062306a36Sopenharmony_ci .port_probe = f81232_port_probe, 99162306a36Sopenharmony_ci .suspend = f81232_suspend, 99262306a36Sopenharmony_ci .resume = f81232_resume, 99362306a36Sopenharmony_ci}; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic struct usb_serial_driver f81534a_device = { 99662306a36Sopenharmony_ci .driver = { 99762306a36Sopenharmony_ci .owner = THIS_MODULE, 99862306a36Sopenharmony_ci .name = "f81534a", 99962306a36Sopenharmony_ci }, 100062306a36Sopenharmony_ci .id_table = f81534a_id_table, 100162306a36Sopenharmony_ci .num_ports = 1, 100262306a36Sopenharmony_ci .open = f81534a_open, 100362306a36Sopenharmony_ci .close = f81232_close, 100462306a36Sopenharmony_ci .dtr_rts = f81232_dtr_rts, 100562306a36Sopenharmony_ci .carrier_raised = f81232_carrier_raised, 100662306a36Sopenharmony_ci .get_serial = f81232_get_serial, 100762306a36Sopenharmony_ci .break_ctl = f81232_break_ctl, 100862306a36Sopenharmony_ci .set_termios = f81232_set_termios, 100962306a36Sopenharmony_ci .tiocmget = f81232_tiocmget, 101062306a36Sopenharmony_ci .tiocmset = f81232_tiocmset, 101162306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 101262306a36Sopenharmony_ci .tx_empty = f81232_tx_empty, 101362306a36Sopenharmony_ci .process_read_urb = f81534a_process_read_urb, 101462306a36Sopenharmony_ci .read_int_callback = f81232_read_int_callback, 101562306a36Sopenharmony_ci .port_probe = f81534a_port_probe, 101662306a36Sopenharmony_ci .suspend = f81232_suspend, 101762306a36Sopenharmony_ci .resume = f81232_resume, 101862306a36Sopenharmony_ci}; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 102162306a36Sopenharmony_ci &f81232_device, 102262306a36Sopenharmony_ci &f81534a_device, 102362306a36Sopenharmony_ci NULL, 102462306a36Sopenharmony_ci}; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic struct usb_driver f81534a_ctrl_driver = { 102762306a36Sopenharmony_ci .name = "f81534a_ctrl", 102862306a36Sopenharmony_ci .id_table = f81534a_ctrl_id_table, 102962306a36Sopenharmony_ci .probe = f81534a_ctrl_probe, 103062306a36Sopenharmony_ci .disconnect = f81534a_ctrl_disconnect, 103162306a36Sopenharmony_ci .resume = f81534a_ctrl_resume, 103262306a36Sopenharmony_ci}; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic int __init f81232_init(void) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci int status; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci status = usb_register_driver(&f81534a_ctrl_driver, THIS_MODULE, 103962306a36Sopenharmony_ci KBUILD_MODNAME); 104062306a36Sopenharmony_ci if (status) 104162306a36Sopenharmony_ci return status; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci status = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, 104462306a36Sopenharmony_ci combined_id_table); 104562306a36Sopenharmony_ci if (status) { 104662306a36Sopenharmony_ci usb_deregister(&f81534a_ctrl_driver); 104762306a36Sopenharmony_ci return status; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return 0; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic void __exit f81232_exit(void) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci usb_serial_deregister_drivers(serial_drivers); 105662306a36Sopenharmony_ci usb_deregister(&f81534a_ctrl_driver); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cimodule_init(f81232_init); 106062306a36Sopenharmony_cimodule_exit(f81232_exit); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ciMODULE_DESCRIPTION("Fintek F81232/532A/534A/535/536 USB to serial driver"); 106362306a36Sopenharmony_ciMODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>"); 106462306a36Sopenharmony_ciMODULE_AUTHOR("Peter Hong <peter_hong@fintek.com.tw>"); 106562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1066