From: "Ivan T. Ivanov" <[email protected]>
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 <[email protected]>
---
.../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
+
+- 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<alias_num>) with a given alias number. Aliases are of the form
+uart<n> where <n> 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/Kconfig b/drivers/tty/serial/Kconfig
index 7e7006f..4482bc7 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1046,6 +1046,24 @@ config SERIAL_MSM_HS
Choose M here to compile it as a module. The module will be
called msm_serial_hs.
+config SERIAL_MSM_HSL
+ tristate "MSM High speed serial lite mode driver"
+ depends on ARM && ARCH_MSM
+ select SERIAL_CORE
+ default n
+ help
+ Select this module to enable MSM high speed lite mode driver
+ for UART controllers found in MSM8974 SoC's
+
+ Choose M here to compile it as a module. The module will be
+ called msm_serial_hsl.
+
+config SERIAL_MSM_HSL_CONSOLE
+ bool "MSM High speed serial lite mode console support"
+ depends on SERIAL_MSM_HSL=y
+ select SERIAL_CORE_CONSOLE
+ default n
+
config SERIAL_VT8500
bool "VIA VT8500 on-chip serial port support"
depends on ARCH_VT8500
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index eedfec4..86c023b 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
+obj-$(CONFIG_SERIAL_MSM_HSL) += msm_serial_hsl.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
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 <[email protected]> */
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/nmi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#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,
+ },
+ [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: <debugfs_root>/msm_serial_hsl/loopback.<id>
+ * 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];
+ } 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
Hi Greg,
On Mon, 2013-07-01 at 12:11 +0300, Ivan T. Ivanov wrote:
> From: "Ivan T. Ivanov" <[email protected]>
>
> 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.
>
Please, could you take a look at this driver. It have been
tested on these new Snapdragon 8074 based DragonBoards.
If there is something that I have to fix or improve in
the code please let me know.
Regards,
Ivan
> Signed-off-by: Ivan T. Ivanov <[email protected]>
> ---
> .../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
> +
> +- 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<alias_num>) with a given alias number. Aliases are of the form
> +uart<n> where <n> 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/Kconfig b/drivers/tty/serial/Kconfig
> index 7e7006f..4482bc7 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1046,6 +1046,24 @@ config SERIAL_MSM_HS
> Choose M here to compile it as a module. The module will be
> called msm_serial_hs.
>
> +config SERIAL_MSM_HSL
> + tristate "MSM High speed serial lite mode driver"
> + depends on ARM && ARCH_MSM
> + select SERIAL_CORE
> + default n
> + help
> + Select this module to enable MSM high speed lite mode driver
> + for UART controllers found in MSM8974 SoC's
> +
> + Choose M here to compile it as a module. The module will be
> + called msm_serial_hsl.
> +
> +config SERIAL_MSM_HSL_CONSOLE
> + bool "MSM High speed serial lite mode console support"
> + depends on SERIAL_MSM_HSL=y
> + select SERIAL_CORE_CONSOLE
> + default n
> +
> config SERIAL_VT8500
> bool "VIA VT8500 on-chip serial port support"
> depends on ARCH_VT8500
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index eedfec4..86c023b 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -58,6 +58,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
> obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
> obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
> obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
> +obj-$(CONFIG_SERIAL_MSM_HSL) += msm_serial_hsl.o
> obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
> obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
> 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 <[email protected]> */
> +
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/nmi.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +
> +#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,
> + },
> + [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: <debugfs_root>/msm_serial_hsl/loopback.<id>
> + * 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];
> + } 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 */
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" <[email protected]>
> >
> > 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 <[email protected]>
> > ---
> > .../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<alias_num>) with a given alias number. Aliases are of the form
> > +uart<n> where <n> 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 <[email protected]> */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/console.h>
> > +#include <linux/debugfs.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/irq.h>
> > +#include <linux/module.h>
> > +#include <linux/nmi.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/serial_core.h>
> > +#include <linux/tty.h>
> > +#include <linux/tty_flip.h>
> > +
> > +#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: <debugfs_root>/msm_serial_hsl/loopback.<id>
> > + * 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 [email protected]
> > 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
>