Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752143AbeADC3g (ORCPT + 1 other); Wed, 3 Jan 2018 21:29:36 -0500 Received: from mail-pl0-f66.google.com ([209.85.160.66]:33986 "EHLO mail-pl0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752071AbeADC3d (ORCPT ); Wed, 3 Jan 2018 21:29:33 -0500 X-Google-Smtp-Source: ACJfBoskZK7fboh8IBGKpJgycy2UBBk0vZxpUmOLtpQ+z4fV4vHe05FiN63CVz+h71KRNxOLjyElog== From: "Ji-Ze Hong (Peter Hong)" X-Google-Original-From: "Ji-Ze Hong (Peter Hong)" To: johan@kernel.org Cc: gregkh@linuxfoundation.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, peter_hong@fintek.com.tw, "Ji-Ze Hong (Peter Hong)" Subject: [PATCH V2 4/5] usb: serial: f81534: add H/W disable port support Date: Thu, 4 Jan 2018 10:29:20 +0800 Message-Id: <1515032961-29131-4-git-send-email-hpeter+linux_kernel@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1515032961-29131-1-git-send-email-hpeter+linux_kernel@gmail.com> References: <1515032961-29131-1-git-send-email-hpeter+linux_kernel@gmail.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Return-Path: The F81532/534 can be disable port by manufacturer with following H/W design. 1: Connect DCD/DSR/CTS/RI pin to ground. 2: Connect RX pin to ground. In driver, we'll implements some detect method likes following: 1: Read MSR. 2: Turn MCR LOOP bit on, off and read LSR after delay with 60ms. It'll contain BREAK status in LSR. Signed-off-by: Ji-Ze Hong (Peter Hong) --- V2: 1: f81534_check_port_hw_disabled() change return type from int to bool. 2: Add help function f81534_set_phy_port_register() / f81534_get_phy_port_register() for f81534_check_port_hw_disabled() to read register without port. 3: Re-write f81534_calc_num_ports() & f81534_attach() to reduce the f81534_check_port_hw_disabled() repeatedly called. drivers/usb/serial/f81534.c | 160 +++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 61 deletions(-) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 7f175f39a171..a4666171239a 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -307,6 +307,20 @@ static int f81534_set_mask_register(struct usb_serial *serial, u16 reg, return f81534_set_register(serial, reg, tmp); } +static int f81534_set_phy_port_register(struct usb_serial *serial, int phy, + u16 reg, u8 data) +{ + return f81534_set_register(serial, reg + F81534_UART_OFFSET * phy, + data); +} + +static int f81534_get_phy_port_register(struct usb_serial *serial, int phy, + u16 reg, u8 *data) +{ + return f81534_get_register(serial, reg + F81534_UART_OFFSET * phy, + data); +} + static int f81534_set_port_register(struct usb_serial_port *port, u16 reg, u8 data) { @@ -730,6 +744,70 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * The F81532/534 will not report serial port to USB serial subsystem when + * H/W DCD/DSR/CTS/RI/RX pin connected to ground. + * + * To detect RX pin status, we'll enable MCR interal loopback, disable it and + * delayed for 60ms. It connected to ground If LSR register report UART_LSR_BI. + */ +static bool f81534_check_port_hw_disabled(struct usb_serial *serial, int phy) +{ + int status; + u8 old_mcr; + u8 msr; + u8 lsr; + u8 msr_mask; + + msr_mask = UART_MSR_DCD | UART_MSR_RI | UART_MSR_DSR | UART_MSR_CTS; + + status = f81534_get_phy_port_register(serial, phy, + F81534_MODEM_STATUS_REG, &msr); + if (status) + return false; + + if ((msr & msr_mask) != msr_mask) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_FIFO_CONTROL_REG, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + if (status) + return false; + + status = f81534_get_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, &old_mcr); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, UART_MCR_LOOP); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, 0x0); + if (status) + return false; + + msleep(60); + + status = f81534_get_phy_port_register(serial, phy, + F81534_LINE_STATUS_REG, &lsr); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, old_mcr); + if (status) + return false; + + if ((lsr & UART_LSR_BI) == UART_LSR_BI) + return true; + + return false; +} + +/* * We had 2 generation of F81532/534 IC. All has an internal storage. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -750,11 +828,10 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) static int f81534_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds) { + struct f81534_serial_private *serial_priv; struct device *dev = &serial->interface->dev; int size_bulk_in = usb_endpoint_maxp(epds->bulk_in[0]); int size_bulk_out = usb_endpoint_maxp(epds->bulk_out[0]); - u8 setting[F81534_CUSTOM_DATA_SIZE]; - u8 setting_idx; u8 num_port = 0; int status; size_t i; @@ -765,8 +842,15 @@ static int f81534_calc_num_ports(struct usb_serial *serial, return -ENODEV; } + serial_priv = devm_kzalloc(&serial->interface->dev, + sizeof(*serial_priv), GFP_KERNEL); + if (!serial_priv) + return -ENOMEM; + + usb_set_serial_data(serial, serial_priv); + /* Check had custom setting */ - status = f81534_find_config_idx(serial, &setting_idx); + status = f81534_find_config_idx(serial, &serial_priv->setting_idx); if (status) { dev_err(&serial->interface->dev, "%s: find idx failed: %d\n", __func__, status); @@ -777,11 +861,12 @@ static int f81534_calc_num_ports(struct usb_serial *serial, * We'll read custom data only when data available, otherwise we'll * read default value instead. */ - if (setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) { + if (serial_priv->setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) { status = f81534_read_flash(serial, F81534_CUSTOM_ADDRESS_START + F81534_CONF_OFFSET, - sizeof(setting), setting); + sizeof(serial_priv->conf_data), + serial_priv->conf_data); if (status) { dev_err(&serial->interface->dev, "%s: get custom data failed: %d\n", @@ -791,13 +876,13 @@ static int f81534_calc_num_ports(struct usb_serial *serial, dev_dbg(&serial->interface->dev, "%s: read config from block: %d\n", __func__, - setting_idx); + serial_priv->setting_idx); } else { /* Read default board setting */ status = f81534_read_flash(serial, - F81534_DEF_CONF_ADDRESS_START, F81534_NUM_PORT, - setting); - + F81534_DEF_CONF_ADDRESS_START, + sizeof(serial_priv->conf_data), + serial_priv->conf_data); if (status) { dev_err(&serial->interface->dev, "%s: read failed: %d\n", __func__, @@ -811,7 +896,10 @@ static int f81534_calc_num_ports(struct usb_serial *serial, /* New style, find all possible ports */ for (i = 0; i < F81534_NUM_PORT; ++i) { - if (setting[i] & F81534_PORT_UNAVAILABLE) + if (f81534_check_port_hw_disabled(serial, i)) + serial_priv->conf_data[i] |= F81534_PORT_UNAVAILABLE; + + if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) continue; ++num_port; @@ -1228,61 +1316,11 @@ static int f81534_attach(struct usb_serial *serial) { struct f81534_serial_private *serial_priv; int index = 0; - int status; int i; - serial_priv = devm_kzalloc(&serial->interface->dev, - sizeof(*serial_priv), GFP_KERNEL); - if (!serial_priv) - return -ENOMEM; - - usb_set_serial_data(serial, serial_priv); - + serial_priv = usb_get_serial_data(serial); mutex_init(&serial_priv->urb_mutex); - /* Check had custom setting */ - status = f81534_find_config_idx(serial, &serial_priv->setting_idx); - if (status) { - dev_err(&serial->interface->dev, "%s: find idx failed: %d\n", - __func__, status); - return status; - } - - /* - * We'll read custom data only when data available, otherwise we'll - * read default value instead. - */ - if (serial_priv->setting_idx == F81534_CUSTOM_NO_CUSTOM_DATA) { - /* - * The default configuration layout: - * byte 0/1/2/3: uart setting - */ - status = f81534_read_flash(serial, - F81534_DEF_CONF_ADDRESS_START, - F81534_DEF_CONF_SIZE, - serial_priv->conf_data); - if (status) { - dev_err(&serial->interface->dev, - "%s: read reserve data failed: %d\n", - __func__, status); - return status; - } - } else { - /* Only read 8 bytes for mode & GPIO */ - status = f81534_read_flash(serial, - F81534_CUSTOM_ADDRESS_START + - F81534_CONF_OFFSET, - sizeof(serial_priv->conf_data), - serial_priv->conf_data); - if (status) { - dev_err(&serial->interface->dev, - "%s: idx: %d get data failed: %d\n", - __func__, serial_priv->setting_idx, - status); - return status; - } - } - /* Assign phy-to-logic mapping */ for (i = 0; i < F81534_NUM_PORT; ++i) { if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) -- 2.7.4