Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752606Ab3GQHjg (ORCPT ); Wed, 17 Jul 2013 03:39:36 -0400 Received: from ns.mm-sol.com ([212.124.72.66]:48265 "EHLO extserv.mm-sol.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752408Ab3GQHje (ORCPT ); Wed, 17 Jul 2013 03:39:34 -0400 Message-ID: <1374046723.2657.27.camel@iivanov-dev> Subject: Re: [PATCH] tty: serial: Add initial MSM UART High Speed Lite driver From: "Ivan T. Ivanov" To: Kumar Gala Cc: gregkh@linuxfoundation.org, grant.likely@linaro.org, rob.herring@calxeda.com, rob@landley.net, jslaby@suse.cz, devicetree-discuss@lists.ozlabs.org, linux-serial@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, mrana@codeaurora.org, sambley@codeaurora.org Date: Wed, 17 Jul 2013 10:38:43 +0300 In-Reply-To: <76DBE056-CDFB-47A5-9AB5-239400A2D4F9@codeaurora.org> References: <1372669910-24409-1-git-send-email-iivanov@mm-sol.com> <76DBE056-CDFB-47A5-9AB5-239400A2D4F9@codeaurora.org> Content-Type: text/plain; charset="UTF-8" X-Mailer: Evolution 3.2.3-0ubuntu6 Content-Transfer-Encoding: 7bit Mime-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 56086 Lines: 1838 Hi Kumar, On Tue, 2013-07-16 at 15:17 -0500, Kumar Gala wrote: > On Jul 1, 2013, at 4:11 AM, Ivan T. Ivanov wrote: > > > From: "Ivan T. Ivanov" > > > > This is a tty driver with console support for Qualcomm's UART > > controllers found in the MSM8974 chipsets. Driver is completely > > based on implementation found in codeaurora.org msm_serial_hs_lite > > with Android dependences removed. Other changes include, moved to > > device managed resources and few cleanups. > > > > Driver functionality was tested in LEGACY_HSUART mode. > > > > Signed-off-by: Ivan T. Ivanov > > --- > > .../bindings/tty/serial/msm_serial_hsl.txt | 52 + > > drivers/tty/serial/Kconfig | 18 + > > drivers/tty/serial/Makefile | 1 + > > drivers/tty/serial/msm_serial_hsl.c | 1399 ++++++++++++++++++++ > > drivers/tty/serial/msm_serial_hsl.h | 294 ++++ > > 5 files changed, 1764 insertions(+) > > create mode 100644 Documentation/devicetree/bindings/tty/serial/msm_serial_hsl.txt > > create mode 100644 drivers/tty/serial/msm_serial_hsl.c > > create mode 100644 drivers/tty/serial/msm_serial_hsl.h > > > > diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial_hsl.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial_hsl.txt > > new file mode 100644 > > index 0000000..972552f > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/tty/serial/msm_serial_hsl.txt > > @@ -0,0 +1,52 @@ > > +* Qualcomm MSM HSUART Lite > > + > > +Required properties: > > +- compatible : > > + "qcom,msm-lsuart-v14" to be used for UARTDM Core v1.4 > > If its really v1.4 than the string should be "qcom,msm-lsuart-v1.4" I am not sure which is the right number here. codeaurora code uses v14 string for compatible name and 1.4 here and there in the comments. I am inclined to believe that 1.4 is true. > > > + > > +- reg : > > + offset and length of the register set for both the device, > > + UART core and GBSI core > > + > > +- reg-names : > > + "uart_mem" to be used as name of the UART core > > + "gbsi_mem" to be used as name of the GBSI core > > + > > +The registers for the "qcom,msm-lsuart-v14" device have specify > > +UART core block. GSBI reg is optional if specified driver will use > > +GSBI specific functionality. > > + > > +- interrupts : interrupts for UART core > > + > > +- clocks : Must contain an entry for each entry in clock-names. > > + > > +- clock-names : Must include the following entries: > > + "core_clk" - mandatory > > + "iface_clk" - optional > > + > > +For details see: > > +Documentation/devicetree/bindings/clock/clock-bindings.txt > > + > > +Example: > > + > > + serial@f991e000 { > > + compatible = "qcom,msm-lsuart-v14"; > > + reg = <0xf991e000 0x1000>; > > + reg-names = "uart_mem"; > > + interrupts = <0 108 0>; > > + clocks = <&blsp1_uart2_apps_cxc>, <&blsp1_ahb_cxc>; > > + clock-names = "core_clk", "iface_clk"; > > + }; > > + > > +Aliases : > > +An alias may be optionally used to bind the UART device to a TTY device > > +(ttyHSL) with a given alias number. Aliases are of the form > > +uart where is an integer representing the alias number to use. > > +On systems with multiple UART devices present, an alias may optionally be > > +defined for such devices. The alias value should be from 0 to 255. > > + > > +Example: > > + > > + aliases { > > + uart4 = &uart7; // This device will be enumerated as ttyHSL4 > > + }; > > diff --git a/drivers/tty/serial/msm_serial_hsl.c b/drivers/tty/serial/msm_serial_hsl.c > > new file mode 100644 > > index 0000000..56c15a8 > > --- /dev/null > > +++ b/drivers/tty/serial/msm_serial_hsl.c > > @@ -0,0 +1,1399 @@ > > +/* > > + * drivers/tty/serial/msm_serial_hsl.c - driver for serial device and console > > + * > > + * Copyright (C) 2007 Google, Inc. > > + * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. > > + * > > + * This software is licensed under the terms of the GNU General Public > > + * License version 2, as published by the Free Software Foundation, and > > + * may be copied, distributed, and modified under those terms. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > + > > +/* Acknowledgements: > > + * This file is based on msm_serial.c, originally > > + * Written by Robert Love */ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include "msm_serial_hsl.h" > > + > > +/* > > + * There are 3 different kind of UART Core available on MSM. > > + * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART > > + * and BSLP based HSUART. > > + */ > > +enum uart_core_type { > > + LEGACY_HSUART, > > + GSBI_HSUART, > > + BLSP_HSUART, > > +}; > > + > > +/* > > + * @uart: uart port instance > > + * @name: name of the port > > + * @clk: reference to core clock > > + * @pclk: reference to interface clock > > + * @imr: shadow interrupt mask register > > + * @mapped_gsbi: GSBI contol memory > > + * @old_snap_state: How many data still left in the FIFO > > + * @tx_timeout - transmit timout ~600x the character transmit time > > + * @uart_type - see enum uart_core_type > > + * @regmap - Register map based on controller version > > + * It is mainly required where same UART is used across different processor. > > + * Make sure that Clock driver for platform support setting clock rate to zero. > > + */ > > +struct msm_hsl_port { > > + struct uart_port uart; > > + char name[16]; > > + struct clk *clk; > > + struct clk *pclk; > > + struct dentry *loopback_dir; > > + unsigned int imr; > > + unsigned int *mapped_gsbi; > > + unsigned int old_snap_state; > > + int tx_timeout; > > + enum uart_core_type uart_type; > > + const unsigned int *regmap; > > +}; > > + > > +#define UARTDM_VERSION_11_13 0 > > +#define UARTDM_VERSION_14 1 > > +#define UART_NR 3 > > + > > +#define to_hsl_port(p) (container_of(p, struct msm_hsl_port, uart)) > > +#define is_console(port) ((port)->cons && \ > > + (port)->cons->index == (port)->line) > > + > > +static const unsigned int regmap[][UARTDM_LAST] = { > > + [UARTDM_VERSION_11_13] = { > > + [UARTDM_MR1] = UARTDM_MR1_ADDR, > > + [UARTDM_MR2] = UARTDM_MR2_ADDR, > > + [UARTDM_IMR] = UARTDM_IMR_ADDR, > > + [UARTDM_SR] = UARTDM_SR_ADDR, > > + [UARTDM_CR] = UARTDM_CR_ADDR, > > + [UARTDM_CSR] = UARTDM_CSR_ADDR, > > + [UARTDM_IPR] = UARTDM_IPR_ADDR, > > + [UARTDM_ISR] = UARTDM_ISR_ADDR, > > + [UARTDM_RX_TOTAL_SNAP] = UARTDM_RX_TOTAL_SNAP_ADDR, > > + [UARTDM_TFWR] = UARTDM_TFWR_ADDR, > > + [UARTDM_RFWR] = UARTDM_RFWR_ADDR, > > + [UARTDM_RF] = UARTDM_RF_ADDR, > > + [UARTDM_TF] = UARTDM_TF_ADDR, > > + [UARTDM_MISR] = UARTDM_MISR_ADDR, > > + [UARTDM_DMRX] = UARTDM_DMRX_ADDR, > > + [UARTDM_NCF_TX] = UARTDM_NCF_TX_ADDR, > > + [UARTDM_DMEN] = UARTDM_DMEN_ADDR, > > + [UARTDM_TXFS] = UARTDM_TXFS_ADDR, > > + [UARTDM_RXFS] = UARTDM_RXFS_ADDR, > > + }, > > Isn't this unsupported code for version 11_13? It is supposed to be supported. I will rework code in the probe method to select the right register map based on compatible field. > > > + [UARTDM_VERSION_14] = { > > + [UARTDM_MR1] = UARTDM_MR1_ADDR, > > + [UARTDM_MR2] = UARTDM_MR2_ADDR, > > + [UARTDM_IMR] = UARTDM_IMR_ADDR_V14, > > + [UARTDM_SR] = UARTDM_SR_ADDR_V14, > > + [UARTDM_CR] = UARTDM_CR_ADDR_V14, > > + [UARTDM_CSR] = UARTDM_CSR_ADDR_V14, > > + [UARTDM_IPR] = UARTDM_IPR_ADDR, > > + [UARTDM_ISR] = UARTDM_ISR_ADDR_V14, > > + [UARTDM_RX_TOTAL_SNAP] = UARTDM_RX_TOTAL_SNAP_ADDR_V14, > > + [UARTDM_TFWR] = UARTDM_TFWR_ADDR, > > + [UARTDM_RFWR] = UARTDM_RFWR_ADDR, > > + [UARTDM_RF] = UARTDM_RF_ADDR_V14, > > + [UARTDM_TF] = UARTDM_TF_ADDR_V14, > > + [UARTDM_MISR] = UARTDM_MISR_ADDR_V14, > > + [UARTDM_DMRX] = UARTDM_DMRX_ADDR, > > + [UARTDM_NCF_TX] = UARTDM_NCF_TX_ADDR, > > + [UARTDM_DMEN] = UARTDM_DMEN_ADDR, > > + [UARTDM_TXFS] = UARTDM_TXFS_ADDR, > > + [UARTDM_RXFS] = UARTDM_RXFS_ADDR, > > + }, > > +}; > > + > > +/* > > + * get_console_state - check the per-port serial console state. > > + * @port: uart_port structure describing the port > > + * > > + * Return the state of serial console availability on port. > > + * return 1: If serial console is enabled on particular UART port. > > + * return 0: If serial console is disabled on particular UART port. > > + */ > > +static int get_console_state(struct uart_port *port) > > +{ > > +#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE > > + if (is_console(port) && (port->cons->flags & CON_ENABLED)) > > + return 1; > > + else > > + return 0; > > +#else > > + return -ENODEV; > > +#endif > > +} > > + > > +static inline void msm_hsl_write(struct uart_port *port, > > + unsigned int val, unsigned int off) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + > > + iowrite32(val, port->membase + hslp->regmap[off]); > > +} > > + > > +static inline unsigned int msm_hsl_read(struct uart_port *port, > > + unsigned int off) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + > > + return ioread32(port->membase + hslp->regmap[off]); > > +} > > + > > +static unsigned int msm_serial_hsl_has_gsbi(struct uart_port *port) > > +{ > > + return (to_hsl_port(port)->uart_type == GSBI_HSUART); > > +} > > + > > +/* > > + * set_gsbi_uart_func_mode: Check the currently used GSBI UART mode > > + * and set the new required GSBI UART Mode if it is different. > > + * @port: uart port > > + */ > > +static void set_gsbi_uart_func_mode(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + unsigned int set_mode = GSBI_PROTOCOL_I2C_UART; > > + unsigned int cur_mode; > > + > > + if (hslp->pclk) > > + clk_prepare_enable(hslp->pclk); > > + > > + /* Read current used GSBI UART Mode and set only if it is different. */ > > + cur_mode = ioread32(hslp->mapped_gsbi + GSBI_CONTROL_ADDR); > > + if ((cur_mode & GSBI_PROTOCOL_CODE_MASK) != set_mode) > > + /* > > + * Programmed GSBI based UART protocol mode i.e. I2C/UART > > + * Shared Mode or UART Mode. > > + */ > > + iowrite32(set_mode, hslp->mapped_gsbi + GSBI_CONTROL_ADDR); > > + > > + if (hslp->pclk) > > + clk_disable_unprepare(hslp->pclk); > > +} > > + > > +static int msm_hsl_clock_enable(struct uart_port *port, int enable) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + int ret = 0; > > + > > + if (enable) { > > + ret = clk_prepare_enable(hslp->clk); > > + if (ret) > > + return ret; > > + if (hslp->pclk) { > > + ret = clk_prepare_enable(hslp->pclk); > > + if (ret) > > + clk_disable_unprepare(hslp->clk); > > + } > > + } else { > > + clk_disable_unprepare(hslp->clk); > > + if (hslp->pclk) > > + clk_disable_unprepare(hslp->pclk); > > + } > > + > > + return ret; > > +} > > + > > +static int msm_hsl_loopback_set(void *data, u64 val) > > +{ > > + struct msm_hsl_port *hslp = data; > > + struct uart_port *port = &(hslp->uart); > > + unsigned long flags; > > + int ret = 0; > > + > > + ret = clk_set_rate(hslp->clk, port->uartclk); > > + if (!ret) > > + msm_hsl_clock_enable(port, 1); > > + else > > + return -EINVAL; > > + > > + if (val) { > > + spin_lock_irqsave(&port->lock, flags); > > + ret = msm_hsl_read(port, UARTDM_MR2); > > + ret |= UARTDM_MR2_LOOP_MODE_BMSK; > > + msm_hsl_write(port, ret, UARTDM_MR2); > > + spin_unlock_irqrestore(&port->lock, flags); > > + } else { > > + spin_lock_irqsave(&port->lock, flags); > > + ret = msm_hsl_read(port, UARTDM_MR2); > > + ret &= ~UARTDM_MR2_LOOP_MODE_BMSK; > > + msm_hsl_write(port, ret, UARTDM_MR2); > > + spin_unlock_irqrestore(&port->lock, flags); > > + } > > + > > + msm_hsl_clock_enable(port, 0); > > + return 0; > > +} > > + > > +static int msm_hsl_loopback_get(void *data, u64 *val) > > +{ > > + struct msm_hsl_port *hslp = data; > > + struct uart_port *port = &hslp->uart; > > + unsigned long flags; > > + int ret = 0; > > + > > + ret = clk_set_rate(hslp->clk, port->uartclk); > > + if (!ret) > > + msm_hsl_clock_enable(port, 1); > > + else > > + return -EINVAL; > > + > > + spin_lock_irqsave(&port->lock, flags); > > + ret = msm_hsl_read(port, UARTDM_MR2); > > + spin_unlock_irqrestore(&port->lock, flags); > > + msm_hsl_clock_enable(port, 0); > > + > > + *val = (ret & UARTDM_MR2_LOOP_MODE_BMSK) ? 1 : 0; > > + return 0; > > +} > > + > > +static void msm_hsl_stop_tx(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + > > + hslp->imr &= ~UARTDM_ISR_TXLEV_BMSK; > > + msm_hsl_write(port, hslp->imr, UARTDM_IMR); > > +} > > + > > +static void msm_hsl_start_tx(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + > > + hslp->imr |= UARTDM_ISR_TXLEV_BMSK; > > + msm_hsl_write(port, hslp->imr, UARTDM_IMR); > > +} > > + > > +static void msm_hsl_stop_rx(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + > > + hslp->imr &= ~(UARTDM_ISR_RXLEV_BMSK | UARTDM_ISR_RXSTALE_BMSK); > > + msm_hsl_write(port, hslp->imr, UARTDM_IMR); > > +} > > + > > +static void msm_hsl_enable_ms(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + > > + hslp->imr |= UARTDM_ISR_DELTA_CTS_BMSK; > > + msm_hsl_write(port, hslp->imr, UARTDM_IMR); > > +} > > + > > +static void msm_hsl_handle_rx(struct uart_port *port, unsigned int misr) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + struct tty_port *tty = &port->state->port; > > + unsigned int sr; > > + int count = 0; > > + > > + /* > > + * Handle overrun. My understanding of the hardware is that overrun > > + * is not tied to the RX buffer, so we handle the case out of band. > > + */ > > + sr = msm_hsl_read(port, UARTDM_SR); > > + if (sr & UARTDM_SR_OVERRUN_BMSK) { > > + port->icount.overrun++; > > + tty_insert_flip_char(tty, 0, TTY_OVERRUN); > > + msm_hsl_write(port, RESET_ERROR_STATUS, UARTDM_CR); > > + } > > + > > + if (misr & UARTDM_ISR_RXSTALE_BMSK) { > > + count = msm_hsl_read(port, UARTDM_RX_TOTAL_SNAP) - > > + hslp->old_snap_state; > > + hslp->old_snap_state = 0; > > + } else { > > + count = 4 * (msm_hsl_read(port, UARTDM_RFWR)); > > + hslp->old_snap_state += count; > > + } > > + > > + /* and now the main RX loop */ > > + while (count > 0) { > > + unsigned int c; > > + char flag = TTY_NORMAL; > > + > > + sr = msm_hsl_read(port, UARTDM_SR); > > + if ((sr & UARTDM_SR_RXRDY_BMSK) == 0) { > > + hslp->old_snap_state -= count; > > + break; > > + } > > + c = msm_hsl_read(port, UARTDM_RF); > > + if (sr & UARTDM_SR_RX_BREAK_BMSK) { > > + port->icount.brk++; > > + if (uart_handle_break(port)) > > + continue; > > + } else if (sr & UARTDM_SR_PAR_FRAME_BMSK) { > > + port->icount.frame++; > > + } else { > > + port->icount.rx++; > > + } > > + > > + /* Mask conditions we're ignorning. */ > > + sr &= port->read_status_mask; > > + if (sr & UARTDM_SR_RX_BREAK_BMSK) > > + flag = TTY_BREAK; > > + else if (sr & UARTDM_SR_PAR_FRAME_BMSK) > > + flag = TTY_FRAME; > > + > > + /* TODO: handle sysrq */ > > + /* if (!uart_handle_sysrq_char(port, c)) */ > > + tty_insert_flip_string(tty, (char *)&c, > > + (count > 4) ? 4 : count); > > + count -= 4; > > + } > > + > > + tty_flip_buffer_push(tty); > > +} > > + > > +static void msm_hsl_dump_regs(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + unsigned int sr, isr, mr1, mr2, ncf, txfs, rxfs, con_state; > > + > > + sr = msm_hsl_read(port, UARTDM_SR); > > + isr = msm_hsl_read(port, UARTDM_ISR); > > + mr1 = msm_hsl_read(port, UARTDM_MR1); > > + mr2 = msm_hsl_read(port, UARTDM_MR2); > > + ncf = msm_hsl_read(port, UARTDM_NCF_TX); > > + txfs = msm_hsl_read(port, UARTDM_TXFS); > > + rxfs = msm_hsl_read(port, UARTDM_RXFS); > > + con_state = get_console_state(port); > > + > > + pr_info("Timeout: %d uS\n", hslp->tx_timeout); > > + pr_info("SR: %08x\n", sr); > > + pr_info("ISR: %08x\n", isr); > > + pr_info("MR1: %08x\n", mr1); > > + pr_info("MR2: %08x\n", mr2); > > + pr_info("NCF: %08x\n", ncf); > > + pr_info("TXFS: %08x\n", txfs); > > + pr_info("RXFS: %08x\n", rxfs); > > + pr_info("Console state: %d\n", con_state); > > +} > > + > > +/* > > + * Wait for transmitter & holding register to empty > > + * Derived from msm_hsl_wait_for_xmitr in 8250 serial driver by Russell King > > + */ > > +static void msm_hsl_wait_for_xmitr(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + int count = 0; > > + int sr; > > + int isr; > > + > > + sr = msm_hsl_read(port, UARTDM_SR); > > + if (sr & UARTDM_SR_TXEMT_BMSK) > > + return; > > + > > + do { > > + isr = msm_hsl_read(port, UARTDM_ISR); > > + sr = msm_hsl_read(port, UARTDM_SR); > > + > > + if ((isr & UARTDM_ISR_TX_READY_BMSK) || > > + (sr & UARTDM_SR_TXEMT_BMSK)) > > + break; > > + > > + udelay(1); > > + touch_nmi_watchdog(); > > + cpu_relax(); > > + > > + if (++count == hslp->tx_timeout) { > > + msm_hsl_dump_regs(port); > > + panic("MSM HSL msm_hsl_wait_for_xmitr is stuck!"); > > + } > > + > > + } while (true); > > + > > + msm_hsl_write(port, CLEAR_TX_READY, UARTDM_CR); > > + > > +} > > + > > +static void msm_hsl_handle_tx(struct uart_port *port) > > +{ > > + struct circ_buf *xmit = &port->state->xmit; > > + unsigned int tf_pointer = 0; > > + int sent_tx; > > + int tx_count; > > + int x; > > + int sr; > > + > > + tx_count = uart_circ_chars_pending(xmit); > > + > > + if (tx_count > (UART_XMIT_SIZE - xmit->tail)) > > + tx_count = UART_XMIT_SIZE - xmit->tail; > > + if (tx_count >= port->fifosize) > > + tx_count = port->fifosize; > > + > > + /* Handle x_char */ > > + if (port->x_char) { > > + msm_hsl_wait_for_xmitr(port); > > + msm_hsl_write(port, tx_count + 1, UARTDM_NCF_TX); > > + msm_hsl_read(port, UARTDM_NCF_TX); > > + msm_hsl_write(port, port->x_char, UARTDM_TF); > > + port->icount.tx++; > > + port->x_char = 0; > > + } else if (tx_count) { > > + msm_hsl_wait_for_xmitr(port); > > + msm_hsl_write(port, tx_count, UARTDM_NCF_TX); > > + msm_hsl_read(port, UARTDM_NCF_TX); > > + } > > + if (!tx_count) { > > + msm_hsl_stop_tx(port); > > + return; > > + } > > + > > + while (tf_pointer < tx_count) { > > + sr = msm_hsl_read(port, UARTDM_SR); > > + if (!(sr & UARTDM_SR_TXRDY_BMSK)) > > + continue; > > + switch (tx_count - tf_pointer) { > > + case 1: > > + x = xmit->buf[xmit->tail]; > > + port->icount.tx++; > > + break; > > + case 2: > > + x = xmit->buf[xmit->tail] > > + | xmit->buf[xmit->tail + 1] << 8; > > + port->icount.tx += 2; > > + break; > > + case 3: > > + x = xmit->buf[xmit->tail] > > + | xmit->buf[xmit->tail + 1] << 8 > > + | xmit->buf[xmit->tail + 2] << 16; > > + port->icount.tx += 3; > > + break; > > + default: > > + x = *((int *)&(xmit->buf[xmit->tail])); > > + port->icount.tx += 4; > > + break; > > + } > > + msm_hsl_write(port, x, UARTDM_TF); > > + xmit->tail = ((tx_count - tf_pointer < 4) ? > > + (tx_count - tf_pointer + xmit->tail) : > > + (xmit->tail + 4)) & (UART_XMIT_SIZE - 1); > > + tf_pointer += 4; > > + sent_tx = 1; > > + } > > + > > + if (uart_circ_empty(xmit)) > > + msm_hsl_stop_tx(port); > > + > > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > > + uart_write_wakeup(port); > > + > > +} > > + > > +static void msm_hsl_handle_delta_cts(struct uart_port *port) > > +{ > > + msm_hsl_write(port, RESET_CTS, UARTDM_CR); > > + port->icount.cts++; > > + wake_up_interruptible(&port->state->port.delta_msr_wait); > > +} > > + > > +static irqreturn_t msm_hsl_irq(int irq, void *dev_id) > > +{ > > + struct uart_port *port = dev_id; > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + unsigned int misr; > > + unsigned long flags; > > + > > + spin_lock_irqsave(&port->lock, flags); > > + misr = msm_hsl_read(port, UARTDM_MISR); > > + /* disable interrupt */ > > + msm_hsl_write(port, 0, UARTDM_IMR); > > + > > + if (misr & (UARTDM_ISR_RXSTALE_BMSK | UARTDM_ISR_RXLEV_BMSK)) { > > + msm_hsl_handle_rx(port, misr); > > + if (misr & (UARTDM_ISR_RXSTALE_BMSK)) > > + msm_hsl_write(port, RESET_STALE_INT, > > + UARTDM_CR); > > + msm_hsl_write(port, 6500, UARTDM_DMRX); > > + msm_hsl_write(port, STALE_EVENT_ENABLE, UARTDM_CR); > > + } > > + if (misr & UARTDM_ISR_TXLEV_BMSK) > > + msm_hsl_handle_tx(port); > > + > > + if (misr & UARTDM_ISR_DELTA_CTS_BMSK) > > + msm_hsl_handle_delta_cts(port); > > + > > + /* restore interrupt */ > > + msm_hsl_write(port, hslp->imr, UARTDM_IMR); > > + spin_unlock_irqrestore(&port->lock, flags); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static unsigned int msm_hsl_tx_empty(struct uart_port *port) > > +{ > > + unsigned int ret; > > + > > + ret = msm_hsl_read(port, UARTDM_SR); > > + ret &= UARTDM_SR_TXEMT_BMSK; > > + > > + if (ret) > > + ret = TIOCSER_TEMT; > > + > > + return ret; > > +} > > + > > +static void msm_hsl_reset(struct uart_port *port) > > +{ > > + /* reset everything */ > > + msm_hsl_write(port, RESET_RX, UARTDM_CR); > > + msm_hsl_write(port, RESET_TX, UARTDM_CR); > > + msm_hsl_write(port, RESET_ERROR_STATUS, UARTDM_CR); > > + msm_hsl_write(port, RESET_BREAK_INT, UARTDM_CR); > > + msm_hsl_write(port, RESET_CTS, UARTDM_CR); > > + msm_hsl_write(port, RFR_LOW, UARTDM_CR); > > +} > > + > > +static unsigned int msm_hsl_get_mctrl(struct uart_port *port) > > +{ > > + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; > > +} > > + > > +static void msm_hsl_set_mctrl(struct uart_port *port, unsigned int mctrl) > > +{ > > + unsigned int mr; > > + unsigned int loop_mode; > > + > > + mr = msm_hsl_read(port, UARTDM_MR1); > > + > > + if (!(mctrl & TIOCM_RTS)) { > > + mr &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; > > + msm_hsl_write(port, mr, UARTDM_MR1); > > + msm_hsl_write(port, RFR_HIGH, UARTDM_CR); > > + } else { > > + mr |= UARTDM_MR1_RX_RDY_CTL_BMSK; > > + msm_hsl_write(port, mr, UARTDM_MR1); > > + } > > + > > + loop_mode = TIOCM_LOOP & mctrl; > > + if (loop_mode) { > > + mr = msm_hsl_read(port, UARTDM_MR2); > > + mr |= UARTDM_MR2_LOOP_MODE_BMSK; > > + msm_hsl_write(port, mr, UARTDM_MR2); > > + > > + /* Reset TX */ > > + msm_hsl_reset(port); > > + > > + /* Turn on Uart Receiver & Transmitter */ > > + msm_hsl_write(port, UARTDM_CR_RX_EN_BMSK | UARTDM_CR_TX_EN_BMSK, > > + UARTDM_CR); > > + } > > +} > > + > > +static void msm_hsl_break_ctl(struct uart_port *port, int break_ctl) > > +{ > > + > > + if (break_ctl) > > + msm_hsl_write(port, START_BREAK, UARTDM_CR); > > + else > > + msm_hsl_write(port, STOP_BREAK, UARTDM_CR); > > +} > > + > > +/* > > + * msm_hsl_set_baud_rate: set requested baud rate > > + * @port: uart port > > + * @baud: baud rate to set (in bps) > > + */ > > +static void msm_hsl_set_baud_rate(struct uart_port *port, unsigned int baud) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + unsigned int baud_code, rxstale, watermark; > > + unsigned int data; > > + > > + switch (baud) { > > + case 300: > > + baud_code = 0x00; > > + rxstale = 1; > > + break; > > + case 600: > > + baud_code = 0x11; > > + rxstale = 1; > > + break; > > + case 1200: > > + baud_code = 0x22; > > + rxstale = 1; > > + break; > > + case 2400: > > + baud_code = 0x33; > > + rxstale = 1; > > + break; > > + case 4800: > > + baud_code = 0x44; > > + rxstale = 1; > > + break; > > + case 9600: > > + baud_code = 0x55; > > + rxstale = 2; > > + break; > > + case 14400: > > + baud_code = 0x66; > > + rxstale = 3; > > + break; > > + case 19200: > > + baud_code = 0x77; > > + rxstale = 4; > > + break; > > + case 28800: > > + baud_code = 0x88; > > + rxstale = 6; > > + break; > > + case 38400: > > + baud_code = 0x99; > > + rxstale = 8; > > + break; > > + case 57600: > > + baud_code = 0xaa; > > + rxstale = 16; > > + break; > > + case 115200: > > + baud_code = 0xcc; > > + rxstale = 31; > > + break; > > + case 230400: > > + baud_code = 0xee; > > + rxstale = 31; > > + break; > > + case 460800: > > + baud_code = 0xff; > > + rxstale = 31; > > + break; > > + default: /*115200 baud rate */ > > + baud_code = 0xcc; > > + rxstale = 31; > > + break; > > + } > > + > > + msm_hsl_write(port, baud_code, UARTDM_CSR); > > + > > + /* > > + * uart baud rate depends on CSR and MND Values > > + * we are updating CSR before and then calling > > + * clk_set_rate which updates MND Values. Hence > > + * dsb requires here. > > + */ > > + mb(); > > + > > + /* > > + * Check requested baud rate and for higher baud rate than 460800, > > + * calculate required uart clock frequency and set the same. > > + */ > > + if (baud > 460800) > > + port->uartclk = baud * 16; > > + else > > + port->uartclk = 7372800; > > + > > + if (clk_set_rate(hslp->clk, port->uartclk)) { > > + WARN_ON(1); > > + return; > > + } > > + > > + /* Set timeout to be ~600x the character transmit time */ > > + hslp->tx_timeout = (1000000000 / baud) * 6; > > + > > + /* RX stale watermark */ > > + watermark = UARTDM_IPR_STALE_LSB_BMSK & rxstale; > > + watermark |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2); > > + msm_hsl_write(port, watermark, UARTDM_IPR); > > + > > + /* Set RX watermark > > + * Configure Rx Watermark as 3/4 size of Rx FIFO. > > + * RFWR register takes value in Words for UARTDM Core > > + * whereas it is consider to be in Bytes for UART Core. > > + * Hence configuring Rx Watermark as 48 Words. > > + */ > > + watermark = (port->fifosize * 3) / 4; > > + msm_hsl_write(port, watermark, UARTDM_RFWR); > > + > > + /* set TX watermark */ > > + msm_hsl_write(port, 0, UARTDM_TFWR); > > + > > + msm_hsl_write(port, CR_PROTECTION_EN, UARTDM_CR); > > + msm_hsl_reset(port); > > + > > + data = UARTDM_CR_TX_EN_BMSK; > > + data |= UARTDM_CR_RX_EN_BMSK; > > + /* enable TX & RX */ > > + msm_hsl_write(port, data, UARTDM_CR); > > + > > + msm_hsl_write(port, RESET_STALE_INT, UARTDM_CR); > > + /* turn on RX and CTS interrupts */ > > + hslp->imr = UARTDM_ISR_RXSTALE_BMSK | UARTDM_ISR_DELTA_CTS_BMSK | > > + UARTDM_ISR_RXLEV_BMSK; > > + msm_hsl_write(port, hslp->imr, UARTDM_IMR); > > + msm_hsl_write(port, 6500, UARTDM_DMRX); > > + msm_hsl_write(port, STALE_EVENT_ENABLE, UARTDM_CR); > > +} > > + > > +static int msm_hsl_startup(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + unsigned int data, rfr_level; > > + int ret; > > + unsigned long flags; > > + > > + snprintf(hslp->name, sizeof(hslp->name), > > + "msm_serial_hsl%d", port->line); > > + > > + if (!(is_console(port)) || (!port->cons) || > > + (port->cons && (!(port->cons->flags & CON_ENABLED)))) { > > + > > + if (msm_serial_hsl_has_gsbi(port)) > > + set_gsbi_uart_func_mode(port); > > + } > > + > > + /* > > + * Set RFR Level as 3/4 of UARTDM FIFO Size > > + * i.e. 48 Words = 192 bytes as Rx FIFO is 64 words ( 256 bytes). > > + */ > > + if (port->fifosize > 48) > > + rfr_level = port->fifosize - 16; > > + else > > + rfr_level = port->fifosize; > > + > > + spin_lock_irqsave(&port->lock, flags); > > + > > + /* set automatic RFR level */ > > + data = msm_hsl_read(port, UARTDM_MR1); > > + data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK; > > + data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK; > > + data |= UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2); > > + data |= UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level; > > + msm_hsl_write(port, data, UARTDM_MR1); > > + spin_unlock_irqrestore(&port->lock, flags); > > + > > + ret = request_irq(port->irq, msm_hsl_irq, IRQF_TRIGGER_HIGH, > > + hslp->name, port); > > + if (ret) > > + dev_err(port->dev, "Failed to request irq\n"); > > + > > + return ret; > > +} > > + > > +static void msm_hsl_shutdown(struct uart_port *port) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + > > + hslp->imr = 0; > > + /* disable interrupts */ > > + msm_hsl_write(port, 0, UARTDM_IMR); > > + > > + free_irq(port->irq, port); > > +} > > + > > +static void msm_hsl_set_termios(struct uart_port *port, > > + struct ktermios *termios, struct ktermios *old) > > +{ > > + unsigned int baud, mr; > > + unsigned long flags; > > + > > + if (!termios->c_cflag) > > + return; > > + > > + /* > > + * Calculate and set baud rate > > + * 300 is the minimum and 4 Mbps is the maximum baud rate > > + * supported by driver. > > + */ > > + baud = uart_get_baud_rate(port, termios, old, 200, 4000000); > > + > > + /* > > + * Due to non-availability of 3.2 Mbps baud rate as standard baud rate > > + * with TTY/serial core. Map 200 BAUD to 3.2 Mbps > > + */ > > + if (baud == 200) > > + baud = 3200000; > > + > > + spin_lock_irqsave(&port->lock, flags); > > + > > + msm_hsl_set_baud_rate(port, baud); > > + > > + /* calculate parity */ > > + mr = msm_hsl_read(port, UARTDM_MR2); > > + mr &= ~UARTDM_MR2_PARITY_MODE_BMSK; > > + if (termios->c_cflag & PARENB) { > > + if (termios->c_cflag & PARODD) > > + mr |= ODD_PARITY; > > + else if (termios->c_cflag & CMSPAR) > > + mr |= SPACE_PARITY; > > + else > > + mr |= EVEN_PARITY; > > + } > > + > > + /* calculate bits per char */ > > + mr &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK; > > + switch (termios->c_cflag & CSIZE) { > > + case CS5: > > + mr |= FIVE_BPC; > > + break; > > + case CS6: > > + mr |= SIX_BPC; > > + break; > > + case CS7: > > + mr |= SEVEN_BPC; > > + break; > > + case CS8: > > + default: > > + mr |= EIGHT_BPC; > > + break; > > + } > > + > > + /* calculate stop bits */ > > + mr &= ~(STOP_BIT_ONE | STOP_BIT_TWO); > > + if (termios->c_cflag & CSTOPB) > > + mr |= STOP_BIT_TWO; > > + else > > + mr |= STOP_BIT_ONE; > > + > > + /* set parity, bits per char, and stop bit */ > > + msm_hsl_write(port, mr, UARTDM_MR2); > > + > > + /* calculate and set hardware flow control */ > > + mr = msm_hsl_read(port, UARTDM_MR1); > > + mr &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK); > > + if (termios->c_cflag & CRTSCTS) { > > + mr |= UARTDM_MR1_CTS_CTL_BMSK; > > + mr |= UARTDM_MR1_RX_RDY_CTL_BMSK; > > + } > > + msm_hsl_write(port, mr, UARTDM_MR1); > > + > > + /* Configure status bits to ignore based on termio flags. */ > > + port->read_status_mask = 0; > > + if (termios->c_iflag & INPCK) > > + port->read_status_mask |= UARTDM_SR_PAR_FRAME_BMSK; > > + if (termios->c_iflag & (BRKINT | PARMRK)) > > + port->read_status_mask |= UARTDM_SR_RX_BREAK_BMSK; > > + > > + uart_update_timeout(port, termios->c_cflag, baud); > > + > > + spin_unlock_irqrestore(&port->lock, flags); > > +} > > + > > +static const char *msm_hsl_type(struct uart_port *port) > > +{ > > + return "MSM"; > > +} > > + > > +static void msm_hsl_release_port(struct uart_port *port) > > +{ > > +} > > + > > +static int msm_hsl_request_port(struct uart_port *port) > > +{ > > + return 0; > > +} > > + > > +static void msm_hsl_config_port(struct uart_port *port, int flags) > > +{ > > + if (flags & UART_CONFIG_TYPE) > > + port->type = PORT_MSM; > > + > > + /* Configure required GSBI based UART protocol. */ > > + if (msm_serial_hsl_has_gsbi(port)) > > + set_gsbi_uart_func_mode(port); > > +} > > + > > +static int msm_hsl_verify_port(struct uart_port *port, > > + struct serial_struct *ser) > > +{ > > + if (ser->type != PORT_UNKNOWN && ser->type != PORT_MSM) > > + return -EINVAL; > > + if (port->irq != ser->irq) > > + return -EINVAL; > > + return 0; > > +} > > + > > +static void msm_hsl_power(struct uart_port *port, unsigned int state, > > + unsigned int oldstate) > > +{ > > + struct msm_hsl_port *hslp = to_hsl_port(port); > > + int ret; > > + > > + switch (state) { > > + case UART_PM_STATE_ON: > > + ret = clk_set_rate(hslp->clk, port->uartclk); > > + if (ret) > > + dev_err(port->dev, "Can't change rate to %u\n", > > + port->uartclk); > > + msm_hsl_clock_enable(port, 1); > > + break; > > + case UART_PM_STATE_OFF: > > + msm_hsl_clock_enable(port, 0); > > + break; > > + default: > > + dev_err(port->dev, "Unknown PM state %d\n", state); > > + } > > +} > > + > > +static struct uart_ops msm_hsl_uart_pops = { > > + .tx_empty = msm_hsl_tx_empty, > > + .set_mctrl = msm_hsl_set_mctrl, > > + .get_mctrl = msm_hsl_get_mctrl, > > + .stop_tx = msm_hsl_stop_tx, > > + .start_tx = msm_hsl_start_tx, > > + .stop_rx = msm_hsl_stop_rx, > > + .enable_ms = msm_hsl_enable_ms, > > + .break_ctl = msm_hsl_break_ctl, > > + .startup = msm_hsl_startup, > > + .shutdown = msm_hsl_shutdown, > > + .set_termios = msm_hsl_set_termios, > > + .type = msm_hsl_type, > > + .release_port = msm_hsl_release_port, > > + .request_port = msm_hsl_request_port, > > + .config_port = msm_hsl_config_port, > > + .verify_port = msm_hsl_verify_port, > > + .pm = msm_hsl_power, > > +}; > > + > > +#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE > > + > > +static struct msm_hsl_port *msm_hsl_uart_ports[UART_NR]; > > + > > +static void msm_hsl_console_putchar(struct uart_port *port, int ch) > > +{ > > + msm_hsl_wait_for_xmitr(port); > > + msm_hsl_write(port, 1, UARTDM_NCF_TX); > > + /* > > + * Dummy read to add 1 AHB clock delay to fix UART hardware bug. > > + * Bug: Delay required on TX-transfer-init. after writing to > > + * NO_CHARS_FOR_TX register. > > + */ > > + msm_hsl_read(port, UARTDM_SR); > > + msm_hsl_write(port, ch, UARTDM_TF); > > +} > > + > > +static void msm_hsl_console_write(struct console *co, const char *s, > > + unsigned int count) > > +{ > > + struct uart_port *port; > > + struct msm_hsl_port *hslp; > > + int locked; > > + > > + BUG_ON(co->index < 0 || co->index >= UART_NR); > > + > > + hslp = msm_hsl_uart_ports[co->index]; > > + port = &hslp->uart; > > + > > + /* not pretty, but we can end up here via various convoluted paths */ > > + if (port->sysrq || oops_in_progress) > > + locked = spin_trylock(&port->lock); > > + else { > > + locked = 1; > > + spin_lock(&port->lock); > > + } > > + msm_hsl_write(port, 0, UARTDM_IMR); > > + uart_console_write(port, s, count, msm_hsl_console_putchar); > > + msm_hsl_write(port, hslp->imr, UARTDM_IMR); > > + if (locked == 1) > > + spin_unlock(&port->lock); > > +} > > + > > +static int msm_hsl_console_setup(struct console *co, char *options) > > +{ > > + struct uart_port *port; > > + int baud = 0, flow, bits, parity, mr2; > > + int ret; > > + > > + if (co->index >= UART_NR || co->index < 0) > > + return -ENXIO; > > + > > + port = &msm_hsl_uart_ports[co->index]->uart; > > + > > + if (!port->membase) > > + return -ENXIO; > > + > > + port->cons = co; > > + > > + pm_runtime_get_noresume(port->dev); > > + > > +#ifndef CONFIG_PM_RUNTIME > > + msm_hsl_clock_enable(port, 1); > > +#endif > > + pm_runtime_resume(port->dev); > > + > > + if (options) > > + uart_parse_options(options, &baud, &parity, &bits, &flow); > > + > > + bits = 8; > > + parity = 'n'; > > + flow = 'n'; > > + msm_hsl_write(port, UARTDM_MR2_BITS_PER_CHAR_8 | > > + STOP_BIT_ONE, UARTDM_MR2); /* 8N1 */ > > + > > + if (baud < 300 || baud > 115200) > > + baud = 115200; > > + > > + msm_hsl_set_baud_rate(port, baud); > > + > > + ret = uart_set_options(port, co, baud, parity, bits, flow); > > + > > + mr2 = msm_hsl_read(port, UARTDM_MR2); > > + mr2 |= UARTDM_MR2_RX_ERROR_CHAR_OFF; > > + mr2 |= UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF; > > + msm_hsl_write(port, mr2, UARTDM_MR2); > > + > > + msm_hsl_reset(port); > > + /* Enable transmitter */ > > + msm_hsl_write(port, CR_PROTECTION_EN, UARTDM_CR); > > + msm_hsl_write(port, UARTDM_CR_TX_EN_BMSK, UARTDM_CR); > > + > > + msm_hsl_write(port, 1, UARTDM_NCF_TX); > > + msm_hsl_read(port, UARTDM_NCF_TX); > > + > > + dev_dbg(port->dev, "Console setup on port #%d\n", port->line); > > + > > + return ret; > > +} > > + > > +static struct uart_driver msm_hsl_uart_driver; > > + > > +static struct console msm_hsl_console = { > > + .name = "ttyHSL", > > + .write = msm_hsl_console_write, > > + .device = uart_console_device, > > + .setup = msm_hsl_console_setup, > > + .flags = CON_PRINTBUFFER, > > + .index = -1, > > + .data = &msm_hsl_uart_driver, > > +}; > > + > > +#define MSM_HSL_CONSOLE (&msm_hsl_console) > > + > > +#else > > +#define MSM_HSL_CONSOLE NULL > > +#endif > > + > > +static struct uart_driver msm_hsl_uart_driver = { > > + .owner = THIS_MODULE, > > + .driver_name = "msm_serial_hsl", > > + .dev_name = "ttyHSL", > > + .nr = UART_NR, > > + .cons = MSM_HSL_CONSOLE, > > +}; > > + > > +static struct dentry *debug_base; > > + > > +DEFINE_SIMPLE_ATTRIBUTE(loopback_enable_fops, msm_hsl_loopback_get, > > + msm_hsl_loopback_set, "%llu\n"); > > +/* > > + * msm_serial_hsl debugfs node: /msm_serial_hsl/loopback. > > + * writing 1 turns on internal loopback mode in HW. Useful for automation > > + * test scripts. > > + * writing 0 disables the internal loopback mode. Default is disabled. > > + */ > > +static void msm_hsl_debugfs_init(struct msm_hsl_port *hslp, int id) > > +{ > > + char node_name[15]; > > + > > + snprintf(node_name, sizeof(node_name), "loopback.%d", id); > > + hslp->loopback_dir = debugfs_create_file(node_name, > > + S_IRUGO | S_IWUSR, > > + debug_base, hslp, > > + &loopback_enable_fops); > > +} > > + > > +static atomic_t msm_serial_hsl_next_id = ATOMIC_INIT(0); > > + > > +static struct of_device_id msm_hsl_match_table[] = { > > + {.compatible = "qcom,msm-lsuart-v14"}, > > + { /* Sentinel */ } > > +}; > > + > > +MODULE_DEVICE_TABLE(of, msm_hsl_match_table); > > + > > +static int msm_serial_hsl_probe(struct platform_device *pdev) > > +{ > > + struct device_node *node = pdev->dev.of_node; > > + struct msm_hsl_port *hslp; > > + struct resource *mem; > > + struct uart_port *port; > > + const struct of_device_id *match; > > + u32 line; > > + int ret; > > + > > + if (pdev->id == -1) > > + pdev->id = atomic_inc_return(&msm_serial_hsl_next_id) - 1; > > + > > + line = pdev->id; > > + > > + /* Use line number from device tree alias if present */ > > + if (!node) > > + return -EINVAL; > > + > > + ret = of_alias_get_id(node, "serial"); > > + if (ret >= 0) > > + line = ret; > > + > > + if (line < 0 || line >= UART_NR) > > + return -ENXIO; > > + > > + pr_info("detected port #%d (ttyHSL%d)\n", pdev->id, line); > > + > > + hslp = devm_kzalloc(&pdev->dev, sizeof(*hslp), GFP_KERNEL); > > + if (!hslp) > > + return -ENOMEM; > > + > > + port = &hslp->uart; > > + port->dev = &pdev->dev; > > + port->uartclk = 7372800; > > + port->iotype = UPIO_MEM; > > + port->ops = &msm_hsl_uart_pops; > > + port->flags = UPF_BOOT_AUTOCONF; > > + port->fifosize = 64; > > + port->line = line; > > + > > + hslp->clk = devm_clk_get(&pdev->dev, "core_clk"); > > + if (IS_ERR(hslp->clk)) { > > + ret = PTR_ERR(hslp->clk); > > + if (ret != -EPROBE_DEFER) > > + dev_err(&pdev->dev, "Error getting core clk\n"); > > + return ret; > > + } > > + > > + /* > > + * Interface clock is not required by all UART configurations. > > + * GSBI UART and BLSP UART needs interface clock but Legacy UART > > + * do not require interface clock. Hence, do not fail probe with > > + * iface of_clk_get_by_name failure. > > + */ > > + hslp->pclk = devm_clk_get(&pdev->dev, "iface_clk"); > > + if (IS_ERR(hslp->pclk)) { > > + ret = PTR_ERR(hslp->pclk); > > + if (ret == -EPROBE_DEFER) > > + return ret; > > + else > > + hslp->pclk = NULL; > > + } > > + > > + hslp->uart_type = LEGACY_HSUART; > > + > > + match = of_match_device(msm_hsl_match_table, &pdev->dev); > > + if (!match) { > > + hslp->regmap = regmap[UARTDM_VERSION_11_13]; > > how would we ever get here? There is no way, of course. I will fix this by adding new compatible = qcom,msm-lsuart-v1.3 field in the match table. Thank you for your comments. Regards, Ivan > > > + } else { > > + hslp->regmap = regmap[UARTDM_VERSION_14]; > > + /* > > + * BLSP based UART configuration is available with > > + * UARTDM v14 Revision. Hence set uart_type as UART_BLSP. > > + */ > > + hslp->uart_type = BLSP_HSUART; > > + } > > + > > + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gbsi_mem"); > > + if (mem) { > > + hslp->mapped_gsbi = devm_request_and_ioremap(&pdev->dev, mem); > > + if (!hslp->mapped_gsbi) > > + dev_warn(&pdev->dev, "GSBI region already claimed\n"); > > + else > > + hslp->uart_type = GSBI_HSUART; > > + } > > + > > + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uart_mem"); > > + if (!mem) { > > + dev_err(&pdev->dev, "Getting UART mem failed\n"); > > + return -ENXIO; > > + } > > + > > + port->mapbase = mem->start; > > + > > + port->irq = platform_get_irq(pdev, 0); > > + if ((int)port->irq < 0) { > > + dev_err(&pdev->dev, "Getting irq failed\n"); > > + return -ENXIO; > > + } > > + > > + port->membase = devm_request_and_ioremap(&pdev->dev, mem); > > + if (!port->membase) { > > + dev_err(&pdev->dev, "UART region already claimed\n"); > > + return -EADDRNOTAVAIL; > > + } > > + > > + device_set_wakeup_capable(&pdev->dev, 1); > > + platform_set_drvdata(pdev, port); > > + pm_runtime_enable(port->dev); > > +#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE > > + msm_hsl_uart_ports[line] = hslp; > > +#endif > > + msm_hsl_debugfs_init(hslp, hslp->uart.line); > > + > > + /* Temporarily increase the refcount on the GSBI clock to avoid a race > > + * condition with the earlyprintk handover mechanism. > > + */ > > + if (hslp->pclk) > > + clk_prepare_enable(hslp->pclk); > > + ret = uart_add_one_port(&msm_hsl_uart_driver, port); > > + if (hslp->pclk) > > + clk_disable_unprepare(hslp->pclk); > > + > > + if (!ret) > > + platform_set_drvdata(pdev, hslp); > > + > > + return ret; > > +} > > + > > +static int msm_serial_hsl_remove(struct platform_device *pdev) > > +{ > > + struct msm_hsl_port *hslp = platform_get_drvdata(pdev); > > + struct uart_port *port; > > + > > + port = &hslp->uart; > > + > > + pm_runtime_put_sync(&pdev->dev); > > + pm_runtime_disable(&pdev->dev); > > + > > + device_set_wakeup_capable(&pdev->dev, 0); > > + platform_set_drvdata(pdev, NULL); > > + > > + uart_remove_one_port(&msm_hsl_uart_driver, port); > > + > > + debugfs_remove(hslp->loopback_dir); > > + return 0; > > +} > > + > > +#ifdef CONFIG_PM > > +static int msm_serial_hsl_suspend(struct device *dev) > > +{ > > + struct msm_hsl_port *hslp = dev_get_drvdata(dev); > > + struct uart_port *port; > > + > > + port = &hslp->uart; > > + > > + if (port) > > + return 0; > > + > > + if (is_console(port)) > > + msm_hsl_clock_enable(port, 0); > > + > > + uart_suspend_port(&msm_hsl_uart_driver, port); > > + if (device_may_wakeup(dev)) > > + enable_irq_wake(port->irq); > > + > > + return 0; > > +} > > + > > +static int msm_serial_hsl_resume(struct device *dev) > > +{ > > + struct msm_hsl_port *hslp = dev_get_drvdata(dev); > > + struct uart_port *port; > > + > > + port = &hslp->uart; > > + > > + if (!port) > > + return 0; > > + > > + uart_resume_port(&msm_hsl_uart_driver, port); > > + if (device_may_wakeup(dev)) > > + disable_irq_wake(port->irq); > > + > > + if (is_console(port)) > > + msm_hsl_clock_enable(port, 1); > > + > > + return 0; > > +} > > +#else > > +#define msm_serial_hsl_suspend NULL > > +#define msm_serial_hsl_resume NULL > > +#endif > > + > > +static int msm_hsl_runtime_suspend(struct device *dev) > > +{ > > + struct msm_hsl_port *hslp = dev_get_drvdata(dev); > > + struct uart_port *port; > > + > > + port = &hslp->uart; > > + > > + dev_dbg(dev, "pm_runtime: suspending\n"); > > + msm_hsl_clock_enable(port, 0); > > + return 0; > > +} > > + > > +static int msm_hsl_runtime_resume(struct device *dev) > > +{ > > + struct msm_hsl_port *hslp = dev_get_drvdata(dev); > > + struct uart_port *port; > > + > > + port = &hslp->uart; > > + > > + dev_dbg(dev, "pm_runtime: resuming\n"); > > + msm_hsl_clock_enable(port, 1); > > + return 0; > > +} > > + > > +static const struct dev_pm_ops msm_hsl_dev_pm_ops = { > > + .suspend = msm_serial_hsl_suspend, > > + .resume = msm_serial_hsl_resume, > > + .runtime_suspend = msm_hsl_runtime_suspend, > > + .runtime_resume = msm_hsl_runtime_resume, > > +}; > > + > > +static struct platform_driver msm_hsl_platform_driver = { > > + .probe = msm_serial_hsl_probe, > > + .remove = msm_serial_hsl_remove, > > + .driver = { > > + .name = "msm_serial_hsl", > > + .owner = THIS_MODULE, > > + .pm = &msm_hsl_dev_pm_ops, > > + .of_match_table = msm_hsl_match_table, > > + }, > > +}; > > + > > +static int __init msm_serial_hsl_init(void) > > +{ > > + int ret; > > + > > + ret = uart_register_driver(&msm_hsl_uart_driver); > > + if (ret) > > + return ret; > > + > > + debug_base = debugfs_create_dir("msm_serial_hsl", NULL); > > + if (IS_ERR_OR_NULL(debug_base)) > > + pr_err("Cannot create debugfs dir\n"); > > + > > + ret = platform_driver_register(&msm_hsl_platform_driver); > > + if (ret) > > + uart_unregister_driver(&msm_hsl_uart_driver); > > + > > + pr_debug("Driver initialized\n"); > > + return ret; > > +} > > + > > +static void __exit msm_serial_hsl_exit(void) > > +{ > > + debugfs_remove_recursive(debug_base); > > +#ifdef CONFIG_SERIAL_MSM_HSL_CONSOLE > > + unregister_console(&msm_hsl_console); > > +#endif > > + platform_driver_unregister(&msm_hsl_platform_driver); > > + uart_unregister_driver(&msm_hsl_uart_driver); > > +} > > + > > +module_init(msm_serial_hsl_init); > > +module_exit(msm_serial_hsl_exit); > > + > > +MODULE_DESCRIPTION("Driver for MSM HSUART UART serial device"); > > +MODULE_LICENSE("GPL v2"); > > diff --git a/drivers/tty/serial/msm_serial_hsl.h b/drivers/tty/serial/msm_serial_hsl.h > > new file mode 100644 > > index 0000000..beb97d4 > > --- /dev/null > > +++ b/drivers/tty/serial/msm_serial_hsl.h > > @@ -0,0 +1,294 @@ > > +/* drivers/tty/serial/msm_serial_hsl.h > > + * > > + * Copyright (c) 2007-2009, 2012-2013,The Linux Foundation. All rights reserved. > > + * > > + * All source code in this file is licensed under the following license > > + * except where indicated. > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License > > + * version 2 as published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. > > + * See the GNU General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public License > > + * along with this program; if not, you can find it at http://www.fsf.org > > + */ > > + > > +#ifndef MSM_SERIAL_HSL_H > > +#define MSM_SERIAL_HSL_H > > + > > +#define GSBI_CONTROL_ADDR 0x0 > > +#define GSBI_PROTOCOL_CODE_MASK 0x30 > > +#define GSBI_PROTOCOL_I2C_UART 0x60 > > +#define GSBI_PROTOCOL_UART 0x40 > > +#define GSBI_PROTOCOL_IDLE 0x0 > > + > > +#define TCSR_ADM_1_A_CRCI_MUX_SEL 0x78 > > +#define TCSR_ADM_1_B_CRCI_MUX_SEL 0x7c > > +#define ADM1_CRCI_GSBI6_RX_SEL 0x800 > > +#define ADM1_CRCI_GSBI6_TX_SEL 0x400 > > + > > +enum msm_hsl_regs { > > + UARTDM_MR1, > > + UARTDM_MR2, > > + UARTDM_IMR, > > + UARTDM_SR, > > + UARTDM_CR, > > + UARTDM_CSR, > > + UARTDM_IPR, > > + UARTDM_ISR, > > + UARTDM_RX_TOTAL_SNAP, > > + UARTDM_RFWR, > > + UARTDM_TFWR, > > + UARTDM_RF, > > + UARTDM_TF, > > + UARTDM_MISR, > > + UARTDM_DMRX, > > + UARTDM_NCF_TX, > > + UARTDM_DMEN, > > + UARTDM_BCR, > > + UARTDM_TXFS, > > + UARTDM_RXFS, > > + UARTDM_LAST, > > +}; > > + > > +#define UARTDM_MR1_ADDR 0x0 > > +#define UARTDM_MR2_ADDR 0x4 > > + > > +/* Backward Compatability Register for UARTDM Core v1.4 */ > > +#define UARTDM_BCR_ADDR 0xc8 > > + > > +/* > > + * UARTDM Core v1.4 STALE_IRQ_EMPTY bit defination > > + * Stale interrupt will fire if bit is set when RX-FIFO is empty > > + */ > > +#define UARTDM_BCR_STALE_IRQ_EMPTY 0x2 > > + > > +/* TRANSFER_CONTROL Register for UARTDM Core v1.4 */ > > +#define UARTDM_RX_TRANS_CTRL_ADDR 0xcc > > + > > +/* TRANSFER_CONTROL Register bits */ > > +#define RX_STALE_AUTO_RE_EN 0x1 > > +#define RX_TRANS_AUTO_RE_ACTIVATE 0x2 > > +#define RX_DMRX_CYCLIC_EN 0x4 > > + > > +/* write only register */ > > +#define UARTDM_IPR_ADDR 0x18 > > +#define UARTDM_TFWR_ADDR 0x1c > > +#define UARTDM_RFWR_ADDR 0x20 > > +#define UARTDM_HCR_ADDR 0x24 > > +#define UARTDM_DMRX_ADDR 0x34 > > +#define UARTDM_DMEN_ADDR 0x3c > > + > > +/* UART_DM_NO_CHARS_FOR_TX */ > > +#define UARTDM_NCF_TX_ADDR 0x40 > > + > > +#define UARTDM_BADR_ADDR 0x44 > > + > > +#define UARTDM_SIM_CFG_ADDR 0x80 > > + > > +/* Read Only register */ > > +#define UARTDM_TXFS_ADDR 0x4c > > +#define UARTDM_RXFS_ADDR 0x50 > > + > > +/* Register field Mask Mapping */ > > +#define UARTDM_SR_RX_BREAK_BMSK BIT(6) > > +#define UARTDM_SR_PAR_FRAME_BMSK BIT(5) > > +#define UARTDM_SR_OVERRUN_BMSK BIT(4) > > +#define UARTDM_SR_TXEMT_BMSK BIT(3) > > +#define UARTDM_SR_TXRDY_BMSK BIT(2) > > +#define UARTDM_SR_RXRDY_BMSK BIT(0) > > + > > +#define UARTDM_CR_TX_DISABLE_BMSK BIT(3) > > +#define UARTDM_CR_RX_DISABLE_BMSK BIT(1) > > +#define UARTDM_CR_TX_EN_BMSK BIT(2) > > +#define UARTDM_CR_RX_EN_BMSK BIT(0) > > + > > +/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */ > > +#define RESET_RX 0x10 > > +#define RESET_TX 0x20 > > +#define RESET_ERROR_STATUS 0x30 > > +#define RESET_BREAK_INT 0x40 > > +#define START_BREAK 0x50 > > +#define STOP_BREAK 0x60 > > +#define RESET_CTS 0x70 > > +#define RESET_STALE_INT 0x80 > > +#define RFR_LOW 0xD0 > > +#define RFR_HIGH 0xE0 > > +#define CR_PROTECTION_EN 0x100 > > +#define STALE_EVENT_ENABLE 0x500 > > +#define STALE_EVENT_DISABLE 0x600 > > +#define FORCE_STALE_EVENT 0x400 > > +#define CLEAR_TX_READY 0x300 > > +#define RESET_TX_ERROR 0x800 > > +#define RESET_TX_DONE 0x810 > > + > > +/* > > + * UARTDM_CR BAM IFC comman bit value > > + * for UARTDM Core v1.4 > > + */ > > +#define START_RX_BAM_IFC 0x850 > > +#define START_TX_BAM_IFC 0x860 > > + > > +#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00 > > +#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f > > +#define UARTDM_MR1_CTS_CTL_BMSK 0x40 > > +#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80 > > + > > +/* > > + * UARTDM Core v1.4 MR2_RFR_CTS_LOOP bitmask > > + * Enables internal loopback between RFR_N of > > + * RX channel and CTS_N of TX channel. > > + */ > > +#define UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK 0x400 > > + > > +#define UARTDM_MR2_LOOP_MODE_BMSK 0x80 > > +#define UARTDM_MR2_ERROR_MODE_BMSK 0x40 > > +#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30 > > +#define UARTDM_MR2_RX_ZERO_CHAR_OFF 0x100 > > +#define UARTDM_MR2_RX_ERROR_CHAR_OFF 0x200 > > +#define UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF 0x100 > > + > > +#define UARTDM_MR2_BITS_PER_CHAR_8 (0x3 << 4) > > + > > +/* bits per character configuration */ > > +#define FIVE_BPC (0 << 4) > > +#define SIX_BPC (1 << 4) > > +#define SEVEN_BPC (2 << 4) > > +#define EIGHT_BPC (3 << 4) > > + > > +#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc > > +#define STOP_BIT_ONE (1 << 2) > > +#define STOP_BIT_TWO (3 << 2) > > + > > +#define UARTDM_MR2_PARITY_MODE_BMSK 0x3 > > + > > +/* Parity configuration */ > > +#define NO_PARITY 0x0 > > +#define EVEN_PARITY 0x2 > > +#define ODD_PARITY 0x1 > > +#define SPACE_PARITY 0x3 > > + > > +#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80 > > +#define UARTDM_IPR_STALE_LSB_BMSK 0x1f > > + > > +/* These can be used for both ISR and IMR register */ > > +#define UARTDM_ISR_TX_READY_BMSK BIT(7) > > +#define UARTDM_ISR_CURRENT_CTS_BMSK BIT(6) > > +#define UARTDM_ISR_DELTA_CTS_BMSK BIT(5) > > +#define UARTDM_ISR_RXLEV_BMSK BIT(4) > > +#define UARTDM_ISR_RXSTALE_BMSK BIT(3) > > +#define UARTDM_ISR_RXBREAK_BMSK BIT(2) > > +#define UARTDM_ISR_RXHUNT_BMSK BIT(1) > > +#define UARTDM_ISR_TXLEV_BMSK BIT(0) > > + > > +/* Field definitions for UART_DM_DMEN*/ > > +#define UARTDM_TX_DM_EN_BMSK 0x1 > > +#define UARTDM_RX_DM_EN_BMSK 0x2 > > + > > +/* > > + * UARTDM Core v1.4 bitmask > > + * Bitmasks for enabling Rx and Tx BAM Interface > > + */ > > +#define UARTDM_TX_BAM_ENABLE_BMSK 0x4 > > +#define UARTDM_RX_BAM_ENABLE_BMSK 0x8 > > + > > +/* > > + * Some of the BLSP Based UART Core(v14) existing register offsets > > + * are different compare to GSBI based UART Core(v13) > > + * Hence add the changed register offsets for UART Core v14 > > + */ > > + > > +/* write only register */ > > +#define UARTDM_CSR_ADDR_V14 0xa0 > > + > > +/* write only register */ > > +#define UARTDM_TF_ADDR_V14 0x100 > > +#define UARTDM_TF2_ADDR_V14 0x104 > > +#define UARTDM_TF3_ADDR_V14 0x108 > > +#define UARTDM_TF4_ADDR_V14 0x10c > > +#define UARTDM_TF5_ADDR_V14 0x110 > > +#define UARTDM_TF6_ADDR_V14 0x114 > > +#define UARTDM_TF7_ADDR_V14 0x118 > > +#define UARTDM_TF8_ADDR_V14 0x11c > > +#define UARTDM_TF9_ADDR_V14 0x120 > > +#define UARTDM_TF10_ADDR_V14 0x124 > > +#define UARTDM_TF11_ADDR_V14 0x128 > > +#define UARTDM_TF12_ADDR_V14 0x12c > > +#define UARTDM_TF13_ADDR_V14 0x130 > > +#define UARTDM_TF14_ADDR_V14 0x134 > > +#define UARTDM_TF15_ADDR_V14 0x138 > > +#define UARTDM_TF16_ADDR_V14 0x13c > > + > > +/* write only register */ > > +#define UARTDM_CR_ADDR_V14 0xa8 > > +/* write only register */ > > +#define UARTDM_IMR_ADDR_V14 0xb0 > > +#define UARTDM_IRDA_ADDR_V14 0xb8 > > + > > +/* Read Only register */ > > +#define UARTDM_SR_ADDR_V14 0xa4 > > + > > +/* Read Only register */ > > +#define UARTDM_RF_ADDR_V14 0x140 > > +#define UARTDM_RF2_ADDR_V14 0x144 > > +#define UARTDM_RF3_ADDR_V14 0x148 > > +#define UARTDM_RF4_ADDR_V14 0x14c > > +#define UARTDM_RF5_ADDR_V14 0x150 > > +#define UARTDM_RF6_ADDR_V14 0x154 > > +#define UARTDM_RF7_ADDR_V14 0x158 > > +#define UARTDM_RF8_ADDR_V14 0x15c > > +#define UARTDM_RF9_ADDR_V14 0x160 > > +#define UARTDM_RF10_ADDR_V14 0x164 > > +#define UARTDM_RF11_ADDR_V14 0x168 > > +#define UARTDM_RF12_ADDR_V14 0x16c > > +#define UARTDM_RF13_ADDR_V14 0x170 > > +#define UARTDM_RF14_ADDR_V14 0x174 > > +#define UARTDM_RF15_ADDR_V14 0x178 > > +#define UARTDM_RF16_ADDR_V14 0x17c > > + > > +/* Read Only register */ > > +#define UARTDM_MISR_ADDR_V14 0xac > > + > > +/* Read Only register */ > > +#define UARTDM_ISR_ADDR_V14 0xb4 > > +#define UARTDM_RX_TOTAL_SNAP_ADDR_V14 0xbc > > + > > +/* Register offsets for UART Core v13 */ > > + > > +/* write only register */ > > +#define UARTDM_CSR_ADDR 0x8 > > + > > +/* write only register */ > > +#define UARTDM_TF_ADDR 0x70 > > +#define UARTDM_TF2_ADDR 0x74 > > +#define UARTDM_TF3_ADDR 0x78 > > +#define UARTDM_TF4_ADDR 0x7c > > + > > +/* write only register */ > > +#define UARTDM_CR_ADDR 0x10 > > +/* write only register */ > > +#define UARTDM_IMR_ADDR 0x14 > > +#define UARTDM_IRDA_ADDR 0x38 > > + > > +/* Read Only register */ > > +#define UARTDM_SR_ADDR 0x8 > > + > > +/* Read Only register */ > > +#define UARTDM_RF_ADDR 0x70 > > +#define UARTDM_RF2_ADDR 0x74 > > +#define UARTDM_RF3_ADDR 0x78 > > +#define UARTDM_RF4_ADDR 0x7c > > + > > +/* Read Only register */ > > +#define UARTDM_MISR_ADDR 0x10 > > + > > +/* Read Only register */ > > +#define UARTDM_ISR_ADDR 0x14 > > +#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38 > > + > > +#endif /* MSM_SERIAL_HSL_H */ > > -- > > 1.7.9.5 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > -- > Employee of Qualcomm Innovation Center, Inc. > Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/