devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/7] Introduce GENI SE Controller Driver
@ 2018-01-13  1:05 Karthikeyan Ramasubramanian
  2018-01-13  1:05 ` [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary Karthikeyan Ramasubramanian
                   ` (5 more replies)
  0 siblings, 6 replies; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby

Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
Wrapper is a next generation programmable module for supporting a wide
range of serial interfaces like UART, SPI, I2C, I3C, etc. A single QUP
module can provide upto 8 Serial Interfaces using its internal Serial
Engines (SE). The protocol supported by each interface is determined by
the firmware loaded to the Serial Engine.

This patch series introduces GENI SE Driver to manage the GENI based QUP
Wrapper and the common aspects of all SEs inside the QUP Wrapper. This
patch series also introduces the UART and I2C Controller drivers to
drive the SEs that are programmed with the respective protocols.

[v2]
 * Updated device tree bindings to describe the hardware
 * Updated SE DT node as child node of QUP Wrapper DT node
 * Moved common AHB clocks to QUP Wrapper DT node
 * Use the standard "clock-frequency" I2C property
 * Update compatible field in UART Controller to reflect hardware manual
 * Addressed other device tree binding specific comments from Rob Herring

Karthikeyan Ramasubramanian (7):
  qcom-geni-se: Add QCOM GENI SE Driver summary
  dt-bindings: soc: qcom: Add device tree binding for GENI SE
  soc: qcom: Add GENI based QUP Wrapper driver
  dt-bindings: i2c: Add device tree bindings for GENI I2C Controller
  i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C
    controller
  dt-bindings: serial: Add bindings for GENI based UART Controller
  tty: serial: msm_geni_serial: Add serial driver support for GENI based
    QUP

 .../devicetree/bindings/i2c/i2c-qcom-geni.txt      |   35 +
 .../devicetree/bindings/serial/qcom,geni-uart.txt  |   29 +
 .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  |   66 +
 Documentation/qcom-geni-se.txt                     |   56 +
 drivers/i2c/busses/Kconfig                         |   10 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-qcom-geni.c                 |  656 +++++++++
 drivers/soc/qcom/Kconfig                           |    8 +
 drivers/soc/qcom/Makefile                          |    1 +
 drivers/soc/qcom/qcom-geni-se.c                    | 1016 ++++++++++++++
 drivers/tty/serial/Kconfig                         |   10 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/qcom_geni_serial.c              | 1414 ++++++++++++++++++++
 include/linux/qcom-geni-se.h                       |  807 +++++++++++
 14 files changed, 4110 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
 create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
 create mode 100644 Documentation/qcom-geni-se.txt
 create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
 create mode 100644 drivers/soc/qcom/qcom-geni-se.c
 create mode 100644 drivers/tty/serial/qcom_geni_serial.c
 create mode 100644 include/linux/qcom-geni-se.h

-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary
  2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
@ 2018-01-13  1:05 ` Karthikeyan Ramasubramanian
  2018-01-16 16:55   ` Bjorn Andersson
  2018-01-13  1:05 ` [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby

Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
Wrapper is a programmable module that is composed of multiple Serial
Engines (SE) and can support various Serial Interfaces like UART, SPI,
I2C, I3C, etc. This document provides a high level overview of the GENI
based QUP Wrapper.

Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
---
 Documentation/qcom-geni-se.txt | 56 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/qcom-geni-se.txt

diff --git a/Documentation/qcom-geni-se.txt b/Documentation/qcom-geni-se.txt
new file mode 100644
index 0000000..dc517ef
--- /dev/null
+++ b/Documentation/qcom-geni-se.txt
@@ -0,0 +1,56 @@
+Introduction
+============
+
+Generic Interface (GENI) Serial Engine (SE) Wrapper driver is introduced
+to manage GENI firmware based Qualcomm Universal Peripheral (QUP) Wrapper
+controller. QUP Wrapper is designed to support various serial bus protocols
+like UART, SPI, I2C, I3C, etc.
+
+Hardware description
+====================
+
+GENI based QUP is a highly-flexible and programmable module for supporting
+a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. A single
+QUP module can provide upto 8 Serial Interfaces, using its internal
+Serial Engines. The actual configuration is determined by the target
+platform configuration. The protocol supported by each interface is
+determined by the firmware loaded to the Serial Engine. Each SE consists
+of a DMA Engine and GENI sub modules which enable Serial Engines to
+support FIFO and DMA modes of operation.
+
+::
+
+                      +-----------------------------------------+
+                      |QUP Wrapper                              |
+                      |         +----------------------------+  |
+   --QUP & SE Clocks-->         | Serial Engine N            |  +-IO------>
+                      |         | ...                        |  | Interface
+   <---Clock Perf.----+    +----+-----------------------+    |  |
+     State Interface  |    | Serial Engine 1            |    |  |
+                      |    |                            |    |  |
+                      |    |                            |    |  |
+   <--------AHB------->    |                            |    |  |
+                      |    |                            +----+  |
+                      |    |                            |       |
+                      |    |                            |       |
+   <------SE IRQ------+    +----------------------------+       |
+                      |                                         |
+                      +-----------------------------------------+
+
+                         Figure 1: GENI based QUP Wrapper
+
+Software description
+====================
+
+GENI SE Wrapper driver is structured into 2 parts:
+
+geni_se_device represents QUP Wrapper controller. This part of the driver
+manages QUP Wrapper information such as hardware version, clock
+performance table that is common to all the internal Serial Engines.
+
+geni_se_rsc represents Serial Engine. This part of the driver manages
+Serial Engine information such as clocks, pinctrl states, containing QUP
+Wrapper. This part of driver also supports operations(eg. initialize the
+concerned Serial Engine, select between FIFO and DMA mode of operation etc.)
+that are common to all the Serial Engines and are independent of Serial
+Interfaces.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
  2018-01-13  1:05 ` [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary Karthikeyan Ramasubramanian
@ 2018-01-13  1:05 ` Karthikeyan Ramasubramanian
  2018-01-17  6:25   ` Bjorn Andersson
  2018-01-13  1:05 ` [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller Karthikeyan Ramasubramanian
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby

Add device tree binding support for the QCOM GENI SE driver.

Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
---
 .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 34 ++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt

diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
new file mode 100644
index 0000000..66f8a16
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
@@ -0,0 +1,34 @@
+Qualcomm Technologies, Inc. GENI Serial Engine QUP Wrapper Controller
+
+Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) wrapper
+is a programmable module for supporting a wide range of serial interfaces
+like UART, SPI, I2C, I3C, etc. A single QUP module can provide upto 8 Serial
+Interfaces, using its internal Serial Engines. The GENI Serial Engine QUP
+Wrapper controller is modeled as a node with zero or more child nodes each
+representing a serial engine.
+
+Required properties:
+- compatible:		Must be "qcom,geni-se-qup".
+- reg:			Must contain QUP register address and length.
+- clock-names:		Must contain "m-ahb" and "s-ahb".
+- clocks:		AHB clocks needed by the device.
+
+Required properties if child node exists:
+- #address-cells: Must be 1
+- #size-cells: Must be 1
+- ranges: Must be present
+
+Properties for children:
+
+A GENI based QUP wrapper controller node can contain 0 or more child nodes
+representing serial devices.  These serial devices can be a QCOM UART, I2C
+controller, spi controller, or some combination of aforementioned devices.
+
+Example:
+	qup0: qcom,geniqup0@8c0000 {
+		compatible = "qcom,geni-se-qup";
+		reg = <0x8c0000 0x6000>;
+		clock-names = "m-ahb", "s-ahb";
+		clocks = <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
+			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+	}
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
       [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2018-01-13  1:05   ` Karthikeyan Ramasubramanian
  2018-01-17  6:20     ` Bjorn Andersson
                       ` (3 more replies)
  2018-01-13  1:05   ` [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
  2018-01-19 18:32   ` [PATCH v2 0/7] Introduce GENI SE Controller Driver Randy Dunlap
  2 siblings, 4 replies; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet-T1hC0tSOHrs, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	wsa-z923LK4zBo2bacvFa/9K2g,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
  Cc: Karthikeyan Ramasubramanian, linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia, Girish Mahadevan

This driver manages the Generic Interface (GENI) firmware based Qualcomm
Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
programmable module composed of multiple Serial Engines (SE) and supports
a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
driver also enables managing the serial interface independent aspects of
Serial Engines.

Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Girish Mahadevan <girishm-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
 drivers/soc/qcom/Kconfig        |    8 +
 drivers/soc/qcom/Makefile       |    1 +
 drivers/soc/qcom/qcom-geni-se.c | 1016 +++++++++++++++++++++++++++++++++++++++
 include/linux/qcom-geni-se.h    |  807 +++++++++++++++++++++++++++++++
 4 files changed, 1832 insertions(+)
 create mode 100644 drivers/soc/qcom/qcom-geni-se.c
 create mode 100644 include/linux/qcom-geni-se.h

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index b81374b..b306d51 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -3,6 +3,14 @@
 #
 menu "Qualcomm SoC drivers"
 
+config QCOM_GENI_SE
+	tristate "QCOM GENI Serial Engine Driver"
+	help
+	  This module is used to manage Generic Interface (GENI) firmware based
+	  Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
+	  module is also used to manage the common aspects of multiple Serial
+	  Engines present in the QUP.
+
 config QCOM_GLINK_SSR
 	tristate "Qualcomm Glink SSR driver"
 	depends on RPMSG
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 40c56f6..74d5db8 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_QCOM_GENI_SE) +=	qcom-geni-se.o
 obj-$(CONFIG_QCOM_GLINK_SSR) +=	glink_ssr.o
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
 obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
new file mode 100644
index 0000000..3f43582
--- /dev/null
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -0,0 +1,1016 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/qcom-geni-se.h>
+#include <linux/spinlock.h>
+
+#define MAX_CLK_PERF_LEVEL 32
+
+/**
+ * @struct geni_se_device - Data structure to represent the QUP Wrapper Core
+ * @dev:		Device pointer of the QUP wrapper core.
+ * @base:		Base address of this instance of QUP wrapper core.
+ * @m_ahb_clk:		Handle to the primary AHB clock.
+ * @s_ahb_clk:		Handle to the secondary AHB clock.
+ * @geni_dev_lock:	Lock to protect the device elements.
+ * @num_clk_levels:	Number of valid clock levels in clk_perf_tbl.
+ * @clk_perf_tbl:	Table of clock frequency input to Serial Engine clock.
+ */
+struct geni_se_device {
+	struct device *dev;
+	void __iomem *base;
+	struct clk *m_ahb_clk;
+	struct clk *s_ahb_clk;
+	struct mutex geni_dev_lock;
+	unsigned int num_clk_levels;
+	unsigned long *clk_perf_tbl;
+};
+
+/* Offset of QUP Hardware Version Register */
+#define QUP_HW_VER (0x4)
+
+#define HW_VER_MAJOR_MASK GENMASK(31, 28)
+#define HW_VER_MAJOR_SHFT 28
+#define HW_VER_MINOR_MASK GENMASK(27, 16)
+#define HW_VER_MINOR_SHFT 16
+#define HW_VER_STEP_MASK GENMASK(15, 0)
+
+/**
+ * geni_read_reg_nolog() - Helper function to read from a GENI register
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ *
+ * Return:	Return the contents of the register.
+ */
+unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
+{
+	return readl_relaxed(base + offset);
+}
+EXPORT_SYMBOL(geni_read_reg_nolog);
+
+/**
+ * geni_write_reg_nolog() - Helper function to write into a GENI register
+ * @value:	Value to be written into the register.
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ */
+void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset)
+{
+	return writel_relaxed(value, (base + offset));
+}
+EXPORT_SYMBOL(geni_write_reg_nolog);
+
+/**
+ * geni_read_reg() - Helper function to read from a GENI register
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ *
+ * Return:	Return the contents of the register.
+ */
+unsigned int geni_read_reg(void __iomem *base, int offset)
+{
+	return readl_relaxed(base + offset);
+}
+EXPORT_SYMBOL(geni_read_reg);
+
+/**
+ * geni_write_reg() - Helper function to write into a GENI register
+ * @value:	Value to be written into the register.
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ */
+void geni_write_reg(unsigned int value, void __iomem *base, int offset)
+{
+	return writel_relaxed(value, (base + offset));
+}
+EXPORT_SYMBOL(geni_write_reg);
+
+/**
+ * geni_get_qup_hw_version() - Read the QUP wrapper Hardware version
+ * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
+ * @major:		Buffer for Major Version field.
+ * @minor:		Buffer for Minor Version field.
+ * @step:		Buffer for Step Version field.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
+			    unsigned int *minor, unsigned int *step)
+{
+	unsigned int version;
+	struct geni_se_device *geni_se_dev;
+
+	if (!wrapper_dev || !major || !minor || !step)
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return -ENODEV;
+
+	version = geni_read_reg(geni_se_dev->base, QUP_HW_VER);
+	*major = (version & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT;
+	*minor = (version & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT;
+	*step = version & HW_VER_STEP_MASK;
+	return 0;
+}
+EXPORT_SYMBOL(geni_get_qup_hw_version);
+
+/**
+ * geni_se_get_proto() - Read the protocol configured for a serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * Return:	Protocol value as configured in the serial engine.
+ */
+int geni_se_get_proto(void __iomem *base)
+{
+	int proto;
+
+	proto = ((geni_read_reg(base, GENI_FW_REVISION_RO)
+			& FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);
+	return proto;
+}
+EXPORT_SYMBOL(geni_se_get_proto);
+
+static int geni_se_irq_en(void __iomem *base)
+{
+	unsigned int common_geni_m_irq_en;
+	unsigned int common_geni_s_irq_en;
+
+	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
+	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
+	/* Common to all modes */
+	common_geni_m_irq_en |= M_COMMON_GENI_M_IRQ_EN;
+	common_geni_s_irq_en |= S_COMMON_GENI_S_IRQ_EN;
+
+	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
+	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
+	return 0;
+}
+
+
+static void geni_se_set_rx_rfr_wm(void __iomem *base, unsigned int rx_wm,
+				  unsigned int rx_rfr)
+{
+	geni_write_reg(rx_wm, base, SE_GENI_RX_WATERMARK_REG);
+	geni_write_reg(rx_rfr, base, SE_GENI_RX_RFR_WATERMARK_REG);
+}
+
+static int geni_se_io_set_mode(void __iomem *base)
+{
+	unsigned int io_mode;
+	unsigned int geni_dma_mode;
+
+	io_mode = geni_read_reg(base, SE_IRQ_EN);
+	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
+
+	io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
+	io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
+	geni_dma_mode &= ~GENI_DMA_MODE_EN;
+
+	geni_write_reg(io_mode, base, SE_IRQ_EN);
+	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
+	geni_write_reg(0, base, SE_GSI_EVENT_EN);
+	return 0;
+}
+
+static void geni_se_io_init(void __iomem *base)
+{
+	unsigned int io_op_ctrl;
+	unsigned int geni_cgc_ctrl;
+	unsigned int dma_general_cfg;
+
+	geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
+	dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
+	geni_cgc_ctrl |= DEFAULT_CGC_EN;
+	dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
+			DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);
+	io_op_ctrl = DEFAULT_IO_OUTPUT_CTRL_MSK;
+	geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
+	geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);
+
+	geni_write_reg(io_op_ctrl, base, GENI_OUTPUT_CTRL);
+	geni_write_reg(FORCE_DEFAULT, base, GENI_FORCE_DEFAULT_REG);
+}
+
+/**
+ * geni_se_init() - Initialize the GENI Serial Engine
+ * @base:	Base address of the serial engine's register block.
+ * @rx_wm:	Receive watermark to be configured.
+ * @rx_rfr_wm:	Ready-for-receive watermark to be configured.
+ *
+ * This function is used to initialize the GENI serial engine, configure
+ * receive watermark and ready-for-receive watermarks.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr)
+{
+	int ret;
+
+	geni_se_io_init(base);
+	ret = geni_se_io_set_mode(base);
+	if (ret)
+		return ret;
+
+	geni_se_set_rx_rfr_wm(base, rx_wm, rx_rfr);
+	ret = geni_se_irq_en(base);
+	return ret;
+}
+EXPORT_SYMBOL(geni_se_init);
+
+static int geni_se_select_fifo_mode(void __iomem *base)
+{
+	int proto = geni_se_get_proto(base);
+	unsigned int common_geni_m_irq_en;
+	unsigned int common_geni_s_irq_en;
+	unsigned int geni_dma_mode;
+
+	geni_write_reg(0, base, SE_GSI_EVENT_EN);
+	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
+	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
+	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
+	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
+	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
+
+	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
+	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
+	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
+	if (proto != UART) {
+		common_geni_m_irq_en |=
+			(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
+			M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
+		common_geni_s_irq_en |= S_CMD_DONE_EN;
+	}
+	geni_dma_mode &= ~GENI_DMA_MODE_EN;
+
+	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
+	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
+	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
+	return 0;
+}
+
+static int geni_se_select_dma_mode(void __iomem *base)
+{
+	unsigned int geni_dma_mode = 0;
+
+	geni_write_reg(0, base, SE_GSI_EVENT_EN);
+	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
+	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
+	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
+	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
+	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
+
+	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
+	geni_dma_mode |= GENI_DMA_MODE_EN;
+	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
+	return 0;
+}
+
+/**
+ * geni_se_select_mode() - Select the serial engine transfer mode
+ * @base:	Base address of the serial engine's register block.
+ * @mode:	Transfer mode to be selected.
+ *
+ * Return:	0 on success, standard Linux error codes on failure.
+ */
+int geni_se_select_mode(void __iomem *base, int mode)
+{
+	int ret = 0;
+
+	switch (mode) {
+	case FIFO_MODE:
+		geni_se_select_fifo_mode(base);
+		break;
+	case SE_DMA:
+		geni_se_select_dma_mode(base);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(geni_se_select_mode);
+
+/**
+ * geni_se_setup_m_cmd() - Setup the primary sequencer
+ * @base:	Base address of the serial engine's register block.
+ * @cmd:	Command/Operation to setup in the primary sequencer.
+ * @params:	Parameter for the sequencer command.
+ *
+ * This function is used to configure the primary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params)
+{
+	u32 m_cmd = (cmd << M_OPCODE_SHFT);
+
+	m_cmd |= (params & M_PARAMS_MSK);
+	geni_write_reg(m_cmd, base, SE_GENI_M_CMD0);
+}
+EXPORT_SYMBOL(geni_se_setup_m_cmd);
+
+/**
+ * geni_se_setup_s_cmd() - Setup the secondary sequencer
+ * @base:	Base address of the serial engine's register block.
+ * @cmd:	Command/Operation to setup in the secondary sequencer.
+ * @params:	Parameter for the sequencer command.
+ *
+ * This function is used to configure the secondary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_s_cmd(void __iomem *base, u32 cmd, u32 params)
+{
+	u32 s_cmd = geni_read_reg(base, SE_GENI_S_CMD0);
+
+	s_cmd &= ~(S_OPCODE_MSK | S_PARAMS_MSK);
+	s_cmd |= (cmd << S_OPCODE_SHFT);
+	s_cmd |= (params & S_PARAMS_MSK);
+	geni_write_reg(s_cmd, base, SE_GENI_S_CMD0);
+}
+EXPORT_SYMBOL(geni_se_setup_s_cmd);
+
+/**
+ * geni_se_cancel_m_cmd() - Cancel the command configured in the primary
+ *                          sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_cancel_m_cmd(void __iomem *base)
+{
+	geni_write_reg(M_GENI_CMD_CANCEL, base, SE_GENI_M_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_cancel_m_cmd);
+
+/**
+ * geni_se_cancel_s_cmd() - Cancel the command configured in the secondary
+ *                          sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_cancel_s_cmd(void __iomem *base)
+{
+	geni_write_reg(S_GENI_CMD_CANCEL, base, SE_GENI_S_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_cancel_s_cmd);
+
+/**
+ * geni_se_abort_m_cmd() - Abort the command configured in the primary sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_abort_m_cmd(void __iomem *base)
+{
+	geni_write_reg(M_GENI_CMD_ABORT, base, SE_GENI_M_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_abort_m_cmd);
+
+/**
+ * geni_se_abort_s_cmd() - Abort the command configured in the secondary
+ *                         sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_abort_s_cmd(void __iomem *base)
+{
+	geni_write_reg(S_GENI_CMD_ABORT, base, SE_GENI_S_CMD_CTRL_REG);
+}
+EXPORT_SYMBOL(geni_se_abort_s_cmd);
+
+/**
+ * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * TX fifo of the serial engine.
+ *
+ * Return:	TX fifo depth in units of FIFO words.
+ */
+int geni_se_get_tx_fifo_depth(void __iomem *base)
+{
+	int tx_fifo_depth;
+
+	tx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_0)
+			& TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT);
+	return tx_fifo_depth;
+}
+EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
+
+/**
+ * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to get the width i.e. word size per element in the
+ * TX fifo of the serial engine.
+ *
+ * Return:	TX fifo width in bits
+ */
+int geni_se_get_tx_fifo_width(void __iomem *base)
+{
+	int tx_fifo_width;
+
+	tx_fifo_width = ((geni_read_reg(base, SE_HW_PARAM_0)
+			& TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT);
+	return tx_fifo_width;
+}
+EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
+
+/**
+ * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * RX fifo of the serial engine.
+ *
+ * Return:	RX fifo depth in units of FIFO words
+ */
+int geni_se_get_rx_fifo_depth(void __iomem *base)
+{
+	int rx_fifo_depth;
+
+	rx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_1)
+			& RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT);
+	return rx_fifo_depth;
+}
+EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
+
+/**
+ * geni_se_get_packing_config() - Get the packing configuration based on input
+ * @bpw:	Bits of data per transfer word.
+ * @pack_words:	Number of words per fifo element.
+ * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
+ * @cfg0:	Output buffer to hold the first half of configuration.
+ * @cfg1:	Output buffer to hold the second half of configuration.
+ *
+ * This function is used to calculate the packing configuration based on
+ * the input packing requirement and the configuration logic.
+ */
+void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
+				unsigned long *cfg0, unsigned long *cfg1)
+{
+	u32 cfg[4] = {0};
+	int len;
+	int temp_bpw = bpw;
+	int idx_start = (msb_to_lsb ? (bpw - 1) : 0);
+	int idx = idx_start;
+	int idx_delta = (msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE);
+	int ceil_bpw = ((bpw & (BITS_PER_BYTE - 1)) ?
+			((bpw & ~(BITS_PER_BYTE - 1)) + BITS_PER_BYTE) : bpw);
+	int iter = (ceil_bpw * pack_words) >> 3;
+	int i;
+
+	if (unlikely(iter <= 0 || iter > 4)) {
+		*cfg0 = 0;
+		*cfg1 = 0;
+		return;
+	}
+
+	for (i = 0; i < iter; i++) {
+		len = (temp_bpw < BITS_PER_BYTE) ?
+				(temp_bpw - 1) : BITS_PER_BYTE - 1;
+		cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));
+		idx = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
+				((i + 1) * BITS_PER_BYTE) + idx_start :
+				idx + idx_delta;
+		temp_bpw = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
+				bpw : (temp_bpw - BITS_PER_BYTE);
+	}
+	cfg[iter - 1] |= 1;
+	*cfg0 = cfg[0] | (cfg[1] << 10);
+	*cfg1 = cfg[2] | (cfg[3] << 10);
+}
+EXPORT_SYMBOL(geni_se_get_packing_config);
+
+/**
+ * geni_se_config_packing() - Packing configuration of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ * @bpw:	Bits of data per transfer word.
+ * @pack_words:	Number of words per fifo element.
+ * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
+ *
+ * This function is used to configure the packing rules for the current
+ * transfer.
+ */
+void geni_se_config_packing(void __iomem *base, int bpw,
+			    int pack_words, bool msb_to_lsb)
+{
+	unsigned long cfg0, cfg1;
+
+	geni_se_get_packing_config(bpw, pack_words, msb_to_lsb, &cfg0, &cfg1);
+	geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
+	geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
+	geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
+	geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
+	if (pack_words || bpw == 32)
+		geni_write_reg((bpw >> 4), base, SE_GENI_BYTE_GRAN);
+}
+EXPORT_SYMBOL(geni_se_config_packing);
+
+static void geni_se_clks_off(struct geni_se_rsc *rsc)
+{
+	struct geni_se_device *geni_se_dev;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev))
+		return;
+
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return;
+
+	clk_disable_unprepare(rsc->se_clk);
+	clk_disable_unprepare(geni_se_dev->s_ahb_clk);
+	clk_disable_unprepare(geni_se_dev->m_ahb_clk);
+}
+
+/**
+ * geni_se_resources_off() - Turn off resources associated with the serial
+ *                           engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_off(struct geni_se_rsc *rsc)
+{
+	int ret = 0;
+	struct geni_se_device *geni_se_dev;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev))
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return -ENODEV;
+
+	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
+	if (ret)
+		return ret;
+
+	geni_se_clks_off(rsc);
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_resources_off);
+
+static int geni_se_clks_on(struct geni_se_rsc *rsc)
+{
+	int ret;
+	struct geni_se_device *geni_se_dev;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev))
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return -EPROBE_DEFER;
+
+	ret = clk_prepare_enable(geni_se_dev->m_ahb_clk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(geni_se_dev->s_ahb_clk);
+	if (ret) {
+		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(rsc->se_clk);
+	if (ret) {
+		clk_disable_unprepare(geni_se_dev->s_ahb_clk);
+		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
+	}
+	return ret;
+}
+
+/**
+ * geni_se_resources_on() - Turn on resources associated with the serial
+ *                          engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_on(struct geni_se_rsc *rsc)
+{
+	int ret = 0;
+	struct geni_se_device *geni_se_dev;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev))
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return -EPROBE_DEFER;
+
+	ret = geni_se_clks_on(rsc);
+	if (ret)
+		return ret;
+
+	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
+	if (ret)
+		geni_se_clks_off(rsc);
+
+	return ret;
+}
+EXPORT_SYMBOL(geni_se_resources_on);
+
+/**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc:	Resource for which the clock table is requested.
+ * @tbl:	Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return:	number of valid performance levels in the table on success,
+ *		standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl)
+{
+	struct geni_se_device *geni_se_dev;
+	int i;
+	unsigned long prev_freq = 0;
+	int ret = 0;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))
+		return -EINVAL;
+
+	*tbl = NULL;
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return -EPROBE_DEFER;
+
+	mutex_lock(&geni_se_dev->geni_dev_lock);
+	if (geni_se_dev->clk_perf_tbl) {
+		*tbl = geni_se_dev->clk_perf_tbl;
+		ret = geni_se_dev->num_clk_levels;
+		goto exit_se_clk_tbl_get;
+	}
+
+	geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
+						MAX_CLK_PERF_LEVEL, GFP_KERNEL);
+	if (!geni_se_dev->clk_perf_tbl) {
+		ret = -ENOMEM;
+		goto exit_se_clk_tbl_get;
+	}
+
+	for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
+		geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
+								prev_freq + 1);
+		if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
+			geni_se_dev->clk_perf_tbl[i] = 0;
+			break;
+		}
+		prev_freq = geni_se_dev->clk_perf_tbl[i];
+	}
+	geni_se_dev->num_clk_levels = i;
+	*tbl = geni_se_dev->clk_perf_tbl;
+	ret = geni_se_dev->num_clk_levels;
+exit_se_clk_tbl_get:
+	mutex_unlock(&geni_se_dev->geni_dev_lock);
+	return ret;
+}
+EXPORT_SYMBOL(geni_se_clk_tbl_get);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc:	Resource for which the clock frequency is requested.
+ * @req_freq:	Requested clock frequency.
+ * @index:	Index of the resultant frequency in the table.
+ * @res_freq:	Resultant frequency which matches or is closer to the
+ *		requested frequency.
+ * @exact:	Flag to indicate exact multiple requirement of the requested
+ *		frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return:	0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
+			   unsigned int *index, unsigned long *res_freq,
+			   bool exact)
+{
+	unsigned long *tbl;
+	int num_clk_levels;
+	int i;
+
+	num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
+	if (num_clk_levels < 0)
+		return num_clk_levels;
+
+	if (num_clk_levels == 0)
+		return -EFAULT;
+
+	*res_freq = 0;
+	for (i = 0; i < num_clk_levels; i++) {
+		if (!(tbl[i] % req_freq)) {
+			*index = i;
+			*res_freq = tbl[i];
+			return 0;
+		}
+
+		if (!(*res_freq) || ((tbl[i] > *res_freq) &&
+				     (tbl[i] < req_freq))) {
+			*index = i;
+			*res_freq = tbl[i];
+		}
+	}
+
+	if (exact || !(*res_freq))
+		return -ENOKEY;
+
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_clk_freq_match);
+
+/**
+ * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
+ * @base:		Base address of the SE register block.
+ * @tx_buf:		Pointer to the TX buffer.
+ * @tx_len:		Length of the TX buffer.
+ * @tx_dma:		Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA TX.
+ *
+ * Return:	0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+			void *tx_buf, int tx_len, dma_addr_t *tx_dma)
+{
+	int ret;
+
+	if (unlikely(!wrapper_dev || !base || !tx_buf || !tx_len || !tx_dma))
+		return -EINVAL;
+
+	ret = geni_se_map_buf(wrapper_dev, tx_dma, tx_buf, tx_len,
+				    DMA_TO_DEVICE);
+	if (ret)
+		return ret;
+
+	geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);
+	geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
+	geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);
+	geni_write_reg(1, base, SE_DMA_TX_ATTR);
+	geni_write_reg(tx_len, base, SE_DMA_TX_LEN);
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_tx_dma_prep);
+
+/**
+ * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
+ * @base:		Base address of the SE register block.
+ * @rx_buf:		Pointer to the RX buffer.
+ * @rx_len:		Length of the RX buffer.
+ * @rx_dma:		Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA RX.
+ *
+ * Return:	0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+			void *rx_buf, int rx_len, dma_addr_t *rx_dma)
+{
+	int ret;
+
+	if (unlikely(!wrapper_dev || !base || !rx_buf || !rx_len || !rx_dma))
+		return -EINVAL;
+
+	ret = geni_se_map_buf(wrapper_dev, rx_dma, rx_buf, rx_len,
+				    DMA_FROM_DEVICE);
+	if (ret)
+		return ret;
+
+	geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
+	geni_write_reg((u32)(*rx_dma), base, SE_DMA_RX_PTR_L);
+	geni_write_reg((u32)((*rx_dma) >> 32), base, SE_DMA_RX_PTR_H);
+	/* RX does not have EOT bit */
+	geni_write_reg(0, base, SE_DMA_RX_ATTR);
+	geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_rx_dma_prep);
+
+/**
+ * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
+ * @tx_dma:		DMA address of the TX buffer.
+ * @tx_len:		Length of the TX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA TX.
+ */
+void geni_se_tx_dma_unprep(struct device *wrapper_dev,
+			   dma_addr_t tx_dma, int tx_len)
+{
+	if (tx_dma)
+		geni_se_unmap_buf(wrapper_dev, &tx_dma, tx_len,
+						DMA_TO_DEVICE);
+}
+EXPORT_SYMBOL(geni_se_tx_dma_unprep);
+
+/**
+ * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
+ * @rx_dma:		DMA address of the RX buffer.
+ * @rx_len:		Length of the RX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA RX.
+ */
+void geni_se_rx_dma_unprep(struct device *wrapper_dev,
+			   dma_addr_t rx_dma, int rx_len)
+{
+	if (rx_dma)
+		geni_se_unmap_buf(wrapper_dev, &rx_dma, rx_len,
+						DMA_FROM_DEVICE);
+}
+EXPORT_SYMBOL(geni_se_rx_dma_unprep);
+
+/**
+ * geni_se_map_buf() - Map a single buffer into QUP wrapper device
+ * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
+ * @iova:		Pointer in which the mapped virtual address is stored.
+ * @buf:		Address of the buffer that needs to be mapped.
+ * @size:		Size of the buffer.
+ * @dir:		Direction of the DMA transfer.
+ *
+ * This function is used to map an already allocated buffer into the
+ * QUP device space.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
+		    void *buf, size_t size, enum dma_data_direction dir)
+{
+	struct device *dev_p;
+	struct geni_se_device *geni_se_dev;
+
+	if (!wrapper_dev || !iova || !buf || !size)
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(wrapper_dev);
+	if (!geni_se_dev || !geni_se_dev->dev)
+		return -ENODEV;
+
+	dev_p = geni_se_dev->dev;
+
+	*iova = dma_map_single(dev_p, buf, size, dir);
+	if (dma_mapping_error(dev_p, *iova))
+		return -EIO;
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_map_buf);
+
+/**
+ * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
+ * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
+ * @iova:		Pointer in which the mapped virtual address is stored.
+ * @size:		Size of the buffer.
+ * @dir:		Direction of the DMA transfer.
+ *
+ * This function is used to unmap an already mapped buffer from the
+ * QUP device space.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
+		      size_t size, enum dma_data_direction dir)
+{
+	struct device *dev_p;
+	struct geni_se_device *geni_se_dev;
+
+	if (!wrapper_dev || !iova || !size)
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(wrapper_dev);
+	if (!geni_se_dev || !geni_se_dev->dev)
+		return -ENODEV;
+
+	dev_p = geni_se_dev->dev;
+	dma_unmap_single(dev_p, *iova, size, dir);
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_unmap_buf);
+
+static const struct of_device_id geni_se_dt_match[] = {
+	{ .compatible = "qcom,geni-se-qup", },
+	{}
+};
+
+static int geni_se_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct geni_se_device *geni_se_dev;
+	int ret = 0;
+
+	geni_se_dev = devm_kzalloc(dev, sizeof(*geni_se_dev), GFP_KERNEL);
+	if (!geni_se_dev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "%s: Mandatory resource info not found\n",
+			__func__);
+		devm_kfree(dev, geni_se_dev);
+		return -EINVAL;
+	}
+
+	geni_se_dev->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR_OR_NULL(geni_se_dev->base)) {
+		dev_err(dev, "%s: Error mapping the resource\n", __func__);
+		devm_kfree(dev, geni_se_dev);
+		return -EFAULT;
+	}
+
+	geni_se_dev->m_ahb_clk = devm_clk_get(dev, "m-ahb");
+	if (IS_ERR(geni_se_dev->m_ahb_clk)) {
+		ret = PTR_ERR(geni_se_dev->m_ahb_clk);
+		dev_err(dev, "Err getting M AHB clk %d\n", ret);
+		devm_iounmap(dev, geni_se_dev->base);
+		devm_kfree(dev, geni_se_dev);
+		return ret;
+	}
+
+	geni_se_dev->s_ahb_clk = devm_clk_get(dev, "s-ahb");
+	if (IS_ERR(geni_se_dev->s_ahb_clk)) {
+		ret = PTR_ERR(geni_se_dev->s_ahb_clk);
+		dev_err(dev, "Err getting S AHB clk %d\n", ret);
+		devm_clk_put(dev, geni_se_dev->m_ahb_clk);
+		devm_iounmap(dev, geni_se_dev->base);
+		devm_kfree(dev, geni_se_dev);
+		return ret;
+	}
+
+	geni_se_dev->dev = dev;
+	mutex_init(&geni_se_dev->geni_dev_lock);
+	dev_set_drvdata(dev, geni_se_dev);
+	dev_dbg(dev, "GENI SE Driver probed\n");
+	return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+}
+
+static int geni_se_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct geni_se_device *geni_se_dev = dev_get_drvdata(dev);
+
+	devm_clk_put(dev, geni_se_dev->s_ahb_clk);
+	devm_clk_put(dev, geni_se_dev->m_ahb_clk);
+	devm_iounmap(dev, geni_se_dev->base);
+	devm_kfree(dev, geni_se_dev);
+	return 0;
+}
+
+static struct platform_driver geni_se_driver = {
+	.driver = {
+		.name = "geni_se_qup",
+		.of_match_table = geni_se_dt_match,
+	},
+	.probe = geni_se_probe,
+	.remove = geni_se_remove,
+};
+
+static int __init geni_se_driver_init(void)
+{
+	return platform_driver_register(&geni_se_driver);
+}
+arch_initcall(geni_se_driver_init);
+
+static void __exit geni_se_driver_exit(void)
+{
+	platform_driver_unregister(&geni_se_driver);
+}
+module_exit(geni_se_driver_exit);
+
+MODULE_DESCRIPTION("GENI Serial Engine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
new file mode 100644
index 0000000..5695da9
--- /dev/null
+++ b/include/linux/qcom-geni-se.h
@@ -0,0 +1,807 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef _LINUX_QCOM_GENI_SE
+#define _LINUX_QCOM_GENI_SE
+#include <linux/clk.h>
+#include <linux/dma-direction.h>
+#include <linux/io.h>
+#include <linux/list.h>
+
+/* Transfer mode supported by GENI Serial Engines */
+enum geni_se_xfer_mode {
+	INVALID,
+	FIFO_MODE,
+	SE_DMA,
+};
+
+/* Protocols supported by GENI Serial Engines */
+enum geni_se_protocol_types {
+	NONE,
+	SPI,
+	UART,
+	I2C,
+	I3C
+};
+
+/**
+ * struct geni_se_rsc - GENI Serial Engine Resource
+ * @wrapper_dev:	Pointer to the parent QUP Wrapper core.
+ * @se_clk:		Handle to the core serial engine clock.
+ * @geni_pinctrl:	Handle to the pinctrl configuration.
+ * @geni_gpio_active:	Handle to the default/active pinctrl state.
+ * @geni_gpi_sleep:	Handle to the sleep pinctrl state.
+ */
+struct geni_se_rsc {
+	struct device *wrapper_dev;
+	struct clk *se_clk;
+	struct pinctrl *geni_pinctrl;
+	struct pinctrl_state *geni_gpio_active;
+	struct pinctrl_state *geni_gpio_sleep;
+};
+
+#define PINCTRL_DEFAULT	"default"
+#define PINCTRL_SLEEP	"sleep"
+
+/* Common SE registers */
+#define GENI_INIT_CFG_REVISION		(0x0)
+#define GENI_S_INIT_CFG_REVISION	(0x4)
+#define GENI_FORCE_DEFAULT_REG		(0x20)
+#define GENI_OUTPUT_CTRL		(0x24)
+#define GENI_CGC_CTRL			(0x28)
+#define SE_GENI_STATUS			(0x40)
+#define GENI_SER_M_CLK_CFG		(0x48)
+#define GENI_SER_S_CLK_CFG		(0x4C)
+#define GENI_CLK_CTRL_RO		(0x60)
+#define GENI_IF_DISABLE_RO		(0x64)
+#define GENI_FW_REVISION_RO		(0x68)
+#define GENI_FW_S_REVISION_RO		(0x6C)
+#define SE_GENI_CLK_SEL			(0x7C)
+#define SE_GENI_BYTE_GRAN		(0x254)
+#define SE_GENI_DMA_MODE_EN		(0x258)
+#define SE_GENI_TX_PACKING_CFG0		(0x260)
+#define SE_GENI_TX_PACKING_CFG1		(0x264)
+#define SE_GENI_RX_PACKING_CFG0		(0x284)
+#define SE_GENI_RX_PACKING_CFG1		(0x288)
+#define SE_GENI_M_CMD0			(0x600)
+#define SE_GENI_M_CMD_CTRL_REG		(0x604)
+#define SE_GENI_M_IRQ_STATUS		(0x610)
+#define SE_GENI_M_IRQ_EN		(0x614)
+#define SE_GENI_M_IRQ_CLEAR		(0x618)
+#define SE_GENI_S_CMD0			(0x630)
+#define SE_GENI_S_CMD_CTRL_REG		(0x634)
+#define SE_GENI_S_IRQ_STATUS		(0x640)
+#define SE_GENI_S_IRQ_EN		(0x644)
+#define SE_GENI_S_IRQ_CLEAR		(0x648)
+#define SE_GENI_TX_FIFOn		(0x700)
+#define SE_GENI_RX_FIFOn		(0x780)
+#define SE_GENI_TX_FIFO_STATUS		(0x800)
+#define SE_GENI_RX_FIFO_STATUS		(0x804)
+#define SE_GENI_TX_WATERMARK_REG	(0x80C)
+#define SE_GENI_RX_WATERMARK_REG	(0x810)
+#define SE_GENI_RX_RFR_WATERMARK_REG	(0x814)
+#define SE_GENI_IOS			(0x908)
+#define SE_GENI_M_GP_LENGTH		(0x910)
+#define SE_GENI_S_GP_LENGTH		(0x914)
+#define SE_GSI_EVENT_EN			(0xE18)
+#define SE_IRQ_EN			(0xE1C)
+#define SE_HW_PARAM_0			(0xE24)
+#define SE_HW_PARAM_1			(0xE28)
+#define SE_DMA_GENERAL_CFG		(0xE30)
+
+/* GENI_OUTPUT_CTRL fields */
+#define DEFAULT_IO_OUTPUT_CTRL_MSK	(GENMASK(6, 0))
+
+/* GENI_FORCE_DEFAULT_REG fields */
+#define FORCE_DEFAULT	(BIT(0))
+
+/* GENI_CGC_CTRL fields */
+#define CFG_AHB_CLK_CGC_ON		(BIT(0))
+#define CFG_AHB_WR_ACLK_CGC_ON		(BIT(1))
+#define DATA_AHB_CLK_CGC_ON		(BIT(2))
+#define SCLK_CGC_ON			(BIT(3))
+#define TX_CLK_CGC_ON			(BIT(4))
+#define RX_CLK_CGC_ON			(BIT(5))
+#define EXT_CLK_CGC_ON			(BIT(6))
+#define PROG_RAM_HCLK_OFF		(BIT(8))
+#define PROG_RAM_SCLK_OFF		(BIT(9))
+#define DEFAULT_CGC_EN			(GENMASK(6, 0))
+
+/* GENI_STATUS fields */
+#define M_GENI_CMD_ACTIVE		(BIT(0))
+#define S_GENI_CMD_ACTIVE		(BIT(12))
+
+/* GENI_SER_M_CLK_CFG/GENI_SER_S_CLK_CFG */
+#define SER_CLK_EN			(BIT(0))
+#define CLK_DIV_MSK			(GENMASK(15, 4))
+#define CLK_DIV_SHFT			(4)
+
+/* CLK_CTRL_RO fields */
+
+/* IF_DISABLE_RO fields */
+
+/* FW_REVISION_RO fields */
+#define FW_REV_PROTOCOL_MSK	(GENMASK(15, 8))
+#define FW_REV_PROTOCOL_SHFT	(8)
+
+/* GENI_CLK_SEL fields */
+#define CLK_SEL_MSK		(GENMASK(2, 0))
+
+/* SE_GENI_DMA_MODE_EN */
+#define GENI_DMA_MODE_EN	(BIT(0))
+
+/* GENI_M_CMD0 fields */
+#define M_OPCODE_MSK		(GENMASK(31, 27))
+#define M_OPCODE_SHFT		(27)
+#define M_PARAMS_MSK		(GENMASK(26, 0))
+
+/* GENI_M_CMD_CTRL_REG */
+#define M_GENI_CMD_CANCEL	BIT(2)
+#define M_GENI_CMD_ABORT	BIT(1)
+#define M_GENI_DISABLE		BIT(0)
+
+/* GENI_S_CMD0 fields */
+#define S_OPCODE_MSK		(GENMASK(31, 27))
+#define S_OPCODE_SHFT		(27)
+#define S_PARAMS_MSK		(GENMASK(26, 0))
+
+/* GENI_S_CMD_CTRL_REG */
+#define S_GENI_CMD_CANCEL	(BIT(2))
+#define S_GENI_CMD_ABORT	(BIT(1))
+#define S_GENI_DISABLE		(BIT(0))
+
+/* GENI_M_IRQ_EN fields */
+#define M_CMD_DONE_EN		(BIT(0))
+#define M_CMD_OVERRUN_EN	(BIT(1))
+#define M_ILLEGAL_CMD_EN	(BIT(2))
+#define M_CMD_FAILURE_EN	(BIT(3))
+#define M_CMD_CANCEL_EN		(BIT(4))
+#define M_CMD_ABORT_EN		(BIT(5))
+#define M_TIMESTAMP_EN		(BIT(6))
+#define M_RX_IRQ_EN		(BIT(7))
+#define M_GP_SYNC_IRQ_0_EN	(BIT(8))
+#define M_GP_IRQ_0_EN		(BIT(9))
+#define M_GP_IRQ_1_EN		(BIT(10))
+#define M_GP_IRQ_2_EN		(BIT(11))
+#define M_GP_IRQ_3_EN		(BIT(12))
+#define M_GP_IRQ_4_EN		(BIT(13))
+#define M_GP_IRQ_5_EN		(BIT(14))
+#define M_IO_DATA_DEASSERT_EN	(BIT(22))
+#define M_IO_DATA_ASSERT_EN	(BIT(23))
+#define M_RX_FIFO_RD_ERR_EN	(BIT(24))
+#define M_RX_FIFO_WR_ERR_EN	(BIT(25))
+#define M_RX_FIFO_WATERMARK_EN	(BIT(26))
+#define M_RX_FIFO_LAST_EN	(BIT(27))
+#define M_TX_FIFO_RD_ERR_EN	(BIT(28))
+#define M_TX_FIFO_WR_ERR_EN	(BIT(29))
+#define M_TX_FIFO_WATERMARK_EN	(BIT(30))
+#define M_SEC_IRQ_EN		(BIT(31))
+#define M_COMMON_GENI_M_IRQ_EN	(GENMASK(6, 1) | \
+				M_IO_DATA_DEASSERT_EN | \
+				M_IO_DATA_ASSERT_EN | M_RX_FIFO_RD_ERR_EN | \
+				M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | \
+				M_TX_FIFO_WR_ERR_EN)
+
+/* GENI_S_IRQ_EN fields */
+#define S_CMD_DONE_EN		(BIT(0))
+#define S_CMD_OVERRUN_EN	(BIT(1))
+#define S_ILLEGAL_CMD_EN	(BIT(2))
+#define S_CMD_FAILURE_EN	(BIT(3))
+#define S_CMD_CANCEL_EN		(BIT(4))
+#define S_CMD_ABORT_EN		(BIT(5))
+#define S_GP_SYNC_IRQ_0_EN	(BIT(8))
+#define S_GP_IRQ_0_EN		(BIT(9))
+#define S_GP_IRQ_1_EN		(BIT(10))
+#define S_GP_IRQ_2_EN		(BIT(11))
+#define S_GP_IRQ_3_EN		(BIT(12))
+#define S_GP_IRQ_4_EN		(BIT(13))
+#define S_GP_IRQ_5_EN		(BIT(14))
+#define S_IO_DATA_DEASSERT_EN	(BIT(22))
+#define S_IO_DATA_ASSERT_EN	(BIT(23))
+#define S_RX_FIFO_RD_ERR_EN	(BIT(24))
+#define S_RX_FIFO_WR_ERR_EN	(BIT(25))
+#define S_RX_FIFO_WATERMARK_EN	(BIT(26))
+#define S_RX_FIFO_LAST_EN	(BIT(27))
+#define S_COMMON_GENI_S_IRQ_EN	(GENMASK(5, 1) | GENMASK(13, 9) | \
+				 S_RX_FIFO_RD_ERR_EN | S_RX_FIFO_WR_ERR_EN)
+
+/*  GENI_/TX/RX/RX_RFR/_WATERMARK_REG fields */
+#define WATERMARK_MSK		(GENMASK(5, 0))
+
+/* GENI_TX_FIFO_STATUS fields */
+#define TX_FIFO_WC		(GENMASK(27, 0))
+
+/*  GENI_RX_FIFO_STATUS fields */
+#define RX_LAST			(BIT(31))
+#define RX_LAST_BYTE_VALID_MSK	(GENMASK(30, 28))
+#define RX_LAST_BYTE_VALID_SHFT	(28)
+#define RX_FIFO_WC_MSK		(GENMASK(24, 0))
+
+/* SE_GSI_EVENT_EN fields */
+#define DMA_RX_EVENT_EN		(BIT(0))
+#define DMA_TX_EVENT_EN		(BIT(1))
+#define GENI_M_EVENT_EN		(BIT(2))
+#define GENI_S_EVENT_EN		(BIT(3))
+
+/* SE_GENI_IOS fields */
+#define IO2_DATA_IN		(BIT(1))
+#define RX_DATA_IN		(BIT(0))
+
+/* SE_IRQ_EN fields */
+#define DMA_RX_IRQ_EN		(BIT(0))
+#define DMA_TX_IRQ_EN		(BIT(1))
+#define GENI_M_IRQ_EN		(BIT(2))
+#define GENI_S_IRQ_EN		(BIT(3))
+
+/* SE_HW_PARAM_0 fields */
+#define TX_FIFO_WIDTH_MSK	(GENMASK(29, 24))
+#define TX_FIFO_WIDTH_SHFT	(24)
+#define TX_FIFO_DEPTH_MSK	(GENMASK(21, 16))
+#define TX_FIFO_DEPTH_SHFT	(16)
+
+/* SE_HW_PARAM_1 fields */
+#define RX_FIFO_WIDTH_MSK	(GENMASK(29, 24))
+#define RX_FIFO_WIDTH_SHFT	(24)
+#define RX_FIFO_DEPTH_MSK	(GENMASK(21, 16))
+#define RX_FIFO_DEPTH_SHFT	(16)
+
+/* SE_DMA_GENERAL_CFG */
+#define DMA_RX_CLK_CGC_ON	(BIT(0))
+#define DMA_TX_CLK_CGC_ON	(BIT(1))
+#define DMA_AHB_SLV_CFG_ON	(BIT(2))
+#define AHB_SEC_SLV_CLK_CGC_ON	(BIT(3))
+#define DUMMY_RX_NON_BUFFERABLE	(BIT(4))
+#define RX_DMA_ZERO_PADDING_EN	(BIT(5))
+#define RX_DMA_IRQ_DELAY_MSK	(GENMASK(8, 6))
+#define RX_DMA_IRQ_DELAY_SHFT	(6)
+
+#define SE_DMA_TX_PTR_L		(0xC30)
+#define SE_DMA_TX_PTR_H		(0xC34)
+#define SE_DMA_TX_ATTR		(0xC38)
+#define SE_DMA_TX_LEN		(0xC3C)
+#define SE_DMA_TX_IRQ_STAT	(0xC40)
+#define SE_DMA_TX_IRQ_CLR	(0xC44)
+#define SE_DMA_TX_IRQ_EN	(0xC48)
+#define SE_DMA_TX_IRQ_EN_SET	(0xC4C)
+#define SE_DMA_TX_IRQ_EN_CLR	(0xC50)
+#define SE_DMA_TX_LEN_IN	(0xC54)
+#define SE_DMA_TX_FSM_RST	(0xC58)
+#define SE_DMA_TX_MAX_BURST	(0xC5C)
+
+#define SE_DMA_RX_PTR_L		(0xD30)
+#define SE_DMA_RX_PTR_H		(0xD34)
+#define SE_DMA_RX_ATTR		(0xD38)
+#define SE_DMA_RX_LEN		(0xD3C)
+#define SE_DMA_RX_IRQ_STAT	(0xD40)
+#define SE_DMA_RX_IRQ_CLR	(0xD44)
+#define SE_DMA_RX_IRQ_EN	(0xD48)
+#define SE_DMA_RX_IRQ_EN_SET	(0xD4C)
+#define SE_DMA_RX_IRQ_EN_CLR	(0xD50)
+#define SE_DMA_RX_LEN_IN	(0xD54)
+#define SE_DMA_RX_FSM_RST	(0xD58)
+#define SE_DMA_RX_MAX_BURST	(0xD5C)
+#define SE_DMA_RX_FLUSH		(0xD60)
+
+/* SE_DMA_TX_IRQ_STAT Register fields */
+#define TX_DMA_DONE		(BIT(0))
+#define TX_EOT			(BIT(1))
+#define TX_SBE			(BIT(2))
+#define TX_RESET_DONE		(BIT(3))
+
+/* SE_DMA_RX_IRQ_STAT Register fields */
+#define RX_DMA_DONE		(BIT(0))
+#define RX_EOT			(BIT(1))
+#define RX_SBE			(BIT(2))
+#define RX_RESET_DONE		(BIT(3))
+#define RX_FLUSH_DONE		(BIT(4))
+#define RX_GENI_GP_IRQ		(GENMASK(10, 5))
+#define RX_GENI_CANCEL_IRQ	(BIT(11))
+#define RX_GENI_GP_IRQ_EXT	(GENMASK(13, 12))
+
+#ifdef CONFIG_QCOM_GENI_SE
+/**
+ * geni_read_reg_nolog() - Helper function to read from a GENI register
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ *
+ * Return:	Return the contents of the register.
+ */
+unsigned int geni_read_reg_nolog(void __iomem *base, int offset);
+
+/**
+ * geni_write_reg_nolog() - Helper function to write into a GENI register
+ * @value:	Value to be written into the register.
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ */
+void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset);
+
+/**
+ * geni_read_reg() - Helper function to read from a GENI register
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ *
+ * Return:	Return the contents of the register.
+ */
+unsigned int geni_read_reg(void __iomem *base, int offset);
+
+/**
+ * geni_write_reg() - Helper function to write into a GENI register
+ * @value:	Value to be written into the register.
+ * @base:	Base address of the serial engine's register block.
+ * @offset:	Offset within the serial engine's register block.
+ */
+void geni_write_reg(unsigned int value, void __iomem *base, int offset);
+
+/**
+ * geni_get_qup_hw_version() - Read the QUP Wrapper Hardware version
+ * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
+ * @major:		Buffer for Major Version field.
+ * @minor:		Buffer for Minor Version field.
+ * @step:		Buffer for Step Version field.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
+			    unsigned int *minor, unsigned int *step);
+
+/**
+ * geni_se_get_proto() - Read the protocol configured for a serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * Return:	Protocol value as configured in the serial engine.
+ */
+int geni_se_get_proto(void __iomem *base);
+
+/**
+ * geni_se_init() - Initialize the GENI Serial Engine
+ * @base:	Base address of the serial engine's register block.
+ * @rx_wm:	Receive watermark to be configured.
+ * @rx_rfr_wm:	Ready-for-receive watermark to be configured.
+ *
+ * This function is used to initialize the GENI serial engine, configure
+ * the transfer mode, receive watermark and ready-for-receive watermarks.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr);
+
+/**
+ * geni_se_select_mode() - Select the serial engine transfer mode
+ * @base:	Base address of the serial engine's register block.
+ * @mode:	Transfer mode to be selected.
+ *
+ * Return:	0 on success, standard Linux error codes on failure.
+ */
+int geni_se_select_mode(void __iomem *base, int mode);
+
+/**
+ * geni_se_setup_m_cmd() - Setup the primary sequencer
+ * @base:	Base address of the serial engine's register block.
+ * @cmd:	Command/Operation to setup in the primary sequencer.
+ * @params:	Parameter for the sequencer command.
+ *
+ * This function is used to configure the primary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params);
+
+/**
+ * geni_se_setup_s_cmd() - Setup the secondary sequencer
+ * @base:	Base address of the serial engine's register block.
+ * @cmd:	Command/Operation to setup in the secondary sequencer.
+ * @params:	Parameter for the sequencer command.
+ *
+ * This function is used to configure the secondary sequencer with the
+ * command and its assoicated parameters.
+ */
+void geni_se_setup_s_cmd(void __iomem *base, u32 cmd, u32 params);
+
+/**
+ * geni_se_cancel_m_cmd() - Cancel the command configured in the primary
+ *                          sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_cancel_m_cmd(void __iomem *base);
+
+/**
+ * geni_se_cancel_s_cmd() - Cancel the command configured in the secondary
+ *			    sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to cancel the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_cancel_s_cmd(void __iomem *base);
+
+/**
+ * geni_se_abort_m_cmd() - Abort the command configured in the primary sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_abort_m_cmd(void __iomem *base);
+
+/**
+ * geni_se_abort_s_cmd() - Abort the command configured in the secondary
+ *			   sequencer
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to force abort the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_abort_s_cmd(void __iomem *base);
+
+/**
+ * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * TX fifo of the serial engine.
+ *
+ * Return:	TX fifo depth in units of FIFO words.
+ */
+int geni_se_get_tx_fifo_depth(void __iomem *base);
+
+/**
+ * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to get the width i.e. word size per element in the
+ * TX fifo of the serial engine.
+ *
+ * Return:	TX fifo width in bits.
+ */
+int geni_se_get_tx_fifo_width(void __iomem *base);
+
+/**
+ * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * This function is used to get the depth i.e. number of elements in the
+ * RX fifo of the serial engine.
+ *
+ * Return:	RX fifo depth in units of FIFO words.
+ */
+int geni_se_get_rx_fifo_depth(void __iomem *base);
+
+/**
+ * geni_se_get_packing_config() - Get the packing configuration based on input
+ * @bpw:	Bits of data per transfer word.
+ * @pack_words:	Number of words per fifo element.
+ * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
+ * @cfg0:	Output buffer to hold the first half of configuration.
+ * @cfg1:	Output buffer to hold the second half of configuration.
+ *
+ * This function is used to calculate the packing configuration based on
+ * the input packing requirement and the configuration logic.
+ */
+void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
+				unsigned long *cfg0, unsigned long *cfg1);
+
+/**
+ * geni_se_config_packing() - Packing configuration of the serial engine
+ * @base:	Base address of the serial engine's register block.
+ * @bpw:	Bits of data per transfer word.
+ * @pack_words:	Number of words per fifo element.
+ * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
+ *
+ * This function is used to configure the packing rules for the current
+ * transfer.
+ */
+void geni_se_config_packing(void __iomem *base, int bpw, int pack_words,
+			    bool msb_to_lsb);
+
+/**
+ * geni_se_resources_off() - Turn off resources associated with the serial
+ *                           engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_off(struct geni_se_rsc *rsc);
+
+/**
+ * geni_se_resources_on() - Turn on resources associated with the serial
+ *                           engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_on(struct geni_se_rsc *rsc);
+
+/**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc:	Resource for which the clock table is requested.
+ * @tbl:	Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return:	number of valid performance levels in the table on success,
+ *		standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc:	Resource for which the clock frequency is requested.
+ * @req_freq:	Requested clock frequency.
+ * @index:	Index of the resultant frequency in the table.
+ * @res_freq:	Resultant frequency which matches or is closer to the
+ *		requested frequency.
+ * @exact:	Flag to indicate exact multiple requirement of the requested
+ *		frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return:	0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
+			   unsigned int *index, unsigned long *res_freq,
+			   bool exact);
+
+/**
+ * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
+ * @base:		Base address of the SE register block.
+ * @tx_buf:		Pointer to the TX buffer.
+ * @tx_len:		Length of the TX buffer.
+ * @tx_dma:		Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA TX.
+ *
+ * Return:	0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+			void *tx_buf, int tx_len, dma_addr_t *tx_dma);
+
+/**
+ * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
+ * @base:		Base address of the SE register block.
+ * @rx_buf:		Pointer to the RX buffer.
+ * @rx_len:		Length of the RX buffer.
+ * @rx_dma:		Pointer to store the mapped DMA address.
+ *
+ * This function is used to prepare the buffers for DMA RX.
+ *
+ * Return:	0 on success, standard Linux error codes on error/failure.
+ */
+int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
+			void *rx_buf, int rx_len, dma_addr_t *rx_dma);
+
+/**
+ * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
+ * @tx_dma:		DMA address of the TX buffer.
+ * @tx_len:		Length of the TX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA TX.
+ */
+void geni_se_tx_dma_unprep(struct device *wrapper_dev,
+			   dma_addr_t tx_dma, int tx_len);
+
+/**
+ * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
+ * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
+ * @rx_dma:		DMA address of the RX buffer.
+ * @rx_len:		Length of the RX buffer.
+ *
+ * This function is used to unprepare the DMA buffers after DMA RX.
+ */
+void geni_se_rx_dma_unprep(struct device *wrapper_dev,
+			   dma_addr_t rx_dma, int rx_len);
+
+/**
+ * geni_se_map_buf() - Map a single buffer into QUP wrapper device
+ * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
+ * @iova:		Pointer in which the mapped virtual address is stored.
+ * @buf:		Address of the buffer that needs to be mapped.
+ * @size:		Size of the buffer.
+ * @dir:		Direction of the DMA transfer.
+ *
+ * This function is used to map an already allocated buffer into the
+ * QUP device space.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
+		    void *buf, size_t size, enum dma_data_direction dir);
+
+/**
+ * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
+ * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
+ * @iova:		Pointer in which the mapped virtual address is stored.
+ * @size:		Size of the buffer.
+ * @dir:		Direction of the DMA transfer.
+ *
+ * This function is used to unmap an already mapped buffer from the
+ * QUP device space.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
+		      size_t size, enum dma_data_direction dir);
+#else
+static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
+{
+	return 0;
+}
+
+static inline void geni_write_reg_nolog(unsigned int value,
+					void __iomem *base, int offset)
+{
+}
+
+static inline unsigned int geni_read_reg(void __iomem *base, int offset)
+{
+	return 0;
+}
+
+static inline void geni_write_reg(unsigned int value, void __iomem *base,
+				  int offset)
+{
+}
+
+static inline int geni_get_qup_hw_version(struct device *wrapper_dev,
+					  unsigned int *major,
+					  unsigned int *minor,
+					  unsigned int *step)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_get_proto(void __iomem *base)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_init(void __iomem *base,
+			       unsigned int rx_wm, unsigned int rx_rfr)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_select_mode(void __iomem *base, int mode)
+{
+	return -ENXIO;
+}
+
+static inline void geni_se_setup_m_cmd(void __iomem *base, u32 cmd,
+				       u32 params)
+{
+}
+
+static inline void geni_se_setup_s_cmd(void __iomem *base, u32 cmd,
+				       u32 params)
+{
+}
+
+static inline void geni_se_cancel_m_cmd(void __iomem *base)
+{
+}
+
+static inline void geni_se_cancel_s_cmd(void __iomem *base)
+{
+}
+
+static inline void geni_se_abort_m_cmd(void __iomem *base)
+{
+}
+
+static inline void geni_se_abort_s_cmd(void __iomem *base)
+{
+}
+
+static inline int geni_se_get_tx_fifo_depth(void __iomem *base)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_get_tx_fifo_width(void __iomem *base)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_get_rx_fifo_depth(void __iomem *base)
+{
+	return -ENXIO;
+}
+
+static inline void geni_se_get_packing_config(int bpw, int pack_words,
+					      bool msb_to_lsb,
+					      unsigned long *cfg0,
+					      unsigned long *cfg1)
+{
+}
+
+static inline void geni_se_config_packing(void __iomem *base, int bpw,
+					  int pack_words, bool msb_to_lsb)
+{
+}
+
+static inline int geni_se_resources_on(struct geni_se_rsc *rsc)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_resources_off(struct geni_se_rsc *rsc)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_clk_tbl_get(struct geni_se_rsc *rsc,
+				      unsigned long **tbl)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_clk_freq_match(struct geni_se_rsc *rsc,
+					 unsigned long req_freq,
+					 unsigned int *index,
+					 unsigned long *res_freq, bool exact)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_tx_dma_prep(struct device *wrapper_dev,
+				      void __iomem *base, void *tx_buf,
+				      int tx_len, dma_addr_t *tx_dma)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_rx_dma_prep(struct device *wrapper_dev,
+				      void __iomem *base, void *rx_buf,
+				      int rx_len, dma_addr_t *rx_dma)
+{
+	return -ENXIO;
+}
+
+static inline void geni_se_tx_dma_unprep(struct device *wrapper_dev,
+					 dma_addr_t tx_dma, int tx_len)
+{
+}
+
+static inline void geni_se_rx_dma_unprep(struct device *wrapper_dev,
+					 dma_addr_t rx_dma, int rx_len)
+{
+}
+
+static inline int geni_se_map_buf(struct device *wrapper_dev,
+				  dma_addr_t *iova, void *buf, size_t size,
+				  enum dma_data_direction dir)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_unmap_buf(struct device *wrapper_dev,
+				    dma_addr_t *iova, size_t size,
+				    enum dma_data_direction dir)
+{
+	return -ENXIO;
+
+}
+
+#endif
+#endif
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller
  2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
  2018-01-13  1:05 ` [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary Karthikeyan Ramasubramanian
  2018-01-13  1:05 ` [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
@ 2018-01-13  1:05 ` Karthikeyan Ramasubramanian
       [not found]   ` <1515805547-22816-5-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2018-01-13  1:05 ` [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller Karthikeyan Ramasubramanian
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, Sagar Dharia

Add device tree binding support for I2C Controller in GENI based
QUP Wrapper.

Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
---
 .../devicetree/bindings/i2c/i2c-qcom-geni.txt      | 35 ++++++++++++++++++++++
 .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 19 ++++++++++++
 2 files changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
new file mode 100644
index 0000000..ea84be7
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
@@ -0,0 +1,35 @@
+Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
+
+Required properties:
+ - compatible: Should be:
+   * "qcom,i2c-geni.
+ - reg: Should contain QUP register address and length.
+ - interrupts: Should contain I2C interrupt.
+ - clock-names: Should contain "se-clk".
+ - clocks: Serial engine core clock needed by the device.
+ - pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
+   should be "active" and "sleep" for the pin confuguration when core is active
+   or when entering sleep state.
+ - #address-cells: Should be <1> Address cells for i2c device address
+ - #size-cells: Should be <0> as i2c addresses have no size component
+
+Optional property:
+ - clock-frequency : Desired I2C bus clock frequency in Hz.
+   When missing default to 400000Hz.
+
+Child nodes should conform to i2c bus binding.
+
+Example:
+
+i2c0: i2c@a94000 {
+	compatible = "qcom,i2c-geni";
+	reg = <0xa94000 0x4000>;
+	interrupts = <GIC_SPI 358 0>;
+	clock-names = "se-clk";
+	clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>;
+	pinctrl-names = "default", "sleep";
+	pinctrl-0 = <&qup_1_i2c_5_active>;
+	pinctrl-1 = <&qup_1_i2c_5_sleep>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+};
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
index 66f8a16..2ffbb3e 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
@@ -24,6 +24,9 @@ A GENI based QUP wrapper controller node can contain 0 or more child nodes
 representing serial devices.  These serial devices can be a QCOM UART, I2C
 controller, spi controller, or some combination of aforementioned devices.
 
+See the following documentation for child node definitions:
+Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
+
 Example:
 	qup0: qcom,geniqup0@8c0000 {
 		compatible = "qcom,geni-se-qup";
@@ -31,4 +34,20 @@ Example:
 		clock-names = "m-ahb", "s-ahb";
 		clocks = <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		i2c0: i2c@a94000 {
+			compatible = "qcom,i2c-geni";
+			reg = <0xa94000 0x4000>;
+			interrupts = <GIC_SPI 358 0>;
+			clock-names = "se-clk";
+			clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>;
+			pinctrl-names = "default", "sleep";
+			pinctrl-0 = <&qup_1_i2c_5_active>;
+			pinctrl-1 = <&qup_1_i2c_5_sleep>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
 	}
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
       [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
@ 2018-01-13  1:05   ` Karthikeyan Ramasubramanian
       [not found]     ` <1515805547-22816-6-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2018-01-24 23:07     ` [v2, " Evan Green
  2018-01-19 18:32   ` [PATCH v2 0/7] Introduce GENI SE Controller Driver Randy Dunlap
  2 siblings, 2 replies; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet-T1hC0tSOHrs, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	wsa-z923LK4zBo2bacvFa/9K2g,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
  Cc: Karthikeyan Ramasubramanian, linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia, Girish Mahadevan

This bus driver supports the GENI based i2c hardware controller in the
Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
module supporting a wide range of serial interfaces including I2C. The
driver supports FIFO mode and DMA mode of transfer and switches modes
dynamically depending on the size of the transfer.

Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Girish Mahadevan <girishm-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
 drivers/i2c/busses/Kconfig         |  10 +
 drivers/i2c/busses/Makefile        |   1 +
 drivers/i2c/busses/i2c-qcom-geni.c | 656 +++++++++++++++++++++++++++++++++++++
 3 files changed, 667 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 009345d..caef309 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -838,6 +838,16 @@ config I2C_PXA_SLAVE
 	  is necessary for systems where the PXA may be a target on the
 	  I2C bus.
 
+config I2C_QCOM_GENI
+	tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
+	depends on ARCH_QCOM
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the Qualcomm Technologies Inc.'s SoCs.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-qcom-geni.
+
 config I2C_QUP
 	tristate "Qualcomm QUP based I2C controller"
 	depends on ARCH_QCOM
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 2ce8576..201fce1 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_PNX)		+= i2c-pnx.o
 obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
 obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
 obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
+obj-$(CONFIG_I2C_QCOM_GENI)	+= i2c-qcom-geni.o
 obj-$(CONFIG_I2C_QUP)		+= i2c-qup.o
 obj-$(CONFIG_I2C_RIIC)		+= i2c-riic.o
 obj-$(CONFIG_I2C_RK3X)		+= i2c-rk3x.o
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
new file mode 100644
index 0000000..59ad4da
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/qcom-geni-se.h>
+
+#define SE_I2C_TX_TRANS_LEN		(0x26C)
+#define SE_I2C_RX_TRANS_LEN		(0x270)
+#define SE_I2C_SCL_COUNTERS		(0x278)
+#define SE_GENI_IOS			(0x908)
+
+#define SE_I2C_ERR  (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
+			M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
+#define SE_I2C_ABORT (1U << 1)
+/* M_CMD OP codes for I2C */
+#define I2C_WRITE		(0x1)
+#define I2C_READ		(0x2)
+#define I2C_WRITE_READ		(0x3)
+#define I2C_ADDR_ONLY		(0x4)
+#define I2C_BUS_CLEAR		(0x6)
+#define I2C_STOP_ON_BUS		(0x7)
+/* M_CMD params for I2C */
+#define PRE_CMD_DELAY		(BIT(0))
+#define TIMESTAMP_BEFORE	(BIT(1))
+#define STOP_STRETCH		(BIT(2))
+#define TIMESTAMP_AFTER		(BIT(3))
+#define POST_COMMAND_DELAY	(BIT(4))
+#define IGNORE_ADD_NACK		(BIT(6))
+#define READ_FINISHED_WITH_ACK	(BIT(7))
+#define BYPASS_ADDR_PHASE	(BIT(8))
+#define SLV_ADDR_MSK		(GENMASK(15, 9))
+#define SLV_ADDR_SHFT		(9)
+
+#define I2C_CORE2X_VOTE		(10000)
+#define GP_IRQ0			0
+#define GP_IRQ1			1
+#define GP_IRQ2			2
+#define GP_IRQ3			3
+#define GP_IRQ4			4
+#define GP_IRQ5			5
+#define GENI_OVERRUN		6
+#define GENI_ILLEGAL_CMD	7
+#define GENI_ABORT_DONE		8
+#define GENI_TIMEOUT		9
+
+#define I2C_NACK		GP_IRQ1
+#define I2C_BUS_PROTO		GP_IRQ3
+#define I2C_ARB_LOST		GP_IRQ4
+#define DM_I2C_CB_ERR		((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
+									<< 5)
+
+#define I2C_AUTO_SUSPEND_DELAY	250
+#define KHz(freq)		(1000 * freq)
+
+struct geni_i2c_dev {
+	struct device *dev;
+	void __iomem *base;
+	unsigned int tx_wm;
+	int irq;
+	int err;
+	struct i2c_adapter adap;
+	struct completion xfer;
+	struct i2c_msg *cur;
+	struct geni_se_rsc i2c_rsc;
+	int cur_wr;
+	int cur_rd;
+	struct device *wrapper_dev;
+	u32 clk_freq_out;
+	int clk_fld_idx;
+};
+
+struct geni_i2c_err_log {
+	int err;
+	const char *msg;
+};
+
+static struct geni_i2c_err_log gi2c_log[] = {
+	[GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
+	[I2C_NACK] = {-ENOTCONN,
+			"NACK: slv unresponsive, check its power/reset-ln"},
+	[GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
+	[I2C_BUS_PROTO] = {-EPROTO,
+				"Bus proto err, noisy/unepxected start/stop"},
+	[I2C_ARB_LOST] = {-EBUSY,
+				"Bus arbitration lost, clock line undriveable"},
+	[GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
+	[GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
+	[GENI_ILLEGAL_CMD] = {-EILSEQ,
+				"Illegal cmd, check GENI cmd-state machine"},
+	[GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
+	[GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
+};
+
+struct geni_i2c_clk_fld {
+	u32	clk_freq_out;
+	u8	clk_div;
+	u8	t_high;
+	u8	t_low;
+	u8	t_cycle;
+};
+
+static struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
+	{KHz(100), 7, 10, 11, 26},
+	{KHz(400), 2,  5, 12, 24},
+	{KHz(1000), 1, 3,  9, 18},
+};
+
+static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
+{
+	int i;
+	int ret = 0;
+	bool clk_map_present = false;
+	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
+
+	for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
+		if (itr->clk_freq_out == gi2c->clk_freq_out) {
+			clk_map_present = true;
+			break;
+		}
+	}
+
+	if (clk_map_present)
+		gi2c->clk_fld_idx = i;
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)
+{
+	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
+
+	geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
+
+	geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
+	geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
+			itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);
+
+	/* Ensure Clk config completes before return */
+	mb();
+}
+
+static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
+{
+	u32 m_cmd = readl_relaxed(gi2c->base + SE_GENI_M_CMD0);
+	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
+	u32 geni_s = readl_relaxed(gi2c->base + SE_GENI_STATUS);
+	u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
+	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
+	u32 rx_st, tx_st;
+
+	if (gi2c->cur)
+		dev_dbg(gi2c->dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
+			gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
+
+	if (err == I2C_NACK || err == GENI_ABORT_DONE) {
+		dev_dbg(gi2c->dev, "%s\n", gi2c_log[err].msg);
+		goto err_ret;
+	} else {
+		dev_err(gi2c->dev, "%s\n", gi2c_log[err].msg);
+	}
+
+	if (dma) {
+		rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
+		tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
+	} else {
+		rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
+		tx_st = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
+	}
+	dev_dbg(gi2c->dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
+		dma, tx_st, rx_st, m_stat);
+	dev_dbg(gi2c->dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
+		m_cmd, geni_s, geni_ios);
+err_ret:
+	gi2c->err = gi2c_log[err].err;
+}
+
+static irqreturn_t geni_i2c_irq(int irq, void *dev)
+{
+	struct geni_i2c_dev *gi2c = dev;
+	int i, j;
+	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
+	u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
+	u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
+	u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
+	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
+	struct i2c_msg *cur = gi2c->cur;
+
+	if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
+		    (dm_rx_st & (DM_I2C_CB_ERR)) ||
+		    (m_stat & M_CMD_ABORT_EN)) {
+
+		if (m_stat & M_GP_IRQ_1_EN)
+			geni_i2c_err(gi2c, I2C_NACK);
+		if (m_stat & M_GP_IRQ_3_EN)
+			geni_i2c_err(gi2c, I2C_BUS_PROTO);
+		if (m_stat & M_GP_IRQ_4_EN)
+			geni_i2c_err(gi2c, I2C_ARB_LOST);
+		if (m_stat & M_CMD_OVERRUN_EN)
+			geni_i2c_err(gi2c, GENI_OVERRUN);
+		if (m_stat & M_ILLEGAL_CMD_EN)
+			geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
+		if (m_stat & M_CMD_ABORT_EN)
+			geni_i2c_err(gi2c, GENI_ABORT_DONE);
+		if (m_stat & M_GP_IRQ_0_EN)
+			geni_i2c_err(gi2c, GP_IRQ0);
+
+		if (!dma)
+			writel_relaxed(0, (gi2c->base +
+					   SE_GENI_TX_WATERMARK_REG));
+		goto irqret;
+	}
+
+	if (dma) {
+		dev_dbg(gi2c->dev, "i2c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st,
+			dm_rx_st);
+		goto irqret;
+	}
+
+	if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
+		(m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {
+		u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
+
+		for (j = 0; j < rxcnt; j++) {
+			u32 temp;
+			int p;
+
+			temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
+			for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
+				i++, p++)
+				cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
+			gi2c->cur_rd = i;
+			if (gi2c->cur_rd == cur->len) {
+				dev_dbg(gi2c->dev, "FIFO i:%d,read 0x%x\n",
+					i, temp);
+				break;
+			}
+		}
+	} else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
+					!(cur->flags & I2C_M_RD)) {
+		for (j = 0; j < gi2c->tx_wm; j++) {
+			u32 temp = 0;
+			int p;
+
+			for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
+				i++, p++)
+				temp |= (((u32)(cur->buf[i]) << (p * 8)));
+			writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);
+			gi2c->cur_wr = i;
+			dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp);
+			if (gi2c->cur_wr == cur->len) {
+				dev_dbg(gi2c->dev, "FIFO i2c bytes done writing\n");
+				writel_relaxed(0,
+				(gi2c->base + SE_GENI_TX_WATERMARK_REG));
+				break;
+			}
+		}
+	}
+irqret:
+	if (m_stat)
+		writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);
+
+	if (dma) {
+		if (dm_tx_st)
+			writel_relaxed(dm_tx_st, gi2c->base +
+				       SE_DMA_TX_IRQ_CLR);
+		if (dm_rx_st)
+			writel_relaxed(dm_rx_st, gi2c->base +
+				       SE_DMA_RX_IRQ_CLR);
+		/* Ensure all writes are done before returning from ISR. */
+		wmb();
+	}
+	/* if this is err with done-bit not set, handle that thr' timeout. */
+	if (m_stat & M_CMD_DONE_EN)
+		complete(&gi2c->xfer);
+	else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
+		complete(&gi2c->xfer);
+
+	return IRQ_HANDLED;
+}
+
+static int geni_i2c_xfer(struct i2c_adapter *adap,
+			 struct i2c_msg msgs[],
+			 int num)
+{
+	struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
+	int i, ret = 0, timeout = 0;
+
+	gi2c->err = 0;
+	gi2c->cur = &msgs[0];
+	reinit_completion(&gi2c->xfer);
+	ret = pm_runtime_get_sync(gi2c->dev);
+	if (ret < 0) {
+		dev_err(gi2c->dev, "error turning SE resources:%d\n", ret);
+		pm_runtime_put_noidle(gi2c->dev);
+		/* Set device in suspended since resume failed */
+		pm_runtime_set_suspended(gi2c->dev);
+		return ret;
+	}
+
+	qcom_geni_i2c_conf(gi2c, 0);
+	dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
+				num, msgs[0].len, msgs[0].flags);
+	for (i = 0; i < num; i++) {
+		int stretch = (i < (num - 1));
+		u32 m_param = 0;
+		u32 m_cmd = 0;
+		dma_addr_t tx_dma = 0;
+		dma_addr_t rx_dma = 0;
+		enum geni_se_xfer_mode mode = FIFO_MODE;
+
+		m_param |= (stretch ? STOP_STRETCH : 0);
+		m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);
+
+		gi2c->cur = &msgs[i];
+		mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE;
+		ret = geni_se_select_mode(gi2c->base, mode);
+		if (ret) {
+			dev_err(gi2c->dev, "%s: Error mode init %d:%d:%d\n",
+				__func__, mode, i, msgs[i].len);
+			break;
+		}
+		if (msgs[i].flags & I2C_M_RD) {
+			dev_dbg(gi2c->dev,
+				"READ,n:%d,i:%d len:%d, stretch:%d\n",
+					num, i, msgs[i].len, stretch);
+			geni_write_reg(msgs[i].len,
+				       gi2c->base, SE_I2C_RX_TRANS_LEN);
+			m_cmd = I2C_READ;
+			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
+			if (mode == SE_DMA) {
+				ret = geni_se_rx_dma_prep(gi2c->wrapper_dev,
+							gi2c->base, msgs[i].buf,
+							msgs[i].len, &rx_dma);
+				if (ret) {
+					mode = FIFO_MODE;
+					ret = geni_se_select_mode(gi2c->base,
+								  mode);
+				}
+			}
+		} else {
+			dev_dbg(gi2c->dev,
+				"WRITE:n:%d,i:%d len:%d, stretch:%d, m_param:0x%x\n",
+					num, i, msgs[i].len, stretch, m_param);
+			geni_write_reg(msgs[i].len, gi2c->base,
+						SE_I2C_TX_TRANS_LEN);
+			m_cmd = I2C_WRITE;
+			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
+			if (mode == SE_DMA) {
+				ret = geni_se_tx_dma_prep(gi2c->wrapper_dev,
+							gi2c->base, msgs[i].buf,
+							msgs[i].len, &tx_dma);
+				if (ret) {
+					mode = FIFO_MODE;
+					ret = geni_se_select_mode(gi2c->base,
+								  mode);
+				}
+			}
+			if (mode == FIFO_MODE) /* Get FIFO IRQ */
+				geni_write_reg(1, gi2c->base,
+						SE_GENI_TX_WATERMARK_REG);
+		}
+		/* Ensure FIFO write go through before waiting for Done evet */
+		mb();
+		timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
+		if (!timeout) {
+			geni_i2c_err(gi2c, GENI_TIMEOUT);
+			gi2c->cur = NULL;
+			geni_se_abort_m_cmd(gi2c->base);
+			timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
+		}
+		gi2c->cur_wr = 0;
+		gi2c->cur_rd = 0;
+		if (mode == SE_DMA) {
+			if (gi2c->err) {
+				if (msgs[i].flags != I2C_M_RD)
+					writel_relaxed(1, gi2c->base +
+							SE_DMA_TX_FSM_RST);
+				else
+					writel_relaxed(1, gi2c->base +
+							SE_DMA_RX_FSM_RST);
+				wait_for_completion_timeout(&gi2c->xfer, HZ);
+			}
+			geni_se_rx_dma_unprep(gi2c->wrapper_dev, rx_dma,
+					      msgs[i].len);
+			geni_se_tx_dma_unprep(gi2c->wrapper_dev, tx_dma,
+					      msgs[i].len);
+		}
+		ret = gi2c->err;
+		if (gi2c->err) {
+			dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err);
+			break;
+		}
+	}
+	if (ret == 0)
+		ret = num;
+
+	pm_runtime_mark_last_busy(gi2c->dev);
+	pm_runtime_put_autosuspend(gi2c->dev);
+	gi2c->cur = NULL;
+	gi2c->err = 0;
+	dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
+	return ret;
+}
+
+static u32 geni_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm geni_i2c_algo = {
+	.master_xfer	= geni_i2c_xfer,
+	.functionality	= geni_i2c_func,
+};
+
+static int geni_i2c_probe(struct platform_device *pdev)
+{
+	struct geni_i2c_dev *gi2c;
+	struct resource *res;
+	int ret;
+
+	gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
+	if (!gi2c)
+		return -ENOMEM;
+
+	gi2c->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	gi2c->wrapper_dev = pdev->dev.parent;
+	gi2c->i2c_rsc.wrapper_dev = pdev->dev.parent;
+	gi2c->i2c_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
+	if (IS_ERR(gi2c->i2c_rsc.se_clk)) {
+		ret = PTR_ERR(gi2c->i2c_rsc.se_clk);
+		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+		return ret;
+	}
+
+	gi2c->base = devm_ioremap_resource(gi2c->dev, res);
+	if (IS_ERR(gi2c->base))
+		return PTR_ERR(gi2c->base);
+
+	gi2c->i2c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_pinctrl)) {
+		dev_err(&pdev->dev, "No pinctrl config specified\n");
+		ret = PTR_ERR(gi2c->i2c_rsc.geni_pinctrl);
+		return ret;
+	}
+	gi2c->i2c_rsc.geni_gpio_active =
+		pinctrl_lookup_state(gi2c->i2c_rsc.geni_pinctrl,
+							PINCTRL_DEFAULT);
+	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_gpio_active)) {
+		dev_err(&pdev->dev, "No default config specified\n");
+		ret = PTR_ERR(gi2c->i2c_rsc.geni_gpio_active);
+		return ret;
+	}
+	gi2c->i2c_rsc.geni_gpio_sleep =
+		pinctrl_lookup_state(gi2c->i2c_rsc.geni_pinctrl,
+							PINCTRL_SLEEP);
+	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_gpio_sleep)) {
+		dev_err(&pdev->dev, "No sleep config specified\n");
+		ret = PTR_ERR(gi2c->i2c_rsc.geni_gpio_sleep);
+		return ret;
+	}
+
+	ret = device_property_read_u32(&pdev->dev, "clock-frequency",
+							&gi2c->clk_freq_out);
+	if (ret) {
+		dev_info(&pdev->dev,
+			"Bus frequency not specified, default to 400KHz.\n");
+		gi2c->clk_freq_out = KHz(400);
+	}
+
+	gi2c->irq = platform_get_irq(pdev, 0);
+	if (gi2c->irq < 0) {
+		dev_err(gi2c->dev, "IRQ error for i2c-geni\n");
+		return gi2c->irq;
+	}
+
+	ret = geni_i2c_clk_map_idx(gi2c);
+	if (ret) {
+		dev_err(gi2c->dev, "Invalid clk frequency %d KHz: %d\n",
+				gi2c->clk_freq_out, ret);
+		return ret;
+	}
+
+	gi2c->adap.algo = &geni_i2c_algo;
+	init_completion(&gi2c->xfer);
+	platform_set_drvdata(pdev, gi2c);
+	ret = devm_request_irq(gi2c->dev, gi2c->irq, geni_i2c_irq,
+			       IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
+	if (ret) {
+		dev_err(gi2c->dev, "Request_irq failed:%d: err:%d\n",
+				   gi2c->irq, ret);
+		return ret;
+	}
+	disable_irq(gi2c->irq);
+	i2c_set_adapdata(&gi2c->adap, gi2c);
+	gi2c->adap.dev.parent = gi2c->dev;
+	gi2c->adap.dev.of_node = pdev->dev.of_node;
+
+	strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
+
+	pm_runtime_set_suspended(gi2c->dev);
+	pm_runtime_set_autosuspend_delay(gi2c->dev, I2C_AUTO_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(gi2c->dev);
+	pm_runtime_enable(gi2c->dev);
+	i2c_add_adapter(&gi2c->adap);
+
+	dev_dbg(gi2c->dev, "I2C probed\n");
+	return 0;
+}
+
+static int geni_i2c_remove(struct platform_device *pdev)
+{
+	struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(gi2c->dev);
+	i2c_del_adapter(&gi2c->adap);
+	return 0;
+}
+
+static int geni_i2c_resume_noirq(struct device *device)
+{
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int geni_i2c_runtime_suspend(struct device *dev)
+{
+	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+	disable_irq(gi2c->irq);
+	geni_se_resources_off(&gi2c->i2c_rsc);
+	return 0;
+}
+
+static int geni_i2c_runtime_resume(struct device *dev)
+{
+	int ret;
+	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
+
+	ret = geni_se_resources_on(&gi2c->i2c_rsc);
+	if (ret)
+		return ret;
+
+	if (!unlikely(gi2c->tx_wm)) {
+		int proto = geni_se_get_proto(gi2c->base);
+		int gi2c_tx_depth = geni_se_get_tx_fifo_depth(gi2c->base);
+
+		if (unlikely(proto != I2C)) {
+			dev_err(gi2c->dev, "Invalid proto %d\n", proto);
+			geni_se_resources_off(&gi2c->i2c_rsc);
+			return -ENXIO;
+		}
+
+		gi2c->tx_wm = gi2c_tx_depth - 1;
+		geni_se_init(gi2c->base, gi2c->tx_wm, gi2c_tx_depth);
+		geni_se_config_packing(gi2c->base, 8, 4, true);
+		dev_dbg(gi2c->dev, "i2c fifo/se-dma mode. fifo depth:%d\n",
+			gi2c_tx_depth);
+	}
+	enable_irq(gi2c->irq);
+
+	return 0;
+}
+
+static int geni_i2c_suspend_noirq(struct device *device)
+{
+	struct geni_i2c_dev *gi2c = dev_get_drvdata(device);
+	int ret;
+
+	/* Make sure no transactions are pending */
+	ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
+	if (!ret) {
+		dev_err(gi2c->dev, "late I2C transaction request\n");
+		return -EBUSY;
+	}
+	if (!pm_runtime_status_suspended(device)) {
+		geni_i2c_runtime_suspend(device);
+		pm_runtime_disable(device);
+		pm_runtime_set_suspended(device);
+		pm_runtime_enable(device);
+	}
+	i2c_unlock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
+	return 0;
+}
+#else
+static int geni_i2c_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int geni_i2c_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+static int geni_i2c_suspend_noirq(struct device *device)
+{
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops geni_i2c_pm_ops = {
+	.suspend_noirq		= geni_i2c_suspend_noirq,
+	.resume_noirq		= geni_i2c_resume_noirq,
+	.runtime_suspend	= geni_i2c_runtime_suspend,
+	.runtime_resume		= geni_i2c_runtime_resume,
+};
+
+static const struct of_device_id geni_i2c_dt_match[] = {
+	{ .compatible = "qcom,i2c-geni" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
+
+static struct platform_driver geni_i2c_driver = {
+	.probe  = geni_i2c_probe,
+	.remove = geni_i2c_remove,
+	.driver = {
+		.name = "i2c_geni",
+		.pm = &geni_i2c_pm_ops,
+		.of_match_table = geni_i2c_dt_match,
+	},
+};
+
+module_platform_driver(geni_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c_geni");
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller
  2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
                   ` (2 preceding siblings ...)
  2018-01-13  1:05 ` [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller Karthikeyan Ramasubramanian
@ 2018-01-13  1:05 ` Karthikeyan Ramasubramanian
  2018-01-17  6:35   ` Bjorn Andersson
  2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
       [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  5 siblings, 1 reply; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, Girish Mahadevan

Add device tree binding support for GENI based UART Controller in the
QUP Wrapper.

Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
---
 .../devicetree/bindings/serial/qcom,geni-uart.txt  | 29 ++++++++++++++++++++++
 .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 13 ++++++++++
 2 files changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt

diff --git a/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
new file mode 100644
index 0000000..e7b9e24
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
@@ -0,0 +1,29 @@
+Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
+
+The Generic Interface (GENI) Serial Engine based UART controller supports
+console use-cases and is supported only by GENI based Qualcomm Universal
+Peripheral (QUP) cores.
+
+Required properties:
+- compatible: should contain "qcom,geni-debug-uart".
+- reg: Should contain UART register location and length.
+- reg-names: Should contain "se-phys".
+- interrupts: Should contain UART core interrupts.
+- clock-names: Should contain "se-clk".
+- clocks: clocks needed for UART, includes the core clock.
+- pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
+  Should be "active" and "sleep" for the pin confuguration when core is active
+  or when entering sleep state.
+
+Example:
+uart0: qcom,serial@a88000 {
+	compatible = "qcom,geni-debug-uart";
+	reg = <0xa88000 0x7000>;
+	reg-names = "se-phys";
+	interrupts = <0 355 0>;
+	clock-names = "se-clk";
+	clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>;
+	pinctrl-names = "default", "sleep";
+	pinctrl-0 = <&qup_1_uart_3_active>;
+	pinctrl-1 = <&qup_1_uart_3_sleep>;
+};
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
index 2ffbb3e..c307788 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
@@ -26,6 +26,7 @@ controller, spi controller, or some combination of aforementioned devices.
 
 See the following documentation for child node definitions:
 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
+Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
 
 Example:
 	qup0: qcom,geniqup0@8c0000 {
@@ -50,4 +51,16 @@ Example:
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
+
+		uart0: qcom,serial@a88000 {
+			compatible = "qcom,geni-debug-uart";
+			reg = <0xa88000 0x7000>;
+			reg-names = "se-phys";
+			interrupts = <0 355 0>;
+			clock-names = "se-clk";
+			clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>;
+			pinctrl-names = "default", "sleep";
+			pinctrl-0 = <&qup_1_uart_3_active>;
+			pinctrl-1 = <&qup_1_uart_3_sleep>;
+		};
 	}
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
                   ` (3 preceding siblings ...)
  2018-01-13  1:05 ` [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller Karthikeyan Ramasubramanian
@ 2018-01-13  1:05 ` Karthikeyan Ramasubramanian
  2018-01-18 19:43   ` Bjorn Andersson
                     ` (5 more replies)
       [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  5 siblings, 6 replies; 42+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-01-13  1:05 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, Girish Mahadevan,
	Sagar Dharia

This driver supports GENI based UART Controller in the Qualcomm SOCs. The
Qualcomm Generic Interface (GENI) is a programmable module supporting a
wide range of serial interfaces including UART. This driver support console
operations using FIFO mode of transfer.

Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
---
 drivers/tty/serial/Kconfig            |   10 +
 drivers/tty/serial/Makefile           |    1 +
 drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
 3 files changed, 1425 insertions(+)
 create mode 100644 drivers/tty/serial/qcom_geni_serial.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index b788fee..1be30e5 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
 	select SERIAL_CORE_CONSOLE
 	select SERIAL_EARLYCON
 
+config SERIAL_QCOM_GENI
+	tristate "QCOM on-chip GENI based serial port support"
+	depends on ARCH_QCOM
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
+	help
+	  Serial driver for Qualcomm Technologies Inc's GENI based QUP
+	  hardware.
+
 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 842d185..64a8d82 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
 obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
 obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
 obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
+obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
new file mode 100644
index 0000000..0dbd329
--- /dev/null
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -0,0 +1,1414 @@
+/*
+ * Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom-geni-se.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/* UART specific GENI registers */
+#define SE_UART_TX_TRANS_CFG		(0x25C)
+#define SE_UART_TX_WORD_LEN		(0x268)
+#define SE_UART_TX_STOP_BIT_LEN		(0x26C)
+#define SE_UART_TX_TRANS_LEN		(0x270)
+#define SE_UART_RX_TRANS_CFG		(0x280)
+#define SE_UART_RX_WORD_LEN		(0x28C)
+#define SE_UART_RX_STALE_CNT		(0x294)
+#define SE_UART_TX_PARITY_CFG		(0x2A4)
+#define SE_UART_RX_PARITY_CFG		(0x2A8)
+
+/* SE_UART_TRANS_CFG */
+#define UART_TX_PAR_EN		(BIT(0))
+#define UART_CTS_MASK		(BIT(1))
+
+/* SE_UART_TX_WORD_LEN */
+#define TX_WORD_LEN_MSK		(GENMASK(9, 0))
+
+/* SE_UART_TX_STOP_BIT_LEN */
+#define TX_STOP_BIT_LEN_MSK	(GENMASK(23, 0))
+#define TX_STOP_BIT_LEN_1	(0)
+#define TX_STOP_BIT_LEN_1_5	(1)
+#define TX_STOP_BIT_LEN_2	(2)
+
+/* SE_UART_TX_TRANS_LEN */
+#define TX_TRANS_LEN_MSK	(GENMASK(23, 0))
+
+/* SE_UART_RX_TRANS_CFG */
+#define UART_RX_INS_STATUS_BIT	(BIT(2))
+#define UART_RX_PAR_EN		(BIT(3))
+
+/* SE_UART_RX_WORD_LEN */
+#define RX_WORD_LEN_MASK	(GENMASK(9, 0))
+
+/* SE_UART_RX_STALE_CNT */
+#define RX_STALE_CNT		(GENMASK(23, 0))
+
+/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
+#define PAR_CALC_EN		(BIT(0))
+#define PAR_MODE_MSK		(GENMASK(2, 1))
+#define PAR_MODE_SHFT		(1)
+#define PAR_EVEN		(0x00)
+#define PAR_ODD			(0x01)
+#define PAR_SPACE		(0x10)
+#define PAR_MARK		(0x11)
+
+/* UART M_CMD OP codes */
+#define UART_START_TX		(0x1)
+#define UART_START_BREAK	(0x4)
+#define UART_STOP_BREAK		(0x5)
+/* UART S_CMD OP codes */
+#define UART_START_READ		(0x1)
+#define UART_PARAM		(0x1)
+
+#define UART_OVERSAMPLING	(32)
+#define STALE_TIMEOUT		(16)
+#define DEFAULT_BITS_PER_CHAR	(10)
+#define GENI_UART_NR_PORTS	(15)
+#define GENI_UART_CONS_PORTS	(1)
+#define DEF_FIFO_DEPTH_WORDS	(16)
+#define DEF_TX_WM		(2)
+#define DEF_FIFO_WIDTH_BITS	(32)
+#define UART_CORE2X_VOTE	(10000)
+#define UART_CONSOLE_RX_WM	(2)
+
+static int owr;
+module_param(owr, int, 0644);
+
+struct qcom_geni_serial_port {
+	struct uart_port uport;
+	char name[20];
+	unsigned int tx_fifo_depth;
+	unsigned int tx_fifo_width;
+	unsigned int rx_fifo_depth;
+	unsigned int tx_wm;
+	unsigned int rx_wm;
+	unsigned int rx_rfr;
+	int xfer_mode;
+	bool port_setup;
+	unsigned int *rx_fifo;
+	int (*handle_rx)(struct uart_port *uport,
+			unsigned int rx_fifo_wc,
+			unsigned int rx_last_byte_valid,
+			unsigned int rx_last,
+			bool drop_rx);
+	struct device *wrapper_dev;
+	struct geni_se_rsc serial_rsc;
+	unsigned int xmit_size;
+	void *rx_buf;
+	unsigned int cur_baud;
+};
+
+static const struct uart_ops qcom_geni_serial_pops;
+static struct uart_driver qcom_geni_console_driver;
+static int handle_rx_console(struct uart_port *uport,
+			unsigned int rx_fifo_wc,
+			unsigned int rx_last_byte_valid,
+			unsigned int rx_last,
+			bool drop_rx);
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
+static int qcom_geni_serial_poll_bit(struct uart_port *uport,
+				int offset, int bit_field, bool set);
+static void qcom_geni_serial_stop_rx(struct uart_port *uport);
+
+static atomic_t uart_line_id = ATOMIC_INIT(0);
+
+#define GET_DEV_PORT(uport) \
+	container_of(uport, struct qcom_geni_serial_port, uport)
+
+static struct qcom_geni_serial_port qcom_geni_console_port;
+
+static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
+{
+	if (cfg_flags & UART_CONFIG_TYPE)
+		uport->type = PORT_MSM;
+}
+
+static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
+{
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
+static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
+							unsigned int mctrl)
+{
+}
+
+static const char *qcom_geni_serial_get_type(struct uart_port *uport)
+{
+	return "MSM";
+}
+
+static struct qcom_geni_serial_port *get_port_from_line(int line)
+{
+	struct qcom_geni_serial_port *port = NULL;
+
+	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
+		port = ERR_PTR(-ENXIO);
+	port = &qcom_geni_console_port;
+	return port;
+}
+
+static int qcom_geni_serial_poll_bit(struct uart_port *uport,
+				int offset, int bit_field, bool set)
+{
+	int iter = 0;
+	unsigned int reg;
+	bool met = false;
+	struct qcom_geni_serial_port *port = NULL;
+	bool cond = false;
+	unsigned int baud = 115200;
+	unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS;
+	unsigned long total_iter = 2000;
+
+
+	if (uport->private_data) {
+		port = GET_DEV_PORT(uport);
+		baud = (port->cur_baud ? port->cur_baud : 115200);
+		fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
+		/*
+		 * Total polling iterations based on FIFO worth of bytes to be
+		 * sent at current baud .Add a little fluff to the wait.
+		 */
+		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
+		total_iter += 50;
+	}
+
+	while (iter < total_iter) {
+		reg = geni_read_reg_nolog(uport->membase, offset);
+		cond = reg & bit_field;
+		if (cond == set) {
+			met = true;
+			break;
+		}
+		udelay(10);
+		iter++;
+	}
+	return met;
+}
+
+static void qcom_geni_serial_setup_tx(struct uart_port *uport,
+				unsigned int xmit_size)
+{
+	u32 m_cmd = 0;
+
+	geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
+	m_cmd |= (UART_START_TX << M_OPCODE_SHFT);
+	geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
+	/*
+	 * Writes to enable the primary sequencer should go through before
+	 * exiting this function.
+	 */
+	mb();
+}
+
+static void qcom_geni_serial_poll_cancel_tx(struct uart_port *uport)
+{
+	int done = 0;
+	unsigned int irq_clear = M_CMD_DONE_EN;
+	unsigned int geni_status = 0;
+
+	done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_CMD_DONE_EN, true);
+	if (!done) {
+		geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
+					SE_GENI_M_CMD_CTRL_REG);
+		owr++;
+		irq_clear |= M_CMD_ABORT_EN;
+		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+							M_CMD_ABORT_EN, true);
+	}
+	geni_status = geni_read_reg_nolog(uport->membase,
+						  SE_GENI_STATUS);
+	if (geni_status & M_GENI_CMD_ACTIVE)
+		owr++;
+	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_abort_rx(struct uart_port *uport)
+{
+	unsigned int irq_clear = S_CMD_DONE_EN;
+
+	geni_se_abort_s_cmd(uport->membase);
+	/* Ensure this goes through before polling. */
+	mb();
+	irq_clear |= S_CMD_ABORT_EN;
+	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+					S_GENI_CMD_ABORT, false);
+	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int qcom_geni_serial_get_char(struct uart_port *uport)
+{
+	unsigned int rx_fifo;
+	unsigned int m_irq_status;
+	unsigned int s_irq_status;
+
+	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+			M_SEC_IRQ_EN, true)))
+		return -ENXIO;
+
+	m_irq_status = geni_read_reg_nolog(uport->membase,
+						SE_GENI_M_IRQ_STATUS);
+	s_irq_status = geni_read_reg_nolog(uport->membase,
+						SE_GENI_S_IRQ_STATUS);
+	geni_write_reg_nolog(m_irq_status, uport->membase,
+						SE_GENI_M_IRQ_CLEAR);
+	geni_write_reg_nolog(s_irq_status, uport->membase,
+						SE_GENI_S_IRQ_CLEAR);
+
+	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
+			RX_FIFO_WC_MSK, true)))
+		return -ENXIO;
+
+	/*
+	 * Read the Rx FIFO only after clearing the interrupt registers and
+	 * getting valid RX fifo status.
+	 */
+	mb();
+	rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
+	rx_fifo &= 0xFF;
+	return rx_fifo;
+}
+
+static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
+					unsigned char c)
+{
+	int b = (int) c;
+	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	geni_write_reg_nolog(port->tx_wm, uport->membase,
+					SE_GENI_TX_WATERMARK_REG);
+	qcom_geni_serial_setup_tx(uport, 1);
+	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+				M_TX_FIFO_WATERMARK_EN, true))
+		WARN_ON(1);
+	geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
+	geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
+							SE_GENI_M_IRQ_CLEAR);
+	/*
+	 * Ensure FIFO write goes through before polling for status but.
+	 */
+	mb();
+	qcom_geni_serial_poll_cancel_tx(uport);
+}
+#endif
+
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
+static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
+{
+	geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn);
+	/*
+	 * Ensure FIFO write clear goes through before
+	 * next iteration.
+	 */
+	mb();
+
+}
+
+static void
+__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
+				unsigned int count)
+{
+	int new_line = 0;
+	int i;
+	int bytes_to_send = count;
+	int fifo_depth = DEF_FIFO_DEPTH_WORDS;
+	int tx_wm = DEF_TX_WM;
+
+	for (i = 0; i < count; i++) {
+		if (s[i] == '\n')
+			new_line++;
+	}
+
+	bytes_to_send += new_line;
+	geni_write_reg_nolog(tx_wm, uport->membase,
+					SE_GENI_TX_WATERMARK_REG);
+	qcom_geni_serial_setup_tx(uport, bytes_to_send);
+	i = 0;
+	while (i < count) {
+		u32 chars_to_write = 0;
+		u32 avail_fifo_bytes = (fifo_depth - tx_wm);
+
+		/*
+		 * If the WM bit never set, then the Tx state machine is not
+		 * in a valid state, so break, cancel/abort any existing
+		 * command. Unfortunately the current data being written is
+		 * lost.
+		 */
+		while (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_TX_FIFO_WATERMARK_EN, true))
+			break;
+		chars_to_write = min((unsigned int)(count - i),
+							avail_fifo_bytes);
+		if ((chars_to_write << 1) > avail_fifo_bytes)
+			chars_to_write = (avail_fifo_bytes >> 1);
+		uart_console_write(uport, (s + i), chars_to_write,
+						qcom_geni_serial_wr_char);
+		geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
+							SE_GENI_M_IRQ_CLEAR);
+		/* Ensure this goes through before polling for WM IRQ again.*/
+		mb();
+		i += chars_to_write;
+	}
+	qcom_geni_serial_poll_cancel_tx(uport);
+}
+
+static void qcom_geni_serial_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	struct uart_port *uport;
+	struct qcom_geni_serial_port *port;
+	int locked = 1;
+	unsigned long flags;
+
+	WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
+
+	port = get_port_from_line(co->index);
+	if (IS_ERR_OR_NULL(port))
+		return;
+
+	uport = &port->uport;
+	if (oops_in_progress)
+		locked = spin_trylock_irqsave(&uport->lock, flags);
+	else
+		spin_lock_irqsave(&uport->lock, flags);
+
+	if (locked) {
+		__qcom_geni_serial_console_write(uport, s, count);
+		spin_unlock_irqrestore(&uport->lock, flags);
+	}
+}
+
+static int handle_rx_console(struct uart_port *uport,
+			unsigned int rx_fifo_wc,
+			unsigned int rx_last_byte_valid,
+			unsigned int rx_last,
+			bool drop_rx)
+{
+	int i, c;
+	unsigned char *rx_char;
+	struct tty_port *tport;
+	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+
+	tport = &uport->state->port;
+	for (i = 0; i < rx_fifo_wc; i++) {
+		int bytes = 4;
+
+		*(qcom_port->rx_fifo) =
+			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
+		if (drop_rx)
+			continue;
+		rx_char = (unsigned char *)qcom_port->rx_fifo;
+
+		if (i == (rx_fifo_wc - 1)) {
+			if (rx_last && rx_last_byte_valid)
+				bytes = rx_last_byte_valid;
+		}
+		for (c = 0; c < bytes; c++) {
+			char flag = TTY_NORMAL;
+			int sysrq;
+
+			uport->icount.rx++;
+			sysrq = uart_handle_sysrq_char(uport, rx_char[c]);
+			if (!sysrq)
+				tty_insert_flip_char(tport, rx_char[c], flag);
+		}
+	}
+	if (!drop_rx)
+		tty_flip_buffer_push(tport);
+	return 0;
+}
+#else
+static int handle_rx_console(struct uart_port *uport,
+			unsigned int rx_fifo_wc,
+			unsigned int rx_last_byte_valid,
+			unsigned int rx_last,
+			bool drop_rx)
+{
+	return -EPERM;
+}
+
+#endif /* (CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)) */
+
+static void qcom_geni_serial_start_tx(struct uart_port *uport)
+{
+	unsigned int geni_m_irq_en;
+	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+	unsigned int geni_status;
+
+	if (qcom_port->xfer_mode == FIFO_MODE) {
+		geni_status = geni_read_reg_nolog(uport->membase,
+						  SE_GENI_STATUS);
+		if (geni_status & M_GENI_CMD_ACTIVE)
+			goto exit_start_tx;
+
+		if (!qcom_geni_serial_tx_empty(uport))
+			goto exit_start_tx;
+
+		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
+						    SE_GENI_M_IRQ_EN);
+		geni_m_irq_en |= (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN);
+
+		geni_write_reg_nolog(qcom_port->tx_wm, uport->membase,
+						SE_GENI_TX_WATERMARK_REG);
+		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
+							SE_GENI_M_IRQ_EN);
+		/* Geni command setup should complete before returning.*/
+		mb();
+	}
+exit_start_tx:
+	return;
+}
+
+static void stop_tx_sequencer(struct uart_port *uport)
+{
+	unsigned int geni_m_irq_en;
+	unsigned int geni_status;
+	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
+	geni_m_irq_en &= ~M_CMD_DONE_EN;
+	if (port->xfer_mode == FIFO_MODE) {
+		geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN;
+		geni_write_reg_nolog(0, uport->membase,
+				     SE_GENI_TX_WATERMARK_REG);
+	}
+	port->xmit_size = 0;
+	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
+	geni_status = geni_read_reg_nolog(uport->membase,
+						SE_GENI_STATUS);
+	/* Possible stop tx is called multiple times. */
+	if (!(geni_status & M_GENI_CMD_ACTIVE))
+		return;
+
+	geni_se_cancel_m_cmd(uport->membase);
+	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_CMD_CANCEL_EN, true)) {
+		geni_se_abort_m_cmd(uport->membase);
+		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_CMD_ABORT_EN, true);
+		geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
+							SE_GENI_M_IRQ_CLEAR);
+	}
+	geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_stop_tx(struct uart_port *uport)
+{
+	stop_tx_sequencer(uport);
+}
+
+static void start_rx_sequencer(struct uart_port *uport)
+{
+	unsigned int geni_s_irq_en;
+	unsigned int geni_m_irq_en;
+	unsigned int geni_status;
+	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+	if (geni_status & S_GENI_CMD_ACTIVE)
+		qcom_geni_serial_stop_rx(uport);
+
+	geni_se_setup_s_cmd(uport->membase, UART_START_READ, 0);
+
+	if (port->xfer_mode == FIFO_MODE) {
+		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
+							SE_GENI_S_IRQ_EN);
+		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
+							SE_GENI_M_IRQ_EN);
+
+		geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
+		geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
+
+		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
+							SE_GENI_S_IRQ_EN);
+		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
+							SE_GENI_M_IRQ_EN);
+	}
+	/*
+	 * Ensure the writes to the secondary sequencer and interrupt enables
+	 * go through.
+	 */
+	mb();
+	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+}
+
+static void qcom_geni_serial_start_rx(struct uart_port *uport)
+{
+	start_rx_sequencer(uport);
+}
+
+static void stop_rx_sequencer(struct uart_port *uport)
+{
+	unsigned int geni_s_irq_en;
+	unsigned int geni_m_irq_en;
+	unsigned int geni_status;
+	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+	u32 irq_clear = S_CMD_DONE_EN;
+	bool done;
+
+	if (port->xfer_mode == FIFO_MODE) {
+		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
+							SE_GENI_S_IRQ_EN);
+		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
+							SE_GENI_M_IRQ_EN);
+		geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
+		geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
+
+		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
+							SE_GENI_S_IRQ_EN);
+		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
+							SE_GENI_M_IRQ_EN);
+	}
+
+	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+	/* Possible stop rx is called multiple times. */
+	if (!(geni_status & S_GENI_CMD_ACTIVE))
+		return;
+	geni_se_cancel_s_cmd(uport->membase);
+	/*
+	 * Ensure that the cancel goes through before polling for the
+	 * cancel control bit.
+	 */
+	mb();
+	done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+					S_GENI_CMD_CANCEL, false);
+	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+	if ((geni_status & S_GENI_CMD_ACTIVE))
+		qcom_geni_serial_abort_rx(uport);
+}
+
+static void qcom_geni_serial_stop_rx(struct uart_port *uport)
+{
+	stop_rx_sequencer(uport);
+}
+
+static int qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
+{
+	int ret = 0;
+	unsigned int rx_fifo_status;
+	unsigned int rx_fifo_wc = 0;
+	unsigned int rx_last_byte_valid = 0;
+	unsigned int rx_last = 0;
+	struct tty_port *tport;
+	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	tport = &uport->state->port;
+	rx_fifo_status = geni_read_reg_nolog(uport->membase,
+				SE_GENI_RX_FIFO_STATUS);
+	rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK;
+	rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >>
+						RX_LAST_BYTE_VALID_SHFT);
+	rx_last = rx_fifo_status & RX_LAST;
+	if (rx_fifo_wc)
+		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
+							rx_last, drop_rx);
+	return ret;
+}
+
+static int qcom_geni_serial_handle_tx(struct uart_port *uport)
+{
+	int ret = 0;
+	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+	struct circ_buf *xmit = &uport->state->xmit;
+	unsigned int avail_fifo_bytes = 0;
+	unsigned int bytes_remaining = 0;
+	int i = 0;
+	unsigned int tx_fifo_status;
+	unsigned int xmit_size;
+	unsigned int fifo_width_bytes = 1;
+	int temp_tail = 0;
+
+	xmit_size = uart_circ_chars_pending(xmit);
+	tx_fifo_status = geni_read_reg_nolog(uport->membase,
+					SE_GENI_TX_FIFO_STATUS);
+	/* Both FIFO and framework buffer are drained */
+	if ((xmit_size == qcom_port->xmit_size) && !tx_fifo_status) {
+		qcom_port->xmit_size = 0;
+		uart_circ_clear(xmit);
+		qcom_geni_serial_stop_tx(uport);
+		goto exit_handle_tx;
+	}
+	xmit_size -= qcom_port->xmit_size;
+
+	avail_fifo_bytes = (qcom_port->tx_fifo_depth - qcom_port->tx_wm) *
+							fifo_width_bytes;
+	temp_tail = (xmit->tail + qcom_port->xmit_size) & (UART_XMIT_SIZE - 1);
+	if (xmit_size > (UART_XMIT_SIZE - temp_tail))
+		xmit_size = (UART_XMIT_SIZE - temp_tail);
+	if (xmit_size > avail_fifo_bytes)
+		xmit_size = avail_fifo_bytes;
+
+	if (!xmit_size)
+		goto exit_handle_tx;
+
+	qcom_geni_serial_setup_tx(uport, xmit_size);
+
+	bytes_remaining = xmit_size;
+	while (i < xmit_size) {
+		unsigned int tx_bytes;
+		unsigned int buf = 0;
+		int c;
+
+		tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
+					bytes_remaining : fifo_width_bytes);
+
+		for (c = 0; c < tx_bytes ; c++)
+			buf |= (xmit->buf[temp_tail + c] << (c * 8));
+		geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
+		i += tx_bytes;
+		temp_tail = (temp_tail + tx_bytes) & (UART_XMIT_SIZE - 1);
+		uport->icount.tx += tx_bytes;
+		bytes_remaining -= tx_bytes;
+		/* Ensure FIFO write goes through */
+		wmb();
+	}
+	qcom_geni_serial_poll_cancel_tx(uport);
+	qcom_port->xmit_size += xmit_size;
+exit_handle_tx:
+	uart_write_wakeup(uport);
+	return ret;
+}
+
+static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
+{
+	unsigned int m_irq_status;
+	unsigned int s_irq_status;
+	struct uart_port *uport = dev;
+	unsigned long flags;
+	unsigned int m_irq_en;
+	bool drop_rx = false;
+	struct tty_port *tport = &uport->state->port;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	if (uport->suspended)
+		goto exit_geni_serial_isr;
+	m_irq_status = geni_read_reg_nolog(uport->membase,
+						SE_GENI_M_IRQ_STATUS);
+	s_irq_status = geni_read_reg_nolog(uport->membase,
+						SE_GENI_S_IRQ_STATUS);
+	m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
+	geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
+	geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
+
+	if ((m_irq_status & M_ILLEGAL_CMD_EN)) {
+		WARN_ON(1);
+		goto exit_geni_serial_isr;
+	}
+
+	if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
+		uport->icount.overrun++;
+		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+	}
+
+	if ((m_irq_status & m_irq_en) &
+	    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
+		qcom_geni_serial_handle_tx(uport);
+
+	if ((s_irq_status & S_GP_IRQ_0_EN) || (s_irq_status & S_GP_IRQ_1_EN)) {
+		if (s_irq_status & S_GP_IRQ_0_EN)
+			uport->icount.parity++;
+		drop_rx = true;
+	} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
+		(s_irq_status & S_GP_IRQ_3_EN)) {
+		uport->icount.brk++;
+	}
+
+	if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
+		(s_irq_status & S_RX_FIFO_LAST_EN))
+		qcom_geni_serial_handle_rx(uport, drop_rx);
+
+exit_geni_serial_isr:
+	spin_unlock_irqrestore(&uport->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
+{
+	struct uart_port *uport;
+
+	if (!port)
+		return -ENODEV;
+
+	uport = &port->uport;
+	port->tx_fifo_depth = geni_se_get_tx_fifo_depth(uport->membase);
+	if (!port->tx_fifo_depth) {
+		dev_err(uport->dev, "%s:Invalid TX FIFO depth read\n",
+								__func__);
+		return -ENXIO;
+	}
+
+	port->tx_fifo_width = geni_se_get_tx_fifo_width(uport->membase);
+	if (!port->tx_fifo_width) {
+		dev_err(uport->dev, "%s:Invalid TX FIFO width read\n",
+								__func__);
+		return -ENXIO;
+	}
+
+	port->rx_fifo_depth = geni_se_get_rx_fifo_depth(uport->membase);
+	if (!port->rx_fifo_depth) {
+		dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
+								__func__);
+		return -ENXIO;
+	}
+
+	uport->fifosize =
+		((port->tx_fifo_depth * port->tx_fifo_width) >> 3);
+	return 0;
+}
+
+static void set_rfr_wm(struct qcom_geni_serial_port *port)
+{
+	/*
+	 * Set RFR (Flow off) to FIFO_DEPTH - 2.
+	 * RX WM level at 10% RX_FIFO_DEPTH.
+	 * TX WM level at 10% TX_FIFO_DEPTH.
+	 */
+	port->rx_rfr = port->rx_fifo_depth - 2;
+	port->rx_wm = UART_CONSOLE_RX_WM;
+	port->tx_wm = 2;
+}
+
+static void qcom_geni_serial_shutdown(struct uart_port *uport)
+{
+	unsigned long flags;
+
+	/* Stop the console before stopping the current tx */
+	console_stop(uport->cons);
+
+	disable_irq(uport->irq);
+	free_irq(uport->irq, uport);
+	spin_lock_irqsave(&uport->lock, flags);
+	qcom_geni_serial_stop_tx(uport);
+	qcom_geni_serial_stop_rx(uport);
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+static int qcom_geni_serial_port_setup(struct uart_port *uport)
+{
+	int ret = 0;
+	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+	unsigned long cfg0, cfg1;
+	unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
+
+	set_rfr_wm(qcom_port);
+	geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT);
+	/*
+	 * Make an unconditional cancel on the main sequencer to reset
+	 * it else we could end up in data loss scenarios.
+	 */
+	qcom_port->xfer_mode = FIFO_MODE;
+	qcom_geni_serial_poll_cancel_tx(uport);
+	geni_se_get_packing_config(8, 1, false, &cfg0, &cfg1);
+	geni_write_reg_nolog(cfg0, uport->membase,
+					SE_GENI_TX_PACKING_CFG0);
+	geni_write_reg_nolog(cfg1, uport->membase,
+					SE_GENI_TX_PACKING_CFG1);
+	geni_se_get_packing_config(8, 4, false, &cfg0, &cfg1);
+	geni_write_reg_nolog(cfg0, uport->membase,
+					SE_GENI_RX_PACKING_CFG0);
+	geni_write_reg_nolog(cfg1, uport->membase,
+					SE_GENI_RX_PACKING_CFG1);
+	ret = geni_se_init(uport->membase, qcom_port->rx_wm, qcom_port->rx_rfr);
+	if (ret) {
+		dev_err(uport->dev, "%s: Fail\n", __func__);
+		goto exit_portsetup;
+	}
+
+	ret = geni_se_select_mode(uport->membase, qcom_port->xfer_mode);
+	if (ret)
+		goto exit_portsetup;
+
+	qcom_port->port_setup = true;
+	/*
+	 * Ensure Port setup related IO completes before returning to
+	 * framework.
+	 */
+	mb();
+exit_portsetup:
+	return ret;
+}
+
+static int qcom_geni_serial_startup(struct uart_port *uport)
+{
+	int ret = 0;
+	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+
+	scnprintf(qcom_port->name, sizeof(qcom_port->name),
+		  "qcom_serial_geni%d",	uport->line);
+
+	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
+		dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
+				 __func__, geni_se_get_proto(uport->membase));
+		ret = -ENXIO;
+		goto exit_startup;
+	}
+
+	get_tx_fifo_size(qcom_port);
+	if (!qcom_port->port_setup) {
+		if (qcom_geni_serial_port_setup(uport))
+			goto exit_startup;
+	}
+
+	/*
+	 * Ensure that all the port configuration writes complete
+	 * before returning to the framework.
+	 */
+	mb();
+	ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
+			qcom_port->name, uport);
+	if (unlikely(ret)) {
+		dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n",
+							__func__, ret);
+		goto exit_startup;
+	}
+
+exit_startup:
+	return ret;
+}
+
+static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
+{
+	unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
+		32000000, 48000000, 64000000, 80000000, 96000000, 100000000};
+	int i;
+	int match = -1;
+
+	for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
+		if (clk_freq > root_freq[i])
+			continue;
+
+		if (!(root_freq[i] % clk_freq)) {
+			match = i;
+			break;
+		}
+	}
+	if (match != -1)
+		*ser_clk = root_freq[match];
+	else
+		pr_err("clk_freq %ld\n", clk_freq);
+	return match;
+}
+
+static void geni_serial_write_term_regs(struct uart_port *uport,
+		u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
+		u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
+		u32 s_clk_cfg)
+{
+	geni_write_reg_nolog(tx_trans_cfg, uport->membase,
+							SE_UART_TX_TRANS_CFG);
+	geni_write_reg_nolog(tx_parity_cfg, uport->membase,
+							SE_UART_TX_PARITY_CFG);
+	geni_write_reg_nolog(rx_trans_cfg, uport->membase,
+							SE_UART_RX_TRANS_CFG);
+	geni_write_reg_nolog(rx_parity_cfg, uport->membase,
+							SE_UART_RX_PARITY_CFG);
+	geni_write_reg_nolog(bits_per_char, uport->membase,
+							SE_UART_TX_WORD_LEN);
+	geni_write_reg_nolog(bits_per_char, uport->membase,
+							SE_UART_RX_WORD_LEN);
+	geni_write_reg_nolog(stop_bit_len, uport->membase,
+						SE_UART_TX_STOP_BIT_LEN);
+	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
+	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);
+}
+
+static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
+{
+	unsigned long ser_clk;
+	int dfs_index;
+	int clk_div = 0;
+
+	*desired_clk_rate = baud * UART_OVERSAMPLING;
+	dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk);
+	if (dfs_index < 0) {
+		pr_err("%s: Can't find matching DFS entry for baud %d\n",
+								__func__, baud);
+		clk_div = -EINVAL;
+		goto exit_get_clk_div_rate;
+	}
+
+	clk_div = ser_clk / *desired_clk_rate;
+	*desired_clk_rate = ser_clk;
+exit_get_clk_div_rate:
+	return clk_div;
+}
+
+static void qcom_geni_serial_set_termios(struct uart_port *uport,
+				struct ktermios *termios, struct ktermios *old)
+{
+	unsigned int baud;
+	unsigned int bits_per_char = 0;
+	unsigned int tx_trans_cfg;
+	unsigned int tx_parity_cfg;
+	unsigned int rx_trans_cfg;
+	unsigned int rx_parity_cfg;
+	unsigned int stop_bit_len;
+	unsigned int clk_div;
+	unsigned long ser_clk_cfg = 0;
+	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
+	unsigned long clk_rate;
+
+	qcom_geni_serial_stop_rx(uport);
+	/* baud rate */
+	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
+	port->cur_baud = baud;
+	clk_div = get_clk_div_rate(baud, &clk_rate);
+	if (clk_div <= 0)
+		goto exit_set_termios;
+
+	uport->uartclk = clk_rate;
+	clk_set_rate(port->serial_rsc.se_clk, clk_rate);
+	ser_clk_cfg |= SER_CLK_EN;
+	ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
+
+	/* parity */
+	tx_trans_cfg = geni_read_reg_nolog(uport->membase,
+							SE_UART_TX_TRANS_CFG);
+	tx_parity_cfg = geni_read_reg_nolog(uport->membase,
+							SE_UART_TX_PARITY_CFG);
+	rx_trans_cfg = geni_read_reg_nolog(uport->membase,
+							SE_UART_RX_TRANS_CFG);
+	rx_parity_cfg = geni_read_reg_nolog(uport->membase,
+							SE_UART_RX_PARITY_CFG);
+	if (termios->c_cflag & PARENB) {
+		tx_trans_cfg |= UART_TX_PAR_EN;
+		rx_trans_cfg |= UART_RX_PAR_EN;
+		tx_parity_cfg |= PAR_CALC_EN;
+		rx_parity_cfg |= PAR_CALC_EN;
+		if (termios->c_cflag & PARODD) {
+			tx_parity_cfg |= PAR_ODD;
+			rx_parity_cfg |= PAR_ODD;
+		} else if (termios->c_cflag & CMSPAR) {
+			tx_parity_cfg |= PAR_SPACE;
+			rx_parity_cfg |= PAR_SPACE;
+		} else {
+			tx_parity_cfg |= PAR_EVEN;
+			rx_parity_cfg |= PAR_EVEN;
+		}
+	} else {
+		tx_trans_cfg &= ~UART_TX_PAR_EN;
+		rx_trans_cfg &= ~UART_RX_PAR_EN;
+		tx_parity_cfg &= ~PAR_CALC_EN;
+		rx_parity_cfg &= ~PAR_CALC_EN;
+	}
+
+	/* bits per char */
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		bits_per_char = 5;
+		break;
+	case CS6:
+		bits_per_char = 6;
+		break;
+	case CS7:
+		bits_per_char = 7;
+		break;
+	case CS8:
+	default:
+		bits_per_char = 8;
+		break;
+	}
+
+	/* stop bits */
+	if (termios->c_cflag & CSTOPB)
+		stop_bit_len = TX_STOP_BIT_LEN_2;
+	else
+		stop_bit_len = TX_STOP_BIT_LEN_1;
+
+	/* flow control, clear the CTS_MASK bit if using flow control. */
+	if (termios->c_cflag & CRTSCTS)
+		tx_trans_cfg &= ~UART_CTS_MASK;
+	else
+		tx_trans_cfg |= UART_CTS_MASK;
+
+	if (likely(baud))
+		uart_update_timeout(uport, termios->c_cflag, baud);
+
+	geni_serial_write_term_regs(uport, tx_trans_cfg, tx_parity_cfg,
+		rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len,
+								ser_clk_cfg);
+exit_set_termios:
+	qcom_geni_serial_start_rx(uport);
+	return;
+
+}
+
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
+{
+	unsigned int tx_fifo_status;
+	unsigned int is_tx_empty = 1;
+
+	tx_fifo_status = geni_read_reg_nolog(uport->membase,
+						SE_GENI_TX_FIFO_STATUS);
+	if (tx_fifo_status)
+		is_tx_empty = 0;
+
+	return is_tx_empty;
+}
+
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
+static int __init qcom_geni_console_setup(struct console *co, char *options)
+{
+	struct uart_port *uport;
+	struct qcom_geni_serial_port *dev_port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret = 0;
+
+	if (unlikely(co->index >= GENI_UART_NR_PORTS  || co->index < 0))
+		return -ENXIO;
+
+	dev_port = get_port_from_line(co->index);
+	if (IS_ERR_OR_NULL(dev_port)) {
+		ret = PTR_ERR(dev_port);
+		pr_err("Invalid line %d(%d)\n", co->index, ret);
+		return ret;
+	}
+
+	uport = &dev_port->uport;
+
+	if (unlikely(!uport->membase))
+		return -ENXIO;
+
+	if (geni_se_resources_on(&dev_port->serial_rsc))
+		WARN_ON(1);
+
+	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
+		geni_se_resources_off(&dev_port->serial_rsc);
+		return -ENXIO;
+	}
+
+	if (!dev_port->port_setup) {
+		qcom_geni_serial_stop_rx(uport);
+		qcom_geni_serial_port_setup(uport);
+	}
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(uport, co, baud, parity, bits, flow);
+}
+
+static int console_register(struct uart_driver *drv)
+{
+	return uart_register_driver(drv);
+}
+static void console_unregister(struct uart_driver *drv)
+{
+	uart_unregister_driver(drv);
+}
+
+static struct console cons_ops = {
+	.name = "ttyMSM",
+	.write = qcom_geni_serial_console_write,
+	.device = uart_console_device,
+	.setup = qcom_geni_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &qcom_geni_console_driver,
+};
+
+static struct uart_driver qcom_geni_console_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "qcom_geni_console",
+	.dev_name = "ttyMSM",
+	.nr =  GENI_UART_NR_PORTS,
+	.cons = &cons_ops,
+};
+#else
+static int console_register(struct uart_driver *drv)
+{
+	return 0;
+}
+
+static void console_unregister(struct uart_driver *drv)
+{
+}
+#endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */
+
+static void qcom_geni_serial_cons_pm(struct uart_port *uport,
+		unsigned int new_state, unsigned int old_state)
+{
+	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
+
+	if (unlikely(!uart_console(uport)))
+		return;
+
+	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+		geni_se_resources_on(&qcom_port->serial_rsc);
+	else if (new_state == UART_PM_STATE_OFF &&
+			old_state == UART_PM_STATE_ON)
+		geni_se_resources_off(&qcom_port->serial_rsc);
+}
+
+static const struct uart_ops qcom_geni_console_pops = {
+	.tx_empty = qcom_geni_serial_tx_empty,
+	.stop_tx = qcom_geni_serial_stop_tx,
+	.start_tx = qcom_geni_serial_start_tx,
+	.stop_rx = qcom_geni_serial_stop_rx,
+	.set_termios = qcom_geni_serial_set_termios,
+	.startup = qcom_geni_serial_startup,
+	.config_port = qcom_geni_serial_config_port,
+	.shutdown = qcom_geni_serial_shutdown,
+	.type = qcom_geni_serial_get_type,
+	.set_mctrl = qcom_geni_cons_set_mctrl,
+	.get_mctrl = qcom_geni_cons_get_mctrl,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	= qcom_geni_serial_get_char,
+	.poll_put_char	= qcom_geni_serial_poll_put_char,
+#endif
+	.pm = qcom_geni_serial_cons_pm,
+};
+
+static const struct of_device_id qcom_geni_serial_match_table[] = {
+	{ .compatible = "qcom,geni-debug-uart",
+			.data = (void *)&qcom_geni_console_driver},
+};
+
+static int qcom_geni_serial_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int line = -1;
+	struct qcom_geni_serial_port *dev_port;
+	struct uart_port *uport;
+	struct resource *res;
+	struct uart_driver *drv;
+	const struct of_device_id *id;
+
+	id = of_match_device(qcom_geni_serial_match_table, &pdev->dev);
+	if (id) {
+		dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
+		drv = (struct uart_driver *)id->data;
+	} else {
+		dev_err(&pdev->dev, "%s: No matching device found", __func__);
+		return -ENODEV;
+	}
+
+	if (pdev->dev.of_node) {
+		if (drv->cons)
+			line = of_alias_get_id(pdev->dev.of_node, "serial");
+	} else {
+		line = pdev->id;
+	}
+
+	if (line < 0)
+		line = atomic_inc_return(&uart_line_id) - 1;
+
+	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
+		return -ENXIO;
+	dev_port = get_port_from_line(line);
+	if (IS_ERR_OR_NULL(dev_port)) {
+		ret = PTR_ERR(dev_port);
+		dev_err(&pdev->dev, "Invalid line %d(%d)\n",
+					line, ret);
+		goto exit_geni_serial_probe;
+	}
+
+	uport = &dev_port->uport;
+
+	/* Don't allow 2 drivers to access the same port */
+	if (uport->private_data) {
+		ret = -ENODEV;
+		goto exit_geni_serial_probe;
+	}
+
+	uport->dev = &pdev->dev;
+	dev_port->wrapper_dev = pdev->dev.parent;
+	dev_port->serial_rsc.wrapper_dev = pdev->dev.parent;
+	dev_port->serial_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
+	if (IS_ERR(dev_port->serial_rsc.se_clk)) {
+		ret = PTR_ERR(dev_port->serial_rsc.se_clk);
+		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+		goto exit_geni_serial_probe;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "se-phys");
+	if (!res) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "Err getting IO region\n");
+		goto exit_geni_serial_probe;
+	}
+
+	uport->mapbase = res->start;
+	uport->membase = devm_ioremap(&pdev->dev, res->start,
+						resource_size(res));
+	if (!uport->membase) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "Err IO mapping serial iomem");
+		goto exit_geni_serial_probe;
+	}
+
+	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
+		dev_err(&pdev->dev, "No pinctrl config specified!\n");
+		ret = PTR_ERR(dev_port->serial_rsc.geni_pinctrl);
+		goto exit_geni_serial_probe;
+	}
+	dev_port->serial_rsc.geni_gpio_active =
+		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
+							PINCTRL_DEFAULT);
+	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
+		dev_err(&pdev->dev, "No default config specified!\n");
+		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
+		goto exit_geni_serial_probe;
+	}
+
+	dev_port->serial_rsc.geni_gpio_sleep =
+		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
+							PINCTRL_SLEEP);
+	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_sleep)) {
+		dev_err(&pdev->dev, "No sleep config specified!\n");
+		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
+		goto exit_geni_serial_probe;
+	}
+
+	dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+	dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+	dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
+	uport->fifosize =
+		((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3);
+
+	uport->irq = platform_get_irq(pdev, 0);
+	if (uport->irq < 0) {
+		ret = uport->irq;
+		dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret);
+		goto exit_geni_serial_probe;
+	}
+
+	uport->private_data = (void *)drv;
+	platform_set_drvdata(pdev, dev_port);
+	dev_port->handle_rx = handle_rx_console;
+	dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32),
+								GFP_KERNEL);
+	dev_port->port_setup = false;
+	return uart_add_one_port(drv, uport);
+
+exit_geni_serial_probe:
+	return ret;
+}
+
+static int qcom_geni_serial_remove(struct platform_device *pdev)
+{
+	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+	struct uart_driver *drv =
+			(struct uart_driver *)port->uport.private_data;
+
+	uart_remove_one_port(drv, &port->uport);
+	return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+	struct uart_port *uport = &port->uport;
+
+	uart_suspend_port((struct uart_driver *)uport->private_data,
+				uport);
+	return 0;
+}
+
+static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+	struct uart_port *uport = &port->uport;
+
+	if (console_suspend_enabled && uport->suspended) {
+		uart_resume_port((struct uart_driver *)uport->private_data,
+									uport);
+		disable_irq(uport->irq);
+	}
+	return 0;
+}
+#else
+static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
+{
+	return 0;
+}
+
+static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
+	.suspend_noirq = qcom_geni_serial_sys_suspend_noirq,
+	.resume_noirq = qcom_geni_serial_sys_resume_noirq,
+};
+
+static struct platform_driver qcom_geni_serial_platform_driver = {
+	.remove = qcom_geni_serial_remove,
+	.probe = qcom_geni_serial_probe,
+	.driver = {
+		.name = "qcom_geni_serial",
+		.of_match_table = qcom_geni_serial_match_table,
+		.pm = &qcom_geni_serial_pm_ops,
+	},
+};
+
+static int __init qcom_geni_serial_init(void)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < GENI_UART_CONS_PORTS; i++) {
+		qcom_geni_console_port.uport.iotype = UPIO_MEM;
+		qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
+		qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
+		qcom_geni_console_port.uport.line = i;
+	}
+
+	ret = console_register(&qcom_geni_console_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&qcom_geni_serial_platform_driver);
+	if (ret) {
+		console_unregister(&qcom_geni_console_driver);
+		return ret;
+	}
+	return ret;
+}
+module_init(qcom_geni_serial_init);
+
+static void __exit qcom_geni_serial_exit(void)
+{
+	platform_driver_unregister(&qcom_geni_serial_platform_driver);
+	console_unregister(&qcom_geni_console_driver);
+}
+module_exit(qcom_geni_serial_exit);
+
+MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("tty:qcom_geni_serial");
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary
  2018-01-13  1:05 ` [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary Karthikeyan Ramasubramanian
@ 2018-01-16 16:55   ` Bjorn Andersson
  2018-01-29 21:52     ` Karthik Ramasubramanian
  0 siblings, 1 reply; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-16 16:55 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
> Wrapper is a programmable module that is composed of multiple Serial
> Engines (SE) and can support various Serial Interfaces like UART, SPI,
> I2C, I3C, etc. This document provides a high level overview of the GENI
> based QUP Wrapper.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>

Rather than adding a disconnected chunk of documentation move this into
the driver(s), using the format described in
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html?highlight=kernel%20doc#overview-documentation-comments 

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
@ 2018-01-17  6:20     ` Bjorn Andersson
  2018-01-18  9:13       ` Rajendra Nayak
  2018-01-31 18:58       ` Karthik Ramasubramanian
       [not found]     ` <1515805547-22816-4-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
                       ` (2 subsequent siblings)
  3 siblings, 2 replies; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-17  6:20 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> This driver manages the Generic Interface (GENI) firmware based Qualcomm
> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
> programmable module composed of multiple Serial Engines (SE) and supports
> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
> driver also enables managing the serial interface independent aspects of
> Serial Engines.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
>  drivers/soc/qcom/Kconfig        |    8 +
>  drivers/soc/qcom/Makefile       |    1 +
>  drivers/soc/qcom/qcom-geni-se.c | 1016 +++++++++++++++++++++++++++++++++++++++

I'm not aware of any non-serial-engine "geni" at Qualcomm, so can we
drop the "se" throughout this driver?

>  include/linux/qcom-geni-se.h    |  807 +++++++++++++++++++++++++++++++
>  4 files changed, 1832 insertions(+)
>  create mode 100644 drivers/soc/qcom/qcom-geni-se.c
>  create mode 100644 include/linux/qcom-geni-se.h
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index b81374b..b306d51 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -3,6 +3,14 @@
>  #
>  menu "Qualcomm SoC drivers"
>  
> +config QCOM_GENI_SE
> +	tristate "QCOM GENI Serial Engine Driver"

Options in drivers/soc/qcom/Kconfig should have "depends on ARCH_QCOM",
as the makefile in this directory won't be processed when !ARCH_QCOM.

> +	help
> +	  This module is used to manage Generic Interface (GENI) firmware based
> +	  Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
> +	  module is also used to manage the common aspects of multiple Serial
> +	  Engines present in the QUP.
> +
>  config QCOM_GLINK_SSR
>  	tristate "Qualcomm Glink SSR driver"
>  	depends on RPMSG
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 40c56f6..74d5db8 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,4 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_QCOM_GENI_SE) +=	qcom-geni-se.o
>  obj-$(CONFIG_QCOM_GLINK_SSR) +=	glink_ssr.o
>  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>  obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> new file mode 100644
> index 0000000..3f43582
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> @@ -0,0 +1,1016 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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.
> + *
> + */

Please use SPDX style license header, i.e. this file should start with:

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 */

> +
> +#include <linux/clk.h>
> +#include <linux/slab.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/io.h>
> +#include <linux/list.h>

This isn't used.

> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>

Is this used?

> +#include <linux/qcom-geni-se.h>
> +#include <linux/spinlock.h>

Is this used?

> +
> +#define MAX_CLK_PERF_LEVEL 32
> +
> +/**
> + * @struct geni_se_device - Data structure to represent the QUP Wrapper Core

Make this name indicate that this is the wrapper; e.g. struct geni_wrapper

> + * @dev:		Device pointer of the QUP wrapper core.
> + * @base:		Base address of this instance of QUP wrapper core.
> + * @m_ahb_clk:		Handle to the primary AHB clock.
> + * @s_ahb_clk:		Handle to the secondary AHB clock.
> + * @geni_dev_lock:	Lock to protect the device elements.
> + * @num_clk_levels:	Number of valid clock levels in clk_perf_tbl.
> + * @clk_perf_tbl:	Table of clock frequency input to Serial Engine clock.
> + */
> +struct geni_se_device {
> +	struct device *dev;
> +	void __iomem *base;
> +	struct clk *m_ahb_clk;
> +	struct clk *s_ahb_clk;

As these two clocks always seems to be operated in tandem use
clk_bulk_data to keep track of them and use the clk_bulk*() operations
to simplify your prepare/unprepare sites.

> +	struct mutex geni_dev_lock;

There's only one lock and it should be obvious from context that it's
the lock of the geni_se_device, so naming this "lock" should be
sufficient.

> +	unsigned int num_clk_levels;
> +	unsigned long *clk_perf_tbl;
> +};
> +
> +/* Offset of QUP Hardware Version Register */
> +#define QUP_HW_VER (0x4)

Drop the parenthesis. It would be nice if this define indicated that
this is a register and not a value. E.g. call it QUP_HW_VER_REG.

> +
> +#define HW_VER_MAJOR_MASK GENMASK(31, 28)
> +#define HW_VER_MAJOR_SHFT 28
> +#define HW_VER_MINOR_MASK GENMASK(27, 16)
> +#define HW_VER_MINOR_SHFT 16
> +#define HW_VER_STEP_MASK GENMASK(15, 0)
> +
> +/**
> + * geni_read_reg_nolog() - Helper function to read from a GENI register
> + * @base:	Base address of the serial engine's register block.
> + * @offset:	Offset within the serial engine's register block.
> + *
> + * Return:	Return the contents of the register.
> + */
> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset)

Remove this function.

> +{
> +	return readl_relaxed(base + offset);
> +}
> +EXPORT_SYMBOL(geni_read_reg_nolog);
> +
> +/**
> + * geni_write_reg_nolog() - Helper function to write into a GENI register
> + * @value:	Value to be written into the register.
> + * @base:	Base address of the serial engine's register block.
> + * @offset:	Offset within the serial engine's register block.
> + */
> +void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset)

Ditto

> +{
> +	return writel_relaxed(value, (base + offset));
> +}
> +EXPORT_SYMBOL(geni_write_reg_nolog);
> +
> +/**
> + * geni_read_reg() - Helper function to read from a GENI register
> + * @base:	Base address of the serial engine's register block.
> + * @offset:	Offset within the serial engine's register block.
> + *
> + * Return:	Return the contents of the register.

Drop the extra spaces after "Return:" in all your kerneldoc.

> + */
> +unsigned int geni_read_reg(void __iomem *base, int offset)
> +{
> +	return readl_relaxed(base + offset);
> +}
> +EXPORT_SYMBOL(geni_read_reg);
> +
> +/**
> + * geni_write_reg() - Helper function to write into a GENI register
> + * @value:	Value to be written into the register.
> + * @base:	Base address of the serial engine's register block.
> + * @offset:	Offset within the serial engine's register block.
> + */
> +void geni_write_reg(unsigned int value, void __iomem *base, int offset)
> +{
> +	return writel_relaxed(value, (base + offset));

As written, this function adds no value and I find it quite confusing
that this is used both for read/writes on the wrapper as well as the
individual functions.

Compare:

	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);

with just inlining:

	writel(common_geni_m_irq_en, base + SE_GENI_M_IRQ_EN);

> +}
> +EXPORT_SYMBOL(geni_write_reg);
> +
> +/**
> + * geni_get_qup_hw_version() - Read the QUP wrapper Hardware version
> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
> + * @major:		Buffer for Major Version field.
> + * @minor:		Buffer for Minor Version field.
> + * @step:		Buffer for Step Version field.
> + *
> + * Return:	0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
> +			    unsigned int *minor, unsigned int *step)
> +{
> +	unsigned int version;
> +	struct geni_se_device *geni_se_dev;
> +
> +	if (!wrapper_dev || !major || !minor || !step)
> +		return -EINVAL;

This would only happen during development, as the engineer calls the
function incorrectly. Consider it a contract that these has to be valid
and skip the checks.

> +
> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
> +	if (unlikely(!geni_se_dev))
> +		return -ENODEV;

Make the children acquire the geni_se_dev handle (keep the type opaque)
and pass that instead of wrapper_dev. Then you can just drop this chunk.

> +
> +	version = geni_read_reg(geni_se_dev->base, QUP_HW_VER);
> +	*major = (version & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT;
> +	*minor = (version & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT;
> +	*step = version & HW_VER_STEP_MASK;
> +	return 0;

If you implement these two changes there's no way this function can
fail, so you don't have to return a value here.

> +}
> +EXPORT_SYMBOL(geni_get_qup_hw_version);
> +
> +/**
> + * geni_se_get_proto() - Read the protocol configured for a serial engine
> + * @base:	Base address of the serial engine's register block.
> + *
> + * Return:	Protocol value as configured in the serial engine.
> + */
> +int geni_se_get_proto(void __iomem *base)

I keep reading this as "geni set get proto", you should be fine just
dropping _se_ from these function names. And if you want to denote that
it's the Qualcomm GENI then make it qcom_geni_*.

But more importantly, it is not obvious when reading this driver that
the typeless "base" being passed is that of the individual functional
block, and not the wrapper.

If you want to do this in an "object oriented" fashion create a struct
that's common for all geni instances, include it in the context of the
individual function devices and pass it into these functions.

> +{
> +	int proto;
> +
> +	proto = ((geni_read_reg(base, GENI_FW_REVISION_RO)
> +			& FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);

Don't do everything in one go, as you can't fit it on one line anyways.
Writing it like this instead makes it easier to read:

	u32 val;

	val = readl(base + GENI_FW_S_REVISION_RO);

	return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;

> +	return proto;
> +}
> +EXPORT_SYMBOL(geni_se_get_proto);
> +
> +static int geni_se_irq_en(void __iomem *base)
> +{
> +	unsigned int common_geni_m_irq_en;
> +	unsigned int common_geni_s_irq_en;

The size of these values isn't unsigned int, it's u32. And if you give
them a shorter name the function becomes easier to read.

Further more, as you don't care about ordering you don't need two of
them either. So you should be able to just do:

	u32 val;

	val = readl(base + SE_GENI_M_IRQ_EN);
	val |= M_COMMON_GENI_M_IRQ_EN;
	writel(val, base + SE_GENI_M_IRQ_EN);

	val = readl(base + SE_GENI_S_IRQ_EN);
	val |= S_COMMON_GENI_S_IRQ_EN;
	writel(val, base + SE_GENI_S_IRQ_EN);

> +
> +	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
> +	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
> +	/* Common to all modes */
> +	common_geni_m_irq_en |= M_COMMON_GENI_M_IRQ_EN;
> +	common_geni_s_irq_en |= S_COMMON_GENI_S_IRQ_EN;
> +
> +	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
> +	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
> +	return 0;

And as this can't fail there's no reason to have a return value.

> +}
> +
> +
> +static void geni_se_set_rx_rfr_wm(void __iomem *base, unsigned int rx_wm,
> +				  unsigned int rx_rfr)
> +{
> +	geni_write_reg(rx_wm, base, SE_GENI_RX_WATERMARK_REG);
> +	geni_write_reg(rx_rfr, base, SE_GENI_RX_RFR_WATERMARK_REG);
> +}
> +
> +static int geni_se_io_set_mode(void __iomem *base)
> +{
> +	unsigned int io_mode;
> +	unsigned int geni_dma_mode;
> +
> +	io_mode = geni_read_reg(base, SE_IRQ_EN);
> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
> +
> +	io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
> +	io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
> +	geni_dma_mode &= ~GENI_DMA_MODE_EN;
> +
> +	geni_write_reg(io_mode, base, SE_IRQ_EN);
> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
> +	return 0;

As this function can't fail, don't return a value.

> +}
> +
> +static void geni_se_io_init(void __iomem *base)
> +{
> +	unsigned int io_op_ctrl;
> +	unsigned int geni_cgc_ctrl;
> +	unsigned int dma_general_cfg;

These are all u32, be explicit.

> +
> +	geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
> +	dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
> +	geni_cgc_ctrl |= DEFAULT_CGC_EN;
> +	dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
> +			DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);

Drop the parenthesis and there's no harm in making multiple assignments
in favour of splitting the line.

> +	io_op_ctrl = DEFAULT_IO_OUTPUT_CTRL_MSK;
> +	geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
> +	geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);

Is there a reason why this chunk of code is a mix of 3 independent
register updates?

> +
> +	geni_write_reg(io_op_ctrl, base, GENI_OUTPUT_CTRL);
> +	geni_write_reg(FORCE_DEFAULT, base, GENI_FORCE_DEFAULT_REG);
> +}
> +
> +/**
> + * geni_se_init() - Initialize the GENI Serial Engine
> + * @base:	Base address of the serial engine's register block.
> + * @rx_wm:	Receive watermark to be configured.
> + * @rx_rfr_wm:	Ready-for-receive watermark to be configured.

What's the unit for these?

> + *
> + * This function is used to initialize the GENI serial engine, configure
> + * receive watermark and ready-for-receive watermarks.
> + *
> + * Return:	0 on success, standard Linux error codes on failure/error.

As neither geni_se_io_set_mode() nor geni_se_irq_en() can fail there's
no way geni_se_init() can fail and as such you don't need a return value
of this function.

> + */
> +int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr)
> +{
> +	int ret;
> +
> +	geni_se_io_init(base);
> +	ret = geni_se_io_set_mode(base);
> +	if (ret)
> +		return ret;
> +
> +	geni_se_set_rx_rfr_wm(base, rx_wm, rx_rfr);
> +	ret = geni_se_irq_en(base);

Just inline these two functions here.

> +	return ret;
> +}
> +EXPORT_SYMBOL(geni_se_init);

This is an excellent candidate for initializing a common "geni
function"-struct shared among the children, i.e. make this:

void geni_init_port(struct geni_port *port, struct device *dev,
		    size_t rx_wm, rfr_wm);

And have this do the ioremap of the memory of dev and initialize some
common "geni_port" struct, then you can pass this to some set of
geni_port_*() helper functions.

> +
> +static int geni_se_select_fifo_mode(void __iomem *base)
> +{
> +	int proto = geni_se_get_proto(base);
> +	unsigned int common_geni_m_irq_en;
> +	unsigned int common_geni_s_irq_en;
> +	unsigned int geni_dma_mode;

These are of type u32.

If you use more succinct names the function will be easier to read; what
about master, slave and mode? (Or m_en, s_en and mode)

> +
> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);

Use lowercase for the hex constants.

> +
> +	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
> +	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
> +	if (proto != UART) {
> +		common_geni_m_irq_en |=
> +			(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
> +			M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);

Drop the parenthesis, split the assignment in multiple to make things
not span 3 lines.

> +		common_geni_s_irq_en |= S_CMD_DONE_EN;
> +	}
> +	geni_dma_mode &= ~GENI_DMA_MODE_EN;

Please group things that are related.

The compiler doesn't care if you stretch the life time of your local
variables through your functions, but the brain does.

> +
> +	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
> +	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
> +	return 0;

This can't fail, and you ignore the returned value in
geni_se_select_mode().

> +}
> +
> +static int geni_se_select_dma_mode(void __iomem *base)
> +{
> +	unsigned int geni_dma_mode = 0;

This is u32, can be shorten to "mode" and it's first use is an
assignment - so you don't have to initialize it here.

> +
> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
> +	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);

Please lower case your hex constants.

> +
> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
> +	geni_dma_mode |= GENI_DMA_MODE_EN;
> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
> +	return 0;

Can't fail.

> +}
> +
> +/**
> + * geni_se_select_mode() - Select the serial engine transfer mode
> + * @base:	Base address of the serial engine's register block.
> + * @mode:	Transfer mode to be selected.
> + *
> + * Return:	0 on success, standard Linux error codes on failure.
> + */
> +int geni_se_select_mode(void __iomem *base, int mode)
> +{
> +	int ret = 0;

Drop the return value and "ret", if you want to help the developer of
child devices you can start off by a WARN_ON(mode != FIFO_MODE && mode
!= SE_DMA);

> +
> +	switch (mode) {
> +	case FIFO_MODE:
> +		geni_se_select_fifo_mode(base);
> +		break;
> +	case SE_DMA:
> +		geni_se_select_dma_mode(base);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(geni_se_select_mode);
> +
> +/**
> + * geni_se_setup_m_cmd() - Setup the primary sequencer
> + * @base:	Base address of the serial engine's register block.
> + * @cmd:	Command/Operation to setup in the primary sequencer.
> + * @params:	Parameter for the sequencer command.
> + *
> + * This function is used to configure the primary sequencer with the
> + * command and its assoicated parameters.
> + */
> +void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params)
> +{
> +	u32 m_cmd = (cmd << M_OPCODE_SHFT);
> +
> +	m_cmd |= (params & M_PARAMS_MSK);

Rather than initializing the variable during declaration and then
amending the value it's cleaner if you do:

	val = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);

> +	geni_write_reg(m_cmd, base, SE_GENI_M_CMD0);
> +}
> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
> +
[..]
> +/**
> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
> + * @base:	Base address of the serial engine's register block.
> + *
> + * This function is used to get the depth i.e. number of elements in the
> + * TX fifo of the serial engine.
> + *
> + * Return:	TX fifo depth in units of FIFO words.
> + */
> +int geni_se_get_tx_fifo_depth(void __iomem *base)
> +{
> +	int tx_fifo_depth;
> +
> +	tx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_0)
> +			& TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT);

It easier to read this way:

	u32 val;

	val = readl(base, SE_HW_PARAM_0);

	return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;

> +	return tx_fifo_depth;
> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
> +
> +/**
> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
> + * @base:	Base address of the serial engine's register block.
> + *
> + * This function is used to get the width i.e. word size per element in the
> + * TX fifo of the serial engine.
> + *
> + * Return:	TX fifo width in bits
> + */
> +int geni_se_get_tx_fifo_width(void __iomem *base)
> +{
> +	int tx_fifo_width;
> +
> +	tx_fifo_width = ((geni_read_reg(base, SE_HW_PARAM_0)
> +			& TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT);
> +	return tx_fifo_width;

Ditto.

> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
> +
> +/**
> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
> + * @base:	Base address of the serial engine's register block.
> + *
> + * This function is used to get the depth i.e. number of elements in the
> + * RX fifo of the serial engine.
> + *
> + * Return:	RX fifo depth in units of FIFO words
> + */
> +int geni_se_get_rx_fifo_depth(void __iomem *base)
> +{
> +	int rx_fifo_depth;
> +
> +	rx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_1)
> +			& RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT);
> +	return rx_fifo_depth;

Ditto.

> +}
> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
> +
> +/**
> + * geni_se_get_packing_config() - Get the packing configuration based on input
> + * @bpw:	Bits of data per transfer word.
> + * @pack_words:	Number of words per fifo element.
> + * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
> + * @cfg0:	Output buffer to hold the first half of configuration.
> + * @cfg1:	Output buffer to hold the second half of configuration.
> + *
> + * This function is used to calculate the packing configuration based on
> + * the input packing requirement and the configuration logic.
> + */
> +void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
> +				unsigned long *cfg0, unsigned long *cfg1)

This function is used only from geni_se_config_packing() which writes
the new config to the associated registers and from the serial driver
that does the same - but here pack_words differ for rx and tx.

If you improve geni_se_config_packing() to take rx and tx fifo sizes
independently you don't have to expose this function to the client
drivers and you don't need to return cfg0 and cfg1 like you do here.

> +{
> +	u32 cfg[4] = {0};
> +	int len;
> +	int temp_bpw = bpw;
> +	int idx_start = (msb_to_lsb ? (bpw - 1) : 0);
> +	int idx = idx_start;
> +	int idx_delta = (msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE);
> +	int ceil_bpw = ((bpw & (BITS_PER_BYTE - 1)) ?
> +			((bpw & ~(BITS_PER_BYTE - 1)) + BITS_PER_BYTE) : bpw);
> +	int iter = (ceil_bpw * pack_words) >> 3;
> +	int i;
> +
> +	if (unlikely(iter <= 0 || iter > 4)) {

Don't use unlikely(), this function is not called one per port, there's
no need to clutter the code to "optimize" it.

> +		*cfg0 = 0;
> +		*cfg1 = 0;
> +		return;
> +	}
> +
> +	for (i = 0; i < iter; i++) {
> +		len = (temp_bpw < BITS_PER_BYTE) ?
> +				(temp_bpw - 1) : BITS_PER_BYTE - 1;
> +		cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));
> +		idx = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
> +				((i + 1) * BITS_PER_BYTE) + idx_start :
> +				idx + idx_delta;
> +		temp_bpw = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
> +				bpw : (temp_bpw - BITS_PER_BYTE);

Each operation in this loop depend on temp_bpw being smaller or larger
than BITS_PER_BYTE, please rewrite it with a single if/else based on
this.

> +	}
> +	cfg[iter - 1] |= 1;

The 1 you're writing here looks like an "EOF". It would be nice with a
comment describing the format of these 4 registers and the meaning of
BIT(0).

> +	*cfg0 = cfg[0] | (cfg[1] << 10);
> +	*cfg1 = cfg[2] | (cfg[3] << 10);
> +}
> +EXPORT_SYMBOL(geni_se_get_packing_config);
> +
> +/**
> + * geni_se_config_packing() - Packing configuration of the serial engine
> + * @base:	Base address of the serial engine's register block.
> + * @bpw:	Bits of data per transfer word.
> + * @pack_words:	Number of words per fifo element.
> + * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
> + *
> + * This function is used to configure the packing rules for the current
> + * transfer.
> + */
> +void geni_se_config_packing(void __iomem *base, int bpw,
> +			    int pack_words, bool msb_to_lsb)
> +{
> +	unsigned long cfg0, cfg1;

These are u32.

> +
> +	geni_se_get_packing_config(bpw, pack_words, msb_to_lsb, &cfg0, &cfg1);
> +	geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
> +	geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
> +	geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
> +	geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
> +	if (pack_words || bpw == 32)
> +		geni_write_reg((bpw >> 4), base, SE_GENI_BYTE_GRAN);
> +}
> +EXPORT_SYMBOL(geni_se_config_packing);
> +
> +static void geni_se_clks_off(struct geni_se_rsc *rsc)
> +{
> +	struct geni_se_device *geni_se_dev;
> +
> +	if (unlikely(!rsc || !rsc->wrapper_dev))

Drop the unlikely(). Why would you be passed a rsc with wrapper_dev
NULL?

> +		return;
> +
> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> +	if (unlikely(!geni_se_dev))
> +		return;
> +
> +	clk_disable_unprepare(rsc->se_clk);
> +	clk_disable_unprepare(geni_se_dev->s_ahb_clk);
> +	clk_disable_unprepare(geni_se_dev->m_ahb_clk);
> +}
> +
> +/**
> + * geni_se_resources_off() - Turn off resources associated with the serial
> + *                           engine
> + * @rsc:	Handle to resources associated with the serial engine.
> + *
> + * Return:	0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_off(struct geni_se_rsc *rsc)
> +{
> +	int ret = 0;

No need to initialize ret.

> +	struct geni_se_device *geni_se_dev;
> +
> +	if (unlikely(!rsc || !rsc->wrapper_dev))
> +		return -EINVAL;
> +
> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> +	if (unlikely(!geni_se_dev))


Only child devices would call this, so it's unlikely that this is a
probe defer.

Also the returned value is not used.

And drop the unlikely()

> +		return -ENODEV;
> +
> +	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
> +	if (ret)
> +		return ret;
> +
> +	geni_se_clks_off(rsc);
> +	return 0;
> +}
> +EXPORT_SYMBOL(geni_se_resources_off);
> +
> +static int geni_se_clks_on(struct geni_se_rsc *rsc)
> +{
> +	int ret;
> +	struct geni_se_device *geni_se_dev;

If you name this "geni" instead, or "wrapper" the rest will be cleaner.

> +
> +	if (unlikely(!rsc || !rsc->wrapper_dev))
> +		return -EINVAL;
> +
> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> +	if (unlikely(!geni_se_dev))
> +		return -EPROBE_DEFER;
> +
> +	ret = clk_prepare_enable(geni_se_dev->m_ahb_clk);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_prepare_enable(geni_se_dev->s_ahb_clk);
> +	if (ret) {
> +		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
> +		return ret;
> +	}

These two could be dealt with in a single clk_bulk_prepare_enable().

> +
> +	ret = clk_prepare_enable(rsc->se_clk);
> +	if (ret) {
> +		clk_disable_unprepare(geni_se_dev->s_ahb_clk);
> +		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
> +	}
> +	return ret;
> +}
> +
> +/**
> + * geni_se_resources_on() - Turn on resources associated with the serial
> + *                          engine
> + * @rsc:	Handle to resources associated with the serial engine.
> + *
> + * Return:	0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_on(struct geni_se_rsc *rsc)
> +{
> +	int ret = 0;
> +	struct geni_se_device *geni_se_dev;
> +
> +	if (unlikely(!rsc || !rsc->wrapper_dev))
> +		return -EINVAL;
> +
> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> +	if (unlikely(!geni_se_dev))

As with geni_se_resources_off()

> +		return -EPROBE_DEFER;
> +
> +	ret = geni_se_clks_on(rsc);
> +	if (ret)
> +		return ret;
> +
> +	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);

pinctrl_pm_select_default_state(rsc->dev); 

So you need the dev associated with the rsc.

> +	if (ret)
> +		geni_se_clks_off(rsc);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(geni_se_resources_on);
> +
> +/**
> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> + * @rsc:	Resource for which the clock table is requested.
> + * @tbl:	Table in which the output is returned.
> + *
> + * This function is called by the protocol drivers to determine the different
> + * clock frequencies supported by Serail Engine Core Clock. The protocol

s/Serail/Serial/

> + * drivers use the output to determine the clock frequency index to be
> + * programmed into DFS.
> + *
> + * Return:	number of valid performance levels in the table on success,
> + *		standard Linux error codes on failure.
> + */
> +int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl)
> +{
> +	struct geni_se_device *geni_se_dev;
> +	int i;
> +	unsigned long prev_freq = 0;
> +	int ret = 0;
> +
> +	if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))

Drop the "unlikely", you're only calling this once.

> +		return -EINVAL;
> +
> +	*tbl = NULL;

Don't touch tbl on error.

> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> +	if (unlikely(!geni_se_dev))
> +		return -EPROBE_DEFER;

How would this even be possible? This function should only be called by
child devices, which per definition means that this device been probed.

> +
> +	mutex_lock(&geni_se_dev->geni_dev_lock);
> +	if (geni_se_dev->clk_perf_tbl) {
> +		*tbl = geni_se_dev->clk_perf_tbl;
> +		ret = geni_se_dev->num_clk_levels;
> +		goto exit_se_clk_tbl_get;
> +	}

You're never freeing clk_perf_tbl, and the only reason you have
geni_dev_lock is to protect the "cached" array in geni_se_dev.

Move the allocation and generation of clk_perf_tbl to geni_se_probe()
and store the value, then make this function just return that array.
That way you can drop the lock and the code becomes cleaner.

> +
> +	geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
> +						MAX_CLK_PERF_LEVEL, GFP_KERNEL);

Use kcalloc()

> +	if (!geni_se_dev->clk_perf_tbl) {
> +		ret = -ENOMEM;
> +		goto exit_se_clk_tbl_get;
> +	}
> +
> +	for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> +		geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
> +								prev_freq + 1);
> +		if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
> +			geni_se_dev->clk_perf_tbl[i] = 0;

Use a local variable to keep the return value of clk_round_rate(), that
way you don't have to revert the value here (not that it matters...) and
you don't need to line wrap the assignment above.

> +			break;
> +		}
> +		prev_freq = geni_se_dev->clk_perf_tbl[i];

...and then you don't need a separate local variable to keep track of
the last value...

> +	}
> +	geni_se_dev->num_clk_levels = i;
> +	*tbl = geni_se_dev->clk_perf_tbl;
> +	ret = geni_se_dev->num_clk_levels;
> +exit_se_clk_tbl_get:

Name your labels based on what action they perform, e.g. "out_unlock"
(not err_unlock here because it's the successful path as well)

> +	mutex_unlock(&geni_se_dev->geni_dev_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
> +
> +/**
> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
> + * @rsc:	Resource for which the clock frequency is requested.
> + * @req_freq:	Requested clock frequency.
> + * @index:	Index of the resultant frequency in the table.
> + * @res_freq:	Resultant frequency which matches or is closer to the
> + *		requested frequency.
> + * @exact:	Flag to indicate exact multiple requirement of the requested
> + *		frequency .
> + *
> + * This function is called by the protocol drivers to determine the matching
> + * or closest frequency of the Serial Engine clock to be selected in order
> + * to meet the performance requirements.

What's the benefit compared to calling clk_round_rate()?

Given that the function can return a rate that is a fraction of the
requested frequency even though "exact" is set, this description isn't
explaining the entire picture.

> + *
> + * Return:	0 on success, standard Linux error codes on failure.

Returning the new clockrate would make more sense.

> + */
> +int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
> +			   unsigned int *index, unsigned long *res_freq,
> +			   bool exact)
> +{
> +	unsigned long *tbl;
> +	int num_clk_levels;
> +	int i;
> +
> +	num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
> +	if (num_clk_levels < 0)
> +		return num_clk_levels;
> +
> +	if (num_clk_levels == 0)
> +		return -EFAULT;
> +
> +	*res_freq = 0;
> +	for (i = 0; i < num_clk_levels; i++) {
> +		if (!(tbl[i] % req_freq)) {
> +			*index = i;
> +			*res_freq = tbl[i];

So this will return a frequency that divides the requested frequency
without a remainder, will index be used to calculate a multiplier from
this?

> +			return 0;
> +		}
> +
> +		if (!(*res_freq) || ((tbl[i] > *res_freq) &&
> +				     (tbl[i] < req_freq))) {
> +			*index = i;
> +			*res_freq = tbl[i];

What if there's a previous value in tbl that given some multiplier has a
smaller difference to the requested frequency?

> +		}
> +	}
> +
> +	if (exact || !(*res_freq))

*res_freq can't be 0, because in the case that num_clk_levels is 0 you
already returned -EFAULT above, in all other cases you will assign it at
least once.

> +		return -ENOKEY;
> +
> +	return 0;

Why not use the return value for the calculated frequency?

> +}
> +EXPORT_SYMBOL(geni_se_clk_freq_match);
> +
> +/**
> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
> + * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
> + * @base:		Base address of the SE register block.
> + * @tx_buf:		Pointer to the TX buffer.
> + * @tx_len:		Length of the TX buffer.
> + * @tx_dma:		Pointer to store the mapped DMA address.
> + *
> + * This function is used to prepare the buffers for DMA TX.
> + *
> + * Return:	0 on success, standard Linux error codes on error/failure.
> + */
> +int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
> +			void *tx_buf, int tx_len, dma_addr_t *tx_dma)

All child devices will have a "base", so if you move that into what you
today call a geni_se_rsc and you pass that to all these functions
operating on behalf of the child device you have the first two
parameters passed in the same object.

A "tx_len" is typically of type size_t.

Also note that there are no other buf than tx_buf, no other len than
tx_len and no other dma address than tx_dma in this function, so you can
drop "tx_" without loosing any information - but improving code
cleanliness.

> +{
> +	int ret;
> +
> +	if (unlikely(!wrapper_dev || !base || !tx_buf || !tx_len || !tx_dma))
> +		return -EINVAL;

Reduce the error checking here.

> +
> +	ret = geni_se_map_buf(wrapper_dev, tx_dma, tx_buf, tx_len,
> +				    DMA_TO_DEVICE);

I think you should pass the wrapper itself to geni_se_tx_dma_prep() and
if you name this "wrapper" (instead of wrapper_dev) you're under 80
chars and don't need the line break.

> +	if (ret)
> +		return ret;
> +
> +	geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);

So that's DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN?

> +	geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
> +	geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);

If you return the dma_addr_t from this function you will have the happy
side effect of being able to use tx_dma directly instead of *tx_dma and
you can remove some craziness from these calls.



> +	geni_write_reg(1, base, SE_DMA_TX_ATTR);

This "1" would be nice to have some clarification on.

> +	geni_write_reg(tx_len, base, SE_DMA_TX_LEN);
> +	return 0;
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
> +
> +/**
> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
> + * @base:		Base address of the SE register block.
> + * @rx_buf:		Pointer to the RX buffer.
> + * @rx_len:		Length of the RX buffer.
> + * @rx_dma:		Pointer to store the mapped DMA address.
> + *
> + * This function is used to prepare the buffers for DMA RX.
> + *
> + * Return:	0 on success, standard Linux error codes on error/failure.

The only real error that can come out of this is dma_mapping_error(), so
just return the dma_addr_t.

> + */
> +int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
> +			void *rx_buf, int rx_len, dma_addr_t *rx_dma)

As with tx, drop all the "rx_". And pass that geni_se_rsc object
instead.

> +{
> +	int ret;
> +
> +	if (unlikely(!wrapper_dev || !base || !rx_buf || !rx_len || !rx_dma))
> +		return -EINVAL;
> +
> +	ret = geni_se_map_buf(wrapper_dev, rx_dma, rx_buf, rx_len,
> +				    DMA_FROM_DEVICE);
> +	if (ret)
> +		return ret;
> +
> +	geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);

We enable same interrupts for rx as tx? Why enable TX interrupt here?

> +	geni_write_reg((u32)(*rx_dma), base, SE_DMA_RX_PTR_L);
> +	geni_write_reg((u32)((*rx_dma) >> 32), base, SE_DMA_RX_PTR_H);
> +	/* RX does not have EOT bit */

Okay? How does this relate to the 0 being written into RX_ATTR?

> +	geni_write_reg(0, base, SE_DMA_RX_ATTR);
> +	geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
> +	return 0;
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
> +
> +/**
> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
> + * @tx_dma:		DMA address of the TX buffer.
> + * @tx_len:		Length of the TX buffer.
> + *
> + * This function is used to unprepare the DMA buffers after DMA TX.
> + */
> +void geni_se_tx_dma_unprep(struct device *wrapper_dev,
> +			   dma_addr_t tx_dma, int tx_len)
> +{
> +	if (tx_dma)
> +		geni_se_unmap_buf(wrapper_dev, &tx_dma, tx_len,
> +						DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
> +
> +/**
> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
> + * @rx_dma:		DMA address of the RX buffer.
> + * @rx_len:		Length of the RX buffer.
> + *
> + * This function is used to unprepare the DMA buffers after DMA RX.
> + */
> +void geni_se_rx_dma_unprep(struct device *wrapper_dev,
> +			   dma_addr_t rx_dma, int rx_len)
> +{
> +	if (rx_dma)
> +		geni_se_unmap_buf(wrapper_dev, &rx_dma, rx_len,
> +						DMA_FROM_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
> +
> +/**
> + * geni_se_map_buf() - Map a single buffer into QUP wrapper device

I find the mixture of prepare and map in the API (where prepare also
maps) confusing, but no-one calls genbi_se_map_buf() can it be made
internal?

> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
> + * @iova:		Pointer in which the mapped virtual address is stored.
> + * @buf:		Address of the buffer that needs to be mapped.
> + * @size:		Size of the buffer.
> + * @dir:		Direction of the DMA transfer.
> + *
> + * This function is used to map an already allocated buffer into the
> + * QUP device space.
> + *
> + * Return:	0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
> +		    void *buf, size_t size, enum dma_data_direction dir)
> +{
> +	struct device *dev_p;
> +	struct geni_se_device *geni_se_dev;
> +
> +	if (!wrapper_dev || !iova || !buf || !size)
> +		return -EINVAL;

No need to check that the programmer of the intermediate function
checked that the programmer of the client filled all these out
correctly.

> +
> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
> +	if (!geni_se_dev || !geni_se_dev->dev)
> +		return -ENODEV;

Pass the geni_se_device from the child driver, to remove the need for
this.

> +
> +	dev_p = geni_se_dev->dev;

Name your variables more succinct and you don't need this local alias.

> +
> +	*iova = dma_map_single(dev_p, buf, size, dir);

Just inline dma_map_single() in the functions calling this.

> +	if (dma_mapping_error(dev_p, *iova))
> +		return -EIO;
> +	return 0;
> +}
> +EXPORT_SYMBOL(geni_se_map_buf);
> +
> +/**
> + * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
> + * @iova:		Pointer in which the mapped virtual address is stored.
> + * @size:		Size of the buffer.
> + * @dir:		Direction of the DMA transfer.
> + *
> + * This function is used to unmap an already mapped buffer from the
> + * QUP device space.
> + *
> + * Return:	0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
> +		      size_t size, enum dma_data_direction dir)

There's no reason for iova to be a pointer. Just pass the dma_addr_t as
is.

Should this function really be exposed to the clients?

> +{
> +	struct device *dev_p;
> +	struct geni_se_device *geni_se_dev;
> +
> +	if (!wrapper_dev || !iova || !size)
> +		return -EINVAL;

Reduce the error checking.

> +
> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
> +	if (!geni_se_dev || !geni_se_dev->dev)
> +		return -ENODEV;

And pass the geni_se_rsc to this function for symmetry purposes, which
would give you the wrapper by just following the pointer and then the
device from there.

> +
> +	dev_p = geni_se_dev->dev;
> +	dma_unmap_single(dev_p, *iova, size, dir);

Just inline dma_unmap_single() in the calling functions.

> +	return 0;
> +}
> +EXPORT_SYMBOL(geni_se_unmap_buf);
> +
> +static const struct of_device_id geni_se_dt_match[] = {
> +	{ .compatible = "qcom,geni-se-qup", },
> +	{}
> +};

Move this next to the geni_se_driver below and don't forget the
MODULE_DEVICE_TABLE()

> +
> +static int geni_se_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	struct geni_se_device *geni_se_dev;

Just name this "geni".

> +	int ret = 0;

No need to initialize ret, it's only ever used after assignment.

> +
> +	geni_se_dev = devm_kzalloc(dev, sizeof(*geni_se_dev), GFP_KERNEL);
> +	if (!geni_se_dev)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "%s: Mandatory resource info not found\n",
> +			__func__);
> +		devm_kfree(dev, geni_se_dev);
> +		return -EINVAL;
> +	}

It's idiomatic to not check for errors of platform_get_resource() as
devm_ioremap_resource() will fail gracefully if this happens.

> +
> +	geni_se_dev->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR_OR_NULL(geni_se_dev->base)) {

This should be IS_ERR() only.

> +		dev_err(dev, "%s: Error mapping the resource\n", __func__);
> +		devm_kfree(dev, geni_se_dev);
> +		return -EFAULT;
> +	}
> +
> +	geni_se_dev->m_ahb_clk = devm_clk_get(dev, "m-ahb");
> +	if (IS_ERR(geni_se_dev->m_ahb_clk)) {
> +		ret = PTR_ERR(geni_se_dev->m_ahb_clk);
> +		dev_err(dev, "Err getting M AHB clk %d\n", ret);
> +		devm_iounmap(dev, geni_se_dev->base);
> +		devm_kfree(dev, geni_se_dev);

The reason we use the devm_ versions of these is so that we don't have
to release our resources explicitly.

> +		return ret;
> +	}
> +
> +	geni_se_dev->s_ahb_clk = devm_clk_get(dev, "s-ahb");
> +	if (IS_ERR(geni_se_dev->s_ahb_clk)) {
> +		ret = PTR_ERR(geni_se_dev->s_ahb_clk);
> +		dev_err(dev, "Err getting S AHB clk %d\n", ret);
> +		devm_clk_put(dev, geni_se_dev->m_ahb_clk);
> +		devm_iounmap(dev, geni_se_dev->base);
> +		devm_kfree(dev, geni_se_dev);
> +		return ret;
> +	}

Use devm_clk_bulk_get().

> +
> +	geni_se_dev->dev = dev;
> +	mutex_init(&geni_se_dev->geni_dev_lock);
> +	dev_set_drvdata(dev, geni_se_dev);
> +	dev_dbg(dev, "GENI SE Driver probed\n");
> +	return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);

You're not depopulating these devices when the wrapper goes away. Use
devm_of_platform_populate() here instead to make that happen.

> +}
> +
> +static int geni_se_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct geni_se_device *geni_se_dev = dev_get_drvdata(dev);
> +
> +	devm_clk_put(dev, geni_se_dev->s_ahb_clk);
> +	devm_clk_put(dev, geni_se_dev->m_ahb_clk);
> +	devm_iounmap(dev, geni_se_dev->base);
> +	devm_kfree(dev, geni_se_dev);

Again, the reason to use devm_* is so that you don't have to free
things...so if this is what you have here you don't even need a remove
function.

> +	return 0;
> +}
> +
> +static struct platform_driver geni_se_driver = {
> +	.driver = {
> +		.name = "geni_se_qup",
> +		.of_match_table = geni_se_dt_match,
> +	},
> +	.probe = geni_se_probe,
> +	.remove = geni_se_remove,
> +};
> +
> +static int __init geni_se_driver_init(void)
> +{
> +	return platform_driver_register(&geni_se_driver);
> +}
> +arch_initcall(geni_se_driver_init);
> +
> +static void __exit geni_se_driver_exit(void)
> +{
> +	platform_driver_unregister(&geni_se_driver);
> +}
> +module_exit(geni_se_driver_exit);

Should be fine to just use module_platform_driver(), you need separate
support for earlycon regardless.

> +
> +MODULE_DESCRIPTION("GENI Serial Engine Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
> new file mode 100644
> index 0000000..5695da9
> --- /dev/null
> +++ b/include/linux/qcom-geni-se.h
> @@ -0,0 +1,807 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
[..]
> + */
> +

Use SPDX header as above.

> +#ifndef _LINUX_QCOM_GENI_SE
> +#define _LINUX_QCOM_GENI_SE
> +#include <linux/clk.h>
> +#include <linux/dma-direction.h>
> +#include <linux/io.h>
> +#include <linux/list.h>

I don't think there's a need for io.h or list.h here.

> +
> +/* Transfer mode supported by GENI Serial Engines */
> +enum geni_se_xfer_mode {
> +	INVALID,
> +	FIFO_MODE,
> +	SE_DMA,

These are quite bad names for things in a header file.

> +};
> +
> +/* Protocols supported by GENI Serial Engines */
> +enum geni_se_protocol_types {
> +	NONE,
> +	SPI,
> +	UART,
> +	I2C,
> +	I3C

Ditto

> +};
> +
> +/**
> + * struct geni_se_rsc - GENI Serial Engine Resource
> + * @wrapper_dev:	Pointer to the parent QUP Wrapper core.
> + * @se_clk:		Handle to the core serial engine clock.
> + * @geni_pinctrl:	Handle to the pinctrl configuration.
> + * @geni_gpio_active:	Handle to the default/active pinctrl state.
> + * @geni_gpi_sleep:	Handle to the sleep pinctrl state.
> + */
> +struct geni_se_rsc {

This looks like the common geni_port or geni_device I requested above.

> +	struct device *wrapper_dev;
> +	struct clk *se_clk;

The one and only clock can be named "clk".

> +	struct pinctrl *geni_pinctrl;
> +	struct pinctrl_state *geni_gpio_active;
> +	struct pinctrl_state *geni_gpio_sleep;

The associated struct device already has init, default, idle and sleep
pinctrl states associated through the device->pins struct. Typically
this means that if you provide a "default" and "sleep" state, the
default will be selected when the device probes.

In order to select between the states call
pinctrl_pm_select_{default,sleep,idle}_state(dev);

> +};
> +
> +#define PINCTRL_DEFAULT	"default"
> +#define PINCTRL_SLEEP	"sleep"
> +
> +/* Common SE registers */

The purpose of the helper functions in the wrapper driver is to hide
the common details from the individual function drivers, so move these
defines into the c-file as they shouldn't be needed in the individual
drivers.

> +#define GENI_INIT_CFG_REVISION		(0x0)

Drop the parenthesis.

[..]
> +#ifdef CONFIG_QCOM_GENI_SE
> +/**
> + * geni_read_reg_nolog() - Helper function to read from a GENI register
> + * @base:	Base address of the serial engine's register block.
> + * @offset:	Offset within the serial engine's register block.
> + *
> + * Return:	Return the contents of the register.
> + */

The kerneldoc goes in the implementation, not the header file. And you
already have a copy there, so remove it from here.

> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset);
[..]
> +#else

I see no point in providing dummy functions for these, just make the
individual drivers either depend or select the core helpers.

> +static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
> +{
> +	return 0;
> +}

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-01-13  1:05 ` [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
@ 2018-01-17  6:25   ` Bjorn Andersson
  2018-01-19 22:53     ` Rob Herring
  2018-02-26 21:24     ` Karthik Ramasubramanian
  0 siblings, 2 replies; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-17  6:25 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> Add device tree binding support for the QCOM GENI SE driver.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>

Better describe the entire GENI, with it's functions in the same
binding.

> ---
>  .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 34 ++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> 
> diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> new file mode 100644
> index 0000000..66f8a16
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> @@ -0,0 +1,34 @@
> +Qualcomm Technologies, Inc. GENI Serial Engine QUP Wrapper Controller
> +
> +Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) wrapper
> +is a programmable module for supporting a wide range of serial interfaces
> +like UART, SPI, I2C, I3C, etc. A single QUP module can provide upto 8 Serial
> +Interfaces, using its internal Serial Engines. The GENI Serial Engine QUP
> +Wrapper controller is modeled as a node with zero or more child nodes each
> +representing a serial engine.
> +
> +Required properties:
> +- compatible:		Must be "qcom,geni-se-qup".
> +- reg:			Must contain QUP register address and length.
> +- clock-names:		Must contain "m-ahb" and "s-ahb".
> +- clocks:		AHB clocks needed by the device.
> +
> +Required properties if child node exists:
> +- #address-cells: Must be 1
> +- #size-cells: Must be 1

But on a 64-bit system, shouldn't these be 2?

> +- ranges: Must be present
> +
> +Properties for children:
> +
> +A GENI based QUP wrapper controller node can contain 0 or more child nodes
> +representing serial devices.  These serial devices can be a QCOM UART, I2C
> +controller, spi controller, or some combination of aforementioned devices.
> +
> +Example:
> +	qup0: qcom,geniqup0@8c0000 {

I don't see a reason for this to be referenced, so skip the label. And
don't use qcom, in the node name; "geni" would be perfectly fine?

> +		compatible = "qcom,geni-se-qup";
> +		reg = <0x8c0000 0x6000>;
> +		clock-names = "m-ahb", "s-ahb";
> +		clocks = <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
> +			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
> +	}

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller
       [not found]   ` <1515805547-22816-5-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2018-01-17  6:31     ` Bjorn Andersson
  2018-02-26 21:28       ` Karthik Ramasubramanian
  0 siblings, 1 reply; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-17  6:31 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet-T1hC0tSOHrs, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	wsa-z923LK4zBo2bacvFa/9K2g,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> Add device tree binding support for I2C Controller in GENI based
> QUP Wrapper.
> 
> Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> ---
>  .../devicetree/bindings/i2c/i2c-qcom-geni.txt      | 35 ++++++++++++++++++++++
>  .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 19 ++++++++++++
>  2 files changed, 54 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
> 
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
> new file mode 100644
> index 0000000..ea84be7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
> @@ -0,0 +1,35 @@
> +Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
> +
> +Required properties:
> + - compatible: Should be:
> +   * "qcom,i2c-geni.

As this is a subset of geni it would look better with qcom,geni-i2c
imho.

> + - reg: Should contain QUP register address and length.
> + - interrupts: Should contain I2C interrupt.
> + - clock-names: Should contain "se-clk".

Omit "clk" from the clock names.

> + - clocks: Serial engine core clock needed by the device.
> + - pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
> +   should be "active" and "sleep" for the pin confuguration when core is active
> +   or when entering sleep state.

No need to describe pinctrl properties - and your description here
doesn't match the code.

> + - #address-cells: Should be <1> Address cells for i2c device address
> + - #size-cells: Should be <0> as i2c addresses have no size component
> +
> +Optional property:
> + - clock-frequency : Desired I2C bus clock frequency in Hz.
> +   When missing default to 400000Hz.
> +
> +Child nodes should conform to i2c bus binding.

..."as described in i2c.txt"

Regards,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller
  2018-01-13  1:05 ` [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller Karthikeyan Ramasubramanian
@ 2018-01-17  6:35   ` Bjorn Andersson
  2018-02-27 13:25     ` Karthik Ramasubramanian
  0 siblings, 1 reply; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-17  6:35 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> Add device tree binding support for GENI based UART Controller in the
> QUP Wrapper.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
>  .../devicetree/bindings/serial/qcom,geni-uart.txt  | 29 ++++++++++++++++++++++
>  .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 13 ++++++++++
>  2 files changed, 42 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
> 
> diff --git a/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
> new file mode 100644
> index 0000000..e7b9e24
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
> @@ -0,0 +1,29 @@
> +Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
> +
> +The Generic Interface (GENI) Serial Engine based UART controller supports
> +console use-cases and is supported only by GENI based Qualcomm Universal
> +Peripheral (QUP) cores.
> +
> +Required properties:
> +- compatible: should contain "qcom,geni-debug-uart".

Why is this uart a _debug_ uart? Is there a separate binding for the
geni-uart?

I like that your naming here matches my suggestion with qcom,geni-i2c.

> +- reg: Should contain UART register location and length.
> +- reg-names: Should contain "se-phys".

No need to specify reg-names for a single reg.

> +- interrupts: Should contain UART core interrupts.
> +- clock-names: Should contain "se-clk".

Omit the "clk"

> +- clocks: clocks needed for UART, includes the core clock.

Be more specific.

> +- pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
> +  Should be "active" and "sleep" for the pin confuguration when core is active
> +  or when entering sleep state.

Omit pinctrl information.

> +
> +Example:
> +uart0: qcom,serial@a88000 {

Don't use qcom, in node name. This should be named "serial".

> +	compatible = "qcom,geni-debug-uart";
> +	reg = <0xa88000 0x7000>;
> +	reg-names = "se-phys";
> +	interrupts = <0 355 0>;

<GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>

> +	clock-names = "se-clk";
> +	clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>;
> +	pinctrl-names = "default", "sleep";
> +	pinctrl-0 = <&qup_1_uart_3_active>;
> +	pinctrl-1 = <&qup_1_uart_3_sleep>;
> +};

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
       [not found]     ` <1515805547-22816-6-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2018-01-18  5:23       ` Bjorn Andersson
  2018-02-27 13:16         ` Karthik Ramasubramanian
  0 siblings, 1 reply; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-18  5:23 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet-T1hC0tSOHrs, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	wsa-z923LK4zBo2bacvFa/9K2g,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia, Girish Mahadevan

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> This bus driver supports the GENI based i2c hardware controller in the
> Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
> module supporting a wide range of serial interfaces including I2C. The
> driver supports FIFO mode and DMA mode of transfer and switches modes
> dynamically depending on the size of the transfer.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Girish Mahadevan <girishm-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> ---
>  drivers/i2c/busses/Kconfig         |  10 +
>  drivers/i2c/busses/Makefile        |   1 +
>  drivers/i2c/busses/i2c-qcom-geni.c | 656 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 667 insertions(+)
>  create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
> 
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 009345d..caef309 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -838,6 +838,16 @@ config I2C_PXA_SLAVE
>  	  is necessary for systems where the PXA may be a target on the
>  	  I2C bus.
>  
> +config I2C_QCOM_GENI
> +	tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
> +	depends on ARCH_QCOM

Just depend on the GENI wrapper as well.

> +	help
> +	  If you say yes to this option, support will be included for the
> +	  built-in I2C interface on the Qualcomm Technologies Inc.'s SoCs.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called i2c-qcom-geni.
> +
>  config I2C_QUP
>  	tristate "Qualcomm QUP based I2C controller"
>  	depends on ARCH_QCOM
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 2ce8576..201fce1 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_PNX)		+= i2c-pnx.o
>  obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
>  obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
>  obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
> +obj-$(CONFIG_I2C_QCOM_GENI)	+= i2c-qcom-geni.o
>  obj-$(CONFIG_I2C_QUP)		+= i2c-qup.o
>  obj-$(CONFIG_I2C_RIIC)		+= i2c-riic.o
>  obj-$(CONFIG_I2C_RK3X)		+= i2c-rk3x.o
> diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
> new file mode 100644
> index 0000000..59ad4da
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
> @@ -0,0 +1,656 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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.
> + *
> + */

Use SPDX license header.

> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>

Unused?

> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/qcom-geni-se.h>
> +
> +#define SE_I2C_TX_TRANS_LEN		(0x26C)

Drop the parenthesis, when not needed.

> +#define SE_I2C_RX_TRANS_LEN		(0x270)
> +#define SE_I2C_SCL_COUNTERS		(0x278)
> +#define SE_GENI_IOS			(0x908)
> +
> +#define SE_I2C_ERR  (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
> +			M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
> +#define SE_I2C_ABORT (1U << 1)
> +/* M_CMD OP codes for I2C */
> +#define I2C_WRITE		(0x1)
> +#define I2C_READ		(0x2)
> +#define I2C_WRITE_READ		(0x3)
> +#define I2C_ADDR_ONLY		(0x4)
> +#define I2C_BUS_CLEAR		(0x6)
> +#define I2C_STOP_ON_BUS		(0x7)
> +/* M_CMD params for I2C */
> +#define PRE_CMD_DELAY		(BIT(0))
> +#define TIMESTAMP_BEFORE	(BIT(1))
> +#define STOP_STRETCH		(BIT(2))
> +#define TIMESTAMP_AFTER		(BIT(3))
> +#define POST_COMMAND_DELAY	(BIT(4))
> +#define IGNORE_ADD_NACK		(BIT(6))
> +#define READ_FINISHED_WITH_ACK	(BIT(7))
> +#define BYPASS_ADDR_PHASE	(BIT(8))
> +#define SLV_ADDR_MSK		(GENMASK(15, 9))
> +#define SLV_ADDR_SHFT		(9)
> +
> +#define I2C_CORE2X_VOTE		(10000)
> +#define GP_IRQ0			0
> +#define GP_IRQ1			1
> +#define GP_IRQ2			2
> +#define GP_IRQ3			3
> +#define GP_IRQ4			4
> +#define GP_IRQ5			5
> +#define GENI_OVERRUN		6
> +#define GENI_ILLEGAL_CMD	7
> +#define GENI_ABORT_DONE		8
> +#define GENI_TIMEOUT		9
> +
> +#define I2C_NACK		GP_IRQ1
> +#define I2C_BUS_PROTO		GP_IRQ3
> +#define I2C_ARB_LOST		GP_IRQ4
> +#define DM_I2C_CB_ERR		((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
> +									<< 5)
> +
> +#define I2C_AUTO_SUSPEND_DELAY	250
> +#define KHz(freq)		(1000 * freq)
> +
> +struct geni_i2c_dev {
> +	struct device *dev;
> +	void __iomem *base;
> +	unsigned int tx_wm;
> +	int irq;
> +	int err;
> +	struct i2c_adapter adap;
> +	struct completion xfer;

How about naming this "done" or something like that, gi2c->xfer doesn't
really give the sense of being a "we're done with the operation"-event.

> +	struct i2c_msg *cur;
> +	struct geni_se_rsc i2c_rsc;
> +	int cur_wr;
> +	int cur_rd;
> +	struct device *wrapper_dev;

This is already availabe in i2c_rsc, and in particular if you pass the
i2c_rsc down to the wrapper in the 2 cases you use the wrapper_dev you
don't need this duplication.

> +	u32 clk_freq_out;
> +	int clk_fld_idx;

Keep track of the const struct geni_i2c_clk_fld * here instead.

> +};
> +
> +struct geni_i2c_err_log {
> +	int err;
> +	const char *msg;
> +};
> +
> +static struct geni_i2c_err_log gi2c_log[] = {
> +	[GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
> +	[I2C_NACK] = {-ENOTCONN,
> +			"NACK: slv unresponsive, check its power/reset-ln"},

Break the 80-char rule, to improve readability.

> +	[GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
> +	[I2C_BUS_PROTO] = {-EPROTO,
> +				"Bus proto err, noisy/unepxected start/stop"},
> +	[I2C_ARB_LOST] = {-EBUSY,
> +				"Bus arbitration lost, clock line undriveable"},
> +	[GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
> +	[GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
> +	[GENI_ILLEGAL_CMD] = {-EILSEQ,
> +				"Illegal cmd, check GENI cmd-state machine"},
> +	[GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
> +	[GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
> +};
> +
> +struct geni_i2c_clk_fld {
> +	u32	clk_freq_out;
> +	u8	clk_div;
> +	u8	t_high;
> +	u8	t_low;
> +	u8	t_cycle;
> +};
> +
> +static struct geni_i2c_clk_fld geni_i2c_clk_map[] = {

const

> +	{KHz(100), 7, 10, 11, 26},
> +	{KHz(400), 2,  5, 12, 24},
> +	{KHz(1000), 1, 3,  9, 18},
> +};
> +
> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
> +{
> +	int i;
> +	int ret = 0;
> +	bool clk_map_present = false;
> +	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
> +
> +	for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
> +		if (itr->clk_freq_out == gi2c->clk_freq_out) {
> +			clk_map_present = true;
> +			break;

Make this:
			gi2c->clk_fld = geni_i2c_clk_map + i;
			return 0;

> +		}
> +	}
> +

...then you can drop ret and clk_map_present and just return -EINVAL
here.

> +	if (clk_map_present)
> +		gi2c->clk_fld_idx = i;
> +	else
> +		ret = -EINVAL;
> +
> +	return ret;
> +}
> +
> +static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)

Drop the "inline", if it makes sense the compiler will inline it, if not
it knows better than we do.

dfs is always 0, so drop this parameter and hard code the value below.

> +{
> +	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
> +
> +	geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
> +
> +	geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
> +	geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
> +			itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);
> +
> +	/* Ensure Clk config completes before return */

That's not what "mb" does, it ensures that later memory operations
aren't reordered beyond the barrier.

> +	mb();
> +}
> +
> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)

Looking at the code in this function it's just a bunch of logic to print
various debug messages...except for the last line that uses the gi2c_log
lookup table to convert the error to a errno. Sneaky...and not very
nice.

> +{
> +	u32 m_cmd = readl_relaxed(gi2c->base + SE_GENI_M_CMD0);

Unless you have a really good reason, just use readl/writel in favour of
their _relaxed versions.

> +	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
> +	u32 geni_s = readl_relaxed(gi2c->base + SE_GENI_STATUS);
> +	u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
> +	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
> +	u32 rx_st, tx_st;
> +
> +	if (gi2c->cur)
> +		dev_dbg(gi2c->dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
> +			gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
> +
> +	if (err == I2C_NACK || err == GENI_ABORT_DONE) {
> +		dev_dbg(gi2c->dev, "%s\n", gi2c_log[err].msg);
> +		goto err_ret;
> +	} else {
> +		dev_err(gi2c->dev, "%s\n", gi2c_log[err].msg);
> +	}
> +
> +	if (dma) {
> +		rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
> +		tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
> +	} else {
> +		rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
> +		tx_st = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
> +	}
> +	dev_dbg(gi2c->dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
> +		dma, tx_st, rx_st, m_stat);
> +	dev_dbg(gi2c->dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
> +		m_cmd, geni_s, geni_ios);
> +err_ret:
> +	gi2c->err = gi2c_log[err].err;
> +}
> +
> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
> +{
> +	struct geni_i2c_dev *gi2c = dev;
> +	int i, j;
> +	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
> +	u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
> +	u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
> +	u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
> +	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
> +	struct i2c_msg *cur = gi2c->cur;
> +
> +	if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
> +		    (dm_rx_st & (DM_I2C_CB_ERR)) ||
> +		    (m_stat & M_CMD_ABORT_EN)) {
> +
> +		if (m_stat & M_GP_IRQ_1_EN)
> +			geni_i2c_err(gi2c, I2C_NACK);
> +		if (m_stat & M_GP_IRQ_3_EN)
> +			geni_i2c_err(gi2c, I2C_BUS_PROTO);
> +		if (m_stat & M_GP_IRQ_4_EN)
> +			geni_i2c_err(gi2c, I2C_ARB_LOST);
> +		if (m_stat & M_CMD_OVERRUN_EN)
> +			geni_i2c_err(gi2c, GENI_OVERRUN);
> +		if (m_stat & M_ILLEGAL_CMD_EN)
> +			geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
> +		if (m_stat & M_CMD_ABORT_EN)
> +			geni_i2c_err(gi2c, GENI_ABORT_DONE);
> +		if (m_stat & M_GP_IRQ_0_EN)
> +			geni_i2c_err(gi2c, GP_IRQ0);
> +
> +		if (!dma)
> +			writel_relaxed(0, (gi2c->base +
> +					   SE_GENI_TX_WATERMARK_REG));

Drop the extra parenthesis. And using writel() instead keeps you under
80 chars.

> +		goto irqret;
> +	}
> +
> +	if (dma) {
> +		dev_dbg(gi2c->dev, "i2c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st,
> +			dm_rx_st);
> +		goto irqret;
> +	}
> +
> +	if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
> +		(m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {

Some of these parenthesis are unnecessary, but more importantly the way
you wrap this line is misleading; the wrapping indicates that the two
latter conditionals are related, but the parenthesis says the first two
are.

The most significant part of this conditional is "is this a read
operation", so put this first.


> +		u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
> +
> +		for (j = 0; j < rxcnt; j++) {
> +			u32 temp;
> +			int p;
> +
> +			temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
> +			for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
> +				i++, p++)
> +				cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
> +			gi2c->cur_rd = i;

The two-range for loop with a line wrap isn't very clean and the wrap
calls for a set of braces - which will look ugly.

How about something like this instead?

			p = 0;
			while (p < 4 && gi2c->cur_rd < cur->len) {
				cur->buf[gi2c->cur_rd++] = temp & 0xff;
				temp >>= 8;
				p++;
			}

> +			if (gi2c->cur_rd == cur->len) {
> +				dev_dbg(gi2c->dev, "FIFO i:%d,read 0x%x\n",
> +					i, temp);

Who's the consumer of this debug line?

> +				break;
> +			}
> +		}
> +	} else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
> +					!(cur->flags & I2C_M_RD)) {
> +		for (j = 0; j < gi2c->tx_wm; j++) {
> +			u32 temp = 0;
> +			int p;
> +
> +			for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
> +				i++, p++)
> +				temp |= (((u32)(cur->buf[i]) << (p * 8)));
> +			writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);

Same concern as above.

> +			gi2c->cur_wr = i;
> +			dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp);
> +			if (gi2c->cur_wr == cur->len) {
> +				dev_dbg(gi2c->dev, "FIFO i2c bytes done writing\n");
> +				writel_relaxed(0,
> +				(gi2c->base + SE_GENI_TX_WATERMARK_REG));

The line break here is bad.  checkpatch.pl --strict will help you find
these.

Also using writel() and dropping the parenthesis keeps you below 80
chars.

> +				break;
> +			}
> +		}
> +	}
> +irqret:
> +	if (m_stat)
> +		writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);

Is it okay for this to be re-ordered wrt above writes?

> +
> +	if (dma) {
> +		if (dm_tx_st)
> +			writel_relaxed(dm_tx_st, gi2c->base +
> +				       SE_DMA_TX_IRQ_CLR);

Use writel() and you don't have to wrap the line.

> +		if (dm_rx_st)
> +			writel_relaxed(dm_rx_st, gi2c->base +
> +				       SE_DMA_RX_IRQ_CLR);
> +		/* Ensure all writes are done before returning from ISR. */

That's not what wmb does.

> +		wmb();
> +	}
> +	/* if this is err with done-bit not set, handle that thr' timeout. */

Is "thr'" should for "through"?

> +	if (m_stat & M_CMD_DONE_EN)
> +		complete(&gi2c->xfer);
> +	else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
> +		complete(&gi2c->xfer);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int geni_i2c_xfer(struct i2c_adapter *adap,
> +			 struct i2c_msg msgs[],
> +			 int num)
> +{
> +	struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
> +	int i, ret = 0, timeout = 0;

No need to initialize these, first use is an assignment.

> +
> +	gi2c->err = 0;
> +	gi2c->cur = &msgs[0];

You're assigning cur in each iteration of the loop below, why do you
need to do it here as well?

> +	reinit_completion(&gi2c->xfer);
> +	ret = pm_runtime_get_sync(gi2c->dev);
> +	if (ret < 0) {
> +		dev_err(gi2c->dev, "error turning SE resources:%d\n", ret);
> +		pm_runtime_put_noidle(gi2c->dev);
> +		/* Set device in suspended since resume failed */
> +		pm_runtime_set_suspended(gi2c->dev);
> +		return ret;

With the questionable assignment above you're leaving the function with
gi2c->cur pointing to an object that will soon be invalid.

> +	}
> +
> +	qcom_geni_i2c_conf(gi2c, 0);
> +	dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
> +				num, msgs[0].len, msgs[0].flags);

Use dynamic function tracing instead of debug prints for this.

> +	for (i = 0; i < num; i++) {

I suggest that you split out two functions here, one for rx-one-message
and one for tx-one-message. There are some duplication between the two,
but not too bad.

> +		int stretch = (i < (num - 1));
> +		u32 m_param = 0;
> +		u32 m_cmd = 0;
> +		dma_addr_t tx_dma = 0;
> +		dma_addr_t rx_dma = 0;
> +		enum geni_se_xfer_mode mode = FIFO_MODE;

No need to initialize mode, as the first use is an assignment.

> +
> +		m_param |= (stretch ? STOP_STRETCH : 0);
> +		m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);

Drop some parenthesis.

> +
> +		gi2c->cur = &msgs[i];
> +		mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE;
> +		ret = geni_se_select_mode(gi2c->base, mode);
> +		if (ret) {
> +			dev_err(gi2c->dev, "%s: Error mode init %d:%d:%d\n",
> +				__func__, mode, i, msgs[i].len);
> +			break;
> +		}
> +		if (msgs[i].flags & I2C_M_RD) {
> +			dev_dbg(gi2c->dev,
> +				"READ,n:%d,i:%d len:%d, stretch:%d\n",
> +					num, i, msgs[i].len, stretch);
> +			geni_write_reg(msgs[i].len,
> +				       gi2c->base, SE_I2C_RX_TRANS_LEN);
> +			m_cmd = I2C_READ;
> +			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);

Drop m_cmd and just write I2C_READ in the function call.

> +			if (mode == SE_DMA) {
> +				ret = geni_se_rx_dma_prep(gi2c->wrapper_dev,
> +							gi2c->base, msgs[i].buf,
> +							msgs[i].len, &rx_dma);
> +				if (ret) {
> +					mode = FIFO_MODE;
> +					ret = geni_se_select_mode(gi2c->base,
> +								  mode);
> +				}
> +			}
> +		} else {
> +			dev_dbg(gi2c->dev,
> +				"WRITE:n:%d,i:%d len:%d, stretch:%d, m_param:0x%x\n",
> +					num, i, msgs[i].len, stretch, m_param);
> +			geni_write_reg(msgs[i].len, gi2c->base,
> +						SE_I2C_TX_TRANS_LEN);
> +			m_cmd = I2C_WRITE;
> +			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
> +			if (mode == SE_DMA) {
> +				ret = geni_se_tx_dma_prep(gi2c->wrapper_dev,
> +							gi2c->base, msgs[i].buf,
> +							msgs[i].len, &tx_dma);
> +				if (ret) {
> +					mode = FIFO_MODE;
> +					ret = geni_se_select_mode(gi2c->base,
> +								  mode);
> +				}
> +			}
> +			if (mode == FIFO_MODE) /* Get FIFO IRQ */
> +				geni_write_reg(1, gi2c->base,
> +						SE_GENI_TX_WATERMARK_REG);
> +		}
> +		/* Ensure FIFO write go through before waiting for Done evet */

That's not what mb does, drop it.

> +		mb();
> +		timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);

As written you should just use "ret", but the return value of
wait_for_completion_timeout() is unsigned long, so change the type of
timeout instead.

> +		if (!timeout) {
> +			geni_i2c_err(gi2c, GENI_TIMEOUT);
> +			gi2c->cur = NULL;
> +			geni_se_abort_m_cmd(gi2c->base);
> +			timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);

What if it takes a fraction above HZ time to complete the previous
operation, then you might end up here with the completion completed, but
from the wrong operation.

> +		}
> +		gi2c->cur_wr = 0;
> +		gi2c->cur_rd = 0;
> +		if (mode == SE_DMA) {
> +			if (gi2c->err) {
> +				if (msgs[i].flags != I2C_M_RD)
> +					writel_relaxed(1, gi2c->base +
> +							SE_DMA_TX_FSM_RST);
> +				else
> +					writel_relaxed(1, gi2c->base +
> +							SE_DMA_RX_FSM_RST);
> +				wait_for_completion_timeout(&gi2c->xfer, HZ);
> +			}
> +			geni_se_rx_dma_unprep(gi2c->wrapper_dev, rx_dma,
> +					      msgs[i].len);

Reduce the length of gi2c->wrapper_dev here by using a local variable,
so that you get below (or close to) 80 chars.

Also, in either rx or tx cases above I see only that you prep one of
thse, and then you're unprepping both.

> +			geni_se_tx_dma_unprep(gi2c->wrapper_dev, tx_dma,
> +					      msgs[i].len);
> +		}
> +		ret = gi2c->err;
> +		if (gi2c->err) {
> +			dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err);
> +			break;
> +		}
> +	}
> +	if (ret == 0)
> +		ret = num;
> +
> +	pm_runtime_mark_last_busy(gi2c->dev);
> +	pm_runtime_put_autosuspend(gi2c->dev);
> +	gi2c->cur = NULL;
> +	gi2c->err = 0;
> +	dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
> +	return ret;
> +}
[..]
> +static int geni_i2c_probe(struct platform_device *pdev)
> +{
> +	struct geni_i2c_dev *gi2c;
> +	struct resource *res;
> +	int ret;
> +
> +	gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
> +	if (!gi2c)
> +		return -ENOMEM;
> +
> +	gi2c->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -EINVAL;

Put this right before devm_ioremap_resource() and drop the error check.

> +
> +	gi2c->wrapper_dev = pdev->dev.parent;
> +	gi2c->i2c_rsc.wrapper_dev = pdev->dev.parent;

As suggested in the other patch, if you have an helper function in the
wrapper driver that initializes the i2c_rsc then this could fill out the
actual wrapper, instead of just the device.

> +	gi2c->i2c_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
> +	if (IS_ERR(gi2c->i2c_rsc.se_clk)) {
> +		ret = PTR_ERR(gi2c->i2c_rsc.se_clk);
> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +		return ret;
> +	}
> +
> +	gi2c->base = devm_ioremap_resource(gi2c->dev, res);
> +	if (IS_ERR(gi2c->base))
> +		return PTR_ERR(gi2c->base);
> +
> +	gi2c->i2c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);

Drop all the pinctrl stuff. You might still want to call
pinctrl_pm_select_{idle,default,sleep}_state(), in the various stages of
PM state though.

> +	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_pinctrl)) {
> +		dev_err(&pdev->dev, "No pinctrl config specified\n");
> +		ret = PTR_ERR(gi2c->i2c_rsc.geni_pinctrl);
> +		return ret;
> +	}
[..]
> +static int geni_i2c_runtime_resume(struct device *dev)
> +{
> +	int ret;
> +	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
> +
> +	ret = geni_se_resources_on(&gi2c->i2c_rsc);
> +	if (ret)
> +		return ret;
> +
> +	if (!unlikely(gi2c->tx_wm)) {

Drop the unlikely()

> +		int proto = geni_se_get_proto(gi2c->base);
> +		int gi2c_tx_depth = geni_se_get_tx_fifo_depth(gi2c->base);
> +
> +		if (unlikely(proto != I2C)) {

Has this changes since probe? Can't we verify that the proto is correct
during probe and then just trust that the hardware doesn't change
function?

> +			dev_err(gi2c->dev, "Invalid proto %d\n", proto);
> +			geni_se_resources_off(&gi2c->i2c_rsc);
> +			return -ENXIO;
> +		}
> +
> +		gi2c->tx_wm = gi2c_tx_depth - 1;
> +		geni_se_init(gi2c->base, gi2c->tx_wm, gi2c_tx_depth);
> +		geni_se_config_packing(gi2c->base, 8, 4, true);
> +		dev_dbg(gi2c->dev, "i2c fifo/se-dma mode. fifo depth:%d\n",
> +			gi2c_tx_depth);
> +	}
> +	enable_irq(gi2c->irq);
> +
> +	return 0;
> +}
[..]
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:i2c_geni");

What will reference the kernel module by this alias?

> -- 
> Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-17  6:20     ` Bjorn Andersson
@ 2018-01-18  9:13       ` Rajendra Nayak
  2018-01-18 16:57         ` Bjorn Andersson
  2018-01-31 18:58       ` Karthik Ramasubramanian
  1 sibling, 1 reply; 42+ messages in thread
From: Rajendra Nayak @ 2018-01-18  9:13 UTC (permalink / raw)
  To: Bjorn Andersson, Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan

[]..

>> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
>> new file mode 100644
>> index 0000000..3f43582
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom-geni-se.c
>> @@ -0,0 +1,1016 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only 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.
>> + *
>> + */
> 
> Please use SPDX style license header, i.e. this file should start with:
> 
> // SPDX-License-Identifier: GPL-2.0
> /*
>  * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>  */

Looks like Mark Brown commented elsewhere [1] that we should use the C++
commenting style even for the Linux Foundation copyright?

[1] https://marc.info/?l=linux-clk&m=151497978626135&w=2 

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-18  9:13       ` Rajendra Nayak
@ 2018-01-18 16:57         ` Bjorn Andersson
  2018-01-19 22:57           ` Rob Herring
  0 siblings, 1 reply; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-18 16:57 UTC (permalink / raw)
  To: Rajendra Nayak
  Cc: Karthikeyan Ramasubramanian, corbet, andy.gross, david.brown,
	robh+dt, mark.rutland, wsa, gregkh, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, Sagar Dharia,
	Girish Mahadevan

On Thu 18 Jan 01:13 PST 2018, Rajendra Nayak wrote:

> []..
> 
> >> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> >> new file mode 100644
> >> index 0000000..3f43582
> >> --- /dev/null
> >> +++ b/drivers/soc/qcom/qcom-geni-se.c
> >> @@ -0,0 +1,1016 @@
> >> +/*
> >> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 and
> >> + * only 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.
> >> + *
> >> + */
> > 
> > Please use SPDX style license header, i.e. this file should start with:
> > 
> > // SPDX-License-Identifier: GPL-2.0
> > /*
> >  * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> >  */
> 
> Looks like Mark Brown commented elsewhere [1] that we should use the C++
> commenting style even for the Linux Foundation copyright?
> 
> [1] https://marc.info/?l=linux-clk&m=151497978626135&w=2 
> 

While I can agree with Mark on the ugliness of the mixed commenting
style, this is the style that I found communicated and is what you find
in other files.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
@ 2018-01-18 19:43   ` Bjorn Andersson
  2018-01-19  7:12   ` Bjorn Andersson
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-18 19:43 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan, Sagar Dharia

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> This driver supports GENI based UART Controller in the Qualcomm SOCs. The
> Qualcomm Generic Interface (GENI) is a programmable module supporting a
> wide range of serial interfaces including UART. This driver support console
> operations using FIFO mode of transfer.
> 
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> ---
>  drivers/tty/serial/Kconfig            |   10 +
>  drivers/tty/serial/Makefile           |    1 +
>  drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
>  3 files changed, 1425 insertions(+)
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index b788fee..1be30e5 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
>  	select SERIAL_CORE_CONSOLE
>  	select SERIAL_EARLYCON
>  
> +config SERIAL_QCOM_GENI
> +	tristate "QCOM on-chip GENI based serial port support"
> +	depends on ARCH_QCOM

depend on the GENI wrapper as well.

> +	select SERIAL_CORE
> +	select SERIAL_CORE_CONSOLE
> +	select SERIAL_EARLYCON
> +	help
> +	  Serial driver for Qualcomm Technologies Inc's GENI based QUP
> +	  hardware.
> +
>  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 842d185..64a8d82 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
>  obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
>  obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
>  obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
> +obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
>  obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
>  obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
>  obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> new file mode 100644
> index 0000000..0dbd329
> --- /dev/null
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -0,0 +1,1414 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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.
> + */

SPDX license header.

> +
> +#include <linux/bitmap.h>

Unused

> +#include <linux/bitops.h>

Unused?

> +#include <linux/delay.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/qcom-geni-se.h>
> +#include <linux/serial.h>
> +#include <linux/serial_core.h>
> +#include <linux/slab.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +
> +/* UART specific GENI registers */
> +#define SE_UART_TX_TRANS_CFG		(0x25C)

Drop the parenthesis

> +#define SE_UART_TX_WORD_LEN		(0x268)
> +#define SE_UART_TX_STOP_BIT_LEN		(0x26C)
> +#define SE_UART_TX_TRANS_LEN		(0x270)
> +#define SE_UART_RX_TRANS_CFG		(0x280)
> +#define SE_UART_RX_WORD_LEN		(0x28C)
> +#define SE_UART_RX_STALE_CNT		(0x294)
> +#define SE_UART_TX_PARITY_CFG		(0x2A4)
> +#define SE_UART_RX_PARITY_CFG		(0x2A8)
> +
> +/* SE_UART_TRANS_CFG */
> +#define UART_TX_PAR_EN		(BIT(0))
> +#define UART_CTS_MASK		(BIT(1))
> +
> +/* SE_UART_TX_WORD_LEN */
> +#define TX_WORD_LEN_MSK		(GENMASK(9, 0))
> +
> +/* SE_UART_TX_STOP_BIT_LEN */
> +#define TX_STOP_BIT_LEN_MSK	(GENMASK(23, 0))
> +#define TX_STOP_BIT_LEN_1	(0)
> +#define TX_STOP_BIT_LEN_1_5	(1)
> +#define TX_STOP_BIT_LEN_2	(2)
> +
> +/* SE_UART_TX_TRANS_LEN */
> +#define TX_TRANS_LEN_MSK	(GENMASK(23, 0))
> +
> +/* SE_UART_RX_TRANS_CFG */
> +#define UART_RX_INS_STATUS_BIT	(BIT(2))
> +#define UART_RX_PAR_EN		(BIT(3))
> +
> +/* SE_UART_RX_WORD_LEN */
> +#define RX_WORD_LEN_MASK	(GENMASK(9, 0))
> +
> +/* SE_UART_RX_STALE_CNT */
> +#define RX_STALE_CNT		(GENMASK(23, 0))
> +
> +/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
> +#define PAR_CALC_EN		(BIT(0))
> +#define PAR_MODE_MSK		(GENMASK(2, 1))
> +#define PAR_MODE_SHFT		(1)
> +#define PAR_EVEN		(0x00)
> +#define PAR_ODD			(0x01)
> +#define PAR_SPACE		(0x10)
> +#define PAR_MARK		(0x11)
> +
> +/* UART M_CMD OP codes */
> +#define UART_START_TX		(0x1)
> +#define UART_START_BREAK	(0x4)
> +#define UART_STOP_BREAK		(0x5)
> +/* UART S_CMD OP codes */
> +#define UART_START_READ		(0x1)
> +#define UART_PARAM		(0x1)
> +
> +#define UART_OVERSAMPLING	(32)
> +#define STALE_TIMEOUT		(16)
> +#define DEFAULT_BITS_PER_CHAR	(10)
> +#define GENI_UART_NR_PORTS	(15)
> +#define GENI_UART_CONS_PORTS	(1)
> +#define DEF_FIFO_DEPTH_WORDS	(16)
> +#define DEF_TX_WM		(2)
> +#define DEF_FIFO_WIDTH_BITS	(32)
> +#define UART_CORE2X_VOTE	(10000)
> +#define UART_CONSOLE_RX_WM	(2)
> +
> +static int owr;
> +module_param(owr, int, 0644);

What's this?

> +
> +struct qcom_geni_serial_port {
> +	struct uart_port uport;
> +	char name[20];
> +	unsigned int tx_fifo_depth;

size_t is a good type for keeping track of sizes.

> +	unsigned int tx_fifo_width;
> +	unsigned int rx_fifo_depth;
> +	unsigned int tx_wm;
> +	unsigned int rx_wm;
> +	unsigned int rx_rfr;

size_t for sizes.

> +	int xfer_mode;
> +	bool port_setup;
> +	unsigned int *rx_fifo;

The fifo is typeless, so it should be void *. It is however only ever
used as a local variable in handle_rx_console() so drop this.

> +	int (*handle_rx)(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx);
> +	struct device *wrapper_dev;
> +	struct geni_se_rsc serial_rsc;
> +	unsigned int xmit_size;

size_t for sizes.

> +	void *rx_buf;
> +	unsigned int cur_baud;
> +};
> +
> +static const struct uart_ops qcom_geni_serial_pops;
> +static struct uart_driver qcom_geni_console_driver;
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx);
> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
> +				int offset, int bit_field, bool set);
> +static void qcom_geni_serial_stop_rx(struct uart_port *uport);
> +
> +static atomic_t uart_line_id = ATOMIC_INIT(0);
> +
> +#define GET_DEV_PORT(uport) \
> +	container_of(uport, struct qcom_geni_serial_port, uport)

The idiomatic name for this macro would be something like "to_dev_port".

The use of "uport" as macro parameter makes this only work if the
parameter is "uport", otherwise the macro will replace the last part of
the container_of as well.

> +
> +static struct qcom_geni_serial_port qcom_geni_console_port;
> +
> +static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
> +{
> +	if (cfg_flags & UART_CONFIG_TYPE)
> +		uport->type = PORT_MSM;
> +}
> +
> +static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
> +{
> +	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
> +}
> +
> +static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
> +							unsigned int mctrl)
> +{
> +}
> +
> +static const char *qcom_geni_serial_get_type(struct uart_port *uport)
> +{
> +	return "MSM";
> +}
> +
> +static struct qcom_geni_serial_port *get_port_from_line(int line)
> +{
> +	struct qcom_geni_serial_port *port = NULL;
> +
> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> +		port = ERR_PTR(-ENXIO);

You need to return here as well...

> +	port = &qcom_geni_console_port;
> +	return port;

Drop the "port" and just return ERR_PTR(-ENXIO) in the error case and
return &qcom_geni_serial_port otherwise.

> +}
> +
> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
> +				int offset, int bit_field, bool set)

This function is returning "yes" or "no", so make it return "bool"

> +{
> +	int iter = 0;
> +	unsigned int reg;
> +	bool met = false;
> +	struct qcom_geni_serial_port *port = NULL;
> +	bool cond = false;
> +	unsigned int baud = 115200;
> +	unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS;
> +	unsigned long total_iter = 2000;

Don't initialize things that will be assigned directly again.

> +
> +
> +	if (uport->private_data) {
> +		port = GET_DEV_PORT(uport);
> +		baud = (port->cur_baud ? port->cur_baud : 115200);
> +		fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
> +		/*
> +		 * Total polling iterations based on FIFO worth of bytes to be
> +		 * sent at current baud .Add a little fluff to the wait.
> +		 */
> +		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
> +		total_iter += 50;
> +	}
> +
> +	while (iter < total_iter) {
> +		reg = geni_read_reg_nolog(uport->membase, offset);
> +		cond = reg & bit_field;
> +		if (cond == set) {
> +			met = true;
> +			break;
> +		}
> +		udelay(10);
> +		iter++;
> +	}

Use readl_poll_timeout() instead of all this.

> +	return met;
> +}
> +
> +static void qcom_geni_serial_setup_tx(struct uart_port *uport,
> +				unsigned int xmit_size)
> +{
> +	u32 m_cmd = 0;

Don't initialize this.

> +
> +	geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
> +	m_cmd |= (UART_START_TX << M_OPCODE_SHFT);

Why OR this into 0?

> +	geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
> +	/*
> +	 * Writes to enable the primary sequencer should go through before
> +	 * exiting this function.
> +	 */
> +	mb();
> +}
> +
> +static void qcom_geni_serial_poll_cancel_tx(struct uart_port *uport)

This function polls for command done and if that doesn't happen in time
it aborts the command. It then clear any interrupts. The function name
however indicates that we're polling for "cancel of tx".

> +{
> +	int done = 0;
> +	unsigned int irq_clear = M_CMD_DONE_EN;
> +	unsigned int geni_status = 0;

Don't initialize done and geni_status.

> +
> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_DONE_EN, true);
> +	if (!done) {
> +		geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
> +					SE_GENI_M_CMD_CTRL_REG);
> +		owr++;

What's owr?

> +		irq_clear |= M_CMD_ABORT_EN;
> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +							M_CMD_ABORT_EN, true);
> +	}
> +	geni_status = geni_read_reg_nolog(uport->membase,
> +						  SE_GENI_STATUS);
> +	if (geni_status & M_GENI_CMD_ACTIVE)
> +		owr++;
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
> +{
> +	unsigned int irq_clear = S_CMD_DONE_EN;
> +
> +	geni_se_abort_s_cmd(uport->membase);
> +	/* Ensure this goes through before polling. */
> +	mb();
> +	irq_clear |= S_CMD_ABORT_EN;
> +	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +					S_GENI_CMD_ABORT, false);
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
> +}
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +static int qcom_geni_serial_get_char(struct uart_port *uport)
> +{
> +	unsigned int rx_fifo;
> +	unsigned int m_irq_status;
> +	unsigned int s_irq_status;

These are u32 and the variable names would benefit from being shorter.

> +
> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +			M_SEC_IRQ_EN, true)))

Drop one parenthesis.

> +		return -ENXIO;
> +
> +	m_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_M_IRQ_STATUS);

Drop the _nolog and rename the variable to reduce the length of this
line.

> +	s_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_S_IRQ_STATUS);
> +	geni_write_reg_nolog(m_irq_status, uport->membase,
> +						SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg_nolog(s_irq_status, uport->membase,
> +						SE_GENI_S_IRQ_CLEAR);

Is it necessary to do this interlaced? Or can you just clear M and then
clear S? I.e. have a single variable named "status".

> +
> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
> +			RX_FIFO_WC_MSK, true)))
> +		return -ENXIO;
> +
> +	/*
> +	 * Read the Rx FIFO only after clearing the interrupt registers and
> +	 * getting valid RX fifo status.
> +	 */
> +	mb();
> +	rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
> +	rx_fifo &= 0xFF;

return rx_fifo & 0xff; instead of the extra step

> +	return rx_fifo;
> +}
> +
> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
> +					unsigned char c)
> +{
> +	int b = (int) c;

Why create this alias?

> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_write_reg_nolog(port->tx_wm, uport->membase,
> +					SE_GENI_TX_WATERMARK_REG);
> +	qcom_geni_serial_setup_tx(uport, 1);
> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +				M_TX_FIFO_WATERMARK_EN, true))
> +		WARN_ON(1);

	WARN_ON(!qcom_geni_serial_poll_bit(...));

> +	geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
> +	geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +	/*
> +	 * Ensure FIFO write goes through before polling for status but.
> +	 */

/* This fits in a one-line comment */

But if this is necessary that means that every time you call
serial_poll() you need to have this comment and a rmb(). Better move it
into the poll function then - or just stop using _relaxed()

> +	mb();
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +}
> +#endif
> +
> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
> +static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
> +{
> +	geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn);
> +	/*
> +	 * Ensure FIFO write clear goes through before
> +	 * next iteration.

This comment is accurate, but unnecessarily line wrapped.

> +	 */
> +	mb();
> +
> +}
> +
> +static void
> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
> +				unsigned int count)
> +{
> +	int new_line = 0;
> +	int i;
> +	int bytes_to_send = count;
> +	int fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	int tx_wm = DEF_TX_WM;
> +
> +	for (i = 0; i < count; i++) {
> +		if (s[i] == '\n')
> +			new_line++;
> +	}

Why do we need to deal with this?

> +
> +	bytes_to_send += new_line;
> +	geni_write_reg_nolog(tx_wm, uport->membase,
> +					SE_GENI_TX_WATERMARK_REG);
> +	qcom_geni_serial_setup_tx(uport, bytes_to_send);
> +	i = 0;
> +	while (i < count) {
> +		u32 chars_to_write = 0;
> +		u32 avail_fifo_bytes = (fifo_depth - tx_wm);

Use size_t for these.

> +
> +		/*
> +		 * If the WM bit never set, then the Tx state machine is not
> +		 * in a valid state, so break, cancel/abort any existing
> +		 * command. Unfortunately the current data being written is
> +		 * lost.
> +		 */
> +		while (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_TX_FIFO_WATERMARK_EN, true))
> +			break;
> +		chars_to_write = min((unsigned int)(count - i),
> +							avail_fifo_bytes);
> +		if ((chars_to_write << 1) > avail_fifo_bytes)

Write chars_to_write * 2, the compiler will be cleaver for you.

> +			chars_to_write = (avail_fifo_bytes >> 1);
> +		uart_console_write(uport, (s + i), chars_to_write,
> +						qcom_geni_serial_wr_char);
> +		geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +		/* Ensure this goes through before polling for WM IRQ again.*/
> +		mb();

Again, if this is necessary move it into the poll function instead of
sprinkling it throughout the driver.

> +		i += chars_to_write;
> +	}
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +}
> +
> +static void qcom_geni_serial_console_write(struct console *co, const char *s,
> +			      unsigned int count)
> +{
> +	struct uart_port *uport;
> +	struct qcom_geni_serial_port *port;
> +	int locked = 1;

Use bool for booleans

> +	unsigned long flags;
> +
> +	WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
> +
> +	port = get_port_from_line(co->index);
> +	if (IS_ERR_OR_NULL(port))

port can't be NULL.

> +		return;
> +
> +	uport = &port->uport;
> +	if (oops_in_progress)
> +		locked = spin_trylock_irqsave(&uport->lock, flags);
> +	else
> +		spin_lock_irqsave(&uport->lock, flags);
> +
> +	if (locked) {
> +		__qcom_geni_serial_console_write(uport, s, count);
> +		spin_unlock_irqrestore(&uport->lock, flags);
> +	}
> +}
> +
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx)
> +{
> +	int i, c;
> +	unsigned char *rx_char;
> +	struct tty_port *tport;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	tport = &uport->state->port;
> +	for (i = 0; i < rx_fifo_wc; i++) {
> +		int bytes = 4;
> +
> +		*(qcom_port->rx_fifo) =
> +			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);

Just store this value in a local variable, as the only consumer is 3
lines down.

> +		if (drop_rx)
> +			continue;
> +		rx_char = (unsigned char *)qcom_port->rx_fifo;
> +
> +		if (i == (rx_fifo_wc - 1)) {
> +			if (rx_last && rx_last_byte_valid)
> +				bytes = rx_last_byte_valid;
> +		}
> +		for (c = 0; c < bytes; c++) {
> +			char flag = TTY_NORMAL;

No need for a local variable here.

> +			int sysrq;
> +
> +			uport->icount.rx++;
> +			sysrq = uart_handle_sysrq_char(uport, rx_char[c]);
> +			if (!sysrq)
> +				tty_insert_flip_char(tport, rx_char[c], flag);
> +		}
> +	}
> +	if (!drop_rx)
> +		tty_flip_buffer_push(tport);
> +	return 0;
> +}
> +#else
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx)
> +{
> +	return -EPERM;
> +}
> +
> +#endif /* (CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)) */
> +
> +static void qcom_geni_serial_start_tx(struct uart_port *uport)
> +{
> +	unsigned int geni_m_irq_en;

This is a u32, name it "irq_en"

> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	unsigned int geni_status;

geni_status should be of type u32 and there's no lack of descriptiveness
naming it "status".

> +
> +	if (qcom_port->xfer_mode == FIFO_MODE) {
> +		geni_status = geni_read_reg_nolog(uport->membase,
> +						  SE_GENI_STATUS);
> +		if (geni_status & M_GENI_CMD_ACTIVE)
> +			goto exit_start_tx;

Just return

> +
> +		if (!qcom_geni_serial_tx_empty(uport))
> +			goto exit_start_tx;

Ditto

> +
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +						    SE_GENI_M_IRQ_EN);
> +		geni_m_irq_en |= (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN);
> +
> +		geni_write_reg_nolog(qcom_port->tx_wm, uport->membase,
> +						SE_GENI_TX_WATERMARK_REG);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +		/* Geni command setup should complete before returning.*/

If that's the case then verify that it's actually done.

> +		mb();
> +	}
> +exit_start_tx:
> +	return;
> +}
> +
> +static void stop_tx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;

These are u32 and can be given more succinct names.

> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);

Drop the _nolog from all of these to make code easier to read.

> +	geni_m_irq_en &= ~M_CMD_DONE_EN;
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN;
> +		geni_write_reg_nolog(0, uport->membase,
> +				     SE_GENI_TX_WATERMARK_REG);
> +	}
> +	port->xmit_size = 0;
> +	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
> +	geni_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_STATUS);
> +	/* Possible stop tx is called multiple times. */
> +	if (!(geni_status & M_GENI_CMD_ACTIVE))
> +		return;
> +
> +	geni_se_cancel_m_cmd(uport->membase);
> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_CANCEL_EN, true)) {
> +		geni_se_abort_m_cmd(uport->membase);
> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_ABORT_EN, true);
> +		geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +	}
> +	geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
> +{
> +	stop_tx_sequencer(uport);
> +}
> +
> +static void start_rx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_s_irq_en;
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	if (geni_status & S_GENI_CMD_ACTIVE)
> +		qcom_geni_serial_stop_rx(uport);
> +
> +	geni_se_setup_s_cmd(uport->membase, UART_START_READ, 0);
> +
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +
> +		geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
> +		geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
> +
> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +	}
> +	/*
> +	 * Ensure the writes to the secondary sequencer and interrupt enables
> +	 * go through.

This is not what mb() does.

> +	 */
> +	mb();
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +}
> +
> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
> +{
> +	start_rx_sequencer(uport);
> +}
> +
> +static void stop_rx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_s_irq_en;
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +	u32 irq_clear = S_CMD_DONE_EN;
> +	bool done;
> +
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +		geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
> +		geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
> +
> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +	}
> +
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	/* Possible stop rx is called multiple times. */
> +	if (!(geni_status & S_GENI_CMD_ACTIVE))
> +		return;
> +	geni_se_cancel_s_cmd(uport->membase);
> +	/*
> +	 * Ensure that the cancel goes through before polling for the
> +	 * cancel control bit.
> +	 */
> +	mb();
> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +					S_GENI_CMD_CANCEL, false);
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +	if ((geni_status & S_GENI_CMD_ACTIVE))
> +		qcom_geni_serial_abort_rx(uport);
> +}
> +
> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
> +{
> +	stop_rx_sequencer(uport);

Just inline the function here.

> +}
> +
> +static int qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
> +{
> +	int ret = 0;
> +	unsigned int rx_fifo_status;
> +	unsigned int rx_fifo_wc = 0;
> +	unsigned int rx_last_byte_valid = 0;
> +	unsigned int rx_last = 0;

The context of this function gives that we're dealing with rx and
rx_fifo, so no need to specify that in each local variable.

Also, they should all be u32 and there's no need to initialize them.

> +	struct tty_port *tport;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	tport = &uport->state->port;
> +	rx_fifo_status = geni_read_reg_nolog(uport->membase,
> +				SE_GENI_RX_FIFO_STATUS);
> +	rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK;
> +	rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >>
> +						RX_LAST_BYTE_VALID_SHFT);
> +	rx_last = rx_fifo_status & RX_LAST;
> +	if (rx_fifo_wc)
> +		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
> +							rx_last, drop_rx);

You're ignoring the return value of handle_rx.

> +	return ret;

You don't care about the return value in the caller and you always
return 0 anyways, so make the function void.

> +}
> +
> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	struct circ_buf *xmit = &uport->state->xmit;
> +	unsigned int avail_fifo_bytes = 0;

Naming avail_fifo_bytes just "avail" is carrying the same information
but is shorter.

> +	unsigned int bytes_remaining = 0;
> +	int i = 0;
> +	unsigned int tx_fifo_status;
> +	unsigned int xmit_size;
> +	unsigned int fifo_width_bytes = 1;

If this really is constantly 1 you can remove the variable and remove a
bit of the complexity in this function.

> +	int temp_tail = 0;

Use size_t for sizes.

> +
> +	xmit_size = uart_circ_chars_pending(xmit);
> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
> +					SE_GENI_TX_FIFO_STATUS);
> +	/* Both FIFO and framework buffer are drained */
> +	if ((xmit_size == qcom_port->xmit_size) && !tx_fifo_status) {

Drop the parenthesis.

> +		qcom_port->xmit_size = 0;
> +		uart_circ_clear(xmit);
> +		qcom_geni_serial_stop_tx(uport);
> +		goto exit_handle_tx;
> +	}
> +	xmit_size -= qcom_port->xmit_size;
> +
> +	avail_fifo_bytes = (qcom_port->tx_fifo_depth - qcom_port->tx_wm) *
> +							fifo_width_bytes;
> +	temp_tail = (xmit->tail + qcom_port->xmit_size) & (UART_XMIT_SIZE - 1);
> +	if (xmit_size > (UART_XMIT_SIZE - temp_tail))
> +		xmit_size = (UART_XMIT_SIZE - temp_tail);
> +	if (xmit_size > avail_fifo_bytes)
> +		xmit_size = avail_fifo_bytes;



> +
> +	if (!xmit_size)
> +		goto exit_handle_tx;
> +
> +	qcom_geni_serial_setup_tx(uport, xmit_size);
> +
> +	bytes_remaining = xmit_size;
> +	while (i < xmit_size) {
> +		unsigned int tx_bytes;
> +		unsigned int buf = 0;
> +		int c;
> +
> +		tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
> +					bytes_remaining : fifo_width_bytes);

		tx_bytes = min(remaining, fifo_width);

> +
> +		for (c = 0; c < tx_bytes ; c++)
> +			buf |= (xmit->buf[temp_tail + c] << (c * 8));
> +		geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
> +		i += tx_bytes;
> +		temp_tail = (temp_tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> +		uport->icount.tx += tx_bytes;
> +		bytes_remaining -= tx_bytes;
> +		/* Ensure FIFO write goes through */
> +		wmb();
> +	}
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +	qcom_port->xmit_size += xmit_size;
> +exit_handle_tx:
> +	uart_write_wakeup(uport);
> +	return ret;
> +}
> +
> +static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
> +{
> +	unsigned int m_irq_status;
> +	unsigned int s_irq_status;
> +	struct uart_port *uport = dev;
> +	unsigned long flags;
> +	unsigned int m_irq_en;
> +	bool drop_rx = false;
> +	struct tty_port *tport = &uport->state->port;
> +
> +	spin_lock_irqsave(&uport->lock, flags);
> +	if (uport->suspended)
> +		goto exit_geni_serial_isr;
> +	m_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_M_IRQ_STATUS);
> +	s_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_S_IRQ_STATUS);
> +	m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
> +	geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +
> +	if ((m_irq_status & M_ILLEGAL_CMD_EN)) {
> +		WARN_ON(1);
> +		goto exit_geni_serial_isr;
> +	}
> +
> +	if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
> +		uport->icount.overrun++;
> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +	}
> +
> +	if ((m_irq_status & m_irq_en) &
> +	    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
> +		qcom_geni_serial_handle_tx(uport);
> +
> +	if ((s_irq_status & S_GP_IRQ_0_EN) || (s_irq_status & S_GP_IRQ_1_EN)) {
> +		if (s_irq_status & S_GP_IRQ_0_EN)
> +			uport->icount.parity++;
> +		drop_rx = true;
> +	} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
> +		(s_irq_status & S_GP_IRQ_3_EN)) {
> +		uport->icount.brk++;
> +	}
> +
> +	if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
> +		(s_irq_status & S_RX_FIFO_LAST_EN))
> +		qcom_geni_serial_handle_rx(uport, drop_rx);
> +
> +exit_geni_serial_isr:
> +	spin_unlock_irqrestore(&uport->lock, flags);
> +	return IRQ_HANDLED;
> +}
> +
> +static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
> +{
> +	struct uart_port *uport;
> +
> +	if (!port)
> +		return -ENODEV;
> +
> +	uport = &port->uport;
> +	port->tx_fifo_depth = geni_se_get_tx_fifo_depth(uport->membase);
> +	if (!port->tx_fifo_depth) {
> +		dev_err(uport->dev, "%s:Invalid TX FIFO depth read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	port->tx_fifo_width = geni_se_get_tx_fifo_width(uport->membase);
> +	if (!port->tx_fifo_width) {
> +		dev_err(uport->dev, "%s:Invalid TX FIFO width read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	port->rx_fifo_depth = geni_se_get_rx_fifo_depth(uport->membase);
> +	if (!port->rx_fifo_depth) {
> +		dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	uport->fifosize =
> +		((port->tx_fifo_depth * port->tx_fifo_width) >> 3);
> +	return 0;
> +}
> +
> +static void set_rfr_wm(struct qcom_geni_serial_port *port)
> +{
> +	/*
> +	 * Set RFR (Flow off) to FIFO_DEPTH - 2.
> +	 * RX WM level at 10% RX_FIFO_DEPTH.
> +	 * TX WM level at 10% TX_FIFO_DEPTH.
> +	 */
> +	port->rx_rfr = port->rx_fifo_depth - 2;
> +	port->rx_wm = UART_CONSOLE_RX_WM;
> +	port->tx_wm = 2;
> +}
> +
> +static void qcom_geni_serial_shutdown(struct uart_port *uport)
> +{
> +	unsigned long flags;
> +
> +	/* Stop the console before stopping the current tx */
> +	console_stop(uport->cons);
> +
> +	disable_irq(uport->irq);
> +	free_irq(uport->irq, uport);
> +	spin_lock_irqsave(&uport->lock, flags);
> +	qcom_geni_serial_stop_tx(uport);
> +	qcom_geni_serial_stop_rx(uport);
> +	spin_unlock_irqrestore(&uport->lock, flags);
> +}
> +
> +static int qcom_geni_serial_port_setup(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	unsigned long cfg0, cfg1;
> +	unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
> +
> +	set_rfr_wm(qcom_port);
> +	geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT);
> +	/*
> +	 * Make an unconditional cancel on the main sequencer to reset
> +	 * it else we could end up in data loss scenarios.
> +	 */
> +	qcom_port->xfer_mode = FIFO_MODE;
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +	geni_se_get_packing_config(8, 1, false, &cfg0, &cfg1);
> +	geni_write_reg_nolog(cfg0, uport->membase,
> +					SE_GENI_TX_PACKING_CFG0);
> +	geni_write_reg_nolog(cfg1, uport->membase,
> +					SE_GENI_TX_PACKING_CFG1);
> +	geni_se_get_packing_config(8, 4, false, &cfg0, &cfg1);
> +	geni_write_reg_nolog(cfg0, uport->membase,
> +					SE_GENI_RX_PACKING_CFG0);
> +	geni_write_reg_nolog(cfg1, uport->membase,
> +					SE_GENI_RX_PACKING_CFG1);
> +	ret = geni_se_init(uport->membase, qcom_port->rx_wm, qcom_port->rx_rfr);
> +	if (ret) {
> +		dev_err(uport->dev, "%s: Fail\n", __func__);
> +		goto exit_portsetup;
> +	}
> +
> +	ret = geni_se_select_mode(uport->membase, qcom_port->xfer_mode);
> +	if (ret)
> +		goto exit_portsetup;
> +
> +	qcom_port->port_setup = true;
> +	/*
> +	 * Ensure Port setup related IO completes before returning to
> +	 * framework.
> +	 */
> +	mb();
> +exit_portsetup:
> +	return ret;
> +}
> +
> +static int qcom_geni_serial_startup(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	scnprintf(qcom_port->name, sizeof(qcom_port->name),
> +		  "qcom_serial_geni%d",	uport->line);
> +
> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {

Drop the unlikely(), in favour of readable code.

> +		dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
> +				 __func__, geni_se_get_proto(uport->membase));

Drop the __func__, the message should describe the issue in itself.

> +		ret = -ENXIO;
> +		goto exit_startup;

We haven't done anything, so just return here.

> +	}
> +
> +	get_tx_fifo_size(qcom_port);
> +	if (!qcom_port->port_setup) {
> +		if (qcom_geni_serial_port_setup(uport))
> +			goto exit_startup;
> +	}
> +
> +	/*
> +	 * Ensure that all the port configuration writes complete
> +	 * before returning to the framework.

That's not what mb() does.

> +	 */
> +	mb();
> +	ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
> +			qcom_port->name, uport);
> +	if (unlikely(ret)) {
> +		dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n",
> +							__func__, ret);

Drop the __func__

> +		goto exit_startup;
> +	}
> +
> +exit_startup:
> +	return ret;
> +}
> +
> +static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
> +{
> +	unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
> +		32000000, 48000000, 64000000, 80000000, 96000000, 100000000};
> +	int i;
> +	int match = -1;
> +
> +	for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
> +		if (clk_freq > root_freq[i])
> +			continue;
> +
> +		if (!(root_freq[i] % clk_freq)) {
> +			match = i;
> +			break;
> +		}
> +	}
> +	if (match != -1)
> +		*ser_clk = root_freq[match];
> +	else
> +		pr_err("clk_freq %ld\n", clk_freq);
> +	return match;
> +}
> +
> +static void geni_serial_write_term_regs(struct uart_port *uport,
> +		u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
> +		u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
> +		u32 s_clk_cfg)
> +{
> +	geni_write_reg_nolog(tx_trans_cfg, uport->membase,
> +							SE_UART_TX_TRANS_CFG);
> +	geni_write_reg_nolog(tx_parity_cfg, uport->membase,
> +							SE_UART_TX_PARITY_CFG);
> +	geni_write_reg_nolog(rx_trans_cfg, uport->membase,
> +							SE_UART_RX_TRANS_CFG);
> +	geni_write_reg_nolog(rx_parity_cfg, uport->membase,
> +							SE_UART_RX_PARITY_CFG);
> +	geni_write_reg_nolog(bits_per_char, uport->membase,
> +							SE_UART_TX_WORD_LEN);
> +	geni_write_reg_nolog(bits_per_char, uport->membase,
> +							SE_UART_RX_WORD_LEN);
> +	geni_write_reg_nolog(stop_bit_len, uport->membase,
> +						SE_UART_TX_STOP_BIT_LEN);
> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);
> +}
> +
> +static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
> +{
> +	unsigned long ser_clk;
> +	int dfs_index;
> +	int clk_div = 0;
> +
> +	*desired_clk_rate = baud * UART_OVERSAMPLING;
> +	dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk);
> +	if (dfs_index < 0) {
> +		pr_err("%s: Can't find matching DFS entry for baud %d\n",
> +								__func__, baud);
> +		clk_div = -EINVAL;
> +		goto exit_get_clk_div_rate;
> +	}
> +
> +	clk_div = ser_clk / *desired_clk_rate;
> +	*desired_clk_rate = ser_clk;
> +exit_get_clk_div_rate:
> +	return clk_div;
> +}
> +
> +static void qcom_geni_serial_set_termios(struct uart_port *uport,
> +				struct ktermios *termios, struct ktermios *old)
> +{
> +	unsigned int baud;
> +	unsigned int bits_per_char = 0;
> +	unsigned int tx_trans_cfg;
> +	unsigned int tx_parity_cfg;
> +	unsigned int rx_trans_cfg;
> +	unsigned int rx_parity_cfg;
> +	unsigned int stop_bit_len;
> +	unsigned int clk_div;
> +	unsigned long ser_clk_cfg = 0;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +	unsigned long clk_rate;
> +
> +	qcom_geni_serial_stop_rx(uport);
> +	/* baud rate */
> +	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
> +	port->cur_baud = baud;
> +	clk_div = get_clk_div_rate(baud, &clk_rate);
> +	if (clk_div <= 0)
> +		goto exit_set_termios;

Name your labels based on their actions; if you name it "out_restart_rx"
you don't even have to look below to know what will happen when you
jump.

> +
> +	uport->uartclk = clk_rate;
> +	clk_set_rate(port->serial_rsc.se_clk, clk_rate);
> +	ser_clk_cfg |= SER_CLK_EN;
> +	ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
> +
> +	/* parity */
> +	tx_trans_cfg = geni_read_reg_nolog(uport->membase,

Drop the _nolog and you don't need to line wrap

> +							SE_UART_TX_TRANS_CFG);
> +	tx_parity_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_TX_PARITY_CFG);
> +	rx_trans_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_RX_TRANS_CFG);
> +	rx_parity_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_RX_PARITY_CFG);
> +	if (termios->c_cflag & PARENB) {
> +		tx_trans_cfg |= UART_TX_PAR_EN;
> +		rx_trans_cfg |= UART_RX_PAR_EN;
> +		tx_parity_cfg |= PAR_CALC_EN;
> +		rx_parity_cfg |= PAR_CALC_EN;
> +		if (termios->c_cflag & PARODD) {
> +			tx_parity_cfg |= PAR_ODD;
> +			rx_parity_cfg |= PAR_ODD;
> +		} else if (termios->c_cflag & CMSPAR) {
> +			tx_parity_cfg |= PAR_SPACE;
> +			rx_parity_cfg |= PAR_SPACE;
> +		} else {
> +			tx_parity_cfg |= PAR_EVEN;
> +			rx_parity_cfg |= PAR_EVEN;
> +		}
> +	} else {
> +		tx_trans_cfg &= ~UART_TX_PAR_EN;
> +		rx_trans_cfg &= ~UART_RX_PAR_EN;
> +		tx_parity_cfg &= ~PAR_CALC_EN;
> +		rx_parity_cfg &= ~PAR_CALC_EN;
> +	}
> +
> +	/* bits per char */
> +	switch (termios->c_cflag & CSIZE) {
> +	case CS5:
> +		bits_per_char = 5;
> +		break;
> +	case CS6:
> +		bits_per_char = 6;
> +		break;
> +	case CS7:
> +		bits_per_char = 7;
> +		break;
> +	case CS8:
> +	default:
> +		bits_per_char = 8;
> +		break;
> +	}
> +
> +	/* stop bits */
> +	if (termios->c_cflag & CSTOPB)
> +		stop_bit_len = TX_STOP_BIT_LEN_2;
> +	else
> +		stop_bit_len = TX_STOP_BIT_LEN_1;
> +
> +	/* flow control, clear the CTS_MASK bit if using flow control. */
> +	if (termios->c_cflag & CRTSCTS)
> +		tx_trans_cfg &= ~UART_CTS_MASK;
> +	else
> +		tx_trans_cfg |= UART_CTS_MASK;
> +
> +	if (likely(baud))
> +		uart_update_timeout(uport, termios->c_cflag, baud);
> +
> +	geni_serial_write_term_regs(uport, tx_trans_cfg, tx_parity_cfg,
> +		rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len,
> +								ser_clk_cfg);
> +exit_set_termios:
> +	qcom_geni_serial_start_rx(uport);
> +	return;
> +
> +}
> +
> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
> +{
> +	unsigned int tx_fifo_status;
> +	unsigned int is_tx_empty = 1;
> +
> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_TX_FIFO_STATUS);

Drop the "_nolog" and naming the result "status" would be just as
expressive, but keep you below 80 chars.

> +	if (tx_fifo_status)
> +		is_tx_empty = 0;
> +
> +	return is_tx_empty;

Instead of what you're doing with is_tx_empty, just do:

	return !readl(membase + FIFO_STATUS);

> +}
> +
> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
> +static int __init qcom_geni_console_setup(struct console *co, char *options)
> +{
> +	struct uart_port *uport;
> +	struct qcom_geni_serial_port *dev_port;
> +	int baud = 115200;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +	int ret = 0;

Drop initialization of all these variables.

> +
> +	if (unlikely(co->index >= GENI_UART_NR_PORTS  || co->index < 0))

Drop the unlikely.

> +		return -ENXIO;
> +
> +	dev_port = get_port_from_line(co->index);
> +	if (IS_ERR_OR_NULL(dev_port)) {
> +		ret = PTR_ERR(dev_port);
> +		pr_err("Invalid line %d(%d)\n", co->index, ret);
> +		return ret;
> +	}
> +
> +	uport = &dev_port->uport;
> +
> +	if (unlikely(!uport->membase))
> +		return -ENXIO;
> +
> +	if (geni_se_resources_on(&dev_port->serial_rsc))
> +		WARN_ON(1);
> +
> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
> +		geni_se_resources_off(&dev_port->serial_rsc);
> +		return -ENXIO;
> +	}
> +
> +	if (!dev_port->port_setup) {
> +		qcom_geni_serial_stop_rx(uport);
> +		qcom_geni_serial_port_setup(uport);
> +	}
> +
> +	if (options)
> +		uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +	return uart_set_options(uport, co, baud, parity, bits, flow);
> +}
> +
> +static int console_register(struct uart_driver *drv)
> +{
> +	return uart_register_driver(drv);
> +}
> +static void console_unregister(struct uart_driver *drv)
> +{
> +	uart_unregister_driver(drv);
> +}
> +
> +static struct console cons_ops = {
> +	.name = "ttyMSM",
> +	.write = qcom_geni_serial_console_write,
> +	.device = uart_console_device,
> +	.setup = qcom_geni_console_setup,
> +	.flags = CON_PRINTBUFFER,
> +	.index = -1,
> +	.data = &qcom_geni_console_driver,
> +};
> +
> +static struct uart_driver qcom_geni_console_driver = {
> +	.owner = THIS_MODULE,
> +	.driver_name = "qcom_geni_console",
> +	.dev_name = "ttyMSM",
> +	.nr =  GENI_UART_NR_PORTS,
> +	.cons = &cons_ops,
> +};
> +#else
> +static int console_register(struct uart_driver *drv)
> +{
> +	return 0;
> +}
> +
> +static void console_unregister(struct uart_driver *drv)
> +{
> +}
> +#endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */
> +
> +static void qcom_geni_serial_cons_pm(struct uart_port *uport,
> +		unsigned int new_state, unsigned int old_state)
> +{
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	if (unlikely(!uart_console(uport)))
> +		return;
> +
> +	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
> +		geni_se_resources_on(&qcom_port->serial_rsc);
> +	else if (new_state == UART_PM_STATE_OFF &&
> +			old_state == UART_PM_STATE_ON)
> +		geni_se_resources_off(&qcom_port->serial_rsc);
> +}
> +
> +static const struct uart_ops qcom_geni_console_pops = {
> +	.tx_empty = qcom_geni_serial_tx_empty,
> +	.stop_tx = qcom_geni_serial_stop_tx,
> +	.start_tx = qcom_geni_serial_start_tx,
> +	.stop_rx = qcom_geni_serial_stop_rx,
> +	.set_termios = qcom_geni_serial_set_termios,
> +	.startup = qcom_geni_serial_startup,
> +	.config_port = qcom_geni_serial_config_port,
> +	.shutdown = qcom_geni_serial_shutdown,
> +	.type = qcom_geni_serial_get_type,
> +	.set_mctrl = qcom_geni_cons_set_mctrl,
> +	.get_mctrl = qcom_geni_cons_get_mctrl,
> +#ifdef CONFIG_CONSOLE_POLL
> +	.poll_get_char	= qcom_geni_serial_get_char,
> +	.poll_put_char	= qcom_geni_serial_poll_put_char,
> +#endif
> +	.pm = qcom_geni_serial_cons_pm,
> +};
> +
> +static const struct of_device_id qcom_geni_serial_match_table[] = {
> +	{ .compatible = "qcom,geni-debug-uart",
> +			.data = (void *)&qcom_geni_console_driver},

Shouldn't need this typecast.

> +};
> +
> +static int qcom_geni_serial_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	int line = -1;
> +	struct qcom_geni_serial_port *dev_port;
> +	struct uart_port *uport;
> +	struct resource *res;
> +	struct uart_driver *drv;
> +	const struct of_device_id *id;
> +
> +	id = of_match_device(qcom_geni_serial_match_table, &pdev->dev);

Use of_device_get_match_data()

> +	if (id) {
> +		dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
> +		drv = (struct uart_driver *)id->data;
> +	} else {
> +		dev_err(&pdev->dev, "%s: No matching device found", __func__);
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.of_node) {
> +		if (drv->cons)
> +			line = of_alias_get_id(pdev->dev.of_node, "serial");
> +	} else {
> +		line = pdev->id;
> +	}
> +
> +	if (line < 0)
> +		line = atomic_inc_return(&uart_line_id) - 1;
> +
> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> +		return -ENXIO;
> +	dev_port = get_port_from_line(line);
> +	if (IS_ERR_OR_NULL(dev_port)) {
> +		ret = PTR_ERR(dev_port);
> +		dev_err(&pdev->dev, "Invalid line %d(%d)\n",
> +					line, ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport = &dev_port->uport;
> +
> +	/* Don't allow 2 drivers to access the same port */
> +	if (uport->private_data) {
> +		ret = -ENODEV;
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport->dev = &pdev->dev;
> +	dev_port->wrapper_dev = pdev->dev.parent;
> +	dev_port->serial_rsc.wrapper_dev = pdev->dev.parent;
> +	dev_port->serial_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
> +	if (IS_ERR(dev_port->serial_rsc.se_clk)) {
> +		ret = PTR_ERR(dev_port->serial_rsc.se_clk);
> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "se-phys");

Don't grab this by name, just pick the first one.

> +	if (!res) {
> +		ret = -ENXIO;
> +		dev_err(&pdev->dev, "Err getting IO region\n");
> +		goto exit_geni_serial_probe;
> +	}

Drop the error handling here.

> +	uport->mapbase = res->start;

No

> +	uport->membase = devm_ioremap(&pdev->dev, res->start,
> +						resource_size(res));
> +	if (!uport->membase) {
> +		ret = -ENOMEM;
> +		dev_err(&pdev->dev, "Err IO mapping serial iomem");
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);

Drop all of the pinctrl stuff.

> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
> +		dev_err(&pdev->dev, "No pinctrl config specified!\n");
> +		ret = PTR_ERR(dev_port->serial_rsc.geni_pinctrl);
> +		goto exit_geni_serial_probe;
> +	}
> +	dev_port->serial_rsc.geni_gpio_active =
> +		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
> +							PINCTRL_DEFAULT);
> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
> +		dev_err(&pdev->dev, "No default config specified!\n");
> +		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	dev_port->serial_rsc.geni_gpio_sleep =
> +		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
> +							PINCTRL_SLEEP);
> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_sleep)) {
> +		dev_err(&pdev->dev, "No sleep config specified!\n");
> +		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
> +	uport->fifosize =
> +		((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3);
> +
> +	uport->irq = platform_get_irq(pdev, 0);
> +	if (uport->irq < 0) {
> +		ret = uport->irq;
> +		dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport->private_data = (void *)drv;
> +	platform_set_drvdata(pdev, dev_port);
> +	dev_port->handle_rx = handle_rx_console;

Why have a function pointer when you're only referencing a fixed
function.

> +	dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32),
> +								GFP_KERNEL);

It takes 8 bytes to store a pointer and the allocation will have some
overhead...just to provide storage space for 4 bytes. And you don't have
any error handling...

> +	dev_port->port_setup = false;
> +	return uart_add_one_port(drv, uport);
> +
> +exit_geni_serial_probe:
> +	return ret;
> +}
> +
> +static int qcom_geni_serial_remove(struct platform_device *pdev)
> +{
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_driver *drv =
> +			(struct uart_driver *)port->uport.private_data;
> +
> +	uart_remove_one_port(drv, &port->uport);
> +	return 0;
> +}
> +
> +
> +#ifdef CONFIG_PM
> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_port *uport = &port->uport;
> +
> +	uart_suspend_port((struct uart_driver *)uport->private_data,
> +				uport);
> +	return 0;
> +}
> +
> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_port *uport = &port->uport;
> +
> +	if (console_suspend_enabled && uport->suspended) {
> +		uart_resume_port((struct uart_driver *)uport->private_data,
> +									uport);
> +		disable_irq(uport->irq);
> +	}
> +	return 0;
> +}
> +#else
> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
> +{
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
> +	.suspend_noirq = qcom_geni_serial_sys_suspend_noirq,
> +	.resume_noirq = qcom_geni_serial_sys_resume_noirq,
> +};
> +
> +static struct platform_driver qcom_geni_serial_platform_driver = {
> +	.remove = qcom_geni_serial_remove,
> +	.probe = qcom_geni_serial_probe,
> +	.driver = {
> +		.name = "qcom_geni_serial",
> +		.of_match_table = qcom_geni_serial_match_table,
> +		.pm = &qcom_geni_serial_pm_ops,
> +	},
> +};
> +
> +static int __init qcom_geni_serial_init(void)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	for (i = 0; i < GENI_UART_CONS_PORTS; i++) {
> +		qcom_geni_console_port.uport.iotype = UPIO_MEM;
> +		qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
> +		qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
> +		qcom_geni_console_port.uport.line = i;
> +	}
> +
> +	ret = console_register(&qcom_geni_console_driver);
> +	if (ret)
> +		return ret;
> +
> +	ret = platform_driver_register(&qcom_geni_serial_platform_driver);
> +	if (ret) {
> +		console_unregister(&qcom_geni_console_driver);
> +		return ret;
> +	}
> +	return ret;
> +}
> +module_init(qcom_geni_serial_init);
> +
> +static void __exit qcom_geni_serial_exit(void)
> +{
> +	platform_driver_unregister(&qcom_geni_serial_platform_driver);
> +	console_unregister(&qcom_geni_console_driver);
> +}
> +module_exit(qcom_geni_serial_exit);
> +
> +MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("tty:qcom_geni_serial");
> -- 
> Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
  2018-01-18 19:43   ` Bjorn Andersson
@ 2018-01-19  7:12   ` Bjorn Andersson
  2018-02-27 15:07     ` Karthik Ramasubramanian
  2018-01-24 23:07   ` [v2, " Evan Green
                     ` (3 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: Bjorn Andersson @ 2018-01-19  7:12 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan, Sagar Dharia

On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:

> This driver supports GENI based UART Controller in the Qualcomm SOCs. The
> Qualcomm Generic Interface (GENI) is a programmable module supporting a
> wide range of serial interfaces including UART. This driver support console
> operations using FIFO mode of transfer.
> 
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>

Please disregard my previous answer to this patch, I hit the wrong key
combo while stashing my reply.

> ---
>  drivers/tty/serial/Kconfig            |   10 +
>  drivers/tty/serial/Makefile           |    1 +
>  drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
>  3 files changed, 1425 insertions(+)
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index b788fee..1be30e5 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
>  	select SERIAL_CORE_CONSOLE
>  	select SERIAL_EARLYCON
>  
> +config SERIAL_QCOM_GENI
> +	tristate "QCOM on-chip GENI based serial port support"
> +	depends on ARCH_QCOM

depend on the GENI wrapper as well.

[..]
> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> new file mode 100644
> index 0000000..0dbd329
> --- /dev/null
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -0,0 +1,1414 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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.
> + */

SPDX license header.

> +
> +#include <linux/bitmap.h>

Unused

> +#include <linux/bitops.h>

Unused?

> +#include <linux/delay.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/qcom-geni-se.h>
> +#include <linux/serial.h>
> +#include <linux/serial_core.h>
> +#include <linux/slab.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +
> +/* UART specific GENI registers */
> +#define SE_UART_TX_TRANS_CFG		(0x25C)

Drop the parenthesis

[..]
> +static int owr;
> +module_param(owr, int, 0644);

What's this?

> +
> +struct qcom_geni_serial_port {
> +	struct uart_port uport;
> +	char name[20];
> +	unsigned int tx_fifo_depth;

size_t is a good type for keeping track of sizes.

> +	unsigned int tx_fifo_width;
> +	unsigned int rx_fifo_depth;
> +	unsigned int tx_wm;
> +	unsigned int rx_wm;
> +	unsigned int rx_rfr;

size_t for sizes.

> +	int xfer_mode;
> +	bool port_setup;
> +	unsigned int *rx_fifo;

The fifo is typeless, so it should be void *. It is however only ever
used as a local variable in handle_rx_console() so drop this.

> +	int (*handle_rx)(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx);
> +	struct device *wrapper_dev;
> +	struct geni_se_rsc serial_rsc;
> +	unsigned int xmit_size;

In other drivers we read bytes of the xmit buffer at xmit->tail, write
to hardware FIFO and update xmit->tail. Why do you keep a going delta
between the tail and our real tail?

> +	void *rx_buf;
> +	unsigned int cur_baud;
> +};
> +
[..]
> +
> +#define GET_DEV_PORT(uport) \
> +	container_of(uport, struct qcom_geni_serial_port, uport)

The idiomatic name for this macro would be something like "to_dev_port".

The use of "uport" as macro parameter makes this only work if the
parameter is "uport", otherwise the macro will replace the last part of
the container_of as well.

[..]
> +static struct qcom_geni_serial_port *get_port_from_line(int line)
> +{
> +	struct qcom_geni_serial_port *port = NULL;
> +
> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> +		port = ERR_PTR(-ENXIO);

You need to return here as well...

> +	port = &qcom_geni_console_port;
> +	return port;

Drop the "port" and just return ERR_PTR(-ENXIO) in the error case and
return &qcom_geni_serial_port otherwise.

> +}
> +
> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
> +				int offset, int bit_field, bool set)

This function is returning "yes" or "no", so make it return "bool"

> +{
> +	int iter = 0;
> +	unsigned int reg;
> +	bool met = false;
> +	struct qcom_geni_serial_port *port = NULL;
> +	bool cond = false;
> +	unsigned int baud = 115200;
> +	unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS;
> +	unsigned long total_iter = 2000;

Don't initialize things that will be assigned directly again.

> +
> +
> +	if (uport->private_data) {

When is this function being called on a non-initialized uport?

> +		port = GET_DEV_PORT(uport);
> +		baud = (port->cur_baud ? port->cur_baud : 115200);

Drop the parenthesis, and consider initializing baud = port->cur_baud
and then give it 115200 if it's zero separately.

> +		fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
> +		/*
> +		 * Total polling iterations based on FIFO worth of bytes to be
> +		 * sent at current baud .Add a little fluff to the wait.
> +		 */
> +		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
> +		total_iter += 50;
> +	}
> +
> +	while (iter < total_iter) {
> +		reg = geni_read_reg_nolog(uport->membase, offset);
> +		cond = reg & bit_field;
> +		if (cond == set) {
> +			met = true;
> +			break;
> +		}
> +		udelay(10);
> +		iter++;
> +	}

Use readl_poll_timeout() instead of all this.

> +	return met;
> +}
> +
> +static void qcom_geni_serial_setup_tx(struct uart_port *uport,
> +				unsigned int xmit_size)
> +{
> +	u32 m_cmd = 0;

Don't initialize this.

> +
> +	geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
> +	m_cmd |= (UART_START_TX << M_OPCODE_SHFT);

Why OR this into 0?

> +	geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
> +	/*
> +	 * Writes to enable the primary sequencer should go through before
> +	 * exiting this function.
> +	 */
> +	mb();
> +}
> +
> +static void qcom_geni_serial_poll_cancel_tx(struct uart_port *uport)

This function polls for command done and if that doesn't happen in time
it aborts the command. It then clear any interrupts. The function name
however indicates that we're polling for "cancel of tx".

> +{
> +	int done = 0;
> +	unsigned int irq_clear = M_CMD_DONE_EN;
> +	unsigned int geni_status = 0;

Don't initialize done and geni_status.

> +
> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_DONE_EN, true);
> +	if (!done) {
> +		geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
> +					SE_GENI_M_CMD_CTRL_REG);
> +		owr++;

What's owr?

> +		irq_clear |= M_CMD_ABORT_EN;
> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +							M_CMD_ABORT_EN, true);
> +	}
> +	geni_status = geni_read_reg_nolog(uport->membase,
> +						  SE_GENI_STATUS);

Why is this line wrapped?

> +	if (geni_status & M_GENI_CMD_ACTIVE)
> +		owr++;
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
> +{
> +	unsigned int irq_clear = S_CMD_DONE_EN;
> +
> +	geni_se_abort_s_cmd(uport->membase);
> +	/* Ensure this goes through before polling. */

Move the mb() to the qcom_geni_serial_poll_bit()

> +	mb();
> +	irq_clear |= S_CMD_ABORT_EN;
> +	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +					S_GENI_CMD_ABORT, false);
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
> +}
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +static int qcom_geni_serial_get_char(struct uart_port *uport)
> +{
> +	unsigned int rx_fifo;
> +	unsigned int m_irq_status;
> +	unsigned int s_irq_status;

These are u32 and the variable names would benefit from being shorter.

> +
> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +			M_SEC_IRQ_EN, true)))

Drop one parenthesis.

> +		return -ENXIO;
> +
> +	m_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_M_IRQ_STATUS);

Drop the _nolog and rename the variable to reduce the length of this
line.

> +	s_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_S_IRQ_STATUS);
> +	geni_write_reg_nolog(m_irq_status, uport->membase,
> +						SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg_nolog(s_irq_status, uport->membase,
> +						SE_GENI_S_IRQ_CLEAR);

Is it necessary to do this interlaced? Or can you just clear M and then
clear S? I.e. have a single variable named "status".

> +
> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
> +			RX_FIFO_WC_MSK, true)))
> +		return -ENXIO;
> +
> +	/*
> +	 * Read the Rx FIFO only after clearing the interrupt registers and
> +	 * getting valid RX fifo status.

Use non-_relaxed readl and you wouldn't have to do this explicitly.

> +	 */
> +	mb();
> +	rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
> +	rx_fifo &= 0xFF;

return rx_fifo & 0xff; instead of the extra step

> +	return rx_fifo;
> +}
> +
> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
> +					unsigned char c)
> +{
> +	int b = (int) c;

Why create this alias?

> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_write_reg_nolog(port->tx_wm, uport->membase,
> +					SE_GENI_TX_WATERMARK_REG);
> +	qcom_geni_serial_setup_tx(uport, 1);
> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +				M_TX_FIFO_WATERMARK_EN, true))
> +		WARN_ON(1);

	WARN_ON(!qcom_geni_serial_poll_bit(...));

> +	geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
> +	geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +	/*
> +	 * Ensure FIFO write goes through before polling for status but.
> +	 */

/* This fits in a one-line comment */

But if this is necessary that means that every time you call
serial_poll() you need to have this comment and a rmb(). Better move it
into the poll function then - or just stop using _relaxed()

> +	mb();
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +}
> +#endif
> +
> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)

The Kconfig specifies that we depend on CONFIG_SERIAL_CORE_CONSOLE, so
no need to check that.

> +static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
> +{
> +	geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn);
> +	/*
> +	 * Ensure FIFO write clear goes through before
> +	 * next iteration.

This comment is accurate, but unnecessarily line wrapped.

> +	 */
> +	mb();
> +
> +}
> +
> +static void
> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
> +				unsigned int count)
> +{
> +	int new_line = 0;
> +	int i;
> +	int bytes_to_send = count;
> +	int fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	int tx_wm = DEF_TX_WM;
> +
> +	for (i = 0; i < count; i++) {
> +		if (s[i] == '\n')
> +			new_line++;
> +	}

Why do we need to deal with this?

> +
> +	bytes_to_send += new_line;
> +	geni_write_reg_nolog(tx_wm, uport->membase,
> +					SE_GENI_TX_WATERMARK_REG);
> +	qcom_geni_serial_setup_tx(uport, bytes_to_send);
> +	i = 0;
> +	while (i < count) {
> +		u32 chars_to_write = 0;
> +		u32 avail_fifo_bytes = (fifo_depth - tx_wm);

Use size_t for these.

> +
> +		/*
> +		 * If the WM bit never set, then the Tx state machine is not
> +		 * in a valid state, so break, cancel/abort any existing
> +		 * command. Unfortunately the current data being written is
> +		 * lost.
> +		 */
> +		while (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_TX_FIFO_WATERMARK_EN, true))
> +			break;
> +		chars_to_write = min((unsigned int)(count - i),
> +							avail_fifo_bytes);
> +		if ((chars_to_write << 1) > avail_fifo_bytes)

Write chars_to_write * 2, the compiler will be cleaver for you.

> +			chars_to_write = (avail_fifo_bytes >> 1);
> +		uart_console_write(uport, (s + i), chars_to_write,
> +						qcom_geni_serial_wr_char);
> +		geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +		/* Ensure this goes through before polling for WM IRQ again.*/
> +		mb();

Again, if this is necessary move it into the poll function instead of
sprinkling it throughout the driver.

> +		i += chars_to_write;
> +	}
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +}
> +
> +static void qcom_geni_serial_console_write(struct console *co, const char *s,
> +			      unsigned int count)
> +{
> +	struct uart_port *uport;
> +	struct qcom_geni_serial_port *port;
> +	int locked = 1;

Use bool for booleans

> +	unsigned long flags;
> +
> +	WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
> +
> +	port = get_port_from_line(co->index);
> +	if (IS_ERR_OR_NULL(port))

port can't be NULL.

> +		return;
> +
> +	uport = &port->uport;
> +	if (oops_in_progress)
> +		locked = spin_trylock_irqsave(&uport->lock, flags);
> +	else
> +		spin_lock_irqsave(&uport->lock, flags);
> +
> +	if (locked) {
> +		__qcom_geni_serial_console_write(uport, s, count);
> +		spin_unlock_irqrestore(&uport->lock, flags);
> +	}
> +}
> +
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,

You should be able to have a single parameter with a better name stating
that "this is the last chunk and it's only N bytes", rather than
encoding this in a bool and a count with obscure names.

> +			bool drop_rx)
> +{
> +	int i, c;
> +	unsigned char *rx_char;
> +	struct tty_port *tport;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	tport = &uport->state->port;
> +	for (i = 0; i < rx_fifo_wc; i++) {
> +		int bytes = 4;
> +
> +		*(qcom_port->rx_fifo) =
> +			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);

Just store this value in a local variable, as the only consumer is 3
lines down.

> +		if (drop_rx)
> +			continue;
> +		rx_char = (unsigned char *)qcom_port->rx_fifo;
> +
> +		if (i == (rx_fifo_wc - 1)) {
> +			if (rx_last && rx_last_byte_valid)

Also, shouldn't rx_last be redundant to the check against rx_fifo_wc?

> +				bytes = rx_last_byte_valid;
> +		}
> +		for (c = 0; c < bytes; c++) {
> +			char flag = TTY_NORMAL;

No need for a local variable here.

> +			int sysrq;
> +
> +			uport->icount.rx++;
> +			sysrq = uart_handle_sysrq_char(uport, rx_char[c]);
> +			if (!sysrq)
> +				tty_insert_flip_char(tport, rx_char[c], flag);
> +		}
> +	}
> +	if (!drop_rx)
> +		tty_flip_buffer_push(tport);
> +	return 0;
> +}
> +#else
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx)
> +{
> +	return -EPERM;
> +}
> +
> +#endif /* (CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)) */
> +
> +static void qcom_geni_serial_start_tx(struct uart_port *uport)
> +{
> +	unsigned int geni_m_irq_en;

This is a u32, name it "irq_en"

> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	unsigned int geni_status;

geni_status should be of type u32 and there's no lack of descriptiveness
naming it "status".

> +
> +	if (qcom_port->xfer_mode == FIFO_MODE) {
> +		geni_status = geni_read_reg_nolog(uport->membase,
> +						  SE_GENI_STATUS);
> +		if (geni_status & M_GENI_CMD_ACTIVE)
> +			goto exit_start_tx;

Just return

> +
> +		if (!qcom_geni_serial_tx_empty(uport))
> +			goto exit_start_tx;

Ditto

> +
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +						    SE_GENI_M_IRQ_EN);
> +		geni_m_irq_en |= (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN);
> +
> +		geni_write_reg_nolog(qcom_port->tx_wm, uport->membase,
> +						SE_GENI_TX_WATERMARK_REG);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +		/* Geni command setup should complete before returning.*/

If that's the case then verify that it's actually done.

> +		mb();
> +	}
> +exit_start_tx:
> +	return;
> +}
> +
> +static void stop_tx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;

These are u32 and can be given more succinct names.

> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);

Drop the _nolog from all of these to make code easier to read.

> +	geni_m_irq_en &= ~M_CMD_DONE_EN;
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN;
> +		geni_write_reg_nolog(0, uport->membase,
> +				     SE_GENI_TX_WATERMARK_REG);
> +	}
> +	port->xmit_size = 0;
> +	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
> +	geni_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_STATUS);
> +	/* Possible stop tx is called multiple times. */
> +	if (!(geni_status & M_GENI_CMD_ACTIVE))
> +		return;
> +
> +	geni_se_cancel_m_cmd(uport->membase);
> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_CANCEL_EN, true)) {
> +		geni_se_abort_m_cmd(uport->membase);
> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_ABORT_EN, true);
> +		geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +	}
> +	geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
> +{
> +	stop_tx_sequencer(uport);

Inline stop_tx_sequencer here...

> +}
> +
> +static void start_rx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_s_irq_en;
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	if (geni_status & S_GENI_CMD_ACTIVE)
> +		qcom_geni_serial_stop_rx(uport);
> +
> +	geni_se_setup_s_cmd(uport->membase, UART_START_READ, 0);
> +
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +
> +		geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
> +		geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
> +
> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);

Do you need to do have these two operations interlaced? Can't you take
care of M and then S using a single variable?

> +	}
> +	/*
> +	 * Ensure the writes to the secondary sequencer and interrupt enables
> +	 * go through.

This is not what mb() does.

> +	 */
> +	mb();
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +}
> +
> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
> +{
> +	start_rx_sequencer(uport);

Inline start_rx_sequencer

> +}
> +
> +static void stop_rx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_s_irq_en;
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +	u32 irq_clear = S_CMD_DONE_EN;
> +	bool done;
> +
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +		geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
> +		geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
> +
> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +	}
> +
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	/* Possible stop rx is called multiple times. */

Shouldn't this be checked before above writes?

> +	if (!(geni_status & S_GENI_CMD_ACTIVE))
> +		return;
> +	geni_se_cancel_s_cmd(uport->membase);
> +	/*
> +	 * Ensure that the cancel goes through before polling for the
> +	 * cancel control bit.
> +	 */
> +	mb();
> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +					S_GENI_CMD_CANCEL, false);
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +	if ((geni_status & S_GENI_CMD_ACTIVE))

Drop the extra parenthesis. Is this checking if we're still active after
the cancel, or is this redundant?

> +		qcom_geni_serial_abort_rx(uport);
> +}
> +
> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
> +{
> +	stop_rx_sequencer(uport);

Just inline the function here.

> +}
> +
> +static int qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
> +{
> +	int ret = 0;
> +	unsigned int rx_fifo_status;
> +	unsigned int rx_fifo_wc = 0;
> +	unsigned int rx_last_byte_valid = 0;
> +	unsigned int rx_last = 0;

The context of this function gives that we're dealing with rx and
rx_fifo, so no need to specify that in each local variable.

Also, they should all be u32 and there's no need to initialize them.

> +	struct tty_port *tport;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	tport = &uport->state->port;
> +	rx_fifo_status = geni_read_reg_nolog(uport->membase,
> +				SE_GENI_RX_FIFO_STATUS);
> +	rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK;
> +	rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >>
> +						RX_LAST_BYTE_VALID_SHFT);
> +	rx_last = rx_fifo_status & RX_LAST;
> +	if (rx_fifo_wc)
> +		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
> +							rx_last, drop_rx);

You're ignoring the return value of handle_rx.

> +	return ret;

You don't care about the return value in the caller and you always
return 0 anyways, so make the function void.

> +}
> +
> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	struct circ_buf *xmit = &uport->state->xmit;

Use size_t for sizes.

> +	unsigned int avail_fifo_bytes = 0;

Naming avail_fifo_bytes just "avail" is carrying the same information
but is shorter.

> +	unsigned int bytes_remaining = 0;
> +	int i = 0;
> +	unsigned int tx_fifo_status;
> +	unsigned int xmit_size;

This is "the number of bytes we're going to take from the uart xmit
buffer and push to the hardware FIFO. Call it "chunk" or something,
because it's not the size of the xmit.

> +	unsigned int fifo_width_bytes = 1;

If this really is constantly 1 you can remove the variable and remove a
bit of the complexity in this function.

> +	int temp_tail = 0;

Local variables are temporary in nature, so no need to name the variable
temp_tail, it is the "tail" we operate on here.

> +
> +	xmit_size = uart_circ_chars_pending(xmit);
> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
> +					SE_GENI_TX_FIFO_STATUS);
> +	/* Both FIFO and framework buffer are drained */
> +	if ((xmit_size == qcom_port->xmit_size) && !tx_fifo_status) {
> +		qcom_port->xmit_size = 0;
> +		uart_circ_clear(xmit);
> +		qcom_geni_serial_stop_tx(uport);
> +		goto exit_handle_tx;
> +	}
> +	xmit_size -= qcom_port->xmit_size;
> +
> +	avail_fifo_bytes = (qcom_port->tx_fifo_depth - qcom_port->tx_wm) *
> +							fifo_width_bytes;
> +	temp_tail = (xmit->tail + qcom_port->xmit_size) & (UART_XMIT_SIZE - 1);
> +	if (xmit_size > (UART_XMIT_SIZE - temp_tail))
> +		xmit_size = (UART_XMIT_SIZE - temp_tail);
> +	if (xmit_size > avail_fifo_bytes)
> +		xmit_size = avail_fifo_bytes;

This section is confusing me, in other drivers we update xmit->tail and
the uart_circ_chars_pending() returns the number of bytes left to be
written. But in your driver we maintain qcom_port->xmit_size as a delta
between the tail and the next byte in the ring buffer to consume.

I find no place were you're actually updating the tail, so I'm wondering
how setting qcom_port->xmit_size to zero above will actually work, as it
doesn't consider that xmit->head might be elsewhere.

The math that you need to come up with is "how much data is there to be
written" and "how much FIFO space do I have" and then take the min() of
those. The prior should be the return value of uart_circ_chars_pending()
straight off.

> +
> +	if (!xmit_size)
> +		goto exit_handle_tx;
> +
> +	qcom_geni_serial_setup_tx(uport, xmit_size);
> +
> +	bytes_remaining = xmit_size;
> +	while (i < xmit_size) {
> +		unsigned int tx_bytes;
> +		unsigned int buf = 0;
> +		int c;
> +
> +		tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
> +					bytes_remaining : fifo_width_bytes);

		tx_bytes = min(remaining, fifo_width);

But fifo_width_bytes is 1, so this can be simplified.

> +
> +		for (c = 0; c < tx_bytes ; c++)
> +			buf |= (xmit->buf[temp_tail + c] << (c * 8));

As bytes_remaining shouldn't be able to reach 0, tx_bytes should always
be 1 and as such this is just a matter of writing
xmit->buf[xmit->tail++] to SE_GENI_TX_FIFOn.

> +		geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
> +		i += tx_bytes;
> +		temp_tail = (temp_tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> +		uport->icount.tx += tx_bytes;
> +		bytes_remaining -= tx_bytes;
> +		/* Ensure FIFO write goes through */

Writes to the same register isn't expected to be reordered, so you
shouldn't need this wmb(). Perhaps it's the same barrier that's needed
for other polls?

> +		wmb();
> +	}
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +	qcom_port->xmit_size += xmit_size;
> +exit_handle_tx:
> +	uart_write_wakeup(uport);
> +	return ret;
> +}
> +
> +static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
> +{
> +	unsigned int m_irq_status;
> +	unsigned int s_irq_status;
> +	struct uart_port *uport = dev;
> +	unsigned long flags;
> +	unsigned int m_irq_en;
> +	bool drop_rx = false;
> +	struct tty_port *tport = &uport->state->port;
> +
> +	spin_lock_irqsave(&uport->lock, flags);
> +	if (uport->suspended)
> +		goto exit_geni_serial_isr;

Do you need to check this within the spinlock?

Name your labels based on their action, not the function they are in. So
a better name would be "out_unlock". But if this can be done outside the
lock just returning would be better.

> +	m_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_M_IRQ_STATUS);
> +	s_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_S_IRQ_STATUS);
> +	m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
> +	geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +
> +	if ((m_irq_status & M_ILLEGAL_CMD_EN)) {

Extra parenthesis, and you can better write this:

	if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
		goto out_unlock;

> +		WARN_ON(1);
> +		goto exit_geni_serial_isr;
> +	}
> +
> +	if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
> +		uport->icount.overrun++;
> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +	}
> +
> +	if ((m_irq_status & m_irq_en) &

Is M_ILLEGAL_CMD_EN part of m_irq_en? Can you mask the m_irq_status
based on the irq_en above instead of doing it like this?

> +	    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
> +		qcom_geni_serial_handle_tx(uport);
> +
> +	if ((s_irq_status & S_GP_IRQ_0_EN) || (s_irq_status & S_GP_IRQ_1_EN)) {

Drop the parenthesis.

> +		if (s_irq_status & S_GP_IRQ_0_EN)
> +			uport->icount.parity++;
> +		drop_rx = true;
> +	} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
> +		(s_irq_status & S_GP_IRQ_3_EN)) {

Ditto.

> +		uport->icount.brk++;
> +	}
> +
> +	if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
> +		(s_irq_status & S_RX_FIFO_LAST_EN))

Ditto.

> +		qcom_geni_serial_handle_rx(uport, drop_rx);
> +
> +exit_geni_serial_isr:
> +	spin_unlock_irqrestore(&uport->lock, flags);
> +	return IRQ_HANDLED;
> +}
> +
> +static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
> +{
> +	struct uart_port *uport;
> +
> +	if (!port)
> +		return -ENODEV;
> +
> +	uport = &port->uport;
> +	port->tx_fifo_depth = geni_se_get_tx_fifo_depth(uport->membase);
> +	if (!port->tx_fifo_depth) {
> +		dev_err(uport->dev, "%s:Invalid TX FIFO depth read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	port->tx_fifo_width = geni_se_get_tx_fifo_width(uport->membase);
> +	if (!port->tx_fifo_width) {
> +		dev_err(uport->dev, "%s:Invalid TX FIFO width read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	port->rx_fifo_depth = geni_se_get_rx_fifo_depth(uport->membase);
> +	if (!port->rx_fifo_depth) {
> +		dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	uport->fifosize =
> +		((port->tx_fifo_depth * port->tx_fifo_width) >> 3);

This line wrap isn't necessary, use division rather than shifting and
describe why the fifosize is 1/8 of the depth * width (is the width
expressed in bits perhaps?)

> +	return 0;
> +}
> +
> +static void set_rfr_wm(struct qcom_geni_serial_port *port)
> +{
> +	/*
> +	 * Set RFR (Flow off) to FIFO_DEPTH - 2.
> +	 * RX WM level at 10% RX_FIFO_DEPTH.
> +	 * TX WM level at 10% TX_FIFO_DEPTH.
> +	 */
> +	port->rx_rfr = port->rx_fifo_depth - 2;
> +	port->rx_wm = UART_CONSOLE_RX_WM;
> +	port->tx_wm = 2;
> +}
> +
> +static void qcom_geni_serial_shutdown(struct uart_port *uport)
> +{
> +	unsigned long flags;
> +
> +	/* Stop the console before stopping the current tx */
> +	console_stop(uport->cons);
> +
> +	disable_irq(uport->irq);
> +	free_irq(uport->irq, uport);
> +	spin_lock_irqsave(&uport->lock, flags);
> +	qcom_geni_serial_stop_tx(uport);
> +	qcom_geni_serial_stop_rx(uport);
> +	spin_unlock_irqrestore(&uport->lock, flags);
> +}
> +
> +static int qcom_geni_serial_port_setup(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	unsigned long cfg0, cfg1;
> +	unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
> +
> +	set_rfr_wm(qcom_port);
> +	geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT);
> +	/*
> +	 * Make an unconditional cancel on the main sequencer to reset
> +	 * it else we could end up in data loss scenarios.
> +	 */
> +	qcom_port->xfer_mode = FIFO_MODE;
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +	geni_se_get_packing_config(8, 1, false, &cfg0, &cfg1);
> +	geni_write_reg_nolog(cfg0, uport->membase,
> +					SE_GENI_TX_PACKING_CFG0);
> +	geni_write_reg_nolog(cfg1, uport->membase,
> +					SE_GENI_TX_PACKING_CFG1);
> +	geni_se_get_packing_config(8, 4, false, &cfg0, &cfg1);
> +	geni_write_reg_nolog(cfg0, uport->membase,
> +					SE_GENI_RX_PACKING_CFG0);
> +	geni_write_reg_nolog(cfg1, uport->membase,
> +					SE_GENI_RX_PACKING_CFG1);

These aren't above 80 chars, why are you line breaking?

> +	ret = geni_se_init(uport->membase, qcom_port->rx_wm, qcom_port->rx_rfr);
> +	if (ret) {
> +		dev_err(uport->dev, "%s: Fail\n", __func__);
> +		goto exit_portsetup;
> +	}
> +
> +	ret = geni_se_select_mode(uport->membase, qcom_port->xfer_mode);
> +	if (ret)

Do you need to roll back any init done in the wrapper code? How about
passing the mode to the init function?

> +		goto exit_portsetup;
> +
> +	qcom_port->port_setup = true;
> +	/*
> +	 * Ensure Port setup related IO completes before returning to
> +	 * framework.

That's not what mb does.

> +	 */
> +	mb();
> +exit_portsetup:
> +	return ret;
> +}
> +
> +static int qcom_geni_serial_startup(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	scnprintf(qcom_port->name, sizeof(qcom_port->name),
> +		  "qcom_serial_geni%d",	uport->line);
> +
> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {

Drop the unlikely(), in favour of readable code.

> +		dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
> +				 __func__, geni_se_get_proto(uport->membase));

Drop the __func__, the message should describe the issue in itself.

> +		ret = -ENXIO;
> +		goto exit_startup;

We haven't done anything, so just return here.

> +	}
> +
> +	get_tx_fifo_size(qcom_port);
> +	if (!qcom_port->port_setup) {
> +		if (qcom_geni_serial_port_setup(uport))
> +			goto exit_startup;
> +	}
> +
> +	/*
> +	 * Ensure that all the port configuration writes complete
> +	 * before returning to the framework.

That's not what mb() does.

> +	 */
> +	mb();
> +	ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
> +			qcom_port->name, uport);
> +	if (unlikely(ret)) {
> +		dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n",
> +							__func__, ret);

Drop the __func__

> +		goto exit_startup;
> +	}
> +
> +exit_startup:
> +	return ret;
> +}
> +
> +static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
> +{
> +	unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
> +		32000000, 48000000, 64000000, 80000000, 96000000, 100000000};
> +	int i;
> +	int match = -1;
> +
> +	for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
> +		if (clk_freq > root_freq[i])
> +			continue;
> +
> +		if (!(root_freq[i] % clk_freq)) {
> +			match = i;
> +			break;
> +		}
> +	}
> +	if (match != -1)
> +		*ser_clk = root_freq[match];
> +	else
> +		pr_err("clk_freq %ld\n", clk_freq);

Errors are for humans to consume, so make the message useful for a
human.

> +	return match;

The index isn't used, so return the frequency instead of filling in the
ser_clk reference.

> +}
> +
> +static void geni_serial_write_term_regs(struct uart_port *uport,
> +		u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
> +		u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
> +		u32 s_clk_cfg)
> +{
> +	geni_write_reg_nolog(tx_trans_cfg, uport->membase,
> +							SE_UART_TX_TRANS_CFG);
> +	geni_write_reg_nolog(tx_parity_cfg, uport->membase,
> +							SE_UART_TX_PARITY_CFG);
> +	geni_write_reg_nolog(rx_trans_cfg, uport->membase,
> +							SE_UART_RX_TRANS_CFG);
> +	geni_write_reg_nolog(rx_parity_cfg, uport->membase,
> +							SE_UART_RX_PARITY_CFG);
> +	geni_write_reg_nolog(bits_per_char, uport->membase,
> +							SE_UART_TX_WORD_LEN);
> +	geni_write_reg_nolog(bits_per_char, uport->membase,
> +							SE_UART_RX_WORD_LEN);
> +	geni_write_reg_nolog(stop_bit_len, uport->membase,
> +						SE_UART_TX_STOP_BIT_LEN);
> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);

Drop the _nolog and you should be able to fit these within 80 chars.

> +}
> +
> +static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
> +{
> +	unsigned long ser_clk;
> +	int dfs_index;
> +	int clk_div = 0;
> +
> +	*desired_clk_rate = baud * UART_OVERSAMPLING;
> +	dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk);
> +	if (dfs_index < 0) {
> +		pr_err("%s: Can't find matching DFS entry for baud %d\n",
> +								__func__, baud);
> +		clk_div = -EINVAL;

Just return -EINVAL here, instead of using goto.

> +		goto exit_get_clk_div_rate;
> +	}
> +
> +	clk_div = ser_clk / *desired_clk_rate;
> +	*desired_clk_rate = ser_clk;
> +exit_get_clk_div_rate:
> +	return clk_div;

Clock functions typically return a frequency, so I think it's better to
return that and pass the divider by reference.

> +}
> +
> +static void qcom_geni_serial_set_termios(struct uart_port *uport,
> +				struct ktermios *termios, struct ktermios *old)
> +{
> +	unsigned int baud;
> +	unsigned int bits_per_char = 0;
> +	unsigned int tx_trans_cfg;
> +	unsigned int tx_parity_cfg;
> +	unsigned int rx_trans_cfg;
> +	unsigned int rx_parity_cfg;
> +	unsigned int stop_bit_len;
> +	unsigned int clk_div;
> +	unsigned long ser_clk_cfg = 0;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +	unsigned long clk_rate;
> +
> +	qcom_geni_serial_stop_rx(uport);
> +	/* baud rate */
> +	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
> +	port->cur_baud = baud;
> +	clk_div = get_clk_div_rate(baud, &clk_rate);
> +	if (clk_div <= 0)
> +		goto exit_set_termios;

Name your labels based on their actions; if you name it "out_restart_rx"
you don't even have to look below to know what will happen when you
jump.

> +
> +	uport->uartclk = clk_rate;
> +	clk_set_rate(port->serial_rsc.se_clk, clk_rate);
> +	ser_clk_cfg |= SER_CLK_EN;
> +	ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
> +
> +	/* parity */
> +	tx_trans_cfg = geni_read_reg_nolog(uport->membase,

Drop the _nolog and you don't need to line wrap

> +							SE_UART_TX_TRANS_CFG);
> +	tx_parity_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_TX_PARITY_CFG);
> +	rx_trans_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_RX_TRANS_CFG);
> +	rx_parity_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_RX_PARITY_CFG);
> +	if (termios->c_cflag & PARENB) {
> +		tx_trans_cfg |= UART_TX_PAR_EN;
> +		rx_trans_cfg |= UART_RX_PAR_EN;
> +		tx_parity_cfg |= PAR_CALC_EN;
> +		rx_parity_cfg |= PAR_CALC_EN;
> +		if (termios->c_cflag & PARODD) {
> +			tx_parity_cfg |= PAR_ODD;
> +			rx_parity_cfg |= PAR_ODD;
> +		} else if (termios->c_cflag & CMSPAR) {
> +			tx_parity_cfg |= PAR_SPACE;
> +			rx_parity_cfg |= PAR_SPACE;
> +		} else {
> +			tx_parity_cfg |= PAR_EVEN;
> +			rx_parity_cfg |= PAR_EVEN;
> +		}
> +	} else {
> +		tx_trans_cfg &= ~UART_TX_PAR_EN;
> +		rx_trans_cfg &= ~UART_RX_PAR_EN;
> +		tx_parity_cfg &= ~PAR_CALC_EN;
> +		rx_parity_cfg &= ~PAR_CALC_EN;
> +	}
> +
> +	/* bits per char */
> +	switch (termios->c_cflag & CSIZE) {
> +	case CS5:
> +		bits_per_char = 5;
> +		break;
> +	case CS6:
> +		bits_per_char = 6;
> +		break;
> +	case CS7:
> +		bits_per_char = 7;
> +		break;
> +	case CS8:
> +	default:
> +		bits_per_char = 8;
> +		break;
> +	}
> +
> +	/* stop bits */
> +	if (termios->c_cflag & CSTOPB)
> +		stop_bit_len = TX_STOP_BIT_LEN_2;
> +	else
> +		stop_bit_len = TX_STOP_BIT_LEN_1;
> +
> +	/* flow control, clear the CTS_MASK bit if using flow control. */
> +	if (termios->c_cflag & CRTSCTS)
> +		tx_trans_cfg &= ~UART_CTS_MASK;
> +	else
> +		tx_trans_cfg |= UART_CTS_MASK;
> +
> +	if (likely(baud))

Don't do likely/unlikely.

> +		uart_update_timeout(uport, termios->c_cflag, baud);
> +
> +	geni_serial_write_term_regs(uport, tx_trans_cfg, tx_parity_cfg,
> +		rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len,
> +								ser_clk_cfg);

It would likely be fine to just inline the register writes here, instead
of this long list of parameters.

> +exit_set_termios:
> +	qcom_geni_serial_start_rx(uport);
> +	return;

No need to keep an empty return last in the function, and drop the empty
line following it.

> +
> +}
> +
> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
> +{
> +	unsigned int tx_fifo_status;
> +	unsigned int is_tx_empty = 1;
> +
> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_TX_FIFO_STATUS);

Drop the "_nolog" and naming the result "status" would be just as
expressive, but keep you below 80 chars.

> +	if (tx_fifo_status)
> +		is_tx_empty = 0;
> +
> +	return is_tx_empty;

Instead of what you're doing with is_tx_empty, just do:

	return !readl(membase + FIFO_STATUS);

> +}
> +
> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
> +static int __init qcom_geni_console_setup(struct console *co, char *options)
> +{
> +	struct uart_port *uport;
> +	struct qcom_geni_serial_port *dev_port;
> +	int baud = 115200;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +	int ret = 0;

Drop initialization of all these variables.

> +
> +	if (unlikely(co->index >= GENI_UART_NR_PORTS  || co->index < 0))

Drop the unlikely.

> +		return -ENXIO;
> +
> +	dev_port = get_port_from_line(co->index);
> +	if (IS_ERR_OR_NULL(dev_port)) {

port can't be NULL.

> +		ret = PTR_ERR(dev_port);
> +		pr_err("Invalid line %d(%d)\n", co->index, ret);
> +		return ret;

Just return PTR_ERR(dev_port);

> +	}
> +
> +	uport = &dev_port->uport;
> +
> +	if (unlikely(!uport->membase))
> +		return -ENXIO;
> +
> +	if (geni_se_resources_on(&dev_port->serial_rsc))
> +		WARN_ON(1);

If this fails we're likely to access unclocked resources causing the
system to restart, so we should probably not continue here.

> +
> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
> +		geni_se_resources_off(&dev_port->serial_rsc);
> +		return -ENXIO;
> +	}
> +
[..]
> +
> +static const struct of_device_id qcom_geni_serial_match_table[] = {
> +	{ .compatible = "qcom,geni-debug-uart",
> +			.data = (void *)&qcom_geni_console_driver},

Shouldn't need this typecast.

Is it necessary to treat the console uart differently and will there be
a patch adding support for non-console instances? How will this differ
and why would that be a different compatible?

> +};
> +
> +static int qcom_geni_serial_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	int line = -1;
> +	struct qcom_geni_serial_port *dev_port;
> +	struct uart_port *uport;
> +	struct resource *res;
> +	struct uart_driver *drv;
> +	const struct of_device_id *id;
> +
> +	id = of_match_device(qcom_geni_serial_match_table, &pdev->dev);

Use of_device_get_match_data()

> +	if (id) {
> +		dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
> +		drv = (struct uart_driver *)id->data;
> +	} else {
> +		dev_err(&pdev->dev, "%s: No matching device found", __func__);
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.of_node) {
> +		if (drv->cons)

The one and only uart_driver has a cons.

> +			line = of_alias_get_id(pdev->dev.of_node, "serial");
> +	} else {
> +		line = pdev->id;
> +	}
> +
> +	if (line < 0)
> +		line = atomic_inc_return(&uart_line_id) - 1;
> +
> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> +		return -ENXIO;
> +	dev_port = get_port_from_line(line);
> +	if (IS_ERR_OR_NULL(dev_port)) {

port can't be NULL.

> +		ret = PTR_ERR(dev_port);
> +		dev_err(&pdev->dev, "Invalid line %d(%d)\n",
> +					line, ret);
> +		goto exit_geni_serial_probe;

You're not doing anything other than returning in
exit_geni_serial_probe, if you just return here I don't need to jump to
the bottom of the function to figure this out...

> +	}
> +
> +	uport = &dev_port->uport;
> +
> +	/* Don't allow 2 drivers to access the same port */
> +	if (uport->private_data) {
> +		ret = -ENODEV;
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport->dev = &pdev->dev;
> +	dev_port->wrapper_dev = pdev->dev.parent;
> +	dev_port->serial_rsc.wrapper_dev = pdev->dev.parent;
> +	dev_port->serial_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
> +	if (IS_ERR(dev_port->serial_rsc.se_clk)) {
> +		ret = PTR_ERR(dev_port->serial_rsc.se_clk);
> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "se-phys");

Don't grab this by name, just pick the first one.

> +	if (!res) {
> +		ret = -ENXIO;
> +		dev_err(&pdev->dev, "Err getting IO region\n");
> +		goto exit_geni_serial_probe;
> +	}

Drop the error handling here.

> +	uport->mapbase = res->start;

No

> +	uport->membase = devm_ioremap(&pdev->dev, res->start,
> +						resource_size(res));
> +	if (!uport->membase) {
> +		ret = -ENOMEM;
> +		dev_err(&pdev->dev, "Err IO mapping serial iomem");
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);

Drop all of the pinctrl stuff.

> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
> +		dev_err(&pdev->dev, "No pinctrl config specified!\n");
> +		ret = PTR_ERR(dev_port->serial_rsc.geni_pinctrl);
> +		goto exit_geni_serial_probe;
> +	}
[..]
> +	dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
> +	uport->fifosize =
> +		((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3);

qcom_geni_serial_startup() sets this as well.

> +
> +	uport->irq = platform_get_irq(pdev, 0);
> +	if (uport->irq < 0) {
> +		ret = uport->irq;
> +		dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport->private_data = (void *)drv;

No need to explicitly typecast to void*

> +	platform_set_drvdata(pdev, dev_port);
> +	dev_port->handle_rx = handle_rx_console;

Why have a function pointer when you're only referencing a fixed
function.

> +	dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32),
> +								GFP_KERNEL);

It takes 8 bytes to store a pointer and the allocation will have some
overhead...just to provide storage space for 4 bytes. And you don't have
any error handling...

> +	dev_port->port_setup = false;
> +	return uart_add_one_port(drv, uport);
> +
> +exit_geni_serial_probe:
> +	return ret;
> +}
> +
> +static int qcom_geni_serial_remove(struct platform_device *pdev)
> +{
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_driver *drv =
> +			(struct uart_driver *)port->uport.private_data;

No need to typecast private_data.

> +
> +	uart_remove_one_port(drv, &port->uport);
> +	return 0;
> +}
> +
> +
> +#ifdef CONFIG_PM

Drop this and mark the functions __maybe_unused.

> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_port *uport = &port->uport;
> +
> +	uart_suspend_port((struct uart_driver *)uport->private_data,
> +				uport);

No need to typecast private_data.

> +	return 0;
> +}
> +
> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_port *uport = &port->uport;
> +
> +	if (console_suspend_enabled && uport->suspended) {
> +		uart_resume_port((struct uart_driver *)uport->private_data,
> +									uport);

No need to typecast private_data.

> +		disable_irq(uport->irq);
> +	}
> +	return 0;
> +}
[..]
> +static int __init qcom_geni_serial_init(void)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	for (i = 0; i < GENI_UART_CONS_PORTS; i++) {

Don't loop over [0,1) to update the one global variable.

> +		qcom_geni_console_port.uport.iotype = UPIO_MEM;
> +		qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
> +		qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
> +		qcom_geni_console_port.uport.line = i;
> +	}
> +
> +	ret = console_register(&qcom_geni_console_driver);
> +	if (ret)
> +		return ret;
> +
> +	ret = platform_driver_register(&qcom_geni_serial_platform_driver);
> +	if (ret) {
> +		console_unregister(&qcom_geni_console_driver);
> +		return ret;
> +	}
> +	return ret;

ret is 0 here, drop the return from within the if statement or just
return 0 for clarity.

> +}
> +module_init(qcom_geni_serial_init);
> +
> +static void __exit qcom_geni_serial_exit(void)
> +{
> +	platform_driver_unregister(&qcom_geni_serial_platform_driver);
> +	console_unregister(&qcom_geni_console_driver);
> +}
> +module_exit(qcom_geni_serial_exit);
> +
> +MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("tty:qcom_geni_serial");

What will request the kernel module by this alias?

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 0/7] Introduce GENI SE Controller Driver
       [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
  2018-01-13  1:05   ` [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
@ 2018-01-19 18:32   ` Randy Dunlap
  2018-01-31 18:59     ` Karthik Ramasubramanian
  2 siblings, 1 reply; 42+ messages in thread
From: Randy Dunlap @ 2018-01-19 18:32 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian, corbet-T1hC0tSOHrs,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	wsa-z923LK4zBo2bacvFa/9K2g,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
  Cc: linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k

On 01/12/2018 05:05 PM, Karthikeyan Ramasubramanian wrote:
> Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
> Wrapper is a next generation programmable module for supporting a wide
> range of serial interfaces like UART, SPI, I2C, I3C, etc. A single QUP
> module can provide upto 8 Serial Interfaces using its internal Serial
> Engines (SE). The protocol supported by each interface is determined by
> the firmware loaded to the Serial Engine.
> 
> This patch series introduces GENI SE Driver to manage the GENI based QUP
> Wrapper and the common aspects of all SEs inside the QUP Wrapper. This
> patch series also introduces the UART and I2C Controller drivers to
> drive the SEs that are programmed with the respective protocols.

Hi,

Will there be follow-up drivers for SPI, I3C, etc.?

Thanks.

> [v2]
>  * Updated device tree bindings to describe the hardware
>  * Updated SE DT node as child node of QUP Wrapper DT node
>  * Moved common AHB clocks to QUP Wrapper DT node
>  * Use the standard "clock-frequency" I2C property
>  * Update compatible field in UART Controller to reflect hardware manual
>  * Addressed other device tree binding specific comments from Rob Herring
> 
> Karthikeyan Ramasubramanian (7):
>   qcom-geni-se: Add QCOM GENI SE Driver summary
>   dt-bindings: soc: qcom: Add device tree binding for GENI SE
>   soc: qcom: Add GENI based QUP Wrapper driver
>   dt-bindings: i2c: Add device tree bindings for GENI I2C Controller
>   i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C
>     controller
>   dt-bindings: serial: Add bindings for GENI based UART Controller
>   tty: serial: msm_geni_serial: Add serial driver support for GENI based
>     QUP
> 
>  .../devicetree/bindings/i2c/i2c-qcom-geni.txt      |   35 +
>  .../devicetree/bindings/serial/qcom,geni-uart.txt  |   29 +
>  .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  |   66 +
>  Documentation/qcom-geni-se.txt                     |   56 +
>  drivers/i2c/busses/Kconfig                         |   10 +
>  drivers/i2c/busses/Makefile                        |    1 +
>  drivers/i2c/busses/i2c-qcom-geni.c                 |  656 +++++++++
>  drivers/soc/qcom/Kconfig                           |    8 +
>  drivers/soc/qcom/Makefile                          |    1 +
>  drivers/soc/qcom/qcom-geni-se.c                    | 1016 ++++++++++++++
>  drivers/tty/serial/Kconfig                         |   10 +
>  drivers/tty/serial/Makefile                        |    1 +
>  drivers/tty/serial/qcom_geni_serial.c              | 1414 ++++++++++++++++++++
>  include/linux/qcom-geni-se.h                       |  807 +++++++++++
>  14 files changed, 4110 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
>  create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
>  create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>  create mode 100644 Documentation/qcom-geni-se.txt
>  create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>  create mode 100644 drivers/soc/qcom/qcom-geni-se.c
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c
>  create mode 100644 include/linux/qcom-geni-se.h
> 


-- 
~Randy
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-01-17  6:25   ` Bjorn Andersson
@ 2018-01-19 22:53     ` Rob Herring
  2018-02-26 21:24     ` Karthik Ramasubramanian
  1 sibling, 0 replies; 42+ messages in thread
From: Rob Herring @ 2018-01-19 22:53 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Karthikeyan Ramasubramanian, corbet, andy.gross, david.brown,
	mark.rutland, wsa, gregkh, linux-doc, linux-arm-msm, devicetree,
	linux-i2c, linux-serial, jslaby

On Tue, Jan 16, 2018 at 10:25:58PM -0800, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
> > Add device tree binding support for the QCOM GENI SE driver.
> > 
> > Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> 
> Better describe the entire GENI, with it's functions in the same
> binding.
> 
> > ---
> >  .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 34 ++++++++++++++++++++++
> >  1 file changed, 34 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> > new file mode 100644
> > index 0000000..66f8a16
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> > @@ -0,0 +1,34 @@
> > +Qualcomm Technologies, Inc. GENI Serial Engine QUP Wrapper Controller
> > +
> > +Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) wrapper
> > +is a programmable module for supporting a wide range of serial interfaces
> > +like UART, SPI, I2C, I3C, etc. A single QUP module can provide upto 8 Serial
> > +Interfaces, using its internal Serial Engines. The GENI Serial Engine QUP
> > +Wrapper controller is modeled as a node with zero or more child nodes each
> > +representing a serial engine.
> > +
> > +Required properties:
> > +- compatible:		Must be "qcom,geni-se-qup".
> > +- reg:			Must contain QUP register address and length.
> > +- clock-names:		Must contain "m-ahb" and "s-ahb".
> > +- clocks:		AHB clocks needed by the device.
> > +
> > +Required properties if child node exists:
> > +- #address-cells: Must be 1
> > +- #size-cells: Must be 1
> 
> But on a 64-bit system, shouldn't these be 2?

No, depends on the bus and ranges. It annoys me to no end that folks 
needlessly use 2 cells when the perpheral address space and/or module 
sizes don't exceed 4G. 

> 
> > +- ranges: Must be present

And ideally with a value, but that's beyond the scope of the binding.

> > +
> > +Properties for children:
> > +
> > +A GENI based QUP wrapper controller node can contain 0 or more child nodes
> > +representing serial devices.  These serial devices can be a QCOM UART, I2C
> > +controller, spi controller, or some combination of aforementioned devices.
> > +
> > +Example:
> > +	qup0: qcom,geniqup0@8c0000 {
> 
> I don't see a reason for this to be referenced, so skip the label. And
> don't use qcom, in the node name; "geni" would be perfectly fine?
> 
> > +		compatible = "qcom,geni-se-qup";
> > +		reg = <0x8c0000 0x6000>;
> > +		clock-names = "m-ahb", "s-ahb";
> > +		clocks = <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
> > +			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
> > +	}
> 
> Regards,
> Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-18 16:57         ` Bjorn Andersson
@ 2018-01-19 22:57           ` Rob Herring
  2018-01-31 19:02             ` Karthik Ramasubramanian
  0 siblings, 1 reply; 42+ messages in thread
From: Rob Herring @ 2018-01-19 22:57 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Rajendra Nayak, Karthikeyan Ramasubramanian, corbet, andy.gross,
	david.brown, mark.rutland, wsa, gregkh, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, Sagar Dharia,
	Girish Mahadevan

On Thu, Jan 18, 2018 at 08:57:45AM -0800, Bjorn Andersson wrote:
> On Thu 18 Jan 01:13 PST 2018, Rajendra Nayak wrote:
> 
> > []..
> > 
> > >> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> > >> new file mode 100644
> > >> index 0000000..3f43582
> > >> --- /dev/null
> > >> +++ b/drivers/soc/qcom/qcom-geni-se.c
> > >> @@ -0,0 +1,1016 @@
> > >> +/*
> > >> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> > >> + *
> > >> + * This program is free software; you can redistribute it and/or modify
> > >> + * it under the terms of the GNU General Public License version 2 and
> > >> + * only 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.
> > >> + *
> > >> + */
> > > 
> > > Please use SPDX style license header, i.e. this file should start with:
> > > 
> > > // SPDX-License-Identifier: GPL-2.0
> > > /*
> > >  * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> > >  */
> > 
> > Looks like Mark Brown commented elsewhere [1] that we should use the C++
> > commenting style even for the Linux Foundation copyright?
> > 
> > [1] https://marc.info/?l=linux-clk&m=151497978626135&w=2 
> > 
> 
> While I can agree with Mark on the ugliness of the mixed commenting
> style, this is the style that I found communicated and is what you find
> in other files.

Well, that's pretty new guidance. Moving target...

Given that Linus said '//' comments are the only thing C++ got right, I 
expect to see more of them.

Rob

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [v2,3/7] soc: qcom: Add GENI based QUP Wrapper driver
       [not found]     ` <1515805547-22816-4-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2018-01-24 23:06       ` Evan Green
  0 siblings, 0 replies; 42+ messages in thread
From: Evan Green @ 2018-01-24 23:06 UTC (permalink / raw)
  To: corbet-T1hC0tSOHrs, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	wsa-z923LK4zBo2bacvFa/9K2g,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
  Cc: Karthikeyan Ramasubramanian, linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia, Girish Mahadevan

On Fri, Jan 12, 2018 at 5:05 PM, Karthikeyan Ramasubramanian
<kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> wrote:
> This driver manages the Generic Interface (GENI) firmware based Qualcomm
> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
> programmable module composed of multiple Serial Engines (SE) and supports
> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
> driver also enables managing the serial interface independent aspects of
> Serial Engines.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Girish Mahadevan <girishm-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> ---
>  drivers/soc/qcom/Kconfig        |    8 +
>  drivers/soc/qcom/Makefile       |    1 +
>  drivers/soc/qcom/qcom-geni-se.c | 1016 +++++++++++++++++++++++++++++++++++++++
>  include/linux/qcom-geni-se.h    |  807 +++++++++++++++++++++++++++++++
>  4 files changed, 1832 insertions(+)
>  create mode 100644 drivers/soc/qcom/qcom-geni-se.c
>  create mode 100644 include/linux/qcom-geni-se.h

I'm a newbie here, just starting to get up to speed. Please forgive
any indiscretions. But I am interested in this driver, so I'm throwing
in my minor comments.


> +/**
> + * geni_se_setup_m_cmd() - Setup the primary sequencer
> + * @base:      Base address of the serial engine's register block.
> + * @cmd:       Command/Operation to setup in the primary sequencer.
> + * @params:    Parameter for the sequencer command.
> + *
> + * This function is used to configure the primary sequencer with the
> + * command and its assoicated parameters.
> + */
> +void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params);
> +
> +/**
> + * geni_se_setup_s_cmd() - Setup the secondary sequencer
> + * @base:      Base address of the serial engine's register block.
> + * @cmd:       Command/Operation to setup in the secondary sequencer.
> + * @params:    Parameter for the sequencer command.
> + *
> + * This function is used to configure the secondary sequencer with the
> + * command and its assoicated parameters.
> + */
> +void geni_se_setup_s_cmd(void __iomem *base, u32 cmd, u32 params);
> +

s/assoicated/associated/ (twice)


> +/**
> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
> + * @wrapper_dev:       QUP Wrapper Device to which the TX buffer is mapped.
> + * @base:              Base address of the SE register block.
> + * @tx_buf:            Pointer to the TX buffer.
> + * @tx_len:            Length of the TX buffer.
> + * @tx_dma:            Pointer to store the mapped DMA address.
> + *
> + * This function is used to prepare the buffers for DMA TX.
> + *
> + * Return:     0 on success, standard Linux error codes on error/failure.
> + */
> +int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
> +                       void *tx_buf, int tx_len, dma_addr_t *tx_dma)
> +{
> +       int ret;
> +
> +       if (unlikely(!wrapper_dev || !base || !tx_buf || !tx_len || !tx_dma))
> +               return -EINVAL;
> +
> +       ret = geni_se_map_buf(wrapper_dev, tx_dma, tx_buf, tx_len,
> +                                   DMA_TO_DEVICE);
> +       if (ret)
> +               return ret;
> +
> +       geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);
> +       geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
> +       geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);
> +       geni_write_reg(1, base, SE_DMA_TX_ATTR);

Bjorn touched on this, but as someone trying to understand what's
going on it would be great to see #defines for the magic values in
here (7 and 1)

> +void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
> +                               unsigned long *cfg0, unsigned long *cfg1)
> +{
> +       u32 cfg[4] = {0};
> +       int len;
> +       int temp_bpw = bpw;
> +       int idx_start = (msb_to_lsb ? (bpw - 1) : 0);
> +       int idx = idx_start;
> +       int idx_delta = (msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE);
> +       int ceil_bpw = ((bpw & (BITS_PER_BYTE - 1)) ?
> +                       ((bpw & ~(BITS_PER_BYTE - 1)) + BITS_PER_BYTE) : bpw);
> +       int iter = (ceil_bpw * pack_words) >> 3;

Is the shift by 3 here really meant to divide by BITS_PER_BYTE? It
might be clearer to divide by BITS_PER_BYTE and let the compiler
convert that into a shift.

> +       int i;
> +
> +       if (unlikely(iter <= 0 || iter > 4)) {
> +               *cfg0 = 0;
> +               *cfg1 = 0;
> +               return;
> +       }
> +
> +       for (i = 0; i < iter; i++) {
> +               len = (temp_bpw < BITS_PER_BYTE) ?
> +                               (temp_bpw - 1) : BITS_PER_BYTE - 1;
> +               cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));

...more magic numbers here. These are register bitfields? It would be
great to get the field shifts defined.

> +               idx = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
> +                               ((i + 1) * BITS_PER_BYTE) + idx_start :
> +                               idx + idx_delta;
> +               temp_bpw = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
> +                               bpw : (temp_bpw - BITS_PER_BYTE);
> +       }
> +       cfg[iter - 1] |= 1;
> +       *cfg0 = cfg[0] | (cfg[1] << 10);
> +       *cfg1 = cfg[2] | (cfg[3] << 10);
> +}

...more magic shifts here.

> +void geni_se_config_packing(void __iomem *base, int bpw,
> +                           int pack_words, bool msb_to_lsb)
> +{
> +       unsigned long cfg0, cfg1;
> +
> +       geni_se_get_packing_config(bpw, pack_words, msb_to_lsb, &cfg0, &cfg1);
> +       geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
> +       geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
> +       geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
> +       geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
> +       if (pack_words || bpw == 32)
> +               geni_write_reg((bpw >> 4), base, SE_GENI_BYTE_GRAN);
> +}

...Same here for the bpw >> 4.

Thanks,
Evan
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [v2, 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-01-13  1:05   ` [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
       [not found]     ` <1515805547-22816-6-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2018-01-24 23:07     ` Evan Green
  1 sibling, 0 replies; 42+ messages in thread
From: Evan Green @ 2018-01-24 23:07 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, Sagar Dharia,
	Girish Mahadevan

On Fri, Jan 12, 2018 at 5:05 PM, Karthikeyan Ramasubramanian
<kramasub@codeaurora.org> wrote:
> This bus driver supports the GENI based i2c hardware controller in the
> Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
> module supporting a wide range of serial interfaces including I2C. The
> driver supports FIFO mode and DMA mode of transfer and switches modes
> dynamically depending on the size of the transfer.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
>  drivers/i2c/busses/Kconfig         |  10 +
>  drivers/i2c/busses/Makefile        |   1 +
>  drivers/i2c/busses/i2c-qcom-geni.c | 656 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 667 insertions(+)
>  create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c

Newbie again, throwing in my two cents.

> +static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)
> +{
> +       struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
> +
> +       geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
> +
> +       geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
> +       geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
> +                       itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);

It would be great to get these register field shifts defined, as
they're otherwise fairly opaque.

> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
> +{
> +       struct geni_i2c_dev *gi2c = dev;
> +       int i, j;
> +       u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
> +       u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
> +       u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
> +       u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
> +       u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
> +       struct i2c_msg *cur = gi2c->cur;
> +
> +       if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
> +                   (dm_rx_st & (DM_I2C_CB_ERR)) ||
> +                   (m_stat & M_CMD_ABORT_EN)) {
> +
> +               if (m_stat & M_GP_IRQ_1_EN)
> +                       geni_i2c_err(gi2c, I2C_NACK);
> +               if (m_stat & M_GP_IRQ_3_EN)
> +                       geni_i2c_err(gi2c, I2C_BUS_PROTO);
> +               if (m_stat & M_GP_IRQ_4_EN)
> +                       geni_i2c_err(gi2c, I2C_ARB_LOST);
> +               if (m_stat & M_CMD_OVERRUN_EN)
> +                       geni_i2c_err(gi2c, GENI_OVERRUN);
> +               if (m_stat & M_ILLEGAL_CMD_EN)
> +                       geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
> +               if (m_stat & M_CMD_ABORT_EN)
> +                       geni_i2c_err(gi2c, GENI_ABORT_DONE);
> +               if (m_stat & M_GP_IRQ_0_EN)
> +                       geni_i2c_err(gi2c, GP_IRQ0);
> +
> +               if (!dma)
> +                       writel_relaxed(0, (gi2c->base +
> +                                          SE_GENI_TX_WATERMARK_REG));

The writes to the TX_WATERMARK_REG here and a little further down in
the irq handler stick out to me. A comment as to why poking at the
watermark register is necessary here would be very helpful.

-Evan

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [v2, 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
  2018-01-18 19:43   ` Bjorn Andersson
  2018-01-19  7:12   ` Bjorn Andersson
@ 2018-01-24 23:07   ` Evan Green
  2018-02-14 11:04   ` [PATCH v2 " Amit Kucheria
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 42+ messages in thread
From: Evan Green @ 2018-01-24 23:07 UTC (permalink / raw)
  To: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa, gregkh
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, Girish Mahadevan,
	Sagar Dharia

On Fri, Jan 12, 2018 at 5:05 PM, Karthikeyan Ramasubramanian
<kramasub@codeaurora.org> wrote:
> +static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
> +{
> +       unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
> +               32000000, 48000000, 64000000, 80000000, 96000000, 100000000};

This table should be static const, right?

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [v2,3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
  2018-01-17  6:20     ` Bjorn Andersson
       [not found]     ` <1515805547-22816-4-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2018-01-26 19:38     ` Doug Anderson
  2018-02-14 11:07     ` [PATCH v2 3/7] " Amit Kucheria
  3 siblings, 0 replies; 42+ messages in thread
From: Doug Anderson @ 2018-01-26 19:38 UTC (permalink / raw)
  To: Jonathan Corbet, andy.gross, david.brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, Sagar Dharia,
	Girish Mahadevan, evgreen

Hi,

On Fri, Jan 12, 2018 at 5:05 PM, Karthikeyan Ramasubramanian
<kramasub@codeaurora.org> wrote:
> This driver manages the Generic Interface (GENI) firmware based Qualcomm
> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
> programmable module composed of multiple Serial Engines (SE) and supports
> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
> driver also enables managing the serial interface independent aspects of
> Serial Engines.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
>  drivers/soc/qcom/Kconfig        |    8 +
>  drivers/soc/qcom/Makefile       |    1 +
>  drivers/soc/qcom/qcom-geni-se.c | 1016 +++++++++++++++++++++++++++++++++++++++
>  include/linux/qcom-geni-se.h    |  807 +++++++++++++++++++++++++++++++
>  4 files changed, 1832 insertions(+)
>  create mode 100644 drivers/soc/qcom/qcom-geni-se.c
>  create mode 100644 include/linux/qcom-geni-se.h
>
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index b81374b..b306d51 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -3,6 +3,14 @@
>  #
>  menu "Qualcomm SoC drivers"
>
> +config QCOM_GENI_SE
> +       tristate "QCOM GENI Serial Engine Driver"

One tiny nit here: maybe it makes sense to add:

depends on ARCH_QCOM

...like all the other stuff in this file?  Otherwise this ends up
polluting .config files for non-Qualcomm architectures.


-Doug

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary
  2018-01-16 16:55   ` Bjorn Andersson
@ 2018-01-29 21:52     ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-01-29 21:52 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby


On 1/16/2018 9:55 AM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
>> Wrapper is a programmable module that is composed of multiple Serial
>> Engines (SE) and can support various Serial Interfaces like UART, SPI,
>> I2C, I3C, etc. This document provides a high level overview of the GENI
>> based QUP Wrapper.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> 
> Rather than adding a disconnected chunk of documentation move this into
> the driver(s), using the format described in
> https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html?highlight=kernel%20doc#overview-documentation-comments
I will move the documentation into the driver source file.
> 
> Regards,
> Bjorn
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-17  6:20     ` Bjorn Andersson
  2018-01-18  9:13       ` Rajendra Nayak
@ 2018-01-31 18:58       ` Karthik Ramasubramanian
  2018-02-05 23:50         ` Bjorn Andersson
  1 sibling, 1 reply; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-01-31 18:58 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan



On 1/16/2018 11:20 PM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> This driver manages the Generic Interface (GENI) firmware based Qualcomm
>> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
>> programmable module composed of multiple Serial Engines (SE) and supports
>> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
>> driver also enables managing the serial interface independent aspects of
>> Serial Engines.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> ---
>>   drivers/soc/qcom/Kconfig        |    8 +
>>   drivers/soc/qcom/Makefile       |    1 +
>>   drivers/soc/qcom/qcom-geni-se.c | 1016 +++++++++++++++++++++++++++++++++++++++
> 
> I'm not aware of any non-serial-engine "geni" at Qualcomm, so can we
> drop the "se" throughout this driver?
Currently GENI is used to program just the serial engines. But it is not 
restricted to that. It can be used to program other hardware cores. 
Hence keeping "se" will help to clarify that this driver is meant for 
GENI based serial engines only.
> 
>>   include/linux/qcom-geni-se.h    |  807 +++++++++++++++++++++++++++++++
>>   4 files changed, 1832 insertions(+)
>>   create mode 100644 drivers/soc/qcom/qcom-geni-se.c
>>   create mode 100644 include/linux/qcom-geni-se.h
>>
>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>> index b81374b..b306d51 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -3,6 +3,14 @@
>>   #
>>   menu "Qualcomm SoC drivers"
>>   
>> +config QCOM_GENI_SE
>> +	tristate "QCOM GENI Serial Engine Driver"
> 
> Options in drivers/soc/qcom/Kconfig should have "depends on ARCH_QCOM",
> as the makefile in this directory won't be processed when !ARCH_QCOM.
I will update the config to depend on ARCH_QCOM.
> 
>> +	help
>> +	  This module is used to manage Generic Interface (GENI) firmware based
>> +	  Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
>> +	  module is also used to manage the common aspects of multiple Serial
>> +	  Engines present in the QUP.
>> +
>>   config QCOM_GLINK_SSR
>>   	tristate "Qualcomm Glink SSR driver"
>>   	depends on RPMSG
>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>> index 40c56f6..74d5db8 100644
>> --- a/drivers/soc/qcom/Makefile
>> +++ b/drivers/soc/qcom/Makefile
>> @@ -1,4 +1,5 @@
>>   # SPDX-License-Identifier: GPL-2.0
>> +obj-$(CONFIG_QCOM_GENI_SE) +=	qcom-geni-se.o
>>   obj-$(CONFIG_QCOM_GLINK_SSR) +=	glink_ssr.o
>>   obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>>   obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
>> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
>> new file mode 100644
>> index 0000000..3f43582
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom-geni-se.c
>> @@ -0,0 +1,1016 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only 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.
>> + *
>> + */
> 
> Please use SPDX style license header, i.e. this file should start with:
> 
> // SPDX-License-Identifier: GPL-2.0
> /*
>   * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>   */
> 
>> +
>> +#include <linux/clk.h>
>> +#include <linux/slab.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/io.h>
>> +#include <linux/list.h>
> 
> This isn't used.
> 
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/pm_runtime.h>
> 
> Is this used?
> 
>> +#include <linux/qcom-geni-se.h>
>> +#include <linux/spinlock.h>
> 
> Is this used?
I will remove including unused header files.
> 
>> +
>> +#define MAX_CLK_PERF_LEVEL 32
>> +
>> +/**
>> + * @struct geni_se_device - Data structure to represent the QUP Wrapper Core
> 
> Make this name indicate that this is the wrapper; e.g. struct geni_wrapper
geni_wrapper makes sense.
> 
>> + * @dev:		Device pointer of the QUP wrapper core.
>> + * @base:		Base address of this instance of QUP wrapper core.
>> + * @m_ahb_clk:		Handle to the primary AHB clock.
>> + * @s_ahb_clk:		Handle to the secondary AHB clock.
>> + * @geni_dev_lock:	Lock to protect the device elements.
>> + * @num_clk_levels:	Number of valid clock levels in clk_perf_tbl.
>> + * @clk_perf_tbl:	Table of clock frequency input to Serial Engine clock.
>> + */
>> +struct geni_se_device {
>> +	struct device *dev;
>> +	void __iomem *base;
>> +	struct clk *m_ahb_clk;
>> +	struct clk *s_ahb_clk;
> 
> As these two clocks always seems to be operated in tandem use
> clk_bulk_data to keep track of them and use the clk_bulk*() operations
> to simplify your prepare/unprepare sites.
I will use the clk_bulk* approach.
> 
>> +	struct mutex geni_dev_lock;
> 
> There's only one lock and it should be obvious from context that it's
> the lock of the geni_se_device, so naming this "lock" should be
> sufficient.
I will rename it to "lock".
> 
>> +	unsigned int num_clk_levels;
>> +	unsigned long *clk_perf_tbl;
>> +};
>> +
>> +/* Offset of QUP Hardware Version Register */
>> +#define QUP_HW_VER (0x4)
> 
> Drop the parenthesis. It would be nice if this define indicated that
> this is a register and not a value. E.g. call it QUP_HW_VER_REG.
Ok.
> 
>> +
>> +#define HW_VER_MAJOR_MASK GENMASK(31, 28)
>> +#define HW_VER_MAJOR_SHFT 28
>> +#define HW_VER_MINOR_MASK GENMASK(27, 16)
>> +#define HW_VER_MINOR_SHFT 16
>> +#define HW_VER_STEP_MASK GENMASK(15, 0)
>> +
>> +/**
>> + * geni_read_reg_nolog() - Helper function to read from a GENI register
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + *
>> + * Return:	Return the contents of the register.
>> + */
>> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
> 
> Remove this function.
Ok.
> 
>> +{
>> +	return readl_relaxed(base + offset);
>> +}
>> +EXPORT_SYMBOL(geni_read_reg_nolog);
>> +
>> +/**
>> + * geni_write_reg_nolog() - Helper function to write into a GENI register
>> + * @value:	Value to be written into the register.
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + */
>> +void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset)
> 
> Ditto
Ok.
> 
>> +{
>> +	return writel_relaxed(value, (base + offset));
>> +}
>> +EXPORT_SYMBOL(geni_write_reg_nolog);
>> +
>> +/**
>> + * geni_read_reg() - Helper function to read from a GENI register
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + *
>> + * Return:	Return the contents of the register.
> 
> Drop the extra spaces after "Return:" in all your kerneldoc.
Ok.
> 
>> + */
>> +unsigned int geni_read_reg(void __iomem *base, int offset)
>> +{
>> +	return readl_relaxed(base + offset);
>> +}
>> +EXPORT_SYMBOL(geni_read_reg);
>> +
>> +/**
>> + * geni_write_reg() - Helper function to write into a GENI register
>> + * @value:	Value to be written into the register.
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + */
>> +void geni_write_reg(unsigned int value, void __iomem *base, int offset)
>> +{
>> +	return writel_relaxed(value, (base + offset));
> 
> As written, this function adds no value and I find it quite confusing
> that this is used both for read/writes on the wrapper as well as the
> individual functions.Ok.
> 
> Compare:
> 
> 	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
> 
> with just inlining:
> 
> 	writel(common_geni_m_irq_en, base + SE_GENI_M_IRQ_EN);
> 
>> +}
>> +EXPORT_SYMBOL(geni_write_reg);
>> +
>> +/**
>> + * geni_get_qup_hw_version() - Read the QUP wrapper Hardware version
>> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
>> + * @major:		Buffer for Major Version field.
>> + * @minor:		Buffer for Minor Version field.
>> + * @step:		Buffer for Step Version field.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
>> +			    unsigned int *minor, unsigned int *step)
>> +{
>> +	unsigned int version;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (!wrapper_dev || !major || !minor || !step)
>> +		return -EINVAL;
> 
> This would only happen during development, as the engineer calls the
> function incorrectly. Consider it a contract that these has to be valid
> and skip the checks.
Ok.
> 
>> +
>> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return -ENODEV;
> 
> Make the children acquire the geni_se_dev handle (keep the type opaque)
> and pass that instead of wrapper_dev. Then you can just drop this chunk.
Ok.
> 
>> +
>> +	version = geni_read_reg(geni_se_dev->base, QUP_HW_VER);
>> +	*major = (version & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT;
>> +	*minor = (version & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT;
>> +	*step = version & HW_VER_STEP_MASK;
>> +	return 0;
> 
> If you implement these two changes there's no way this function can
> fail, so you don't have to return a value here.
Ok.
> 
>> +}
>> +EXPORT_SYMBOL(geni_get_qup_hw_version);
>> +
>> +/**
>> + * geni_se_get_proto() - Read the protocol configured for a serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * Return:	Protocol value as configured in the serial engine.
>> + */
>> +int geni_se_get_proto(void __iomem *base)
> 
> I keep reading this as "geni set get proto", you should be fine just
> dropping _se_ from these function names. And if you want to denote that
> it's the Qualcomm GENI then make it qcom_geni_*.
"_se" prefix is used to help differentiate operations on a serial engine 
from those on the wrapper. I can rename it as geni_se_read_proto to 
avoid the confusion.
> 
> But more importantly, it is not obvious when reading this driver that
> the typeless "base" being passed is that of the individual functional
> block, and not the wrapper.
> 
> If you want to do this in an "object oriented" fashion create a struct
> that's common for all geni instances, include it in the context of the
> individual function devices and pass it into these functions.
There is a structure named geni_se_rsc to group resources associated 
with a serial engine. I can rename it to geni_serial_engine and include 
the opaque "base" address in that structure. That structure can be 
passed as an input parameter to all the serial engine operations.
> 
>> +{
>> +	int proto;
>> +
>> +	proto = ((geni_read_reg(base, GENI_FW_REVISION_RO)
>> +			& FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);
> 
> Don't do everything in one go, as you can't fit it on one line anyways.
> Writing it like this instead makes it easier to read:
> 
> 	u32 val;
> 
> 	val = readl(base + GENI_FW_S_REVISION_RO);
> 
> 	return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
Ok.
> 
>> +	return proto;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_proto);
>> +
>> +static int geni_se_irq_en(void __iomem *base)
>> +{
>> +	unsigned int common_geni_m_irq_en;
>> +	unsigned int common_geni_s_irq_en;
> 
> The size of these values isn't unsigned int, it's u32. And if you give
> them a shorter name the function becomes easier to read.
> 
> Further more, as you don't care about ordering you don't need two of
> them either. So you should be able to just do:
> 
> 	u32 val;
> 
> 	val = readl(base + SE_GENI_M_IRQ_EN);
> 	val |= M_COMMON_GENI_M_IRQ_EN;
> 	writel(val, base + SE_GENI_M_IRQ_EN);
> 
> 	val = readl(base + SE_GENI_S_IRQ_EN);
> 	val |= S_COMMON_GENI_S_IRQ_EN;
> 	writel(val, base + SE_GENI_S_IRQ_EN);
Ok.
> 
>> +
>> +	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
>> +	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
>> +	/* Common to all modes */
>> +	common_geni_m_irq_en |= M_COMMON_GENI_M_IRQ_EN;
>> +	common_geni_s_irq_en |= S_COMMON_GENI_S_IRQ_EN;
>> +
>> +	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
>> +	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
>> +	return 0;
> 
> And as this can't fail there's no reason to have a return value.
Ok.
> 
>> +}
>> +
>> +
>> +static void geni_se_set_rx_rfr_wm(void __iomem *base, unsigned int rx_wm,
>> +				  unsigned int rx_rfr)
>> +{
>> +	geni_write_reg(rx_wm, base, SE_GENI_RX_WATERMARK_REG);
>> +	geni_write_reg(rx_rfr, base, SE_GENI_RX_RFR_WATERMARK_REG);
>> +}
>> +
>> +static int geni_se_io_set_mode(void __iomem *base)
>> +{
>> +	unsigned int io_mode;
>> +	unsigned int geni_dma_mode;
>> +
>> +	io_mode = geni_read_reg(base, SE_IRQ_EN);
>> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
>> +
>> +	io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
>> +	io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
>> +	geni_dma_mode &= ~GENI_DMA_MODE_EN;
>> +
>> +	geni_write_reg(io_mode, base, SE_IRQ_EN);
>> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
>> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
>> +	return 0;
> 
> As this function can't fail, don't return a value.
Ok.
> 
>> +}
>> +
>> +static void geni_se_io_init(void __iomem *base)
>> +{
>> +	unsigned int io_op_ctrl;
>> +	unsigned int geni_cgc_ctrl;
>> +	unsigned int dma_general_cfg;
> 
> These are all u32, be explicit.
Ok.
> 
>> +
>> +	geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
>> +	dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
>> +	geni_cgc_ctrl |= DEFAULT_CGC_EN;
>> +	dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
>> +			DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);
> 
> Drop the parenthesis and there's no harm in making multiple assignments
> in favour of splitting the line.
Ok.
> 
>> +	io_op_ctrl = DEFAULT_IO_OUTPUT_CTRL_MSK;
>> +	geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
>> +	geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);
> 
> Is there a reason why this chunk of code is a mix of 3 independent
> register updates?
I am not sure I understand the context of your question. This is how the 
hardware programming manual is describing to program the registers as 
part of initializing a serial engine. Please let me know if this is not 
the information you are looking for.
> 
>> +
>> +	geni_write_reg(io_op_ctrl, base, GENI_OUTPUT_CTRL);
>> +	geni_write_reg(FORCE_DEFAULT, base, GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +/**
>> + * geni_se_init() - Initialize the GENI Serial Engine
>> + * @base:	Base address of the serial engine's register block.
>> + * @rx_wm:	Receive watermark to be configured.
>> + * @rx_rfr_wm:	Ready-for-receive watermark to be configured.
> 
> What's the unit for these?
The unit is the number of hardware FIFO words.
> 
>> + *
>> + * This function is used to initialize the GENI serial engine, configure
>> + * receive watermark and ready-for-receive watermarks.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
> 
> As neither geni_se_io_set_mode() nor geni_se_irq_en() can fail there's
> no way geni_se_init() can fail and as such you don't need a return value
> of this function.
Ok.
> 
>> + */
>> +int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr)
>> +{
>> +	int ret;
>> +
>> +	geni_se_io_init(base);
>> +	ret = geni_se_io_set_mode(base);
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_se_set_rx_rfr_wm(base, rx_wm, rx_rfr);
>> +	ret = geni_se_irq_en(base);
> 
> Just inline these two functions here.
> 
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_init);
> 
> This is an excellent candidate for initializing a common "geni
> function"-struct shared among the children, i.e. make this:
> 
> void geni_init_port(struct geni_port *port, struct device *dev,
> 		    size_t rx_wm, rfr_wm);
> 
> And have this do the ioremap of the memory of dev and initialize some
> common "geni_port" struct, then you can pass this to some set of
> geni_port_*() helper functions.
Sure, this sounds like a good place to do ioremap of children and 
initialize common serial engine resources.
> 
>> +
>> +static int geni_se_select_fifo_mode(void __iomem *base)
>> +{
>> +	int proto = geni_se_get_proto(base);
>> +	unsigned int common_geni_m_irq_en;
>> +	unsigned int common_geni_s_irq_en;
>> +	unsigned int geni_dma_mode;
> 
> These are of type u32.
> 
> If you use more succinct names the function will be easier to read; what
> about master, slave and mode? (Or m_en, s_en and mode)
Ok.
> 
>> +
>> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
> 
> Use lowercase for the hex constants.
Ok.
> 
>> +
>> +	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
>> +	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
>> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
>> +	if (proto != UART) {
>> +		common_geni_m_irq_en |=
>> +			(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
>> +			M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
> 
> Drop the parenthesis, split the assignment in multiple to make things
> not span 3 lines.
Ok.
> 
>> +		common_geni_s_irq_en |= S_CMD_DONE_EN;
>> +	}
>> +	geni_dma_mode &= ~GENI_DMA_MODE_EN;
> 
> Please group things that are related.
> 
> The compiler doesn't care if you stretch the life time of your local
> variables through your functions, but the brain does.
Ok.
> 
>> +
>> +	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
>> +	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
>> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
>> +	return 0;
> 
> This can't fail, and you ignore the returned value in
> geni_se_select_mode().
Ok.
> 
>> +}
>> +
>> +static int geni_se_select_dma_mode(void __iomem *base)
>> +{
>> +	unsigned int geni_dma_mode = 0;
> 
> This is u32, can be shorten to "mode" and it's first use is an
> assignment - so you don't have to initialize it here.
Ok.
> 
>> +
>> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
> 
> Please lower case your hex constants.
Ok.
> 
>> +
>> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
>> +	geni_dma_mode |= GENI_DMA_MODE_EN;
>> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
>> +	return 0;
> 
> Can't fail.
Ok.
> 
>> +}
>> +
>> +/**
>> + * geni_se_select_mode() - Select the serial engine transfer mode
>> + * @base:	Base address of the serial engine's register block.
>> + * @mode:	Transfer mode to be selected.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure.
>> + */
>> +int geni_se_select_mode(void __iomem *base, int mode)
>> +{
>> +	int ret = 0;
> 
> Drop the return value and "ret", if you want to help the developer of
> child devices you can start off by a WARN_ON(mode != FIFO_MODE && mode
> != SE_DMA);
Ok.
> 
>> +
>> +	switch (mode) {
>> +	case FIFO_MODE:
>> +		geni_se_select_fifo_mode(base);
>> +		break;
>> +	case SE_DMA:
>> +		geni_se_select_dma_mode(base);
>> +		break;
>> +	default:
>> +		ret = -EINVAL;
>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_select_mode);
>> +
>> +/**
>> + * geni_se_setup_m_cmd() - Setup the primary sequencer
>> + * @base:	Base address of the serial engine's register block.
>> + * @cmd:	Command/Operation to setup in the primary sequencer.
>> + * @params:	Parameter for the sequencer command.
>> + *
>> + * This function is used to configure the primary sequencer with the
>> + * command and its assoicated parameters.
>> + */
>> +void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params)
>> +{
>> +	u32 m_cmd = (cmd << M_OPCODE_SHFT);
>> +
>> +	m_cmd |= (params & M_PARAMS_MSK);
> 
> Rather than initializing the variable during declaration and then
> amending the value it's cleaner if you do:
> 
> 	val = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
Ok.
> 
>> +	geni_write_reg(m_cmd, base, SE_GENI_M_CMD0);
>> +}
>> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
>> +
> [..]
>> +/**
>> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * This function is used to get the depth i.e. number of elements in the
>> + * TX fifo of the serial engine.
>> + *
>> + * Return:	TX fifo depth in units of FIFO words.
>> + */
>> +int geni_se_get_tx_fifo_depth(void __iomem *base)
>> +{
>> +	int tx_fifo_depth;
>> +
>> +	tx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_0)
>> +			& TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT);
> 
> It easier to read this way:
> 
> 	u32 val;
> 
> 	val = readl(base, SE_HW_PARAM_0);
> 
> 	return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
Ok.
> 
>> +	return tx_fifo_depth;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
>> +
>> +/**
>> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * This function is used to get the width i.e. word size per element in the
>> + * TX fifo of the serial engine.
>> + *
>> + * Return:	TX fifo width in bits
>> + */
>> +int geni_se_get_tx_fifo_width(void __iomem *base)
>> +{
>> +	int tx_fifo_width;
>> +
>> +	tx_fifo_width = ((geni_read_reg(base, SE_HW_PARAM_0)
>> +			& TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT);
>> +	return tx_fifo_width;
> 
> Ditto.
Ok.
> 
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
>> +
>> +/**
>> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * This function is used to get the depth i.e. number of elements in the
>> + * RX fifo of the serial engine.
>> + *
>> + * Return:	RX fifo depth in units of FIFO words
>> + */
>> +int geni_se_get_rx_fifo_depth(void __iomem *base)
>> +{
>> +	int rx_fifo_depth;
>> +
>> +	rx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_1)
>> +			& RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT);
>> +	return rx_fifo_depth;
> 
> Ditto.
Ok.
> 
>> +}
>> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
>> +
>> +/**
>> + * geni_se_get_packing_config() - Get the packing configuration based on input
>> + * @bpw:	Bits of data per transfer word.
>> + * @pack_words:	Number of words per fifo element.
>> + * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
>> + * @cfg0:	Output buffer to hold the first half of configuration.
>> + * @cfg1:	Output buffer to hold the second half of configuration.
>> + *
>> + * This function is used to calculate the packing configuration based on
>> + * the input packing requirement and the configuration logic.
>> + */
>> +void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
>> +				unsigned long *cfg0, unsigned long *cfg1)
> 
> This function is used only from geni_se_config_packing() which writes
> the new config to the associated registers and from the serial driver
> that does the same - but here pack_words differ for rx and tx.
> 
> If you improve geni_se_config_packing() to take rx and tx fifo sizes
> independently you don't have to expose this function to the client
> drivers and you don't need to return cfg0 and cfg1 like you do here.
Ok.
> 
>> +{
>> +	u32 cfg[4] = {0};
>> +	int len;
>> +	int temp_bpw = bpw;
>> +	int idx_start = (msb_to_lsb ? (bpw - 1) : 0);
>> +	int idx = idx_start;
>> +	int idx_delta = (msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE);
>> +	int ceil_bpw = ((bpw & (BITS_PER_BYTE - 1)) ?
>> +			((bpw & ~(BITS_PER_BYTE - 1)) + BITS_PER_BYTE) : bpw);
>> +	int iter = (ceil_bpw * pack_words) >> 3;
>> +	int i;
>> +
>> +	if (unlikely(iter <= 0 || iter > 4)) {
> 
> Don't use unlikely(), this function is not called one per port, there's
> no need to clutter the code to "optimize" it.
> 
>> +		*cfg0 = 0;
>> +		*cfg1 = 0;
>> +		return;
>> +	}
>> +
>> +	for (i = 0; i < iter; i++) {
>> +		len = (temp_bpw < BITS_PER_BYTE) ?
>> +				(temp_bpw - 1) : BITS_PER_BYTE - 1;
>> +		cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));
>> +		idx = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
>> +				((i + 1) * BITS_PER_BYTE) + idx_start :
>> +				idx + idx_delta;
>> +		temp_bpw = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
>> +				bpw : (temp_bpw - BITS_PER_BYTE);
> 
> Each operation in this loop depend on temp_bpw being smaller or larger
> than BITS_PER_BYTE, please rewrite it with a single if/else based on
> this.
> 
>> +	}
>> +	cfg[iter - 1] |= 1;
> 
> The 1 you're writing here looks like an "EOF". It would be nice with a
> comment describing the format of these 4 registers and the meaning of
> BIT(0).
> 
>> +	*cfg0 = cfg[0] | (cfg[1] << 10);
>> +	*cfg1 = cfg[2] | (cfg[3] << 10);
>> +}
>> +EXPORT_SYMBOL(geni_se_get_packing_config);
>> +
>> +/**
>> + * geni_se_config_packing() - Packing configuration of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + * @bpw:	Bits of data per transfer word.
>> + * @pack_words:	Number of words per fifo element.
>> + * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
>> + *
>> + * This function is used to configure the packing rules for the current
>> + * transfer.
>> + */
>> +void geni_se_config_packing(void __iomem *base, int bpw,
>> +			    int pack_words, bool msb_to_lsb)
>> +{
>> +	unsigned long cfg0, cfg1;
> 
> These are u32.
Ok.
> 
>> +
>> +	geni_se_get_packing_config(bpw, pack_words, msb_to_lsb, &cfg0, &cfg1);
>> +	geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
>> +	geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
>> +	geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
>> +	geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
>> +	if (pack_words || bpw == 32)
>> +		geni_write_reg((bpw >> 4), base, SE_GENI_BYTE_GRAN);
>> +}
>> +EXPORT_SYMBOL(geni_se_config_packing);
>> +
>> +static void geni_se_clks_off(struct geni_se_rsc *rsc)
>> +{
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
> 
> Drop the unlikely(). Why would you be passed a rsc with wrapper_dev
> NULL?
Used it just to handle any development time issues. I will remove the 
unlikely().
> 
>> +		return;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return;
>> +
>> +	clk_disable_unprepare(rsc->se_clk);
>> +	clk_disable_unprepare(geni_se_dev->s_ahb_clk);
>> +	clk_disable_unprepare(geni_se_dev->m_ahb_clk);
>> +}
>> +
>> +/**
>> + * geni_se_resources_off() - Turn off resources associated with the serial
>> + *                           engine
>> + * @rsc:	Handle to resources associated with the serial engine.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_off(struct geni_se_rsc *rsc)
>> +{
>> +	int ret = 0;
> 
> No need to initialize ret.
Ok.
> 
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
>> +		return -EINVAL;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
> 
> 
> Only child devices would call this, so it's unlikely that this is a
> probe defer.
> 
> Also the returned value is not used.
> 
> And drop the unlikely()
Ok.
> 
>> +		return -ENODEV;
>> +
>> +	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_se_clks_off(rsc);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_off);
>> +
>> +static int geni_se_clks_on(struct geni_se_rsc *rsc)
>> +{
>> +	int ret;
>> +	struct geni_se_device *geni_se_dev;
> 
> If you name this "geni" instead, or "wrapper" the rest will be cleaner.
Ok.
> 
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
>> +		return -EINVAL;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return -EPROBE_DEFER;
>> +
>> +	ret = clk_prepare_enable(geni_se_dev->m_ahb_clk);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = clk_prepare_enable(geni_se_dev->s_ahb_clk);
>> +	if (ret) {
>> +		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
>> +		return ret;
>> +	}
> 
> These two could be dealt with in a single clk_bulk_prepare_enable().
Ok.
> 
>> +
>> +	ret = clk_prepare_enable(rsc->se_clk);
>> +	if (ret) {
>> +		clk_disable_unprepare(geni_se_dev->s_ahb_clk);
>> +		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
>> +	}
>> +	return ret;
>> +}
>> +
>> +/**
>> + * geni_se_resources_on() - Turn on resources associated with the serial
>> + *                          engine
>> + * @rsc:	Handle to resources associated with the serial engine.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_on(struct geni_se_rsc *rsc)
>> +{
>> +	int ret = 0;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
>> +		return -EINVAL;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
> 
> As with geni_se_resources_off()
Ok.
> 
>> +		return -EPROBE_DEFER;
>> +
>> +	ret = geni_se_clks_on(rsc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
> 
> pinctrl_pm_select_default_state(rsc->dev);
> 
> So you need the dev associated with the rsc.
> 
>> +	if (ret)
>> +		geni_se_clks_off(rsc);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_on);
>> +
>> +/**
>> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
>> + * @rsc:	Resource for which the clock table is requested.
>> + * @tbl:	Table in which the output is returned.
>> + *
>> + * This function is called by the protocol drivers to determine the different
>> + * clock frequencies supported by Serail Engine Core Clock. The protocol
> 
> s/Serail/Serial/
Ok.
> 
>> + * drivers use the output to determine the clock frequency index to be
>> + * programmed into DFS.
>> + *
>> + * Return:	number of valid performance levels in the table on success,
>> + *		standard Linux error codes on failure.
>> + */
>> +int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl)
>> +{
>> +	struct geni_se_device *geni_se_dev;
>> +	int i;
>> +	unsigned long prev_freq = 0;
>> +	int ret = 0;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))
> 
> Drop the "unlikely", you're only calling this once.
Ok.
> 
>> +		return -EINVAL;
>> +
>> +	*tbl = NULL;
> 
> Don't touch tbl on error.
Ok.
> 
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return -EPROBE_DEFER;
> 
> How would this even be possible? This function should only be called by
> child devices, which per definition means that this device been probed.
I agree. Prior to this patchset, the serial engine nodes were not 
children nodes of wrapper devices. Hence the check existed. Now that the 
device tree nodes are re-structured, this check can be removed.
> 
>> +
>> +	mutex_lock(&geni_se_dev->geni_dev_lock);
>> +	if (geni_se_dev->clk_perf_tbl) {
>> +		*tbl = geni_se_dev->clk_perf_tbl;
>> +		ret = geni_se_dev->num_clk_levels;
>> +		goto exit_se_clk_tbl_get;
>> +	}
> 
> You're never freeing clk_perf_tbl, and the only reason you have
> geni_dev_lock is to protect the "cached" array in geni_se_dev.
> 
> Move the allocation and generation of clk_perf_tbl to geni_se_probe()
> and store the value, then make this function just return that array.
> That way you can drop the lock and the code becomes cleaner.
Currently this driver is initializing in arch_init. It can be moved to 
module_init stage as long as the clk_round_rate() returns -EPROBE_DEFER 
when it is not ready. I will look into it and do the appropriate change.
> 
>> +
>> +	geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
>> +						MAX_CLK_PERF_LEVEL, GFP_KERNEL);
> 
> Use kcalloc()
Ok.
> 
>> +	if (!geni_se_dev->clk_perf_tbl) {
>> +		ret = -ENOMEM;
>> +		goto exit_se_clk_tbl_get;
>> +	}
>> +
>> +	for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
>> +		geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
>> +								prev_freq + 1);
>> +		if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
>> +			geni_se_dev->clk_perf_tbl[i] = 0;
> 
> Use a local variable to keep the return value of clk_round_rate(), that
> way you don't have to revert the value here (not that it matters...) and
> you don't need to line wrap the assignment above.
Ok.
> 
>> +			break;
>> +		}
>> +		prev_freq = geni_se_dev->clk_perf_tbl[i];
> 
> ...and then you don't need a separate local variable to keep track of
> the last value...
Ok.
> 
>> +	}
>> +	geni_se_dev->num_clk_levels = i;
>> +	*tbl = geni_se_dev->clk_perf_tbl;
>> +	ret = geni_se_dev->num_clk_levels;
>> +exit_se_clk_tbl_get:
> 
> Name your labels based on what action they perform, e.g. "out_unlock"
> (not err_unlock here because it's the successful path as well)
Ok.
> 
>> +	mutex_unlock(&geni_se_dev->geni_dev_lock);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
>> +
>> +/**
>> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
>> + * @rsc:	Resource for which the clock frequency is requested.
>> + * @req_freq:	Requested clock frequency.
>> + * @index:	Index of the resultant frequency in the table.
>> + * @res_freq:	Resultant frequency which matches or is closer to the
>> + *		requested frequency.
>> + * @exact:	Flag to indicate exact multiple requirement of the requested
>> + *		frequency .
>> + *
>> + * This function is called by the protocol drivers to determine the matching
>> + * or closest frequency of the Serial Engine clock to be selected in order
>> + * to meet the performance requirements.
> 
> What's the benefit compared to calling clk_round_rate()?
> 
> Given that the function can return a rate that is a fraction of the
> requested frequency even though "exact" is set, this description isn't
> explaining the entire picture.
I agree, the description makes it look like clk_round_rate(). But it is 
different from clk_round_rate() in the sense that it will try to return 
the matching or exact multiple of the requested frequency. If there is 
no matching or exact multiple value found, then it returns the closest 
floor frequency available. I will update the description accordingly.
> 
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure.
> 
> Returning the new clockrate would make more sense.
> 
>> + */
>> +int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
>> +			   unsigned int *index, unsigned long *res_freq,
>> +			   bool exact)
>> +{
>> +	unsigned long *tbl;
>> +	int num_clk_levels;
>> +	int i;
>> +
>> +	num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
>> +	if (num_clk_levels < 0)
>> +		return num_clk_levels;
>> +
>> +	if (num_clk_levels == 0)
>> +		return -EFAULT;
>> +
>> +	*res_freq = 0;
>> +	for (i = 0; i < num_clk_levels; i++) {
>> +		if (!(tbl[i] % req_freq)) {
>> +			*index = i;
>> +			*res_freq = tbl[i];
> 
> So this will return a frequency that divides the requested frequency
> without a remainder, will index be used to calculate a multiplier from
> this?
The serial interface drivers need to program the hardware register with 
the index of the clock rate from the clock controller source. Hence the 
index is returned.
> 
>> +			return 0;
>> +		}
>> +
>> +		if (!(*res_freq) || ((tbl[i] > *res_freq) &&
>> +				     (tbl[i] < req_freq))) {
>> +			*index = i;
>> +			*res_freq = tbl[i];
> 
> What if there's a previous value in tbl that given some multiplier has a
> smaller difference to the requested frequency?
At this point the requirement is to return matching or exact multiple of 
the requested frequency. If those criteria are not met, return the 
closest floor frequency.
> 
>> +		}
>> +	}
>> +
>> +	if (exact || !(*res_freq))
> 
> *res_freq can't be 0, because in the case that num_clk_levels is 0 you
> already returned -EFAULT above, in all other cases you will assign it at
> least once.
Ok.
> 
>> +		return -ENOKEY;
>> +
>> +	return 0;
> 
> Why not use the return value for the calculated frequency?
With frequency being unsigned long, I dont see a clean way to return any 
error.
> 
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_freq_match);
>> +
>> +/**
>> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
>> + * @base:		Base address of the SE register block.
>> + * @tx_buf:		Pointer to the TX buffer.
>> + * @tx_len:		Length of the TX buffer.
>> + * @tx_dma:		Pointer to store the mapped DMA address.
>> + *
>> + * This function is used to prepare the buffers for DMA TX.
>> + *
>> + * Return:	0 on success, standard Linux error codes on error/failure.
>> + */
>> +int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
>> +			void *tx_buf, int tx_len, dma_addr_t *tx_dma)
> 
> All child devices will have a "base", so if you move that into what you
> today call a geni_se_rsc and you pass that to all these functions
> operating on behalf of the child device you have the first two
> parameters passed in the same object.
> 
> A "tx_len" is typically of type size_t.
> 
> Also note that there are no other buf than tx_buf, no other len than
> tx_len and no other dma address than tx_dma in this function, so you can
> drop "tx_" without loosing any information - but improving code
> cleanliness.
Ok.
> 
>> +{
>> +	int ret;
>> +
>> +	if (unlikely(!wrapper_dev || !base || !tx_buf || !tx_len || !tx_dma))
>> +		return -EINVAL;
> 
> Reduce the error checking here.
Ok.
> 
>> +
>> +	ret = geni_se_map_buf(wrapper_dev, tx_dma, tx_buf, tx_len,
>> +				    DMA_TO_DEVICE);
> 
> I think you should pass the wrapper itself to geni_se_tx_dma_prep() and
> if you name this "wrapper" (instead of wrapper_dev) you're under 80
> chars and don't need the line break.
Ok.
> 
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);
> 
> So that's DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN?
No, that is DMA_DONE_EN (BIT 0), EOT_EN (BIT 1) and AHB_ERR_EN (BIT 2).
> 
>> +	geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
>> +	geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);
> 
> If you return the dma_addr_t from this function you will have the happy
> side effect of being able to use tx_dma directly instead of *tx_dma and
> you can remove some craziness from these calls.
> 
> 
> 
>> +	geni_write_reg(1, base, SE_DMA_TX_ATTR);
> 
> This "1" would be nice to have some clarification on.
This indicates to the hardware that the current buffer is of 
End-of-Transfer Type. I will add a comment here or replace the magic 1 
with an appropriate preprocessor macro.
> 
>> +	geni_write_reg(tx_len, base, SE_DMA_TX_LEN);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>> +
>> +/**
>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
>> + * @base:		Base address of the SE register block.
>> + * @rx_buf:		Pointer to the RX buffer.
>> + * @rx_len:		Length of the RX buffer.
>> + * @rx_dma:		Pointer to store the mapped DMA address.
>> + *
>> + * This function is used to prepare the buffers for DMA RX.
>> + *
>> + * Return:	0 on success, standard Linux error codes on error/failure.
> 
> The only real error that can come out of this is dma_mapping_error(), so
> just return the dma_addr_t.
Ok.
> 
>> + */
>> +int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
>> +			void *rx_buf, int rx_len, dma_addr_t *rx_dma)
> 
> As with tx, drop all the "rx_". And pass that geni_se_rsc object
> instead.
Ok.
> 
>> +{
>> +	int ret;
>> +
>> +	if (unlikely(!wrapper_dev || !base || !rx_buf || !rx_len || !rx_dma))
>> +		return -EINVAL;
>> +
>> +	ret = geni_se_map_buf(wrapper_dev, rx_dma, rx_buf, rx_len,
>> +				    DMA_FROM_DEVICE);
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
> 
> We enable same interrupts for rx as tx? Why enable TX interrupt here?
No, the interrupts enabled are DMA_DONE_EN, EOT_EN and AHB_ERR_EN.
> 
>> +	geni_write_reg((u32)(*rx_dma), base, SE_DMA_RX_PTR_L);
>> +	geni_write_reg((u32)((*rx_dma) >> 32), base, SE_DMA_RX_PTR_H);
>> +	/* RX does not have EOT bit */
> 
> Okay? How does this relate to the 0 being written into RX_ATTR?
Instead of EOT bit, this register has RX DMA mode bits. Writing 0 means 
the buffer is a single buffer. Writing 1 means the data is present as 
part of the command itself and 2 means the buffer is scatter-gather buffer.
> 
>> +	geni_write_reg(0, base, SE_DMA_RX_ATTR);
>> +	geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
>> +
>> +/**
>> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
>> + * @tx_dma:		DMA address of the TX buffer.
>> + * @tx_len:		Length of the TX buffer.
>> + *
>> + * This function is used to unprepare the DMA buffers after DMA TX.
>> + */
>> +void geni_se_tx_dma_unprep(struct device *wrapper_dev,
>> +			   dma_addr_t tx_dma, int tx_len)
>> +{
>> +	if (tx_dma)
>> +		geni_se_unmap_buf(wrapper_dev, &tx_dma, tx_len,
>> +						DMA_TO_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
>> +
>> +/**
>> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
>> + * @rx_dma:		DMA address of the RX buffer.
>> + * @rx_len:		Length of the RX buffer.
>> + *
>> + * This function is used to unprepare the DMA buffers after DMA RX.
>> + */
>> +void geni_se_rx_dma_unprep(struct device *wrapper_dev,
>> +			   dma_addr_t rx_dma, int rx_len)
>> +{
>> +	if (rx_dma)
>> +		geni_se_unmap_buf(wrapper_dev, &rx_dma, rx_len,
>> +						DMA_FROM_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>> +
>> +/**
>> + * geni_se_map_buf() - Map a single buffer into QUP wrapper device
> 
> I find the mixture of prepare and map in the API (where prepare also
> maps) confusing, but no-one calls genbi_se_map_buf() can it be made
> internal?
Sure it can be made internal and later exposed when the serial interface 
drivers need it.
> 
>> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
>> + * @iova:		Pointer in which the mapped virtual address is stored.
>> + * @buf:		Address of the buffer that needs to be mapped.
>> + * @size:		Size of the buffer.
>> + * @dir:		Direction of the DMA transfer.
>> + *
>> + * This function is used to map an already allocated buffer into the
>> + * QUP device space.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
>> +		    void *buf, size_t size, enum dma_data_direction dir)
>> +{
>> +	struct device *dev_p;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (!wrapper_dev || !iova || !buf || !size)
>> +		return -EINVAL;
> 
> No need to check that the programmer of the intermediate function
> checked that the programmer of the client filled all these out
> correctly.
Ok.
> 
>> +
>> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
>> +	if (!geni_se_dev || !geni_se_dev->dev)
>> +		return -ENODEV;
> 
> Pass the geni_se_device from the child driver, to remove the need for
> this.
Ok.
> 
>> +
>> +	dev_p = geni_se_dev->dev;
> 
> Name your variables more succinct and you don't need this local alias.
Ok.
> 
>> +
>> +	*iova = dma_map_single(dev_p, buf, size, dir);
> 
> Just inline dma_map_single() in the functions calling this.
Ok.
> 
>> +	if (dma_mapping_error(dev_p, *iova))
>> +		return -EIO;
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_map_buf);
>> +
>> +/**
>> + * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
>> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
>> + * @iova:		Pointer in which the mapped virtual address is stored.
>> + * @size:		Size of the buffer.
>> + * @dir:		Direction of the DMA transfer.
>> + *
>> + * This function is used to unmap an already mapped buffer from the
>> + * QUP device space.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
>> +		      size_t size, enum dma_data_direction dir)
> 
> There's no reason for iova to be a pointer. Just pass the dma_addr_t as
> is.
Ok.
> 
> Should this function really be exposed to the clients?
It can be made internal based on the current use-case.
> 
>> +{
>> +	struct device *dev_p;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (!wrapper_dev || !iova || !size)
>> +		return -EINVAL;
> 
> Reduce the error checking.
Ok.
> 
>> +
>> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
>> +	if (!geni_se_dev || !geni_se_dev->dev)
>> +		return -ENODEV;
> 
> And pass the geni_se_rsc to this function for symmetry purposes, which
> would give you the wrapper by just following the pointer and then the
> device from there.
Ok.
> 
>> +
>> +	dev_p = geni_se_dev->dev;
>> +	dma_unmap_single(dev_p, *iova, size, dir);
> 
> Just inline dma_unmap_single() in the calling functions.
Ok.
> 
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_unmap_buf);
>> +
>> +static const struct of_device_id geni_se_dt_match[] = {
>> +	{ .compatible = "qcom,geni-se-qup", },
>> +	{}
>> +};
> 
> Move this next to the geni_se_driver below and don't forget the
> MODULE_DEVICE_TABLE()
Ok.
> 
>> +
>> +static int geni_se_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct resource *res;
>> +	struct geni_se_device *geni_se_dev;
> 
> Just name this "geni".
Ok.
> 
>> +	int ret = 0;
> 
> No need to initialize ret, it's only ever used after assignment.
Ok.
> 
>> +
>> +	geni_se_dev = devm_kzalloc(dev, sizeof(*geni_se_dev), GFP_KERNEL);
>> +	if (!geni_se_dev)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(dev, "%s: Mandatory resource info not found\n",
>> +			__func__);
>> +		devm_kfree(dev, geni_se_dev);
>> +		return -EINVAL;
>> +	}
> 
> It's idiomatic to not check for errors of platform_get_resource() as
> devm_ioremap_resource() will fail gracefully if this happens.
Ok, I will remove the check and use the error returned by 
devm_ioremap_resource, if any.
> 
>> +
>> +	geni_se_dev->base = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR_OR_NULL(geni_se_dev->base)) {
> 
> This should be IS_ERR() only.
Ok.
> 
>> +		dev_err(dev, "%s: Error mapping the resource\n", __func__);
>> +		devm_kfree(dev, geni_se_dev);
>> +		return -EFAULT;
>> +	}
>> +
>> +	geni_se_dev->m_ahb_clk = devm_clk_get(dev, "m-ahb");
>> +	if (IS_ERR(geni_se_dev->m_ahb_clk)) {
>> +		ret = PTR_ERR(geni_se_dev->m_ahb_clk);
>> +		dev_err(dev, "Err getting M AHB clk %d\n", ret);
>> +		devm_iounmap(dev, geni_se_dev->base);
>> +		devm_kfree(dev, geni_se_dev);
> 
> The reason we use the devm_ versions of these is so that we don't have
> to release our resources explicitly.
Ok, I will remove releasing the resources explicitly.
> 
>> +		return ret;
>> +	}
>> +
>> +	geni_se_dev->s_ahb_clk = devm_clk_get(dev, "s-ahb");
>> +	if (IS_ERR(geni_se_dev->s_ahb_clk)) {
>> +		ret = PTR_ERR(geni_se_dev->s_ahb_clk);
>> +		dev_err(dev, "Err getting S AHB clk %d\n", ret);
>> +		devm_clk_put(dev, geni_se_dev->m_ahb_clk);
>> +		devm_iounmap(dev, geni_se_dev->base);
>> +		devm_kfree(dev, geni_se_dev);
>> +		return ret;
>> +	}
> 
> Use devm_clk_bulk_get().
Ok.
> 
>> +
>> +	geni_se_dev->dev = dev;
>> +	mutex_init(&geni_se_dev->geni_dev_lock);
>> +	dev_set_drvdata(dev, geni_se_dev);
>> +	dev_dbg(dev, "GENI SE Driver probed\n");
>> +	return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
> 
> You're not depopulating these devices when the wrapper goes away. Use
> devm_of_platform_populate() here instead to make that happen.
Ok.
> 
>> +}
>> +
>> +static int geni_se_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct geni_se_device *geni_se_dev = dev_get_drvdata(dev);
>> +
>> +	devm_clk_put(dev, geni_se_dev->s_ahb_clk);
>> +	devm_clk_put(dev, geni_se_dev->m_ahb_clk);
>> +	devm_iounmap(dev, geni_se_dev->base);
>> +	devm_kfree(dev, geni_se_dev);
> 
> Again, the reason to use devm_* is so that you don't have to free
> things...so if this is what you have here you don't even need a remove
> function.
Ok.
> 
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver geni_se_driver = {
>> +	.driver = {
>> +		.name = "geni_se_qup",
>> +		.of_match_table = geni_se_dt_match,
>> +	},
>> +	.probe = geni_se_probe,
>> +	.remove = geni_se_remove,
>> +};
>> +
>> +static int __init geni_se_driver_init(void)
>> +{
>> +	return platform_driver_register(&geni_se_driver);
>> +}
>> +arch_initcall(geni_se_driver_init);
>> +
>> +static void __exit geni_se_driver_exit(void)
>> +{
>> +	platform_driver_unregister(&geni_se_driver);
>> +}
>> +module_exit(geni_se_driver_exit);
> 
> Should be fine to just use module_platform_driver(), you need separate
> support for earlycon regardless.
Ok.
> 
>> +
>> +MODULE_DESCRIPTION("GENI Serial Engine Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
>> new file mode 100644
>> index 0000000..5695da9
>> --- /dev/null
>> +++ b/include/linux/qcom-geni-se.h
>> @@ -0,0 +1,807 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> [..]
>> + */
>> +
> 
> Use SPDX header as above.
Ok.
> 
>> +#ifndef _LINUX_QCOM_GENI_SE
>> +#define _LINUX_QCOM_GENI_SE
>> +#include <linux/clk.h>
>> +#include <linux/dma-direction.h>
>> +#include <linux/io.h>
>> +#include <linux/list.h>
> 
> I don't think there's a need for io.h or list.h here.
Ok.
> 
>> +
>> +/* Transfer mode supported by GENI Serial Engines */
>> +enum geni_se_xfer_mode {
>> +	INVALID,
>> +	FIFO_MODE,
>> +	SE_DMA,
> 
> These are quite bad names for things in a header file.
I am not sure if you are suggesting me to use a prefix to make these 
definitions look GENI specific. I would prefer some clarification in 
this regard.
> 
>> +};
>> +
>> +/* Protocols supported by GENI Serial Engines */
>> +enum geni_se_protocol_types {
>> +	NONE,
>> +	SPI,
>> +	UART,
>> +	I2C,
>> +	I3C
> 
> Ditto
> 
>> +};
>> +
>> +/**
>> + * struct geni_se_rsc - GENI Serial Engine Resource
>> + * @wrapper_dev:	Pointer to the parent QUP Wrapper core.
>> + * @se_clk:		Handle to the core serial engine clock.
>> + * @geni_pinctrl:	Handle to the pinctrl configuration.
>> + * @geni_gpio_active:	Handle to the default/active pinctrl state.
>> + * @geni_gpi_sleep:	Handle to the sleep pinctrl state.
>> + */
>> +struct geni_se_rsc {
> 
> This looks like the common geni_port or geni_device I requested above.
Yes that is right. I will use it to include the serial engine base address.
> 
>> +	struct device *wrapper_dev;
>> +	struct clk *se_clk;
> 
> The one and only clock can be named "clk".
Ok.
> 
>> +	struct pinctrl *geni_pinctrl;
>> +	struct pinctrl_state *geni_gpio_active;
>> +	struct pinctrl_state *geni_gpio_sleep;
> 
> The associated struct device already has init, default, idle and sleep
> pinctrl states associated through the device->pins struct. Typically
> this means that if you provide a "default" and "sleep" state, the
> default will be selected when the device probes.
> 
> In order to select between the states call
> pinctrl_pm_select_{default,sleep,idle}_state(dev);
Ok.
> 
>> +};
>> +
>> +#define PINCTRL_DEFAULT	"default"
>> +#define PINCTRL_SLEEP	"sleep"
>> +
>> +/* Common SE registers */
> 
> The purpose of the helper functions in the wrapper driver is to hide
> the common details from the individual function drivers, so move these
> defines into the c-file as they shouldn't be needed in the individual
> drivers.
I will move the register definitions that are accessed through helper 
functions inside the source file. I may have to keep those register 
definitions that are common and are directly accessed by the serial 
interface drivers here so that there are no duplicate definitions.
> 
>> +#define GENI_INIT_CFG_REVISION		(0x0)
> 
> Drop the parenthesis.
Ok.
> 
> [..]
>> +#ifdef CONFIG_QCOM_GENI_SE
>> +/**
>> + * geni_read_reg_nolog() - Helper function to read from a GENI register
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + *
>> + * Return:	Return the contents of the register.
>> + */
> 
> The kerneldoc goes in the implementation, not the header file. And you
> already have a copy there, so remove it from here.
Ok.
> 
>> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset);
> [..]
>> +#else
> 
> I see no point in providing dummy functions for these, just make the
> individual drivers either depend or select the core helpers.
Ok.
> 
>> +static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
>> +{
>> +	return 0;
>> +}
> 
> Regards,
> Bjorn
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 0/7] Introduce GENI SE Controller Driver
  2018-01-19 18:32   ` [PATCH v2 0/7] Introduce GENI SE Controller Driver Randy Dunlap
@ 2018-01-31 18:59     ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-01-31 18:59 UTC (permalink / raw)
  To: Randy Dunlap, corbet, andy.gross, david.brown, robh+dt,
	mark.rutland, wsa, gregkh
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial, jslaby



On 1/19/2018 11:32 AM, Randy Dunlap wrote:
> On 01/12/2018 05:05 PM, Karthikeyan Ramasubramanian wrote:
>> Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
>> Wrapper is a next generation programmable module for supporting a wide
>> range of serial interfaces like UART, SPI, I2C, I3C, etc. A single QUP
>> module can provide upto 8 Serial Interfaces using its internal Serial
>> Engines (SE). The protocol supported by each interface is determined by
>> the firmware loaded to the Serial Engine.
>>
>> This patch series introduces GENI SE Driver to manage the GENI based QUP
>> Wrapper and the common aspects of all SEs inside the QUP Wrapper. This
>> patch series also introduces the UART and I2C Controller drivers to
>> drive the SEs that are programmed with the respective protocols.
> 
> Hi,
> 
> Will there be follow-up drivers for SPI, I3C, etc.?
Yes.
> 
> Thanks.
> 
>> [v2]
>>   * Updated device tree bindings to describe the hardware
>>   * Updated SE DT node as child node of QUP Wrapper DT node
>>   * Moved common AHB clocks to QUP Wrapper DT node
>>   * Use the standard "clock-frequency" I2C property
>>   * Update compatible field in UART Controller to reflect hardware manual
>>   * Addressed other device tree binding specific comments from Rob Herring
>>
>> Karthikeyan Ramasubramanian (7):
>>    qcom-geni-se: Add QCOM GENI SE Driver summary
>>    dt-bindings: soc: qcom: Add device tree binding for GENI SE
>>    soc: qcom: Add GENI based QUP Wrapper driver
>>    dt-bindings: i2c: Add device tree bindings for GENI I2C Controller
>>    i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C
>>      controller
>>    dt-bindings: serial: Add bindings for GENI based UART Controller
>>    tty: serial: msm_geni_serial: Add serial driver support for GENI based
>>      QUP
>>
>>   .../devicetree/bindings/i2c/i2c-qcom-geni.txt      |   35 +
>>   .../devicetree/bindings/serial/qcom,geni-uart.txt  |   29 +
>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  |   66 +
>>   Documentation/qcom-geni-se.txt                     |   56 +
>>   drivers/i2c/busses/Kconfig                         |   10 +
>>   drivers/i2c/busses/Makefile                        |    1 +
>>   drivers/i2c/busses/i2c-qcom-geni.c                 |  656 +++++++++
>>   drivers/soc/qcom/Kconfig                           |    8 +
>>   drivers/soc/qcom/Makefile                          |    1 +
>>   drivers/soc/qcom/qcom-geni-se.c                    | 1016 ++++++++++++++
>>   drivers/tty/serial/Kconfig                         |   10 +
>>   drivers/tty/serial/Makefile                        |    1 +
>>   drivers/tty/serial/qcom_geni_serial.c              | 1414 ++++++++++++++++++++
>>   include/linux/qcom-geni-se.h                       |  807 +++++++++++
>>   14 files changed, 4110 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
>>   create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
>>   create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>>   create mode 100644 Documentation/qcom-geni-se.txt
>>   create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>>   create mode 100644 drivers/soc/qcom/qcom-geni-se.c
>>   create mode 100644 drivers/tty/serial/qcom_geni_serial.c
>>   create mode 100644 include/linux/qcom-geni-se.h
>>
> 
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-19 22:57           ` Rob Herring
@ 2018-01-31 19:02             ` Karthik Ramasubramanian
       [not found]               ` <1abb0679-1997-9b70-30bd-d3472cea7053-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  0 siblings, 1 reply; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-01-31 19:02 UTC (permalink / raw)
  To: Rob Herring, Bjorn Andersson
  Cc: Rajendra Nayak, corbet, andy.gross, david.brown, mark.rutland,
	wsa, gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan



On 1/19/2018 3:57 PM, Rob Herring wrote:
> On Thu, Jan 18, 2018 at 08:57:45AM -0800, Bjorn Andersson wrote:
>> On Thu 18 Jan 01:13 PST 2018, Rajendra Nayak wrote:
>>
>>> []..
>>>
>>>>> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
>>>>> new file mode 100644
>>>>> index 0000000..3f43582
>>>>> --- /dev/null
>>>>> +++ b/drivers/soc/qcom/qcom-geni-se.c
>>>>> @@ -0,0 +1,1016 @@
>>>>> +/*
>>>>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>>>>> + *
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU General Public License version 2 and
>>>>> + * only 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.
>>>>> + *
>>>>> + */
>>>>
>>>> Please use SPDX style license header, i.e. this file should start with:
>>>>
>>>> // SPDX-License-Identifier: GPL-2.0
>>>> /*
>>>>   * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>>>>   */
>>>
>>> Looks like Mark Brown commented elsewhere [1] that we should use the C++
>>> commenting style even for the Linux Foundation copyright?
>>>
>>> [1] https://marc.info/?l=linux-clk&m=151497978626135&w=2
>>>
>>
>> While I can agree with Mark on the ugliness of the mixed commenting
>> style, this is the style that I found communicated and is what you find
>> in other files.
> 
> Well, that's pretty new guidance. Moving target...
> 
> Given that Linus said '//' comments are the only thing C++ got right, I
> expect to see more of them.
I believe that in the source file I have to use C++ style comments(as 
per this discussion) and in the header file I have to use C-style 
comments (as per https://lwn.net/Articles/739183/ for reasons related to 
tooling). Please correct me otherwise.
> 
> Rob
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-31 18:58       ` Karthik Ramasubramanian
@ 2018-02-05 23:50         ` Bjorn Andersson
  0 siblings, 0 replies; 42+ messages in thread
From: Bjorn Andersson @ 2018-02-05 23:50 UTC (permalink / raw)
  To: Karthik Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan

On Wed 31 Jan 10:58 PST 2018, Karthik Ramasubramanian wrote:
> On 1/16/2018 11:20 PM, Bjorn Andersson wrote:
> > On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
[..]
> > I'm not aware of any non-serial-engine "geni" at Qualcomm, so can we
> > drop the "se" throughout this driver?
> Currently GENI is used to program just the serial engines. But it is not
> restricted to that. It can be used to program other hardware cores. Hence
> keeping "se" will help to clarify that this driver is meant for GENI based
> serial engines only.

Okay, fair enough.

[..]
> > > diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
[..]
> > > +	geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
> > > +	dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
> > > +	geni_cgc_ctrl |= DEFAULT_CGC_EN;
> > > +	dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
> > > +			DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);
> > 
> > Drop the parenthesis and there's no harm in making multiple assignments
> > in favour of splitting the line.
> Ok.
> > 
> > > +	io_op_ctrl = DEFAULT_IO_OUTPUT_CTRL_MSK;
> > > +	geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
> > > +	geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);
> > 
> > Is there a reason why this chunk of code is a mix of 3 independent
> > register updates?
> I am not sure I understand the context of your question. This is how the
> hardware programming manual is describing to program the registers as part
> of initializing a serial engine. Please let me know if this is not the
> information you are looking for.

Can you please double check with the hardware guys if it really is
required that you do:

a = read(A)
b = read(B)

modify a
modify b
assign c

write(a)
write(b)
write(c)

And if that is the case, then try to make things as easy to read as
possible - e.g. by inlining the value of "c" and adding an empty line
between reads, modifications and writes as I did here.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
       [not found]               ` <1abb0679-1997-9b70-30bd-d3472cea7053-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2018-02-05 23:53                 ` Bjorn Andersson
  0 siblings, 0 replies; 42+ messages in thread
From: Bjorn Andersson @ 2018-02-05 23:53 UTC (permalink / raw)
  To: Karthik Ramasubramanian
  Cc: Rob Herring, Rajendra Nayak, corbet-T1hC0tSOHrs,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	wsa-z923LK4zBo2bacvFa/9K2g,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia, Girish Mahadevan

On Wed 31 Jan 11:02 PST 2018, Karthik Ramasubramanian wrote:
> On 1/19/2018 3:57 PM, Rob Herring wrote:
> > On Thu, Jan 18, 2018 at 08:57:45AM -0800, Bjorn Andersson wrote:
> > > On Thu 18 Jan 01:13 PST 2018, Rajendra Nayak wrote:
[..]
> > > > > 
> > > > > Please use SPDX style license header, i.e. this file should start with:
> > > > > 
> > > > > // SPDX-License-Identifier: GPL-2.0
> > > > > /*
> > > > >   * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> > > > >   */
> > > > 
> > > > Looks like Mark Brown commented elsewhere [1] that we should use the C++
> > > > commenting style even for the Linux Foundation copyright?
> > > > 
> > > > [1] https://marc.info/?l=linux-clk&m=151497978626135&w=2
> > > > 
> > > 
> > > While I can agree with Mark on the ugliness of the mixed commenting
> > > style, this is the style that I found communicated and is what you find
> > > in other files.
> > 
> > Well, that's pretty new guidance. Moving target...
> > 
> > Given that Linus said '//' comments are the only thing C++ got right, I
> > expect to see more of them.
> I believe that in the source file I have to use C++ style comments(as per
> this discussion) and in the header file I have to use C-style comments (as
> per https://lwn.net/Articles/739183/ for reasons related to tooling). Please
> correct me otherwise.

I had missed that detail, you're right.

Thanks,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
                     ` (2 preceding siblings ...)
  2018-01-24 23:07   ` [v2, " Evan Green
@ 2018-02-14 11:04   ` Amit Kucheria
  2018-02-23 18:06   ` [v2, " Guenter Roeck
  2018-02-23 19:05   ` Doug Anderson
  5 siblings, 0 replies; 42+ messages in thread
From: Amit Kucheria @ 2018-02-14 11:04 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, Andy Gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan, Sagar Dharia

On Sat, Jan 13, 2018 at 6:35 AM, Karthikeyan Ramasubramanian
<kramasub@codeaurora.org> wrote:
> This driver supports GENI based UART Controller in the Qualcomm SOCs. The
> Qualcomm Generic Interface (GENI) is a programmable module supporting a
> wide range of serial interfaces including UART. This driver support console
> operations using FIFO mode of transfer.
>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> ---
>  drivers/tty/serial/Kconfig            |   10 +
>  drivers/tty/serial/Makefile           |    1 +
>  drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
>  3 files changed, 1425 insertions(+)
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c

> +       if (!uport->membase) {
> +               ret = -ENOMEM;
> +               dev_err(&pdev->dev, "Err IO mapping serial iomem");
> +               goto exit_geni_serial_probe;
> +       }
> +
> +       dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);

You need to include linux/pinctrl/consumer.h for devm_pinctrl_get

I couldn't compile test it w/o it.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
                       ` (2 preceding siblings ...)
  2018-01-26 19:38     ` Doug Anderson
@ 2018-02-14 11:07     ` Amit Kucheria
  2018-02-16 20:44       ` Karthik Ramasubramanian
  3 siblings, 1 reply; 42+ messages in thread
From: Amit Kucheria @ 2018-02-14 11:07 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, Andy Gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan

On Sat, Jan 13, 2018 at 6:35 AM, Karthikeyan Ramasubramanian
<kramasub@codeaurora.org> wrote:
> This driver manages the Generic Interface (GENI) firmware based Qualcomm
> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
> programmable module composed of multiple Serial Engines (SE) and supports
> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
> driver also enables managing the serial interface independent aspects of
> Serial Engines.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>


> +int geni_se_resources_off(struct geni_se_rsc *rsc)
> +{
> +       int ret = 0;
> +       struct geni_se_device *geni_se_dev;
> +
> +       if (unlikely(!rsc || !rsc->wrapper_dev))
> +               return -EINVAL;
> +
> +       geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> +       if (unlikely(!geni_se_dev))
> +               return -ENODEV;
> +
> +       ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);

You need to include linux/pinctrl/consumer.h for devm_pinctrl_get

I couldn't compile test it w/o it.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
  2018-02-14 11:07     ` [PATCH v2 3/7] " Amit Kucheria
@ 2018-02-16 20:44       ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-02-16 20:44 UTC (permalink / raw)
  To: Amit Kucheria
  Cc: corbet, Andy Gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan



On 2/14/2018 4:07 AM, Amit Kucheria wrote:
> On Sat, Jan 13, 2018 at 6:35 AM, Karthikeyan Ramasubramanian
> <kramasub@codeaurora.org> wrote:
>> This driver manages the Generic Interface (GENI) firmware based Qualcomm
>> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
>> programmable module composed of multiple Serial Engines (SE) and supports
>> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
>> driver also enables managing the serial interface independent aspects of
>> Serial Engines.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> 
> 
>> +int geni_se_resources_off(struct geni_se_rsc *rsc)
>> +{
>> +       int ret = 0;
>> +       struct geni_se_device *geni_se_dev;
>> +
>> +       if (unlikely(!rsc || !rsc->wrapper_dev))
>> +               return -EINVAL;
>> +
>> +       geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +       if (unlikely(!geni_se_dev))
>> +               return -ENODEV;
>> +
>> +       ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
> 
> You need to include linux/pinctrl/consumer.h for devm_pinctrl_get
> 
> I couldn't compile test it w/o it.
> 
I think marking the dependencies might help with this compilation issue. 
Else I will include the concerned header file. One way or the other, I 
will ensure that this issue does not get hit in my follow-up patch series.

Regards,
Karthik.

-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [v2, 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
                     ` (3 preceding siblings ...)
  2018-02-14 11:04   ` [PATCH v2 " Amit Kucheria
@ 2018-02-23 18:06   ` Guenter Roeck
  2018-02-27 13:23     ` Karthik Ramasubramanian
  2018-02-23 19:05   ` Doug Anderson
  5 siblings, 1 reply; 42+ messages in thread
From: Guenter Roeck @ 2018-02-23 18:06 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan, Sagar Dharia

On Fri, Jan 12, 2018 at 06:05:47PM -0700, Karthikeyan Ramasubramanian wrote:
> This driver supports GENI based UART Controller in the Qualcomm SOCs. The
> Qualcomm Generic Interface (GENI) is a programmable module supporting a
> wide range of serial interfaces including UART. This driver support console
> operations using FIFO mode of transfer.
> 
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> ---
>  drivers/tty/serial/Kconfig            |   10 +
>  drivers/tty/serial/Makefile           |    1 +
>  drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
>  3 files changed, 1425 insertions(+)
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index b788fee..1be30e5 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
>  	select SERIAL_CORE_CONSOLE
>  	select SERIAL_EARLYCON
>  
> +config SERIAL_QCOM_GENI
> +	tristate "QCOM on-chip GENI based serial port support"
> +	depends on ARCH_QCOM
> +	select SERIAL_CORE
> +	select SERIAL_CORE_CONSOLE

Serial console drivers have to be bool, not tristate.
Building a console driver as module doesn't make sense
(it is needed before modules are loaded). It also results
in a build failure due to a symbol which is not exported.

> +	select SERIAL_EARLYCON
> +	help
> +	  Serial driver for Qualcomm Technologies Inc's GENI based QUP
> +	  hardware.
> +
>  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 842d185..64a8d82 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
>  obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
>  obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
>  obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
> +obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
>  obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
>  obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
>  obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> new file mode 100644
> index 0000000..0dbd329
> --- /dev/null
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -0,0 +1,1414 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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.
> + */
> +
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/qcom-geni-se.h>
> +#include <linux/serial.h>
> +#include <linux/serial_core.h>
> +#include <linux/slab.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +
> +/* UART specific GENI registers */
> +#define SE_UART_TX_TRANS_CFG		(0x25C)
> +#define SE_UART_TX_WORD_LEN		(0x268)
> +#define SE_UART_TX_STOP_BIT_LEN		(0x26C)
> +#define SE_UART_TX_TRANS_LEN		(0x270)
> +#define SE_UART_RX_TRANS_CFG		(0x280)
> +#define SE_UART_RX_WORD_LEN		(0x28C)
> +#define SE_UART_RX_STALE_CNT		(0x294)
> +#define SE_UART_TX_PARITY_CFG		(0x2A4)
> +#define SE_UART_RX_PARITY_CFG		(0x2A8)
> +
> +/* SE_UART_TRANS_CFG */
> +#define UART_TX_PAR_EN		(BIT(0))
> +#define UART_CTS_MASK		(BIT(1))
> +
> +/* SE_UART_TX_WORD_LEN */
> +#define TX_WORD_LEN_MSK		(GENMASK(9, 0))
> +
> +/* SE_UART_TX_STOP_BIT_LEN */
> +#define TX_STOP_BIT_LEN_MSK	(GENMASK(23, 0))
> +#define TX_STOP_BIT_LEN_1	(0)
> +#define TX_STOP_BIT_LEN_1_5	(1)
> +#define TX_STOP_BIT_LEN_2	(2)
> +
> +/* SE_UART_TX_TRANS_LEN */
> +#define TX_TRANS_LEN_MSK	(GENMASK(23, 0))
> +
> +/* SE_UART_RX_TRANS_CFG */
> +#define UART_RX_INS_STATUS_BIT	(BIT(2))
> +#define UART_RX_PAR_EN		(BIT(3))
> +
> +/* SE_UART_RX_WORD_LEN */
> +#define RX_WORD_LEN_MASK	(GENMASK(9, 0))
> +
> +/* SE_UART_RX_STALE_CNT */
> +#define RX_STALE_CNT		(GENMASK(23, 0))
> +
> +/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
> +#define PAR_CALC_EN		(BIT(0))
> +#define PAR_MODE_MSK		(GENMASK(2, 1))
> +#define PAR_MODE_SHFT		(1)
> +#define PAR_EVEN		(0x00)
> +#define PAR_ODD			(0x01)
> +#define PAR_SPACE		(0x10)
> +#define PAR_MARK		(0x11)
> +
> +/* UART M_CMD OP codes */
> +#define UART_START_TX		(0x1)
> +#define UART_START_BREAK	(0x4)
> +#define UART_STOP_BREAK		(0x5)
> +/* UART S_CMD OP codes */
> +#define UART_START_READ		(0x1)
> +#define UART_PARAM		(0x1)
> +
> +#define UART_OVERSAMPLING	(32)
> +#define STALE_TIMEOUT		(16)
> +#define DEFAULT_BITS_PER_CHAR	(10)
> +#define GENI_UART_NR_PORTS	(15)
> +#define GENI_UART_CONS_PORTS	(1)
> +#define DEF_FIFO_DEPTH_WORDS	(16)
> +#define DEF_TX_WM		(2)
> +#define DEF_FIFO_WIDTH_BITS	(32)
> +#define UART_CORE2X_VOTE	(10000)
> +#define UART_CONSOLE_RX_WM	(2)
> +
> +static int owr;
> +module_param(owr, int, 0644);
> +
> +struct qcom_geni_serial_port {
> +	struct uart_port uport;
> +	char name[20];
> +	unsigned int tx_fifo_depth;
> +	unsigned int tx_fifo_width;
> +	unsigned int rx_fifo_depth;
> +	unsigned int tx_wm;
> +	unsigned int rx_wm;
> +	unsigned int rx_rfr;
> +	int xfer_mode;
> +	bool port_setup;
> +	unsigned int *rx_fifo;
> +	int (*handle_rx)(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx);
> +	struct device *wrapper_dev;
> +	struct geni_se_rsc serial_rsc;
> +	unsigned int xmit_size;
> +	void *rx_buf;
> +	unsigned int cur_baud;
> +};
> +
> +static const struct uart_ops qcom_geni_serial_pops;
> +static struct uart_driver qcom_geni_console_driver;
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx);
> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
> +				int offset, int bit_field, bool set);
> +static void qcom_geni_serial_stop_rx(struct uart_port *uport);
> +
> +static atomic_t uart_line_id = ATOMIC_INIT(0);
> +
> +#define GET_DEV_PORT(uport) \
> +	container_of(uport, struct qcom_geni_serial_port, uport)
> +
> +static struct qcom_geni_serial_port qcom_geni_console_port;
> +
> +static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
> +{
> +	if (cfg_flags & UART_CONFIG_TYPE)
> +		uport->type = PORT_MSM;
> +}
> +
> +static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
> +{
> +	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
> +}
> +
> +static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
> +							unsigned int mctrl)
> +{
> +}
> +
> +static const char *qcom_geni_serial_get_type(struct uart_port *uport)
> +{
> +	return "MSM";
> +}
> +
> +static struct qcom_geni_serial_port *get_port_from_line(int line)
> +{
> +	struct qcom_geni_serial_port *port = NULL;
> +
> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> +		port = ERR_PTR(-ENXIO);
> +	port = &qcom_geni_console_port;
> +	return port;
> +}
> +
> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
> +				int offset, int bit_field, bool set)
> +{
> +	int iter = 0;
> +	unsigned int reg;
> +	bool met = false;
> +	struct qcom_geni_serial_port *port = NULL;
> +	bool cond = false;
> +	unsigned int baud = 115200;
> +	unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS;
> +	unsigned long total_iter = 2000;
> +
> +
> +	if (uport->private_data) {
> +		port = GET_DEV_PORT(uport);
> +		baud = (port->cur_baud ? port->cur_baud : 115200);
> +		fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
> +		/*
> +		 * Total polling iterations based on FIFO worth of bytes to be
> +		 * sent at current baud .Add a little fluff to the wait.
> +		 */
> +		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
> +		total_iter += 50;
> +	}
> +
> +	while (iter < total_iter) {
> +		reg = geni_read_reg_nolog(uport->membase, offset);
> +		cond = reg & bit_field;
> +		if (cond == set) {
> +			met = true;
> +			break;
> +		}
> +		udelay(10);
> +		iter++;
> +	}
> +	return met;
> +}
> +
> +static void qcom_geni_serial_setup_tx(struct uart_port *uport,
> +				unsigned int xmit_size)
> +{
> +	u32 m_cmd = 0;
> +
> +	geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
> +	m_cmd |= (UART_START_TX << M_OPCODE_SHFT);
> +	geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
> +	/*
> +	 * Writes to enable the primary sequencer should go through before
> +	 * exiting this function.
> +	 */
> +	mb();
> +}
> +
> +static void qcom_geni_serial_poll_cancel_tx(struct uart_port *uport)
> +{
> +	int done = 0;
> +	unsigned int irq_clear = M_CMD_DONE_EN;
> +	unsigned int geni_status = 0;
> +
> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_DONE_EN, true);
> +	if (!done) {
> +		geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
> +					SE_GENI_M_CMD_CTRL_REG);
> +		owr++;
> +		irq_clear |= M_CMD_ABORT_EN;
> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +							M_CMD_ABORT_EN, true);
> +	}
> +	geni_status = geni_read_reg_nolog(uport->membase,
> +						  SE_GENI_STATUS);
> +	if (geni_status & M_GENI_CMD_ACTIVE)
> +		owr++;
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
> +{
> +	unsigned int irq_clear = S_CMD_DONE_EN;
> +
> +	geni_se_abort_s_cmd(uport->membase);
> +	/* Ensure this goes through before polling. */
> +	mb();
> +	irq_clear |= S_CMD_ABORT_EN;
> +	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +					S_GENI_CMD_ABORT, false);
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
> +}
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +static int qcom_geni_serial_get_char(struct uart_port *uport)
> +{
> +	unsigned int rx_fifo;
> +	unsigned int m_irq_status;
> +	unsigned int s_irq_status;
> +
> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +			M_SEC_IRQ_EN, true)))
> +		return -ENXIO;
> +
> +	m_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_M_IRQ_STATUS);
> +	s_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_S_IRQ_STATUS);
> +	geni_write_reg_nolog(m_irq_status, uport->membase,
> +						SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg_nolog(s_irq_status, uport->membase,
> +						SE_GENI_S_IRQ_CLEAR);
> +
> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
> +			RX_FIFO_WC_MSK, true)))
> +		return -ENXIO;
> +
> +	/*
> +	 * Read the Rx FIFO only after clearing the interrupt registers and
> +	 * getting valid RX fifo status.
> +	 */
> +	mb();
> +	rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
> +	rx_fifo &= 0xFF;
> +	return rx_fifo;
> +}
> +
> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
> +					unsigned char c)
> +{
> +	int b = (int) c;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_write_reg_nolog(port->tx_wm, uport->membase,
> +					SE_GENI_TX_WATERMARK_REG);
> +	qcom_geni_serial_setup_tx(uport, 1);
> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +				M_TX_FIFO_WATERMARK_EN, true))
> +		WARN_ON(1);
> +	geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
> +	geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +	/*
> +	 * Ensure FIFO write goes through before polling for status but.
> +	 */
> +	mb();
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +}
> +#endif
> +
> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)

"if defined(CONFIG_SERIAL_CORE_CONSOLE)" doesn't make much sense here
since the Kconfig entry selects it. Given that, the entire conditional
is unnecessary since it will always be true.

> +static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
> +{
> +	geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn);
> +	/*
> +	 * Ensure FIFO write clear goes through before
> +	 * next iteration.
> +	 */
> +	mb();
> +
> +}
> +
> +static void
> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
> +				unsigned int count)
> +{
> +	int new_line = 0;
> +	int i;
> +	int bytes_to_send = count;
> +	int fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	int tx_wm = DEF_TX_WM;
> +
> +	for (i = 0; i < count; i++) {
> +		if (s[i] == '\n')
> +			new_line++;
> +	}
> +
> +	bytes_to_send += new_line;
> +	geni_write_reg_nolog(tx_wm, uport->membase,
> +					SE_GENI_TX_WATERMARK_REG);
> +	qcom_geni_serial_setup_tx(uport, bytes_to_send);
> +	i = 0;
> +	while (i < count) {
> +		u32 chars_to_write = 0;
> +		u32 avail_fifo_bytes = (fifo_depth - tx_wm);
> +
> +		/*
> +		 * If the WM bit never set, then the Tx state machine is not
> +		 * in a valid state, so break, cancel/abort any existing
> +		 * command. Unfortunately the current data being written is
> +		 * lost.
> +		 */
> +		while (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_TX_FIFO_WATERMARK_EN, true))
> +			break;
> +		chars_to_write = min((unsigned int)(count - i),
> +							avail_fifo_bytes);
> +		if ((chars_to_write << 1) > avail_fifo_bytes)
> +			chars_to_write = (avail_fifo_bytes >> 1);
> +		uart_console_write(uport, (s + i), chars_to_write,
> +						qcom_geni_serial_wr_char);
> +		geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +		/* Ensure this goes through before polling for WM IRQ again.*/
> +		mb();
> +		i += chars_to_write;
> +	}
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +}
> +
> +static void qcom_geni_serial_console_write(struct console *co, const char *s,
> +			      unsigned int count)
> +{
> +	struct uart_port *uport;
> +	struct qcom_geni_serial_port *port;
> +	int locked = 1;
> +	unsigned long flags;
> +
> +	WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
> +
> +	port = get_port_from_line(co->index);
> +	if (IS_ERR_OR_NULL(port))
> +		return;
> +
> +	uport = &port->uport;
> +	if (oops_in_progress)
> +		locked = spin_trylock_irqsave(&uport->lock, flags);
> +	else
> +		spin_lock_irqsave(&uport->lock, flags);
> +
> +	if (locked) {
> +		__qcom_geni_serial_console_write(uport, s, count);
> +		spin_unlock_irqrestore(&uport->lock, flags);
> +	}
> +}
> +
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx)
> +{
> +	int i, c;
> +	unsigned char *rx_char;
> +	struct tty_port *tport;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	tport = &uport->state->port;
> +	for (i = 0; i < rx_fifo_wc; i++) {
> +		int bytes = 4;
> +
> +		*(qcom_port->rx_fifo) =
> +			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
> +		if (drop_rx)
> +			continue;
> +		rx_char = (unsigned char *)qcom_port->rx_fifo;
> +
> +		if (i == (rx_fifo_wc - 1)) {
> +			if (rx_last && rx_last_byte_valid)
> +				bytes = rx_last_byte_valid;
> +		}
> +		for (c = 0; c < bytes; c++) {
> +			char flag = TTY_NORMAL;
> +			int sysrq;
> +
> +			uport->icount.rx++;
> +			sysrq = uart_handle_sysrq_char(uport, rx_char[c]);
> +			if (!sysrq)
> +				tty_insert_flip_char(tport, rx_char[c], flag);
> +		}
> +	}
> +	if (!drop_rx)
> +		tty_flip_buffer_push(tport);
> +	return 0;
> +}
> +#else
> +static int handle_rx_console(struct uart_port *uport,
> +			unsigned int rx_fifo_wc,
> +			unsigned int rx_last_byte_valid,
> +			unsigned int rx_last,
> +			bool drop_rx)
> +{
> +	return -EPERM;
> +}
> +
> +#endif /* (CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)) */
> +
> +static void qcom_geni_serial_start_tx(struct uart_port *uport)
> +{
> +	unsigned int geni_m_irq_en;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	unsigned int geni_status;
> +
> +	if (qcom_port->xfer_mode == FIFO_MODE) {
> +		geni_status = geni_read_reg_nolog(uport->membase,
> +						  SE_GENI_STATUS);
> +		if (geni_status & M_GENI_CMD_ACTIVE)
> +			goto exit_start_tx;
> +
> +		if (!qcom_geni_serial_tx_empty(uport))
> +			goto exit_start_tx;
> +
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +						    SE_GENI_M_IRQ_EN);
> +		geni_m_irq_en |= (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN);
> +
> +		geni_write_reg_nolog(qcom_port->tx_wm, uport->membase,
> +						SE_GENI_TX_WATERMARK_REG);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +		/* Geni command setup should complete before returning.*/
> +		mb();
> +	}
> +exit_start_tx:
> +	return;
> +}
> +
> +static void stop_tx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
> +	geni_m_irq_en &= ~M_CMD_DONE_EN;
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN;
> +		geni_write_reg_nolog(0, uport->membase,
> +				     SE_GENI_TX_WATERMARK_REG);
> +	}
> +	port->xmit_size = 0;
> +	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
> +	geni_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_STATUS);
> +	/* Possible stop tx is called multiple times. */
> +	if (!(geni_status & M_GENI_CMD_ACTIVE))
> +		return;
> +
> +	geni_se_cancel_m_cmd(uport->membase);
> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_CANCEL_EN, true)) {
> +		geni_se_abort_m_cmd(uport->membase);
> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +						M_CMD_ABORT_EN, true);
> +		geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
> +							SE_GENI_M_IRQ_CLEAR);
> +	}
> +	geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
> +{
> +	stop_tx_sequencer(uport);
> +}
> +
> +static void start_rx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_s_irq_en;
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	if (geni_status & S_GENI_CMD_ACTIVE)
> +		qcom_geni_serial_stop_rx(uport);
> +
> +	geni_se_setup_s_cmd(uport->membase, UART_START_READ, 0);
> +
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +
> +		geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
> +		geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
> +
> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +	}
> +	/*
> +	 * Ensure the writes to the secondary sequencer and interrupt enables
> +	 * go through.
> +	 */
> +	mb();
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +}
> +
> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
> +{
> +	start_rx_sequencer(uport);
> +}
> +
> +static void stop_rx_sequencer(struct uart_port *uport)
> +{
> +	unsigned int geni_s_irq_en;
> +	unsigned int geni_m_irq_en;
> +	unsigned int geni_status;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +	u32 irq_clear = S_CMD_DONE_EN;
> +	bool done;
> +
> +	if (port->xfer_mode == FIFO_MODE) {
> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +		geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
> +		geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
> +
> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
> +							SE_GENI_S_IRQ_EN);
> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
> +							SE_GENI_M_IRQ_EN);
> +	}
> +
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	/* Possible stop rx is called multiple times. */
> +	if (!(geni_status & S_GENI_CMD_ACTIVE))
> +		return;
> +	geni_se_cancel_s_cmd(uport->membase);
> +	/*
> +	 * Ensure that the cancel goes through before polling for the
> +	 * cancel control bit.
> +	 */
> +	mb();
> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +					S_GENI_CMD_CANCEL, false);
> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +	if ((geni_status & S_GENI_CMD_ACTIVE))
> +		qcom_geni_serial_abort_rx(uport);
> +}
> +
> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
> +{
> +	stop_rx_sequencer(uport);
> +}
> +
> +static int qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
> +{
> +	int ret = 0;
> +	unsigned int rx_fifo_status;
> +	unsigned int rx_fifo_wc = 0;
> +	unsigned int rx_last_byte_valid = 0;
> +	unsigned int rx_last = 0;
> +	struct tty_port *tport;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +
> +	tport = &uport->state->port;
> +	rx_fifo_status = geni_read_reg_nolog(uport->membase,
> +				SE_GENI_RX_FIFO_STATUS);
> +	rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK;
> +	rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >>
> +						RX_LAST_BYTE_VALID_SHFT);
> +	rx_last = rx_fifo_status & RX_LAST;
> +	if (rx_fifo_wc)
> +		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
> +							rx_last, drop_rx);
> +	return ret;
> +}
> +
> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	struct circ_buf *xmit = &uport->state->xmit;
> +	unsigned int avail_fifo_bytes = 0;
> +	unsigned int bytes_remaining = 0;
> +	int i = 0;
> +	unsigned int tx_fifo_status;
> +	unsigned int xmit_size;
> +	unsigned int fifo_width_bytes = 1;
> +	int temp_tail = 0;
> +
> +	xmit_size = uart_circ_chars_pending(xmit);
> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
> +					SE_GENI_TX_FIFO_STATUS);
> +	/* Both FIFO and framework buffer are drained */
> +	if ((xmit_size == qcom_port->xmit_size) && !tx_fifo_status) {
> +		qcom_port->xmit_size = 0;
> +		uart_circ_clear(xmit);
> +		qcom_geni_serial_stop_tx(uport);
> +		goto exit_handle_tx;
> +	}
> +	xmit_size -= qcom_port->xmit_size;
> +
> +	avail_fifo_bytes = (qcom_port->tx_fifo_depth - qcom_port->tx_wm) *
> +							fifo_width_bytes;
> +	temp_tail = (xmit->tail + qcom_port->xmit_size) & (UART_XMIT_SIZE - 1);
> +	if (xmit_size > (UART_XMIT_SIZE - temp_tail))
> +		xmit_size = (UART_XMIT_SIZE - temp_tail);
> +	if (xmit_size > avail_fifo_bytes)
> +		xmit_size = avail_fifo_bytes;
> +
> +	if (!xmit_size)
> +		goto exit_handle_tx;
> +
> +	qcom_geni_serial_setup_tx(uport, xmit_size);
> +
> +	bytes_remaining = xmit_size;
> +	while (i < xmit_size) {
> +		unsigned int tx_bytes;
> +		unsigned int buf = 0;
> +		int c;
> +
> +		tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
> +					bytes_remaining : fifo_width_bytes);
> +
> +		for (c = 0; c < tx_bytes ; c++)
> +			buf |= (xmit->buf[temp_tail + c] << (c * 8));
> +		geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
> +		i += tx_bytes;
> +		temp_tail = (temp_tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> +		uport->icount.tx += tx_bytes;
> +		bytes_remaining -= tx_bytes;
> +		/* Ensure FIFO write goes through */
> +		wmb();
> +	}
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +	qcom_port->xmit_size += xmit_size;
> +exit_handle_tx:
> +	uart_write_wakeup(uport);
> +	return ret;
> +}
> +
> +static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
> +{
> +	unsigned int m_irq_status;
> +	unsigned int s_irq_status;
> +	struct uart_port *uport = dev;
> +	unsigned long flags;
> +	unsigned int m_irq_en;
> +	bool drop_rx = false;
> +	struct tty_port *tport = &uport->state->port;
> +
> +	spin_lock_irqsave(&uport->lock, flags);
> +	if (uport->suspended)
> +		goto exit_geni_serial_isr;
> +	m_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_M_IRQ_STATUS);
> +	s_irq_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_S_IRQ_STATUS);
> +	m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
> +	geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
> +	geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
> +
> +	if ((m_irq_status & M_ILLEGAL_CMD_EN)) {
> +		WARN_ON(1);
> +		goto exit_geni_serial_isr;
> +	}
> +
> +	if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
> +		uport->icount.overrun++;
> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +	}
> +
> +	if ((m_irq_status & m_irq_en) &
> +	    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
> +		qcom_geni_serial_handle_tx(uport);
> +
> +	if ((s_irq_status & S_GP_IRQ_0_EN) || (s_irq_status & S_GP_IRQ_1_EN)) {
> +		if (s_irq_status & S_GP_IRQ_0_EN)
> +			uport->icount.parity++;
> +		drop_rx = true;
> +	} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
> +		(s_irq_status & S_GP_IRQ_3_EN)) {
> +		uport->icount.brk++;
> +	}
> +
> +	if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
> +		(s_irq_status & S_RX_FIFO_LAST_EN))
> +		qcom_geni_serial_handle_rx(uport, drop_rx);
> +
> +exit_geni_serial_isr:
> +	spin_unlock_irqrestore(&uport->lock, flags);
> +	return IRQ_HANDLED;
> +}
> +
> +static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
> +{
> +	struct uart_port *uport;
> +
> +	if (!port)
> +		return -ENODEV;
> +
> +	uport = &port->uport;
> +	port->tx_fifo_depth = geni_se_get_tx_fifo_depth(uport->membase);
> +	if (!port->tx_fifo_depth) {
> +		dev_err(uport->dev, "%s:Invalid TX FIFO depth read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	port->tx_fifo_width = geni_se_get_tx_fifo_width(uport->membase);
> +	if (!port->tx_fifo_width) {
> +		dev_err(uport->dev, "%s:Invalid TX FIFO width read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	port->rx_fifo_depth = geni_se_get_rx_fifo_depth(uport->membase);
> +	if (!port->rx_fifo_depth) {
> +		dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
> +								__func__);
> +		return -ENXIO;
> +	}
> +
> +	uport->fifosize =
> +		((port->tx_fifo_depth * port->tx_fifo_width) >> 3);
> +	return 0;
> +}
> +
> +static void set_rfr_wm(struct qcom_geni_serial_port *port)
> +{
> +	/*
> +	 * Set RFR (Flow off) to FIFO_DEPTH - 2.
> +	 * RX WM level at 10% RX_FIFO_DEPTH.
> +	 * TX WM level at 10% TX_FIFO_DEPTH.
> +	 */
> +	port->rx_rfr = port->rx_fifo_depth - 2;
> +	port->rx_wm = UART_CONSOLE_RX_WM;
> +	port->tx_wm = 2;
> +}
> +
> +static void qcom_geni_serial_shutdown(struct uart_port *uport)
> +{
> +	unsigned long flags;
> +
> +	/* Stop the console before stopping the current tx */
> +	console_stop(uport->cons);
> +
> +	disable_irq(uport->irq);
> +	free_irq(uport->irq, uport);
> +	spin_lock_irqsave(&uport->lock, flags);
> +	qcom_geni_serial_stop_tx(uport);
> +	qcom_geni_serial_stop_rx(uport);
> +	spin_unlock_irqrestore(&uport->lock, flags);
> +}
> +
> +static int qcom_geni_serial_port_setup(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +	unsigned long cfg0, cfg1;
> +	unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
> +
> +	set_rfr_wm(qcom_port);
> +	geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT);
> +	/*
> +	 * Make an unconditional cancel on the main sequencer to reset
> +	 * it else we could end up in data loss scenarios.
> +	 */
> +	qcom_port->xfer_mode = FIFO_MODE;
> +	qcom_geni_serial_poll_cancel_tx(uport);
> +	geni_se_get_packing_config(8, 1, false, &cfg0, &cfg1);
> +	geni_write_reg_nolog(cfg0, uport->membase,
> +					SE_GENI_TX_PACKING_CFG0);
> +	geni_write_reg_nolog(cfg1, uport->membase,
> +					SE_GENI_TX_PACKING_CFG1);
> +	geni_se_get_packing_config(8, 4, false, &cfg0, &cfg1);
> +	geni_write_reg_nolog(cfg0, uport->membase,
> +					SE_GENI_RX_PACKING_CFG0);
> +	geni_write_reg_nolog(cfg1, uport->membase,
> +					SE_GENI_RX_PACKING_CFG1);
> +	ret = geni_se_init(uport->membase, qcom_port->rx_wm, qcom_port->rx_rfr);
> +	if (ret) {
> +		dev_err(uport->dev, "%s: Fail\n", __func__);
> +		goto exit_portsetup;
> +	}
> +
> +	ret = geni_se_select_mode(uport->membase, qcom_port->xfer_mode);
> +	if (ret)
> +		goto exit_portsetup;
> +
> +	qcom_port->port_setup = true;
> +	/*
> +	 * Ensure Port setup related IO completes before returning to
> +	 * framework.
> +	 */
> +	mb();
> +exit_portsetup:
> +	return ret;
> +}
> +
> +static int qcom_geni_serial_startup(struct uart_port *uport)
> +{
> +	int ret = 0;
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	scnprintf(qcom_port->name, sizeof(qcom_port->name),
> +		  "qcom_serial_geni%d",	uport->line);
> +
> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
> +		dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
> +				 __func__, geni_se_get_proto(uport->membase));
> +		ret = -ENXIO;
> +		goto exit_startup;
> +	}
> +
> +	get_tx_fifo_size(qcom_port);
> +	if (!qcom_port->port_setup) {
> +		if (qcom_geni_serial_port_setup(uport))
> +			goto exit_startup;
> +	}
> +
> +	/*
> +	 * Ensure that all the port configuration writes complete
> +	 * before returning to the framework.
> +	 */
> +	mb();
> +	ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
> +			qcom_port->name, uport);
> +	if (unlikely(ret)) {
> +		dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n",
> +							__func__, ret);
> +		goto exit_startup;
> +	}
> +
> +exit_startup:
> +	return ret;
> +}
> +
> +static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
> +{
> +	unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
> +		32000000, 48000000, 64000000, 80000000, 96000000, 100000000};
> +	int i;
> +	int match = -1;
> +
> +	for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
> +		if (clk_freq > root_freq[i])
> +			continue;
> +
> +		if (!(root_freq[i] % clk_freq)) {
> +			match = i;
> +			break;
> +		}
> +	}
> +	if (match != -1)
> +		*ser_clk = root_freq[match];
> +	else
> +		pr_err("clk_freq %ld\n", clk_freq);
> +	return match;
> +}
> +
> +static void geni_serial_write_term_regs(struct uart_port *uport,
> +		u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
> +		u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
> +		u32 s_clk_cfg)
> +{
> +	geni_write_reg_nolog(tx_trans_cfg, uport->membase,
> +							SE_UART_TX_TRANS_CFG);
> +	geni_write_reg_nolog(tx_parity_cfg, uport->membase,
> +							SE_UART_TX_PARITY_CFG);
> +	geni_write_reg_nolog(rx_trans_cfg, uport->membase,
> +							SE_UART_RX_TRANS_CFG);
> +	geni_write_reg_nolog(rx_parity_cfg, uport->membase,
> +							SE_UART_RX_PARITY_CFG);
> +	geni_write_reg_nolog(bits_per_char, uport->membase,
> +							SE_UART_TX_WORD_LEN);
> +	geni_write_reg_nolog(bits_per_char, uport->membase,
> +							SE_UART_RX_WORD_LEN);
> +	geni_write_reg_nolog(stop_bit_len, uport->membase,
> +						SE_UART_TX_STOP_BIT_LEN);
> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);
> +}
> +
> +static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
> +{
> +	unsigned long ser_clk;
> +	int dfs_index;
> +	int clk_div = 0;
> +
> +	*desired_clk_rate = baud * UART_OVERSAMPLING;
> +	dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk);
> +	if (dfs_index < 0) {
> +		pr_err("%s: Can't find matching DFS entry for baud %d\n",
> +								__func__, baud);
> +		clk_div = -EINVAL;
> +		goto exit_get_clk_div_rate;
> +	}
> +
> +	clk_div = ser_clk / *desired_clk_rate;
> +	*desired_clk_rate = ser_clk;
> +exit_get_clk_div_rate:
> +	return clk_div;
> +}
> +
> +static void qcom_geni_serial_set_termios(struct uart_port *uport,
> +				struct ktermios *termios, struct ktermios *old)
> +{
> +	unsigned int baud;
> +	unsigned int bits_per_char = 0;
> +	unsigned int tx_trans_cfg;
> +	unsigned int tx_parity_cfg;
> +	unsigned int rx_trans_cfg;
> +	unsigned int rx_parity_cfg;
> +	unsigned int stop_bit_len;
> +	unsigned int clk_div;
> +	unsigned long ser_clk_cfg = 0;
> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
> +	unsigned long clk_rate;
> +
> +	qcom_geni_serial_stop_rx(uport);
> +	/* baud rate */
> +	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
> +	port->cur_baud = baud;
> +	clk_div = get_clk_div_rate(baud, &clk_rate);
> +	if (clk_div <= 0)
> +		goto exit_set_termios;
> +
> +	uport->uartclk = clk_rate;
> +	clk_set_rate(port->serial_rsc.se_clk, clk_rate);
> +	ser_clk_cfg |= SER_CLK_EN;
> +	ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
> +
> +	/* parity */
> +	tx_trans_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_TX_TRANS_CFG);
> +	tx_parity_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_TX_PARITY_CFG);
> +	rx_trans_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_RX_TRANS_CFG);
> +	rx_parity_cfg = geni_read_reg_nolog(uport->membase,
> +							SE_UART_RX_PARITY_CFG);
> +	if (termios->c_cflag & PARENB) {
> +		tx_trans_cfg |= UART_TX_PAR_EN;
> +		rx_trans_cfg |= UART_RX_PAR_EN;
> +		tx_parity_cfg |= PAR_CALC_EN;
> +		rx_parity_cfg |= PAR_CALC_EN;
> +		if (termios->c_cflag & PARODD) {
> +			tx_parity_cfg |= PAR_ODD;
> +			rx_parity_cfg |= PAR_ODD;
> +		} else if (termios->c_cflag & CMSPAR) {
> +			tx_parity_cfg |= PAR_SPACE;
> +			rx_parity_cfg |= PAR_SPACE;
> +		} else {
> +			tx_parity_cfg |= PAR_EVEN;
> +			rx_parity_cfg |= PAR_EVEN;
> +		}
> +	} else {
> +		tx_trans_cfg &= ~UART_TX_PAR_EN;
> +		rx_trans_cfg &= ~UART_RX_PAR_EN;
> +		tx_parity_cfg &= ~PAR_CALC_EN;
> +		rx_parity_cfg &= ~PAR_CALC_EN;
> +	}
> +
> +	/* bits per char */
> +	switch (termios->c_cflag & CSIZE) {
> +	case CS5:
> +		bits_per_char = 5;
> +		break;
> +	case CS6:
> +		bits_per_char = 6;
> +		break;
> +	case CS7:
> +		bits_per_char = 7;
> +		break;
> +	case CS8:
> +	default:
> +		bits_per_char = 8;
> +		break;
> +	}
> +
> +	/* stop bits */
> +	if (termios->c_cflag & CSTOPB)
> +		stop_bit_len = TX_STOP_BIT_LEN_2;
> +	else
> +		stop_bit_len = TX_STOP_BIT_LEN_1;
> +
> +	/* flow control, clear the CTS_MASK bit if using flow control. */
> +	if (termios->c_cflag & CRTSCTS)
> +		tx_trans_cfg &= ~UART_CTS_MASK;
> +	else
> +		tx_trans_cfg |= UART_CTS_MASK;
> +
> +	if (likely(baud))
> +		uart_update_timeout(uport, termios->c_cflag, baud);
> +
> +	geni_serial_write_term_regs(uport, tx_trans_cfg, tx_parity_cfg,
> +		rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len,
> +								ser_clk_cfg);
> +exit_set_termios:
> +	qcom_geni_serial_start_rx(uport);
> +	return;
> +
> +}
> +
> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
> +{
> +	unsigned int tx_fifo_status;
> +	unsigned int is_tx_empty = 1;
> +
> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
> +						SE_GENI_TX_FIFO_STATUS);
> +	if (tx_fifo_status)
> +		is_tx_empty = 0;
> +
> +	return is_tx_empty;
> +}
> +
> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
> +static int __init qcom_geni_console_setup(struct console *co, char *options)
> +{
> +	struct uart_port *uport;
> +	struct qcom_geni_serial_port *dev_port;
> +	int baud = 115200;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +	int ret = 0;
> +
> +	if (unlikely(co->index >= GENI_UART_NR_PORTS  || co->index < 0))
> +		return -ENXIO;
> +
> +	dev_port = get_port_from_line(co->index);
> +	if (IS_ERR_OR_NULL(dev_port)) {
> +		ret = PTR_ERR(dev_port);
> +		pr_err("Invalid line %d(%d)\n", co->index, ret);
> +		return ret;
> +	}
> +
> +	uport = &dev_port->uport;
> +
> +	if (unlikely(!uport->membase))
> +		return -ENXIO;
> +
> +	if (geni_se_resources_on(&dev_port->serial_rsc))
> +		WARN_ON(1);
> +
> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
> +		geni_se_resources_off(&dev_port->serial_rsc);
> +		return -ENXIO;
> +	}
> +
> +	if (!dev_port->port_setup) {
> +		qcom_geni_serial_stop_rx(uport);
> +		qcom_geni_serial_port_setup(uport);
> +	}
> +
> +	if (options)
> +		uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +	return uart_set_options(uport, co, baud, parity, bits, flow);
> +}
> +
> +static int console_register(struct uart_driver *drv)
> +{
> +	return uart_register_driver(drv);
> +}
> +static void console_unregister(struct uart_driver *drv)
> +{
> +	uart_unregister_driver(drv);
> +}
> +
> +static struct console cons_ops = {
> +	.name = "ttyMSM",
> +	.write = qcom_geni_serial_console_write,
> +	.device = uart_console_device,
> +	.setup = qcom_geni_console_setup,
> +	.flags = CON_PRINTBUFFER,
> +	.index = -1,
> +	.data = &qcom_geni_console_driver,
> +};
> +
> +static struct uart_driver qcom_geni_console_driver = {
> +	.owner = THIS_MODULE,
> +	.driver_name = "qcom_geni_console",
> +	.dev_name = "ttyMSM",
> +	.nr =  GENI_UART_NR_PORTS,
> +	.cons = &cons_ops,
> +};
> +#else
> +static int console_register(struct uart_driver *drv)
> +{
> +	return 0;
> +}
> +
> +static void console_unregister(struct uart_driver *drv)
> +{
> +}
> +#endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */
> +
> +static void qcom_geni_serial_cons_pm(struct uart_port *uport,
> +		unsigned int new_state, unsigned int old_state)
> +{
> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
> +
> +	if (unlikely(!uart_console(uport)))
> +		return;
> +
> +	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
> +		geni_se_resources_on(&qcom_port->serial_rsc);
> +	else if (new_state == UART_PM_STATE_OFF &&
> +			old_state == UART_PM_STATE_ON)
> +		geni_se_resources_off(&qcom_port->serial_rsc);
> +}
> +
> +static const struct uart_ops qcom_geni_console_pops = {
> +	.tx_empty = qcom_geni_serial_tx_empty,
> +	.stop_tx = qcom_geni_serial_stop_tx,
> +	.start_tx = qcom_geni_serial_start_tx,
> +	.stop_rx = qcom_geni_serial_stop_rx,
> +	.set_termios = qcom_geni_serial_set_termios,
> +	.startup = qcom_geni_serial_startup,
> +	.config_port = qcom_geni_serial_config_port,
> +	.shutdown = qcom_geni_serial_shutdown,
> +	.type = qcom_geni_serial_get_type,
> +	.set_mctrl = qcom_geni_cons_set_mctrl,
> +	.get_mctrl = qcom_geni_cons_get_mctrl,
> +#ifdef CONFIG_CONSOLE_POLL
> +	.poll_get_char	= qcom_geni_serial_get_char,
> +	.poll_put_char	= qcom_geni_serial_poll_put_char,
> +#endif
> +	.pm = qcom_geni_serial_cons_pm,
> +};
> +
> +static const struct of_device_id qcom_geni_serial_match_table[] = {
> +	{ .compatible = "qcom,geni-debug-uart",
> +			.data = (void *)&qcom_geni_console_driver},
> +};
> +
> +static int qcom_geni_serial_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	int line = -1;
> +	struct qcom_geni_serial_port *dev_port;
> +	struct uart_port *uport;
> +	struct resource *res;
> +	struct uart_driver *drv;
> +	const struct of_device_id *id;
> +
> +	id = of_match_device(qcom_geni_serial_match_table, &pdev->dev);
> +	if (id) {
> +		dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
> +		drv = (struct uart_driver *)id->data;
> +	} else {
> +		dev_err(&pdev->dev, "%s: No matching device found", __func__);
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.of_node) {
> +		if (drv->cons)
> +			line = of_alias_get_id(pdev->dev.of_node, "serial");
> +	} else {
> +		line = pdev->id;
> +	}
> +
> +	if (line < 0)
> +		line = atomic_inc_return(&uart_line_id) - 1;
> +
> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> +		return -ENXIO;
> +	dev_port = get_port_from_line(line);
> +	if (IS_ERR_OR_NULL(dev_port)) {
> +		ret = PTR_ERR(dev_port);
> +		dev_err(&pdev->dev, "Invalid line %d(%d)\n",
> +					line, ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport = &dev_port->uport;
> +
> +	/* Don't allow 2 drivers to access the same port */
> +	if (uport->private_data) {
> +		ret = -ENODEV;
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport->dev = &pdev->dev;
> +	dev_port->wrapper_dev = pdev->dev.parent;
> +	dev_port->serial_rsc.wrapper_dev = pdev->dev.parent;
> +	dev_port->serial_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
> +	if (IS_ERR(dev_port->serial_rsc.se_clk)) {
> +		ret = PTR_ERR(dev_port->serial_rsc.se_clk);
> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "se-phys");
> +	if (!res) {
> +		ret = -ENXIO;
> +		dev_err(&pdev->dev, "Err getting IO region\n");
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport->mapbase = res->start;
> +	uport->membase = devm_ioremap(&pdev->dev, res->start,
> +						resource_size(res));
> +	if (!uport->membase) {
> +		ret = -ENOMEM;
> +		dev_err(&pdev->dev, "Err IO mapping serial iomem");
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
> +		dev_err(&pdev->dev, "No pinctrl config specified!\n");
> +		ret = PTR_ERR(dev_port->serial_rsc.geni_pinctrl);
> +		goto exit_geni_serial_probe;
> +	}
> +	dev_port->serial_rsc.geni_gpio_active =
> +		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
> +							PINCTRL_DEFAULT);
> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
> +		dev_err(&pdev->dev, "No default config specified!\n");
> +		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	dev_port->serial_rsc.geni_gpio_sleep =
> +		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
> +							PINCTRL_SLEEP);
> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_sleep)) {
> +		dev_err(&pdev->dev, "No sleep config specified!\n");
> +		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +	dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
> +	uport->fifosize =
> +		((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3);
> +
> +	uport->irq = platform_get_irq(pdev, 0);
> +	if (uport->irq < 0) {
> +		ret = uport->irq;
> +		dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret);
> +		goto exit_geni_serial_probe;
> +	}
> +
> +	uport->private_data = (void *)drv;
> +	platform_set_drvdata(pdev, dev_port);
> +	dev_port->handle_rx = handle_rx_console;
> +	dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32),
> +								GFP_KERNEL);
> +	dev_port->port_setup = false;
> +	return uart_add_one_port(drv, uport);
> +
> +exit_geni_serial_probe:
> +	return ret;
> +}
> +
> +static int qcom_geni_serial_remove(struct platform_device *pdev)
> +{
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_driver *drv =
> +			(struct uart_driver *)port->uport.private_data;
> +
> +	uart_remove_one_port(drv, &port->uport);
> +	return 0;
> +}
> +
> +
> +#ifdef CONFIG_PM
> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_port *uport = &port->uport;
> +
> +	uart_suspend_port((struct uart_driver *)uport->private_data,
> +				uport);
> +	return 0;
> +}
> +
> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +	struct uart_port *uport = &port->uport;
> +
> +	if (console_suspend_enabled && uport->suspended) {
> +		uart_resume_port((struct uart_driver *)uport->private_data,
> +									uport);
> +		disable_irq(uport->irq);
> +	}
> +	return 0;
> +}
> +#else
> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
> +{
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
> +	.suspend_noirq = qcom_geni_serial_sys_suspend_noirq,
> +	.resume_noirq = qcom_geni_serial_sys_resume_noirq,
> +};
> +
> +static struct platform_driver qcom_geni_serial_platform_driver = {
> +	.remove = qcom_geni_serial_remove,
> +	.probe = qcom_geni_serial_probe,
> +	.driver = {
> +		.name = "qcom_geni_serial",
> +		.of_match_table = qcom_geni_serial_match_table,
> +		.pm = &qcom_geni_serial_pm_ops,
> +	},
> +};
> +
> +static int __init qcom_geni_serial_init(void)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	for (i = 0; i < GENI_UART_CONS_PORTS; i++) {
> +		qcom_geni_console_port.uport.iotype = UPIO_MEM;
> +		qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
> +		qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
> +		qcom_geni_console_port.uport.line = i;
> +	}
> +
> +	ret = console_register(&qcom_geni_console_driver);
> +	if (ret)
> +		return ret;
> +
> +	ret = platform_driver_register(&qcom_geni_serial_platform_driver);
> +	if (ret) {
> +		console_unregister(&qcom_geni_console_driver);
> +		return ret;
> +	}
> +	return ret;
> +}
> +module_init(qcom_geni_serial_init);
> +
> +static void __exit qcom_geni_serial_exit(void)
> +{
> +	platform_driver_unregister(&qcom_geni_serial_platform_driver);
> +	console_unregister(&qcom_geni_console_driver);
> +}
> +module_exit(qcom_geni_serial_exit);
> +
> +MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("tty:qcom_geni_serial");

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [v2, 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
                     ` (4 preceding siblings ...)
  2018-02-23 18:06   ` [v2, " Guenter Roeck
@ 2018-02-23 19:05   ` Doug Anderson
  5 siblings, 0 replies; 42+ messages in thread
From: Doug Anderson @ 2018-02-23 19:05 UTC (permalink / raw)
  To: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby,
	Girish Mahadevan, Sagar Dharia, Guenter Roeck

Hi,

On Fri, Jan 12, 2018 at 5:05 PM, Karthikeyan Ramasubramanian
<kramasub@codeaurora.org> wrote:
> This driver supports GENI based UART Controller in the Qualcomm SOCs. The
> Qualcomm Generic Interface (GENI) is a programmable module supporting a
> wide range of serial interfaces including UART. This driver support console
> operations using FIFO mode of transfer.
>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> ---
>  drivers/tty/serial/Kconfig            |   10 +
>  drivers/tty/serial/Makefile           |    1 +
>  drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
>  3 files changed, 1425 insertions(+)
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index b788fee..1be30e5 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
>         select SERIAL_CORE_CONSOLE
>         select SERIAL_EARLYCON
>
> +config SERIAL_QCOM_GENI
> +       tristate "QCOM on-chip GENI based serial port support"

Should this be bool instead of tristate?  Guenter noticed that we'll
get link errors if we try building this as a module.  Specifically, he
noted that the "qcom_geni_serial.c" file refers to
"uart_console_device" and "uart_console_device" is not exported (and
shouldn't be since a console has to be available before modules are
loaded).


-Doug

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-01-17  6:25   ` Bjorn Andersson
  2018-01-19 22:53     ` Rob Herring
@ 2018-02-26 21:24     ` Karthik Ramasubramanian
  1 sibling, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-02-26 21:24 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby



On 1/16/2018 11:25 PM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> Add device tree binding support for the QCOM GENI SE driver.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> 
> Better describe the entire GENI, with it's functions in the same
> binding.
Ok.
> 
>> ---
>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 34 ++++++++++++++++++++++
>>   1 file changed, 34 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>>
>> diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>> new file mode 100644
>> index 0000000..66f8a16
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>> @@ -0,0 +1,34 @@
>> +Qualcomm Technologies, Inc. GENI Serial Engine QUP Wrapper Controller
>> +
>> +Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) wrapper
>> +is a programmable module for supporting a wide range of serial interfaces
>> +like UART, SPI, I2C, I3C, etc. A single QUP module can provide upto 8 Serial
>> +Interfaces, using its internal Serial Engines. The GENI Serial Engine QUP
>> +Wrapper controller is modeled as a node with zero or more child nodes each
>> +representing a serial engine.
>> +
>> +Required properties:
>> +- compatible:		Must be "qcom,geni-se-qup".
>> +- reg:			Must contain QUP register address and length.
>> +- clock-names:		Must contain "m-ahb" and "s-ahb".
>> +- clocks:		AHB clocks needed by the device.
>> +
>> +Required properties if child node exists:
>> +- #address-cells: Must be 1
>> +- #size-cells: Must be 1
> 
> But on a 64-bit system, shouldn't these be 2?
As pointed out by Rob in his response, the module size is less than 4G 
and hence 1.
> 
>> +- ranges: Must be present
>> +
>> +Properties for children:
>> +
>> +A GENI based QUP wrapper controller node can contain 0 or more child nodes
>> +representing serial devices.  These serial devices can be a QCOM UART, I2C
>> +controller, spi controller, or some combination of aforementioned devices.
>> +
>> +Example:
>> +	qup0: qcom,geniqup0@8c0000 {
> 
> I don't see a reason for this to be referenced, so skip the label. And
> don't use qcom, in the node name; "geni" would be perfectly fine?
Ok.
> 
>> +		compatible = "qcom,geni-se-qup";
>> +		reg = <0x8c0000 0x6000>;
>> +		clock-names = "m-ahb", "s-ahb";
>> +		clocks = <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
>> +			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
>> +	}
> 
> Regards,
> Bjorn
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller
  2018-01-17  6:31     ` Bjorn Andersson
@ 2018-02-26 21:28       ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-02-26 21:28 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia



On 1/16/2018 11:31 PM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> Add device tree binding support for I2C Controller in GENI based
>> QUP Wrapper.
>>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> ---
>>   .../devicetree/bindings/i2c/i2c-qcom-geni.txt      | 35 ++++++++++++++++++++++
>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 19 ++++++++++++
>>   2 files changed, 54 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
>> new file mode 100644
>> index 0000000..ea84be7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
>> @@ -0,0 +1,35 @@
>> +Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
>> +
>> +Required properties:
>> + - compatible: Should be:
>> +   * "qcom,i2c-geni.
> 
> As this is a subset of geni it would look better with qcom,geni-i2c
> imho.
Ok.
> 
>> + - reg: Should contain QUP register address and length.
>> + - interrupts: Should contain I2C interrupt.
>> + - clock-names: Should contain "se-clk".
> 
> Omit "clk" from the clock names.
Ok.
> 
>> + - clocks: Serial engine core clock needed by the device.
>> + - pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
>> +   should be "active" and "sleep" for the pin confuguration when core is active
>> +   or when entering sleep state.
> 
> No need to describe pinctrl properties - and your description here
> doesn't match the code.
Removed as it is called by the device core before probe.
> 
>> + - #address-cells: Should be <1> Address cells for i2c device address
>> + - #size-cells: Should be <0> as i2c addresses have no size component
>> +
>> +Optional property:
>> + - clock-frequency : Desired I2C bus clock frequency in Hz.
>> +   When missing default to 400000Hz.
>> +
>> +Child nodes should conform to i2c bus binding.
> 
> ..."as described in i2c.txt"
Ok.
> 
> Regards,
> Bjorn
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-01-18  5:23       ` Bjorn Andersson
@ 2018-02-27 13:16         ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-02-27 13:16 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Sagar Dharia, Girish Mahadevan



On 1/17/2018 10:23 PM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> This bus driver supports the GENI based i2c hardware controller in the
>> Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
>> module supporting a wide range of serial interfaces including I2C. The
>> driver supports FIFO mode and DMA mode of transfer and switches modes
>> dynamically depending on the size of the transfer.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> ---
>>   drivers/i2c/busses/Kconfig         |  10 +
>>   drivers/i2c/busses/Makefile        |   1 +
>>   drivers/i2c/busses/i2c-qcom-geni.c | 656 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 667 insertions(+)
>>   create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>>
>> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
>> index 009345d..caef309 100644
>> --- a/drivers/i2c/busses/Kconfig
>> +++ b/drivers/i2c/busses/Kconfig
>> @@ -838,6 +838,16 @@ config I2C_PXA_SLAVE
>>   	  is necessary for systems where the PXA may be a target on the
>>   	  I2C bus.
>>   
>> +config I2C_QCOM_GENI
>> +	tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
>> +	depends on ARCH_QCOM
> 
> Just depend on the GENI wrapper as well.
Ok.
> 
>> +	help
>> +	  If you say yes to this option, support will be included for the
>> +	  built-in I2C interface on the Qualcomm Technologies Inc.'s SoCs.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called i2c-qcom-geni.
>> +
>>   config I2C_QUP
>>   	tristate "Qualcomm QUP based I2C controller"
>>   	depends on ARCH_QCOM
>> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
>> index 2ce8576..201fce1 100644
>> --- a/drivers/i2c/busses/Makefile
>> +++ b/drivers/i2c/busses/Makefile
>> @@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_PNX)		+= i2c-pnx.o
>>   obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
>>   obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
>>   obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
>> +obj-$(CONFIG_I2C_QCOM_GENI)	+= i2c-qcom-geni.o
>>   obj-$(CONFIG_I2C_QUP)		+= i2c-qup.o
>>   obj-$(CONFIG_I2C_RIIC)		+= i2c-riic.o
>>   obj-$(CONFIG_I2C_RK3X)		+= i2c-rk3x.o
>> diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
>> new file mode 100644
>> index 0000000..59ad4da
>> --- /dev/null
>> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
>> @@ -0,0 +1,656 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only 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.
>> + *
>> + */
> 
> Use SPDX license header.
Ok.
> 
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
> 
> Unused?
Ok, I will remove unused header files.
> 
>> +#include <linux/err.h>
>> +#include <linux/i2c.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/qcom-geni-se.h>
>> +
>> +#define SE_I2C_TX_TRANS_LEN		(0x26C)
> 
> Drop the parenthesis, when not needed.
Ok.
> 
>> +#define SE_I2C_RX_TRANS_LEN		(0x270)
>> +#define SE_I2C_SCL_COUNTERS		(0x278)
>> +#define SE_GENI_IOS			(0x908)
>> +
>> +#define SE_I2C_ERR  (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
>> +			M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
>> +#define SE_I2C_ABORT (1U << 1)
>> +/* M_CMD OP codes for I2C */
>> +#define I2C_WRITE		(0x1)
>> +#define I2C_READ		(0x2)
>> +#define I2C_WRITE_READ		(0x3)
>> +#define I2C_ADDR_ONLY		(0x4)
>> +#define I2C_BUS_CLEAR		(0x6)
>> +#define I2C_STOP_ON_BUS		(0x7)
>> +/* M_CMD params for I2C */
>> +#define PRE_CMD_DELAY		(BIT(0))
>> +#define TIMESTAMP_BEFORE	(BIT(1))
>> +#define STOP_STRETCH		(BIT(2))
>> +#define TIMESTAMP_AFTER		(BIT(3))
>> +#define POST_COMMAND_DELAY	(BIT(4))
>> +#define IGNORE_ADD_NACK		(BIT(6))
>> +#define READ_FINISHED_WITH_ACK	(BIT(7))
>> +#define BYPASS_ADDR_PHASE	(BIT(8))
>> +#define SLV_ADDR_MSK		(GENMASK(15, 9))
>> +#define SLV_ADDR_SHFT		(9)
>> +
>> +#define I2C_CORE2X_VOTE		(10000)
>> +#define GP_IRQ0			0
>> +#define GP_IRQ1			1
>> +#define GP_IRQ2			2
>> +#define GP_IRQ3			3
>> +#define GP_IRQ4			4
>> +#define GP_IRQ5			5
>> +#define GENI_OVERRUN		6
>> +#define GENI_ILLEGAL_CMD	7
>> +#define GENI_ABORT_DONE		8
>> +#define GENI_TIMEOUT		9
>> +
>> +#define I2C_NACK		GP_IRQ1
>> +#define I2C_BUS_PROTO		GP_IRQ3
>> +#define I2C_ARB_LOST		GP_IRQ4
>> +#define DM_I2C_CB_ERR		((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
>> +									<< 5)
>> +
>> +#define I2C_AUTO_SUSPEND_DELAY	250
>> +#define KHz(freq)		(1000 * freq)
>> +
>> +struct geni_i2c_dev {
>> +	struct device *dev;
>> +	void __iomem *base;
>> +	unsigned int tx_wm;
>> +	int irq;
>> +	int err;
>> +	struct i2c_adapter adap;
>> +	struct completion xfer;
> 
> How about naming this "done" or something like that, gi2c->xfer doesn't
> really give the sense of being a "we're done with the operation"-event.
Ok.
> 
>> +	struct i2c_msg *cur;
>> +	struct geni_se_rsc i2c_rsc;
>> +	int cur_wr;
>> +	int cur_rd;
>> +	struct device *wrapper_dev;
> 
> This is already availabe in i2c_rsc, and in particular if you pass the
> i2c_rsc down to the wrapper in the 2 cases you use the wrapper_dev you
> don't need this duplication.
Ok.
> 
>> +	u32 clk_freq_out;
>> +	int clk_fld_idx;
> 
> Keep track of the const struct geni_i2c_clk_fld * here instead.
Ok.
> 
>> +};
>> +
>> +struct geni_i2c_err_log {
>> +	int err;
>> +	const char *msg;
>> +};
>> +
>> +static struct geni_i2c_err_log gi2c_log[] = {
>> +	[GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
>> +	[I2C_NACK] = {-ENOTCONN,
>> +			"NACK: slv unresponsive, check its power/reset-ln"},
> 
> Break the 80-char rule, to improve readability.
Ok.
> 
>> +	[GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
>> +	[I2C_BUS_PROTO] = {-EPROTO,
>> +				"Bus proto err, noisy/unepxected start/stop"},
>> +	[I2C_ARB_LOST] = {-EBUSY,
>> +				"Bus arbitration lost, clock line undriveable"},
>> +	[GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
>> +	[GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
>> +	[GENI_ILLEGAL_CMD] = {-EILSEQ,
>> +				"Illegal cmd, check GENI cmd-state machine"},
>> +	[GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
>> +	[GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
>> +};
>> +
>> +struct geni_i2c_clk_fld {
>> +	u32	clk_freq_out;
>> +	u8	clk_div;
>> +	u8	t_high;
>> +	u8	t_low;
>> +	u8	t_cycle;
>> +};
>> +
>> +static struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
> 
> const
Ok.
> 
>> +	{KHz(100), 7, 10, 11, 26},
>> +	{KHz(400), 2,  5, 12, 24},
>> +	{KHz(1000), 1, 3,  9, 18},
>> +};
>> +
>> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
>> +{
>> +	int i;
>> +	int ret = 0;
>> +	bool clk_map_present = false;
>> +	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
>> +		if (itr->clk_freq_out == gi2c->clk_freq_out) {
>> +			clk_map_present = true;
>> +			break;
> 
> Make this:
> 			gi2c->clk_fld = geni_i2c_clk_map + i;
> 			return 0;
Ok.
> 
>> +		}
>> +	}
>> +
> 
> ...then you can drop ret and clk_map_present and just return -EINVAL
> here.
Ok.
> 
>> +	if (clk_map_present)
>> +		gi2c->clk_fld_idx = i;
>> +	else
>> +		ret = -EINVAL;
>> +
>> +	return ret;
>> +}
>> +
>> +static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)
> 
> Drop the "inline", if it makes sense the compiler will inline it, if not
> it knows better than we do.
> 
> dfs is always 0, so drop this parameter and hard code the value below.
Ok.
> 
>> +{
>> +	struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
>> +
>> +	geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
>> +
>> +	geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
>> +	geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
>> +			itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);
>> +
>> +	/* Ensure Clk config completes before return */
> 
> That's not what "mb" does, it ensures that later memory operations
> aren't reordered beyond the barrier.
Our intention also is for ordering purposes so that any later register 
writes/reads does not get re-ordered until the writes to clock 
configuration register is complete. I will update the comment.
> 
>> +	mb();
>> +}
>> +
>> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
> 
> Looking at the code in this function it's just a bunch of logic to print
> various debug messages...except for the last line that uses the gi2c_log
> lookup table to convert the error to a errno. Sneaky...and not very
> nice.
I will update this function so that it is more obvious.
> 
>> +{
>> +	u32 m_cmd = readl_relaxed(gi2c->base + SE_GENI_M_CMD0);
> 
> Unless you have a really good reason, just use readl/writel in favour of
> their _relaxed versions.
The goal is to maintain the order of execution to the Serial Engine 
register block. I believe _relaxed help enusre that order with a little 
less overhead compared to non_relaxed function. Please correct me otherwise.
> 
>> +	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
>> +	u32 geni_s = readl_relaxed(gi2c->base + SE_GENI_STATUS);
>> +	u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
>> +	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
>> +	u32 rx_st, tx_st;
>> +
>> +	if (gi2c->cur)
>> +		dev_dbg(gi2c->dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
>> +			gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
>> +
>> +	if (err == I2C_NACK || err == GENI_ABORT_DONE) {
>> +		dev_dbg(gi2c->dev, "%s\n", gi2c_log[err].msg);
>> +		goto err_ret;
>> +	} else {
>> +		dev_err(gi2c->dev, "%s\n", gi2c_log[err].msg);
>> +	}
>> +
>> +	if (dma) {
>> +		rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
>> +		tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
>> +	} else {
>> +		rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
>> +		tx_st = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
>> +	}
>> +	dev_dbg(gi2c->dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
>> +		dma, tx_st, rx_st, m_stat);
>> +	dev_dbg(gi2c->dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
>> +		m_cmd, geni_s, geni_ios);
>> +err_ret:
>> +	gi2c->err = gi2c_log[err].err;
>> +}
>> +
>> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
>> +{
>> +	struct geni_i2c_dev *gi2c = dev;
>> +	int i, j;
>> +	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
>> +	u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
>> +	u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
>> +	u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
>> +	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
>> +	struct i2c_msg *cur = gi2c->cur;
>> +
>> +	if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
>> +		    (dm_rx_st & (DM_I2C_CB_ERR)) ||
>> +		    (m_stat & M_CMD_ABORT_EN)) {
>> +
>> +		if (m_stat & M_GP_IRQ_1_EN)
>> +			geni_i2c_err(gi2c, I2C_NACK);
>> +		if (m_stat & M_GP_IRQ_3_EN)
>> +			geni_i2c_err(gi2c, I2C_BUS_PROTO);
>> +		if (m_stat & M_GP_IRQ_4_EN)
>> +			geni_i2c_err(gi2c, I2C_ARB_LOST);
>> +		if (m_stat & M_CMD_OVERRUN_EN)
>> +			geni_i2c_err(gi2c, GENI_OVERRUN);
>> +		if (m_stat & M_ILLEGAL_CMD_EN)
>> +			geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
>> +		if (m_stat & M_CMD_ABORT_EN)
>> +			geni_i2c_err(gi2c, GENI_ABORT_DONE);
>> +		if (m_stat & M_GP_IRQ_0_EN)
>> +			geni_i2c_err(gi2c, GP_IRQ0);
>> +
>> +		if (!dma)
>> +			writel_relaxed(0, (gi2c->base +
>> +					   SE_GENI_TX_WATERMARK_REG));
> 
> Drop the extra parenthesis. And using writel() instead keeps you under
> 80 chars.
Ok, to dropping the parenthesis.
> 
>> +		goto irqret;
>> +	}
>> +
>> +	if (dma) {
>> +		dev_dbg(gi2c->dev, "i2c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st,
>> +			dm_rx_st);
>> +		goto irqret;
>> +	}
>> +
>> +	if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
>> +		(m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {
> 
> Some of these parenthesis are unnecessary, but more importantly the way
> you wrap this line is misleading; the wrapping indicates that the two
> latter conditionals are related, but the parenthesis says the first two
> are.
> 
> The most significant part of this conditional is "is this a read
> operation", so put this first.
Ok.
> 
> 
>> +		u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
>> +
>> +		for (j = 0; j < rxcnt; j++) {
>> +			u32 temp;
>> +			int p;
>> +
>> +			temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
>> +			for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
>> +				i++, p++)
>> +				cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
>> +			gi2c->cur_rd = i;
> 
> The two-range for loop with a line wrap isn't very clean and the wrap
> calls for a set of braces - which will look ugly.
> 
> How about something like this instead?
> 
> 			p = 0;
> 			while (p < 4 && gi2c->cur_rd < cur->len) {
> 				cur->buf[gi2c->cur_rd++] = temp & 0xff;
> 				temp >>= 8;
> 				p++;
> 			}
Ok.
> 
>> +			if (gi2c->cur_rd == cur->len) {
>> +				dev_dbg(gi2c->dev, "FIFO i:%d,read 0x%x\n",
>> +					i, temp);
> 
> Who's the consumer of this debug line?
I will make the debug message more comprehensible, if that is what you mean.
> 
>> +				break;
>> +			}
>> +		}
>> +	} else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
>> +					!(cur->flags & I2C_M_RD)) {
>> +		for (j = 0; j < gi2c->tx_wm; j++) {
>> +			u32 temp = 0;
>> +			int p;
>> +
>> +			for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
>> +				i++, p++)
>> +				temp |= (((u32)(cur->buf[i]) << (p * 8)));
>> +			writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);
> 
> Same concern as above.
Ok.
> 
>> +			gi2c->cur_wr = i;
>> +			dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp);
>> +			if (gi2c->cur_wr == cur->len) {
>> +				dev_dbg(gi2c->dev, "FIFO i2c bytes done writing\n");
>> +				writel_relaxed(0,
>> +				(gi2c->base + SE_GENI_TX_WATERMARK_REG));
> 
> The line break here is bad.  checkpatch.pl --strict will help you find
> these.
Ok/
> 
> Also using writel() and dropping the parenthesis keeps you below 80
> chars.
Ok, to dropping the parenthesis.
> 
>> +				break;
>> +			}
>> +		}
>> +	}
>> +irqret:
>> +	if (m_stat)
>> +		writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);
> 
> Is it okay for this to be re-ordered wrt above writes?
It is okay. The interrupt bitmask can be cleared anytime while handling 
the IRQ.
> 
>> +
>> +	if (dma) {
>> +		if (dm_tx_st)
>> +			writel_relaxed(dm_tx_st, gi2c->base +
>> +				       SE_DMA_TX_IRQ_CLR);
> 
> Use writel() and you don't have to wrap the line.
Please refer to my earlier response regarding using _relaxed() variants.
> 
>> +		if (dm_rx_st)
>> +			writel_relaxed(dm_rx_st, gi2c->base +
>> +				       SE_DMA_RX_IRQ_CLR);
>> +		/* Ensure all writes are done before returning from ISR. */
> 
> That's not what wmb does.
I will drop it.
> 
>> +		wmb();
>> +	}
>> +	/* if this is err with done-bit not set, handle that thr' timeout. */
> 
> Is "thr'" should for "through"?
Yes. I will make it clear.
> 
>> +	if (m_stat & M_CMD_DONE_EN)
>> +		complete(&gi2c->xfer);
>> +	else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
>> +		complete(&gi2c->xfer);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int geni_i2c_xfer(struct i2c_adapter *adap,
>> +			 struct i2c_msg msgs[],
>> +			 int num)
>> +{
>> +	struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
>> +	int i, ret = 0, timeout = 0;
> 
> No need to initialize these, first use is an assignment.
Ok.
> 
>> +
>> +	gi2c->err = 0;
>> +	gi2c->cur = &msgs[0];
> 
> You're assigning cur in each iteration of the loop below, why do you
> need to do it here as well?
I will remove.
> 
>> +	reinit_completion(&gi2c->xfer);
>> +	ret = pm_runtime_get_sync(gi2c->dev);
>> +	if (ret < 0) {
>> +		dev_err(gi2c->dev, "error turning SE resources:%d\n", ret);
>> +		pm_runtime_put_noidle(gi2c->dev);
>> +		/* Set device in suspended since resume failed */
>> +		pm_runtime_set_suspended(gi2c->dev);
>> +		return ret;
> 
> With the questionable assignment above you're leaving the function with
> gi2c->cur pointing to an object that will soon be invalid.
I will fix the assignment.
> 
>> +	}
>> +
>> +	qcom_geni_i2c_conf(gi2c, 0);
>> +	dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
>> +				num, msgs[0].len, msgs[0].flags);
> 
> Use dynamic function tracing instead of debug prints for this.
Ok. I will remove the debug statements for now and use dynamic function 
tracing in a different patch.
> 
>> +	for (i = 0; i < num; i++) {
> 
> I suggest that you split out two functions here, one for rx-one-message
> and one for tx-one-message. There are some duplication between the two,
> but not too bad.
Ok.
> 
>> +		int stretch = (i < (num - 1));
>> +		u32 m_param = 0;
>> +		u32 m_cmd = 0;
>> +		dma_addr_t tx_dma = 0;
>> +		dma_addr_t rx_dma = 0;
>> +		enum geni_se_xfer_mode mode = FIFO_MODE;
> 
> No need to initialize mode, as the first use is an assignment.
Ok.
> 
>> +
>> +		m_param |= (stretch ? STOP_STRETCH : 0);
>> +		m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);
> 
> Drop some parenthesis.
Ok.
> 
>> +
>> +		gi2c->cur = &msgs[i];
>> +		mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE;
>> +		ret = geni_se_select_mode(gi2c->base, mode);
>> +		if (ret) {
>> +			dev_err(gi2c->dev, "%s: Error mode init %d:%d:%d\n",
>> +				__func__, mode, i, msgs[i].len);
>> +			break;
>> +		}
>> +		if (msgs[i].flags & I2C_M_RD) {
>> +			dev_dbg(gi2c->dev,
>> +				"READ,n:%d,i:%d len:%d, stretch:%d\n",
>> +					num, i, msgs[i].len, stretch);
>> +			geni_write_reg(msgs[i].len,
>> +				       gi2c->base, SE_I2C_RX_TRANS_LEN);
>> +			m_cmd = I2C_READ;
>> +			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
> 
> Drop m_cmd and just write I2C_READ in the function call.
Ok.
> 
>> +			if (mode == SE_DMA) {
>> +				ret = geni_se_rx_dma_prep(gi2c->wrapper_dev,
>> +							gi2c->base, msgs[i].buf,
>> +							msgs[i].len, &rx_dma);
>> +				if (ret) {
>> +					mode = FIFO_MODE;
>> +					ret = geni_se_select_mode(gi2c->base,
>> +								  mode);
>> +				}
>> +			}
>> +		} else {
>> +			dev_dbg(gi2c->dev,
>> +				"WRITE:n:%d,i:%d len:%d, stretch:%d, m_param:0x%x\n",
>> +					num, i, msgs[i].len, stretch, m_param);
>> +			geni_write_reg(msgs[i].len, gi2c->base,
>> +						SE_I2C_TX_TRANS_LEN);
>> +			m_cmd = I2C_WRITE;
>> +			geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
>> +			if (mode == SE_DMA) {
>> +				ret = geni_se_tx_dma_prep(gi2c->wrapper_dev,
>> +							gi2c->base, msgs[i].buf,
>> +							msgs[i].len, &tx_dma);
>> +				if (ret) {
>> +					mode = FIFO_MODE;
>> +					ret = geni_se_select_mode(gi2c->base,
>> +								  mode);
>> +				}
>> +			}
>> +			if (mode == FIFO_MODE) /* Get FIFO IRQ */
>> +				geni_write_reg(1, gi2c->base,
>> +						SE_GENI_TX_WATERMARK_REG);
>> +		}
>> +		/* Ensure FIFO write go through before waiting for Done evet */
> 
> That's not what mb does, drop it.
Ok.
> 
>> +		mb();
>> +		timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
> 
> As written you should just use "ret", but the return value of
> wait_for_completion_timeout() is unsigned long, so change the type of
> timeout instead.
Ok.
> 
>> +		if (!timeout) {
>> +			geni_i2c_err(gi2c, GENI_TIMEOUT);
>> +			gi2c->cur = NULL;
>> +			geni_se_abort_m_cmd(gi2c->base);
>> +			timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
> 
> What if it takes a fraction above HZ time to complete the previous
> operation, then you might end up here with the completion completed, but
> from the wrong operation.
I will update it to checking the specific "abort" flag using 
readl_poll_timeout.
> 
>> +		}
>> +		gi2c->cur_wr = 0;
>> +		gi2c->cur_rd = 0;
>> +		if (mode == SE_DMA) {
>> +			if (gi2c->err) {
>> +				if (msgs[i].flags != I2C_M_RD)
>> +					writel_relaxed(1, gi2c->base +
>> +							SE_DMA_TX_FSM_RST);
>> +				else
>> +					writel_relaxed(1, gi2c->base +
>> +							SE_DMA_RX_FSM_RST);
>> +				wait_for_completion_timeout(&gi2c->xfer, HZ);
>> +			}
>> +			geni_se_rx_dma_unprep(gi2c->wrapper_dev, rx_dma,
>> +					      msgs[i].len);
> 
> Reduce the length of gi2c->wrapper_dev here by using a local variable,
> so that you get below (or close to) 80 chars.
> 
> Also, in either rx or tx cases above I see only that you prep one of
> thse, and then you're unprepping both.
I will re-organize the tx and rx into 2 separate functions. That way all 
the comments will be taken care of.
> 
>> +			geni_se_tx_dma_unprep(gi2c->wrapper_dev, tx_dma,
>> +					      msgs[i].len);
>> +		}
>> +		ret = gi2c->err;
>> +		if (gi2c->err) {
>> +			dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err);
>> +			break;
>> +		}
>> +	}
>> +	if (ret == 0)
>> +		ret = num;
>> +
>> +	pm_runtime_mark_last_busy(gi2c->dev);
>> +	pm_runtime_put_autosuspend(gi2c->dev);
>> +	gi2c->cur = NULL;
>> +	gi2c->err = 0;
>> +	dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
>> +	return ret;
>> +}
> [..]
>> +static int geni_i2c_probe(struct platform_device *pdev)
>> +{
>> +	struct geni_i2c_dev *gi2c;
>> +	struct resource *res;
>> +	int ret;
>> +
>> +	gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
>> +	if (!gi2c)
>> +		return -ENOMEM;
>> +
>> +	gi2c->dev = &pdev->dev;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res)
>> +		return -EINVAL;
> 
> Put this right before devm_ioremap_resource() and drop the error check.
Ok.
> 
>> +
>> +	gi2c->wrapper_dev = pdev->dev.parent;
>> +	gi2c->i2c_rsc.wrapper_dev = pdev->dev.parent;
> 
> As suggested in the other patch, if you have an helper function in the
> wrapper driver that initializes the i2c_rsc then this could fill out the
> actual wrapper, instead of just the device.
Ok.
> 
>> +	gi2c->i2c_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
>> +	if (IS_ERR(gi2c->i2c_rsc.se_clk)) {
>> +		ret = PTR_ERR(gi2c->i2c_rsc.se_clk);
>> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	gi2c->base = devm_ioremap_resource(gi2c->dev, res);
>> +	if (IS_ERR(gi2c->base))
>> +		return PTR_ERR(gi2c->base);
>> +
>> +	gi2c->i2c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
> 
> Drop all the pinctrl stuff. You might still want to call
> pinctrl_pm_select_{idle,default,sleep}_state(), in the various stages of
> PM state though.
Ok.
> 
>> +	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_pinctrl)) {
>> +		dev_err(&pdev->dev, "No pinctrl config specified\n");
>> +		ret = PTR_ERR(gi2c->i2c_rsc.geni_pinctrl);
>> +		return ret;
>> +	}
> [..]
>> +static int geni_i2c_runtime_resume(struct device *dev)
>> +{
>> +	int ret;
>> +	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
>> +
>> +	ret = geni_se_resources_on(&gi2c->i2c_rsc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (!unlikely(gi2c->tx_wm)) {
> 
> Drop the unlikely()
Ok.
> 
>> +		int proto = geni_se_get_proto(gi2c->base);
>> +		int gi2c_tx_depth = geni_se_get_tx_fifo_depth(gi2c->base);
>> +
>> +		if (unlikely(proto != I2C)) {
> 
> Has this changes since probe? Can't we verify that the proto is correct
> during probe and then just trust that the hardware doesn't change
> function?
Yes we can do that. I will move the check to the probe.
> 
>> +			dev_err(gi2c->dev, "Invalid proto %d\n", proto);
>> +			geni_se_resources_off(&gi2c->i2c_rsc);
>> +			return -ENXIO;
>> +		}
>> +
>> +		gi2c->tx_wm = gi2c_tx_depth - 1;
>> +		geni_se_init(gi2c->base, gi2c->tx_wm, gi2c_tx_depth);
>> +		geni_se_config_packing(gi2c->base, 8, 4, true);
>> +		dev_dbg(gi2c->dev, "i2c fifo/se-dma mode. fifo depth:%d\n",
>> +			gi2c_tx_depth);
>> +	}
>> +	enable_irq(gi2c->irq);
>> +
>> +	return 0;
>> +}
> [..]
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:i2c_geni");
> 
> What will reference the kernel module by this alias?
I will remove it.
> 
>> -- 
>> Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
>> a Linux Foundation Collaborative Project
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [v2, 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-02-23 18:06   ` [v2, " Guenter Roeck
@ 2018-02-27 13:23     ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-02-27 13:23 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan, Sagar Dharia



On 2/23/2018 11:06 AM, Guenter Roeck wrote:
> On Fri, Jan 12, 2018 at 06:05:47PM -0700, Karthikeyan Ramasubramanian wrote:
>> This driver supports GENI based UART Controller in the Qualcomm SOCs. The
>> Qualcomm Generic Interface (GENI) is a programmable module supporting a
>> wide range of serial interfaces including UART. This driver support console
>> operations using FIFO mode of transfer.
>>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> ---
>>   drivers/tty/serial/Kconfig            |   10 +
>>   drivers/tty/serial/Makefile           |    1 +
>>   drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
>>   3 files changed, 1425 insertions(+)
>>   create mode 100644 drivers/tty/serial/qcom_geni_serial.c
>>
>> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
>> index b788fee..1be30e5 100644
>> --- a/drivers/tty/serial/Kconfig
>> +++ b/drivers/tty/serial/Kconfig
>> @@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
>>   	select SERIAL_CORE_CONSOLE
>>   	select SERIAL_EARLYCON
>>   
>> +config SERIAL_QCOM_GENI
>> +	tristate "QCOM on-chip GENI based serial port support"
>> +	depends on ARCH_QCOM
>> +	select SERIAL_CORE
>> +	select SERIAL_CORE_CONSOLE
> 
> Serial console drivers have to be bool, not tristate.
> Building a console driver as module doesn't make sense
> (it is needed before modules are loaded). It also results
> in a build failure due to a symbol which is not exported.
I will mark it as bool.
> 
>> +	select SERIAL_EARLYCON
>> +	help
>> +	  Serial driver for Qualcomm Technologies Inc's GENI based QUP
>> +	  hardware.
>> +
>>   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 842d185..64a8d82 100644
>> --- a/drivers/tty/serial/Makefile
>> +++ b/drivers/tty/serial/Makefile
>> @@ -63,6 +63,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
>>   obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
>>   obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
>>   obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
>> +obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
>>   obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
>>   obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
>>   obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
>> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
>> new file mode 100644
>> index 0000000..0dbd329
>> --- /dev/null
>> +++ b/drivers/tty/serial/qcom_geni_serial.c
>> @@ -0,0 +1,1414 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only 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.
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +#include <linux/bitops.h>
>> +#include <linux/delay.h>
>> +#include <linux/console.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/qcom-geni-se.h>
>> +#include <linux/serial.h>
>> +#include <linux/serial_core.h>
>> +#include <linux/slab.h>
>> +#include <linux/tty.h>
>> +#include <linux/tty_flip.h>
>> +
>> +/* UART specific GENI registers */
>> +#define SE_UART_TX_TRANS_CFG		(0x25C)
>> +#define SE_UART_TX_WORD_LEN		(0x268)
>> +#define SE_UART_TX_STOP_BIT_LEN		(0x26C)
>> +#define SE_UART_TX_TRANS_LEN		(0x270)
>> +#define SE_UART_RX_TRANS_CFG		(0x280)
>> +#define SE_UART_RX_WORD_LEN		(0x28C)
>> +#define SE_UART_RX_STALE_CNT		(0x294)
>> +#define SE_UART_TX_PARITY_CFG		(0x2A4)
>> +#define SE_UART_RX_PARITY_CFG		(0x2A8)
>> +
>> +/* SE_UART_TRANS_CFG */
>> +#define UART_TX_PAR_EN		(BIT(0))
>> +#define UART_CTS_MASK		(BIT(1))
>> +
>> +/* SE_UART_TX_WORD_LEN */
>> +#define TX_WORD_LEN_MSK		(GENMASK(9, 0))
>> +
>> +/* SE_UART_TX_STOP_BIT_LEN */
>> +#define TX_STOP_BIT_LEN_MSK	(GENMASK(23, 0))
>> +#define TX_STOP_BIT_LEN_1	(0)
>> +#define TX_STOP_BIT_LEN_1_5	(1)
>> +#define TX_STOP_BIT_LEN_2	(2)
>> +
>> +/* SE_UART_TX_TRANS_LEN */
>> +#define TX_TRANS_LEN_MSK	(GENMASK(23, 0))
>> +
>> +/* SE_UART_RX_TRANS_CFG */
>> +#define UART_RX_INS_STATUS_BIT	(BIT(2))
>> +#define UART_RX_PAR_EN		(BIT(3))
>> +
>> +/* SE_UART_RX_WORD_LEN */
>> +#define RX_WORD_LEN_MASK	(GENMASK(9, 0))
>> +
>> +/* SE_UART_RX_STALE_CNT */
>> +#define RX_STALE_CNT		(GENMASK(23, 0))
>> +
>> +/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
>> +#define PAR_CALC_EN		(BIT(0))
>> +#define PAR_MODE_MSK		(GENMASK(2, 1))
>> +#define PAR_MODE_SHFT		(1)
>> +#define PAR_EVEN		(0x00)
>> +#define PAR_ODD			(0x01)
>> +#define PAR_SPACE		(0x10)
>> +#define PAR_MARK		(0x11)
>> +
>> +/* UART M_CMD OP codes */
>> +#define UART_START_TX		(0x1)
>> +#define UART_START_BREAK	(0x4)
>> +#define UART_STOP_BREAK		(0x5)
>> +/* UART S_CMD OP codes */
>> +#define UART_START_READ		(0x1)
>> +#define UART_PARAM		(0x1)
>> +
>> +#define UART_OVERSAMPLING	(32)
>> +#define STALE_TIMEOUT		(16)
>> +#define DEFAULT_BITS_PER_CHAR	(10)
>> +#define GENI_UART_NR_PORTS	(15)
>> +#define GENI_UART_CONS_PORTS	(1)
>> +#define DEF_FIFO_DEPTH_WORDS	(16)
>> +#define DEF_TX_WM		(2)
>> +#define DEF_FIFO_WIDTH_BITS	(32)
>> +#define UART_CORE2X_VOTE	(10000)
>> +#define UART_CONSOLE_RX_WM	(2)
>> +
>> +static int owr;
>> +module_param(owr, int, 0644);
>> +
>> +struct qcom_geni_serial_port {
>> +	struct uart_port uport;
>> +	char name[20];
>> +	unsigned int tx_fifo_depth;
>> +	unsigned int tx_fifo_width;
>> +	unsigned int rx_fifo_depth;
>> +	unsigned int tx_wm;
>> +	unsigned int rx_wm;
>> +	unsigned int rx_rfr;
>> +	int xfer_mode;
>> +	bool port_setup;
>> +	unsigned int *rx_fifo;
>> +	int (*handle_rx)(struct uart_port *uport,
>> +			unsigned int rx_fifo_wc,
>> +			unsigned int rx_last_byte_valid,
>> +			unsigned int rx_last,
>> +			bool drop_rx);
>> +	struct device *wrapper_dev;
>> +	struct geni_se_rsc serial_rsc;
>> +	unsigned int xmit_size;
>> +	void *rx_buf;
>> +	unsigned int cur_baud;
>> +};
>> +
>> +static const struct uart_ops qcom_geni_serial_pops;
>> +static struct uart_driver qcom_geni_console_driver;
>> +static int handle_rx_console(struct uart_port *uport,
>> +			unsigned int rx_fifo_wc,
>> +			unsigned int rx_last_byte_valid,
>> +			unsigned int rx_last,
>> +			bool drop_rx);
>> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
>> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +				int offset, int bit_field, bool set);
>> +static void qcom_geni_serial_stop_rx(struct uart_port *uport);
>> +
>> +static atomic_t uart_line_id = ATOMIC_INIT(0);
>> +
>> +#define GET_DEV_PORT(uport) \
>> +	container_of(uport, struct qcom_geni_serial_port, uport)
>> +
>> +static struct qcom_geni_serial_port qcom_geni_console_port;
>> +
>> +static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
>> +{
>> +	if (cfg_flags & UART_CONFIG_TYPE)
>> +		uport->type = PORT_MSM;
>> +}
>> +
>> +static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
>> +{
>> +	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
>> +}
>> +
>> +static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
>> +							unsigned int mctrl)
>> +{
>> +}
>> +
>> +static const char *qcom_geni_serial_get_type(struct uart_port *uport)
>> +{
>> +	return "MSM";
>> +}
>> +
>> +static struct qcom_geni_serial_port *get_port_from_line(int line)
>> +{
>> +	struct qcom_geni_serial_port *port = NULL;
>> +
>> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
>> +		port = ERR_PTR(-ENXIO);
>> +	port = &qcom_geni_console_port;
>> +	return port;
>> +}
>> +
>> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +				int offset, int bit_field, bool set)
>> +{
>> +	int iter = 0;
>> +	unsigned int reg;
>> +	bool met = false;
>> +	struct qcom_geni_serial_port *port = NULL;
>> +	bool cond = false;
>> +	unsigned int baud = 115200;
>> +	unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS;
>> +	unsigned long total_iter = 2000;
>> +
>> +
>> +	if (uport->private_data) {
>> +		port = GET_DEV_PORT(uport);
>> +		baud = (port->cur_baud ? port->cur_baud : 115200);
>> +		fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
>> +		/*
>> +		 * Total polling iterations based on FIFO worth of bytes to be
>> +		 * sent at current baud .Add a little fluff to the wait.
>> +		 */
>> +		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
>> +		total_iter += 50;
>> +	}
>> +
>> +	while (iter < total_iter) {
>> +		reg = geni_read_reg_nolog(uport->membase, offset);
>> +		cond = reg & bit_field;
>> +		if (cond == set) {
>> +			met = true;
>> +			break;
>> +		}
>> +		udelay(10);
>> +		iter++;
>> +	}
>> +	return met;
>> +}
>> +
>> +static void qcom_geni_serial_setup_tx(struct uart_port *uport,
>> +				unsigned int xmit_size)
>> +{
>> +	u32 m_cmd = 0;
>> +
>> +	geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
>> +	m_cmd |= (UART_START_TX << M_OPCODE_SHFT);
>> +	geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
>> +	/*
>> +	 * Writes to enable the primary sequencer should go through before
>> +	 * exiting this function.
>> +	 */
>> +	mb();
>> +}
>> +
>> +static void qcom_geni_serial_poll_cancel_tx(struct uart_port *uport)
>> +{
>> +	int done = 0;
>> +	unsigned int irq_clear = M_CMD_DONE_EN;
>> +	unsigned int geni_status = 0;
>> +
>> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_CMD_DONE_EN, true);
>> +	if (!done) {
>> +		geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
>> +					SE_GENI_M_CMD_CTRL_REG);
>> +		owr++;
>> +		irq_clear |= M_CMD_ABORT_EN;
>> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +							M_CMD_ABORT_EN, true);
>> +	}
>> +	geni_status = geni_read_reg_nolog(uport->membase,
>> +						  SE_GENI_STATUS);
>> +	if (geni_status & M_GENI_CMD_ACTIVE)
>> +		owr++;
>> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
>> +{
>> +	unsigned int irq_clear = S_CMD_DONE_EN;
>> +
>> +	geni_se_abort_s_cmd(uport->membase);
>> +	/* Ensure this goes through before polling. */
>> +	mb();
>> +	irq_clear |= S_CMD_ABORT_EN;
>> +	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +					S_GENI_CMD_ABORT, false);
>> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
>> +	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +static int qcom_geni_serial_get_char(struct uart_port *uport)
>> +{
>> +	unsigned int rx_fifo;
>> +	unsigned int m_irq_status;
>> +	unsigned int s_irq_status;
>> +
>> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +			M_SEC_IRQ_EN, true)))
>> +		return -ENXIO;
>> +
>> +	m_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_M_IRQ_STATUS);
>> +	s_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_S_IRQ_STATUS);
>> +	geni_write_reg_nolog(m_irq_status, uport->membase,
>> +						SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg_nolog(s_irq_status, uport->membase,
>> +						SE_GENI_S_IRQ_CLEAR);
>> +
>> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
>> +			RX_FIFO_WC_MSK, true)))
>> +		return -ENXIO;
>> +
>> +	/*
>> +	 * Read the Rx FIFO only after clearing the interrupt registers and
>> +	 * getting valid RX fifo status.
>> +	 */
>> +	mb();
>> +	rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
>> +	rx_fifo &= 0xFF;
>> +	return rx_fifo;
>> +}
>> +
>> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
>> +					unsigned char c)
>> +{
>> +	int b = (int) c;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	geni_write_reg_nolog(port->tx_wm, uport->membase,
>> +					SE_GENI_TX_WATERMARK_REG);
>> +	qcom_geni_serial_setup_tx(uport, 1);
>> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +				M_TX_FIFO_WATERMARK_EN, true))
>> +		WARN_ON(1);
>> +	geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
>> +	geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
>> +							SE_GENI_M_IRQ_CLEAR);
>> +	/*
>> +	 * Ensure FIFO write goes through before polling for status but.
>> +	 */
>> +	mb();
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +}
>> +#endif
>> +
>> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
> 
> "if defined(CONFIG_SERIAL_CORE_CONSOLE)" doesn't make much sense here
> since the Kconfig entry selects it. Given that, the entire conditional
> is unnecessary since it will always be true.
> 
>> +static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
>> +{
>> +	geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn);
>> +	/*
>> +	 * Ensure FIFO write clear goes through before
>> +	 * next iteration.
>> +	 */
>> +	mb();
>> +
>> +}
>> +
>> +static void
>> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
>> +				unsigned int count)
>> +{
>> +	int new_line = 0;
>> +	int i;
>> +	int bytes_to_send = count;
>> +	int fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +	int tx_wm = DEF_TX_WM;
>> +
>> +	for (i = 0; i < count; i++) {
>> +		if (s[i] == '\n')
>> +			new_line++;
>> +	}
>> +
>> +	bytes_to_send += new_line;
>> +	geni_write_reg_nolog(tx_wm, uport->membase,
>> +					SE_GENI_TX_WATERMARK_REG);
>> +	qcom_geni_serial_setup_tx(uport, bytes_to_send);
>> +	i = 0;
>> +	while (i < count) {
>> +		u32 chars_to_write = 0;
>> +		u32 avail_fifo_bytes = (fifo_depth - tx_wm);
>> +
>> +		/*
>> +		 * If the WM bit never set, then the Tx state machine is not
>> +		 * in a valid state, so break, cancel/abort any existing
>> +		 * command. Unfortunately the current data being written is
>> +		 * lost.
>> +		 */
>> +		while (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_TX_FIFO_WATERMARK_EN, true))
>> +			break;
>> +		chars_to_write = min((unsigned int)(count - i),
>> +							avail_fifo_bytes);
>> +		if ((chars_to_write << 1) > avail_fifo_bytes)
>> +			chars_to_write = (avail_fifo_bytes >> 1);
>> +		uart_console_write(uport, (s + i), chars_to_write,
>> +						qcom_geni_serial_wr_char);
>> +		geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
>> +							SE_GENI_M_IRQ_CLEAR);
>> +		/* Ensure this goes through before polling for WM IRQ again.*/
>> +		mb();
>> +		i += chars_to_write;
>> +	}
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +}
>> +
>> +static void qcom_geni_serial_console_write(struct console *co, const char *s,
>> +			      unsigned int count)
>> +{
>> +	struct uart_port *uport;
>> +	struct qcom_geni_serial_port *port;
>> +	int locked = 1;
>> +	unsigned long flags;
>> +
>> +	WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
>> +
>> +	port = get_port_from_line(co->index);
>> +	if (IS_ERR_OR_NULL(port))
>> +		return;
>> +
>> +	uport = &port->uport;
>> +	if (oops_in_progress)
>> +		locked = spin_trylock_irqsave(&uport->lock, flags);
>> +	else
>> +		spin_lock_irqsave(&uport->lock, flags);
>> +
>> +	if (locked) {
>> +		__qcom_geni_serial_console_write(uport, s, count);
>> +		spin_unlock_irqrestore(&uport->lock, flags);
>> +	}
>> +}
>> +
>> +static int handle_rx_console(struct uart_port *uport,
>> +			unsigned int rx_fifo_wc,
>> +			unsigned int rx_last_byte_valid,
>> +			unsigned int rx_last,
>> +			bool drop_rx)
>> +{
>> +	int i, c;
>> +	unsigned char *rx_char;
>> +	struct tty_port *tport;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +
>> +	tport = &uport->state->port;
>> +	for (i = 0; i < rx_fifo_wc; i++) {
>> +		int bytes = 4;
>> +
>> +		*(qcom_port->rx_fifo) =
>> +			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
>> +		if (drop_rx)
>> +			continue;
>> +		rx_char = (unsigned char *)qcom_port->rx_fifo;
>> +
>> +		if (i == (rx_fifo_wc - 1)) {
>> +			if (rx_last && rx_last_byte_valid)
>> +				bytes = rx_last_byte_valid;
>> +		}
>> +		for (c = 0; c < bytes; c++) {
>> +			char flag = TTY_NORMAL;
>> +			int sysrq;
>> +
>> +			uport->icount.rx++;
>> +			sysrq = uart_handle_sysrq_char(uport, rx_char[c]);
>> +			if (!sysrq)
>> +				tty_insert_flip_char(tport, rx_char[c], flag);
>> +		}
>> +	}
>> +	if (!drop_rx)
>> +		tty_flip_buffer_push(tport);
>> +	return 0;
>> +}
>> +#else
>> +static int handle_rx_console(struct uart_port *uport,
>> +			unsigned int rx_fifo_wc,
>> +			unsigned int rx_last_byte_valid,
>> +			unsigned int rx_last,
>> +			bool drop_rx)
>> +{
>> +	return -EPERM;
>> +}
>> +
>> +#endif /* (CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)) */
>> +
>> +static void qcom_geni_serial_start_tx(struct uart_port *uport)
>> +{
>> +	unsigned int geni_m_irq_en;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +	unsigned int geni_status;
>> +
>> +	if (qcom_port->xfer_mode == FIFO_MODE) {
>> +		geni_status = geni_read_reg_nolog(uport->membase,
>> +						  SE_GENI_STATUS);
>> +		if (geni_status & M_GENI_CMD_ACTIVE)
>> +			goto exit_start_tx;
>> +
>> +		if (!qcom_geni_serial_tx_empty(uport))
>> +			goto exit_start_tx;
>> +
>> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
>> +						    SE_GENI_M_IRQ_EN);
>> +		geni_m_irq_en |= (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN);
>> +
>> +		geni_write_reg_nolog(qcom_port->tx_wm, uport->membase,
>> +						SE_GENI_TX_WATERMARK_REG);
>> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +		/* Geni command setup should complete before returning.*/
>> +		mb();
>> +	}
>> +exit_start_tx:
>> +	return;
>> +}
>> +
>> +static void stop_tx_sequencer(struct uart_port *uport)
>> +{
>> +	unsigned int geni_m_irq_en;
>> +	unsigned int geni_status;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
>> +	geni_m_irq_en &= ~M_CMD_DONE_EN;
>> +	if (port->xfer_mode == FIFO_MODE) {
>> +		geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN;
>> +		geni_write_reg_nolog(0, uport->membase,
>> +				     SE_GENI_TX_WATERMARK_REG);
>> +	}
>> +	port->xmit_size = 0;
>> +	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
>> +	geni_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_STATUS);
>> +	/* Possible stop tx is called multiple times. */
>> +	if (!(geni_status & M_GENI_CMD_ACTIVE))
>> +		return;
>> +
>> +	geni_se_cancel_m_cmd(uport->membase);
>> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_CMD_CANCEL_EN, true)) {
>> +		geni_se_abort_m_cmd(uport->membase);
>> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_CMD_ABORT_EN, true);
>> +		geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
>> +							SE_GENI_M_IRQ_CLEAR);
>> +	}
>> +	geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
>> +{
>> +	stop_tx_sequencer(uport);
>> +}
>> +
>> +static void start_rx_sequencer(struct uart_port *uport)
>> +{
>> +	unsigned int geni_s_irq_en;
>> +	unsigned int geni_m_irq_en;
>> +	unsigned int geni_status;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +	if (geni_status & S_GENI_CMD_ACTIVE)
>> +		qcom_geni_serial_stop_rx(uport);
>> +
>> +	geni_se_setup_s_cmd(uport->membase, UART_START_READ, 0);
>> +
>> +	if (port->xfer_mode == FIFO_MODE) {
>> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +
>> +		geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
>> +		geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
>> +
>> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +	}
>> +	/*
>> +	 * Ensure the writes to the secondary sequencer and interrupt enables
>> +	 * go through.
>> +	 */
>> +	mb();
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +}
>> +
>> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
>> +{
>> +	start_rx_sequencer(uport);
>> +}
>> +
>> +static void stop_rx_sequencer(struct uart_port *uport)
>> +{
>> +	unsigned int geni_s_irq_en;
>> +	unsigned int geni_m_irq_en;
>> +	unsigned int geni_status;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +	u32 irq_clear = S_CMD_DONE_EN;
>> +	bool done;
>> +
>> +	if (port->xfer_mode == FIFO_MODE) {
>> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +		geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
>> +		geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
>> +
>> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +	}
>> +
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +	/* Possible stop rx is called multiple times. */
>> +	if (!(geni_status & S_GENI_CMD_ACTIVE))
>> +		return;
>> +	geni_se_cancel_s_cmd(uport->membase);
>> +	/*
>> +	 * Ensure that the cancel goes through before polling for the
>> +	 * cancel control bit.
>> +	 */
>> +	mb();
>> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +					S_GENI_CMD_CANCEL, false);
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
>> +	if ((geni_status & S_GENI_CMD_ACTIVE))
>> +		qcom_geni_serial_abort_rx(uport);
>> +}
>> +
>> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
>> +{
>> +	stop_rx_sequencer(uport);
>> +}
>> +
>> +static int qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
>> +{
>> +	int ret = 0;
>> +	unsigned int rx_fifo_status;
>> +	unsigned int rx_fifo_wc = 0;
>> +	unsigned int rx_last_byte_valid = 0;
>> +	unsigned int rx_last = 0;
>> +	struct tty_port *tport;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	tport = &uport->state->port;
>> +	rx_fifo_status = geni_read_reg_nolog(uport->membase,
>> +				SE_GENI_RX_FIFO_STATUS);
>> +	rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK;
>> +	rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >>
>> +						RX_LAST_BYTE_VALID_SHFT);
>> +	rx_last = rx_fifo_status & RX_LAST;
>> +	if (rx_fifo_wc)
>> +		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
>> +							rx_last, drop_rx);
>> +	return ret;
>> +}
>> +
>> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
>> +{
>> +	int ret = 0;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +	struct circ_buf *xmit = &uport->state->xmit;
>> +	unsigned int avail_fifo_bytes = 0;
>> +	unsigned int bytes_remaining = 0;
>> +	int i = 0;
>> +	unsigned int tx_fifo_status;
>> +	unsigned int xmit_size;
>> +	unsigned int fifo_width_bytes = 1;
>> +	int temp_tail = 0;
>> +
>> +	xmit_size = uart_circ_chars_pending(xmit);
>> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
>> +					SE_GENI_TX_FIFO_STATUS);
>> +	/* Both FIFO and framework buffer are drained */
>> +	if ((xmit_size == qcom_port->xmit_size) && !tx_fifo_status) {
>> +		qcom_port->xmit_size = 0;
>> +		uart_circ_clear(xmit);
>> +		qcom_geni_serial_stop_tx(uport);
>> +		goto exit_handle_tx;
>> +	}
>> +	xmit_size -= qcom_port->xmit_size;
>> +
>> +	avail_fifo_bytes = (qcom_port->tx_fifo_depth - qcom_port->tx_wm) *
>> +							fifo_width_bytes;
>> +	temp_tail = (xmit->tail + qcom_port->xmit_size) & (UART_XMIT_SIZE - 1);
>> +	if (xmit_size > (UART_XMIT_SIZE - temp_tail))
>> +		xmit_size = (UART_XMIT_SIZE - temp_tail);
>> +	if (xmit_size > avail_fifo_bytes)
>> +		xmit_size = avail_fifo_bytes;
>> +
>> +	if (!xmit_size)
>> +		goto exit_handle_tx;
>> +
>> +	qcom_geni_serial_setup_tx(uport, xmit_size);
>> +
>> +	bytes_remaining = xmit_size;
>> +	while (i < xmit_size) {
>> +		unsigned int tx_bytes;
>> +		unsigned int buf = 0;
>> +		int c;
>> +
>> +		tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
>> +					bytes_remaining : fifo_width_bytes);
>> +
>> +		for (c = 0; c < tx_bytes ; c++)
>> +			buf |= (xmit->buf[temp_tail + c] << (c * 8));
>> +		geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
>> +		i += tx_bytes;
>> +		temp_tail = (temp_tail + tx_bytes) & (UART_XMIT_SIZE - 1);
>> +		uport->icount.tx += tx_bytes;
>> +		bytes_remaining -= tx_bytes;
>> +		/* Ensure FIFO write goes through */
>> +		wmb();
>> +	}
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +	qcom_port->xmit_size += xmit_size;
>> +exit_handle_tx:
>> +	uart_write_wakeup(uport);
>> +	return ret;
>> +}
>> +
>> +static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
>> +{
>> +	unsigned int m_irq_status;
>> +	unsigned int s_irq_status;
>> +	struct uart_port *uport = dev;
>> +	unsigned long flags;
>> +	unsigned int m_irq_en;
>> +	bool drop_rx = false;
>> +	struct tty_port *tport = &uport->state->port;
>> +
>> +	spin_lock_irqsave(&uport->lock, flags);
>> +	if (uport->suspended)
>> +		goto exit_geni_serial_isr;
>> +	m_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_M_IRQ_STATUS);
>> +	s_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_S_IRQ_STATUS);
>> +	m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
>> +	geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
>> +
>> +	if ((m_irq_status & M_ILLEGAL_CMD_EN)) {
>> +		WARN_ON(1);
>> +		goto exit_geni_serial_isr;
>> +	}
>> +
>> +	if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
>> +		uport->icount.overrun++;
>> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
>> +	}
>> +
>> +	if ((m_irq_status & m_irq_en) &
>> +	    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
>> +		qcom_geni_serial_handle_tx(uport);
>> +
>> +	if ((s_irq_status & S_GP_IRQ_0_EN) || (s_irq_status & S_GP_IRQ_1_EN)) {
>> +		if (s_irq_status & S_GP_IRQ_0_EN)
>> +			uport->icount.parity++;
>> +		drop_rx = true;
>> +	} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
>> +		(s_irq_status & S_GP_IRQ_3_EN)) {
>> +		uport->icount.brk++;
>> +	}
>> +
>> +	if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
>> +		(s_irq_status & S_RX_FIFO_LAST_EN))
>> +		qcom_geni_serial_handle_rx(uport, drop_rx);
>> +
>> +exit_geni_serial_isr:
>> +	spin_unlock_irqrestore(&uport->lock, flags);
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
>> +{
>> +	struct uart_port *uport;
>> +
>> +	if (!port)
>> +		return -ENODEV;
>> +
>> +	uport = &port->uport;
>> +	port->tx_fifo_depth = geni_se_get_tx_fifo_depth(uport->membase);
>> +	if (!port->tx_fifo_depth) {
>> +		dev_err(uport->dev, "%s:Invalid TX FIFO depth read\n",
>> +								__func__);
>> +		return -ENXIO;
>> +	}
>> +
>> +	port->tx_fifo_width = geni_se_get_tx_fifo_width(uport->membase);
>> +	if (!port->tx_fifo_width) {
>> +		dev_err(uport->dev, "%s:Invalid TX FIFO width read\n",
>> +								__func__);
>> +		return -ENXIO;
>> +	}
>> +
>> +	port->rx_fifo_depth = geni_se_get_rx_fifo_depth(uport->membase);
>> +	if (!port->rx_fifo_depth) {
>> +		dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
>> +								__func__);
>> +		return -ENXIO;
>> +	}
>> +
>> +	uport->fifosize =
>> +		((port->tx_fifo_depth * port->tx_fifo_width) >> 3);
>> +	return 0;
>> +}
>> +
>> +static void set_rfr_wm(struct qcom_geni_serial_port *port)
>> +{
>> +	/*
>> +	 * Set RFR (Flow off) to FIFO_DEPTH - 2.
>> +	 * RX WM level at 10% RX_FIFO_DEPTH.
>> +	 * TX WM level at 10% TX_FIFO_DEPTH.
>> +	 */
>> +	port->rx_rfr = port->rx_fifo_depth - 2;
>> +	port->rx_wm = UART_CONSOLE_RX_WM;
>> +	port->tx_wm = 2;
>> +}
>> +
>> +static void qcom_geni_serial_shutdown(struct uart_port *uport)
>> +{
>> +	unsigned long flags;
>> +
>> +	/* Stop the console before stopping the current tx */
>> +	console_stop(uport->cons);
>> +
>> +	disable_irq(uport->irq);
>> +	free_irq(uport->irq, uport);
>> +	spin_lock_irqsave(&uport->lock, flags);
>> +	qcom_geni_serial_stop_tx(uport);
>> +	qcom_geni_serial_stop_rx(uport);
>> +	spin_unlock_irqrestore(&uport->lock, flags);
>> +}
>> +
>> +static int qcom_geni_serial_port_setup(struct uart_port *uport)
>> +{
>> +	int ret = 0;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +	unsigned long cfg0, cfg1;
>> +	unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
>> +
>> +	set_rfr_wm(qcom_port);
>> +	geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT);
>> +	/*
>> +	 * Make an unconditional cancel on the main sequencer to reset
>> +	 * it else we could end up in data loss scenarios.
>> +	 */
>> +	qcom_port->xfer_mode = FIFO_MODE;
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +	geni_se_get_packing_config(8, 1, false, &cfg0, &cfg1);
>> +	geni_write_reg_nolog(cfg0, uport->membase,
>> +					SE_GENI_TX_PACKING_CFG0);
>> +	geni_write_reg_nolog(cfg1, uport->membase,
>> +					SE_GENI_TX_PACKING_CFG1);
>> +	geni_se_get_packing_config(8, 4, false, &cfg0, &cfg1);
>> +	geni_write_reg_nolog(cfg0, uport->membase,
>> +					SE_GENI_RX_PACKING_CFG0);
>> +	geni_write_reg_nolog(cfg1, uport->membase,
>> +					SE_GENI_RX_PACKING_CFG1);
>> +	ret = geni_se_init(uport->membase, qcom_port->rx_wm, qcom_port->rx_rfr);
>> +	if (ret) {
>> +		dev_err(uport->dev, "%s: Fail\n", __func__);
>> +		goto exit_portsetup;
>> +	}
>> +
>> +	ret = geni_se_select_mode(uport->membase, qcom_port->xfer_mode);
>> +	if (ret)
>> +		goto exit_portsetup;
>> +
>> +	qcom_port->port_setup = true;
>> +	/*
>> +	 * Ensure Port setup related IO completes before returning to
>> +	 * framework.
>> +	 */
>> +	mb();
>> +exit_portsetup:
>> +	return ret;
>> +}
>> +
>> +static int qcom_geni_serial_startup(struct uart_port *uport)
>> +{
>> +	int ret = 0;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +
>> +	scnprintf(qcom_port->name, sizeof(qcom_port->name),
>> +		  "qcom_serial_geni%d",	uport->line);
>> +
>> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
>> +		dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
>> +				 __func__, geni_se_get_proto(uport->membase));
>> +		ret = -ENXIO;
>> +		goto exit_startup;
>> +	}
>> +
>> +	get_tx_fifo_size(qcom_port);
>> +	if (!qcom_port->port_setup) {
>> +		if (qcom_geni_serial_port_setup(uport))
>> +			goto exit_startup;
>> +	}
>> +
>> +	/*
>> +	 * Ensure that all the port configuration writes complete
>> +	 * before returning to the framework.
>> +	 */
>> +	mb();
>> +	ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
>> +			qcom_port->name, uport);
>> +	if (unlikely(ret)) {
>> +		dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n",
>> +							__func__, ret);
>> +		goto exit_startup;
>> +	}
>> +
>> +exit_startup:
>> +	return ret;
>> +}
>> +
>> +static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
>> +{
>> +	unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
>> +		32000000, 48000000, 64000000, 80000000, 96000000, 100000000};
>> +	int i;
>> +	int match = -1;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
>> +		if (clk_freq > root_freq[i])
>> +			continue;
>> +
>> +		if (!(root_freq[i] % clk_freq)) {
>> +			match = i;
>> +			break;
>> +		}
>> +	}
>> +	if (match != -1)
>> +		*ser_clk = root_freq[match];
>> +	else
>> +		pr_err("clk_freq %ld\n", clk_freq);
>> +	return match;
>> +}
>> +
>> +static void geni_serial_write_term_regs(struct uart_port *uport,
>> +		u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
>> +		u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
>> +		u32 s_clk_cfg)
>> +{
>> +	geni_write_reg_nolog(tx_trans_cfg, uport->membase,
>> +							SE_UART_TX_TRANS_CFG);
>> +	geni_write_reg_nolog(tx_parity_cfg, uport->membase,
>> +							SE_UART_TX_PARITY_CFG);
>> +	geni_write_reg_nolog(rx_trans_cfg, uport->membase,
>> +							SE_UART_RX_TRANS_CFG);
>> +	geni_write_reg_nolog(rx_parity_cfg, uport->membase,
>> +							SE_UART_RX_PARITY_CFG);
>> +	geni_write_reg_nolog(bits_per_char, uport->membase,
>> +							SE_UART_TX_WORD_LEN);
>> +	geni_write_reg_nolog(bits_per_char, uport->membase,
>> +							SE_UART_RX_WORD_LEN);
>> +	geni_write_reg_nolog(stop_bit_len, uport->membase,
>> +						SE_UART_TX_STOP_BIT_LEN);
>> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
>> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);
>> +}
>> +
>> +static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
>> +{
>> +	unsigned long ser_clk;
>> +	int dfs_index;
>> +	int clk_div = 0;
>> +
>> +	*desired_clk_rate = baud * UART_OVERSAMPLING;
>> +	dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk);
>> +	if (dfs_index < 0) {
>> +		pr_err("%s: Can't find matching DFS entry for baud %d\n",
>> +								__func__, baud);
>> +		clk_div = -EINVAL;
>> +		goto exit_get_clk_div_rate;
>> +	}
>> +
>> +	clk_div = ser_clk / *desired_clk_rate;
>> +	*desired_clk_rate = ser_clk;
>> +exit_get_clk_div_rate:
>> +	return clk_div;
>> +}
>> +
>> +static void qcom_geni_serial_set_termios(struct uart_port *uport,
>> +				struct ktermios *termios, struct ktermios *old)
>> +{
>> +	unsigned int baud;
>> +	unsigned int bits_per_char = 0;
>> +	unsigned int tx_trans_cfg;
>> +	unsigned int tx_parity_cfg;
>> +	unsigned int rx_trans_cfg;
>> +	unsigned int rx_parity_cfg;
>> +	unsigned int stop_bit_len;
>> +	unsigned int clk_div;
>> +	unsigned long ser_clk_cfg = 0;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +	unsigned long clk_rate;
>> +
>> +	qcom_geni_serial_stop_rx(uport);
>> +	/* baud rate */
>> +	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
>> +	port->cur_baud = baud;
>> +	clk_div = get_clk_div_rate(baud, &clk_rate);
>> +	if (clk_div <= 0)
>> +		goto exit_set_termios;
>> +
>> +	uport->uartclk = clk_rate;
>> +	clk_set_rate(port->serial_rsc.se_clk, clk_rate);
>> +	ser_clk_cfg |= SER_CLK_EN;
>> +	ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
>> +
>> +	/* parity */
>> +	tx_trans_cfg = geni_read_reg_nolog(uport->membase,
>> +							SE_UART_TX_TRANS_CFG);
>> +	tx_parity_cfg = geni_read_reg_nolog(uport->membase,
>> +							SE_UART_TX_PARITY_CFG);
>> +	rx_trans_cfg = geni_read_reg_nolog(uport->membase,
>> +							SE_UART_RX_TRANS_CFG);
>> +	rx_parity_cfg = geni_read_reg_nolog(uport->membase,
>> +							SE_UART_RX_PARITY_CFG);
>> +	if (termios->c_cflag & PARENB) {
>> +		tx_trans_cfg |= UART_TX_PAR_EN;
>> +		rx_trans_cfg |= UART_RX_PAR_EN;
>> +		tx_parity_cfg |= PAR_CALC_EN;
>> +		rx_parity_cfg |= PAR_CALC_EN;
>> +		if (termios->c_cflag & PARODD) {
>> +			tx_parity_cfg |= PAR_ODD;
>> +			rx_parity_cfg |= PAR_ODD;
>> +		} else if (termios->c_cflag & CMSPAR) {
>> +			tx_parity_cfg |= PAR_SPACE;
>> +			rx_parity_cfg |= PAR_SPACE;
>> +		} else {
>> +			tx_parity_cfg |= PAR_EVEN;
>> +			rx_parity_cfg |= PAR_EVEN;
>> +		}
>> +	} else {
>> +		tx_trans_cfg &= ~UART_TX_PAR_EN;
>> +		rx_trans_cfg &= ~UART_RX_PAR_EN;
>> +		tx_parity_cfg &= ~PAR_CALC_EN;
>> +		rx_parity_cfg &= ~PAR_CALC_EN;
>> +	}
>> +
>> +	/* bits per char */
>> +	switch (termios->c_cflag & CSIZE) {
>> +	case CS5:
>> +		bits_per_char = 5;
>> +		break;
>> +	case CS6:
>> +		bits_per_char = 6;
>> +		break;
>> +	case CS7:
>> +		bits_per_char = 7;
>> +		break;
>> +	case CS8:
>> +	default:
>> +		bits_per_char = 8;
>> +		break;
>> +	}
>> +
>> +	/* stop bits */
>> +	if (termios->c_cflag & CSTOPB)
>> +		stop_bit_len = TX_STOP_BIT_LEN_2;
>> +	else
>> +		stop_bit_len = TX_STOP_BIT_LEN_1;
>> +
>> +	/* flow control, clear the CTS_MASK bit if using flow control. */
>> +	if (termios->c_cflag & CRTSCTS)
>> +		tx_trans_cfg &= ~UART_CTS_MASK;
>> +	else
>> +		tx_trans_cfg |= UART_CTS_MASK;
>> +
>> +	if (likely(baud))
>> +		uart_update_timeout(uport, termios->c_cflag, baud);
>> +
>> +	geni_serial_write_term_regs(uport, tx_trans_cfg, tx_parity_cfg,
>> +		rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len,
>> +								ser_clk_cfg);
>> +exit_set_termios:
>> +	qcom_geni_serial_start_rx(uport);
>> +	return;
>> +
>> +}
>> +
>> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
>> +{
>> +	unsigned int tx_fifo_status;
>> +	unsigned int is_tx_empty = 1;
>> +
>> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_TX_FIFO_STATUS);
>> +	if (tx_fifo_status)
>> +		is_tx_empty = 0;
>> +
>> +	return is_tx_empty;
>> +}
>> +
>> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
>> +static int __init qcom_geni_console_setup(struct console *co, char *options)
>> +{
>> +	struct uart_port *uport;
>> +	struct qcom_geni_serial_port *dev_port;
>> +	int baud = 115200;
>> +	int bits = 8;
>> +	int parity = 'n';
>> +	int flow = 'n';
>> +	int ret = 0;
>> +
>> +	if (unlikely(co->index >= GENI_UART_NR_PORTS  || co->index < 0))
>> +		return -ENXIO;
>> +
>> +	dev_port = get_port_from_line(co->index);
>> +	if (IS_ERR_OR_NULL(dev_port)) {
>> +		ret = PTR_ERR(dev_port);
>> +		pr_err("Invalid line %d(%d)\n", co->index, ret);
>> +		return ret;
>> +	}
>> +
>> +	uport = &dev_port->uport;
>> +
>> +	if (unlikely(!uport->membase))
>> +		return -ENXIO;
>> +
>> +	if (geni_se_resources_on(&dev_port->serial_rsc))
>> +		WARN_ON(1);
>> +
>> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
>> +		geni_se_resources_off(&dev_port->serial_rsc);
>> +		return -ENXIO;
>> +	}
>> +
>> +	if (!dev_port->port_setup) {
>> +		qcom_geni_serial_stop_rx(uport);
>> +		qcom_geni_serial_port_setup(uport);
>> +	}
>> +
>> +	if (options)
>> +		uart_parse_options(options, &baud, &parity, &bits, &flow);
>> +
>> +	return uart_set_options(uport, co, baud, parity, bits, flow);
>> +}
>> +
>> +static int console_register(struct uart_driver *drv)
>> +{
>> +	return uart_register_driver(drv);
>> +}
>> +static void console_unregister(struct uart_driver *drv)
>> +{
>> +	uart_unregister_driver(drv);
>> +}
>> +
>> +static struct console cons_ops = {
>> +	.name = "ttyMSM",
>> +	.write = qcom_geni_serial_console_write,
>> +	.device = uart_console_device,
>> +	.setup = qcom_geni_console_setup,
>> +	.flags = CON_PRINTBUFFER,
>> +	.index = -1,
>> +	.data = &qcom_geni_console_driver,
>> +};
>> +
>> +static struct uart_driver qcom_geni_console_driver = {
>> +	.owner = THIS_MODULE,
>> +	.driver_name = "qcom_geni_console",
>> +	.dev_name = "ttyMSM",
>> +	.nr =  GENI_UART_NR_PORTS,
>> +	.cons = &cons_ops,
>> +};
>> +#else
>> +static int console_register(struct uart_driver *drv)
>> +{
>> +	return 0;
>> +}
>> +
>> +static void console_unregister(struct uart_driver *drv)
>> +{
>> +}
>> +#endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */
>> +
>> +static void qcom_geni_serial_cons_pm(struct uart_port *uport,
>> +		unsigned int new_state, unsigned int old_state)
>> +{
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +
>> +	if (unlikely(!uart_console(uport)))
>> +		return;
>> +
>> +	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
>> +		geni_se_resources_on(&qcom_port->serial_rsc);
>> +	else if (new_state == UART_PM_STATE_OFF &&
>> +			old_state == UART_PM_STATE_ON)
>> +		geni_se_resources_off(&qcom_port->serial_rsc);
>> +}
>> +
>> +static const struct uart_ops qcom_geni_console_pops = {
>> +	.tx_empty = qcom_geni_serial_tx_empty,
>> +	.stop_tx = qcom_geni_serial_stop_tx,
>> +	.start_tx = qcom_geni_serial_start_tx,
>> +	.stop_rx = qcom_geni_serial_stop_rx,
>> +	.set_termios = qcom_geni_serial_set_termios,
>> +	.startup = qcom_geni_serial_startup,
>> +	.config_port = qcom_geni_serial_config_port,
>> +	.shutdown = qcom_geni_serial_shutdown,
>> +	.type = qcom_geni_serial_get_type,
>> +	.set_mctrl = qcom_geni_cons_set_mctrl,
>> +	.get_mctrl = qcom_geni_cons_get_mctrl,
>> +#ifdef CONFIG_CONSOLE_POLL
>> +	.poll_get_char	= qcom_geni_serial_get_char,
>> +	.poll_put_char	= qcom_geni_serial_poll_put_char,
>> +#endif
>> +	.pm = qcom_geni_serial_cons_pm,
>> +};
>> +
>> +static const struct of_device_id qcom_geni_serial_match_table[] = {
>> +	{ .compatible = "qcom,geni-debug-uart",
>> +			.data = (void *)&qcom_geni_console_driver},
>> +};
>> +
>> +static int qcom_geni_serial_probe(struct platform_device *pdev)
>> +{
>> +	int ret = 0;
>> +	int line = -1;
>> +	struct qcom_geni_serial_port *dev_port;
>> +	struct uart_port *uport;
>> +	struct resource *res;
>> +	struct uart_driver *drv;
>> +	const struct of_device_id *id;
>> +
>> +	id = of_match_device(qcom_geni_serial_match_table, &pdev->dev);
>> +	if (id) {
>> +		dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
>> +		drv = (struct uart_driver *)id->data;
>> +	} else {
>> +		dev_err(&pdev->dev, "%s: No matching device found", __func__);
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (pdev->dev.of_node) {
>> +		if (drv->cons)
>> +			line = of_alias_get_id(pdev->dev.of_node, "serial");
>> +	} else {
>> +		line = pdev->id;
>> +	}
>> +
>> +	if (line < 0)
>> +		line = atomic_inc_return(&uart_line_id) - 1;
>> +
>> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
>> +		return -ENXIO;
>> +	dev_port = get_port_from_line(line);
>> +	if (IS_ERR_OR_NULL(dev_port)) {
>> +		ret = PTR_ERR(dev_port);
>> +		dev_err(&pdev->dev, "Invalid line %d(%d)\n",
>> +					line, ret);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	uport = &dev_port->uport;
>> +
>> +	/* Don't allow 2 drivers to access the same port */
>> +	if (uport->private_data) {
>> +		ret = -ENODEV;
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	uport->dev = &pdev->dev;
>> +	dev_port->wrapper_dev = pdev->dev.parent;
>> +	dev_port->serial_rsc.wrapper_dev = pdev->dev.parent;
>> +	dev_port->serial_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
>> +	if (IS_ERR(dev_port->serial_rsc.se_clk)) {
>> +		ret = PTR_ERR(dev_port->serial_rsc.se_clk);
>> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "se-phys");
>> +	if (!res) {
>> +		ret = -ENXIO;
>> +		dev_err(&pdev->dev, "Err getting IO region\n");
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	uport->mapbase = res->start;
>> +	uport->membase = devm_ioremap(&pdev->dev, res->start,
>> +						resource_size(res));
>> +	if (!uport->membase) {
>> +		ret = -ENOMEM;
>> +		dev_err(&pdev->dev, "Err IO mapping serial iomem");
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
>> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
>> +		dev_err(&pdev->dev, "No pinctrl config specified!\n");
>> +		ret = PTR_ERR(dev_port->serial_rsc.geni_pinctrl);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +	dev_port->serial_rsc.geni_gpio_active =
>> +		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
>> +							PINCTRL_DEFAULT);
>> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
>> +		dev_err(&pdev->dev, "No default config specified!\n");
>> +		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	dev_port->serial_rsc.geni_gpio_sleep =
>> +		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
>> +							PINCTRL_SLEEP);
>> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_sleep)) {
>> +		dev_err(&pdev->dev, "No sleep config specified!\n");
>> +		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +	dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +	dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
>> +	uport->fifosize =
>> +		((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3);
>> +
>> +	uport->irq = platform_get_irq(pdev, 0);
>> +	if (uport->irq < 0) {
>> +		ret = uport->irq;
>> +		dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	uport->private_data = (void *)drv;
>> +	platform_set_drvdata(pdev, dev_port);
>> +	dev_port->handle_rx = handle_rx_console;
>> +	dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32),
>> +								GFP_KERNEL);
>> +	dev_port->port_setup = false;
>> +	return uart_add_one_port(drv, uport);
>> +
>> +exit_geni_serial_probe:
>> +	return ret;
>> +}
>> +
>> +static int qcom_geni_serial_remove(struct platform_device *pdev)
>> +{
>> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +	struct uart_driver *drv =
>> +			(struct uart_driver *)port->uport.private_data;
>> +
>> +	uart_remove_one_port(drv, &port->uport);
>> +	return 0;
>> +}
>> +
>> +
>> +#ifdef CONFIG_PM
>> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
>> +{
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +	struct uart_port *uport = &port->uport;
>> +
>> +	uart_suspend_port((struct uart_driver *)uport->private_data,
>> +				uport);
>> +	return 0;
>> +}
>> +
>> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
>> +{
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +	struct uart_port *uport = &port->uport;
>> +
>> +	if (console_suspend_enabled && uport->suspended) {
>> +		uart_resume_port((struct uart_driver *)uport->private_data,
>> +									uport);
>> +		disable_irq(uport->irq);
>> +	}
>> +	return 0;
>> +}
>> +#else
>> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
>> +{
>> +	return 0;
>> +}
>> +
>> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
>> +{
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
>> +	.suspend_noirq = qcom_geni_serial_sys_suspend_noirq,
>> +	.resume_noirq = qcom_geni_serial_sys_resume_noirq,
>> +};
>> +
>> +static struct platform_driver qcom_geni_serial_platform_driver = {
>> +	.remove = qcom_geni_serial_remove,
>> +	.probe = qcom_geni_serial_probe,
>> +	.driver = {
>> +		.name = "qcom_geni_serial",
>> +		.of_match_table = qcom_geni_serial_match_table,
>> +		.pm = &qcom_geni_serial_pm_ops,
>> +	},
>> +};
>> +
>> +static int __init qcom_geni_serial_init(void)
>> +{
>> +	int ret = 0;
>> +	int i;
>> +
>> +	for (i = 0; i < GENI_UART_CONS_PORTS; i++) {
>> +		qcom_geni_console_port.uport.iotype = UPIO_MEM;
>> +		qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
>> +		qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
>> +		qcom_geni_console_port.uport.line = i;
>> +	}
>> +
>> +	ret = console_register(&qcom_geni_console_driver);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = platform_driver_register(&qcom_geni_serial_platform_driver);
>> +	if (ret) {
>> +		console_unregister(&qcom_geni_console_driver);
>> +		return ret;
>> +	}
>> +	return ret;
>> +}
>> +module_init(qcom_geni_serial_init);
>> +
>> +static void __exit qcom_geni_serial_exit(void)
>> +{
>> +	platform_driver_unregister(&qcom_geni_serial_platform_driver);
>> +	console_unregister(&qcom_geni_console_driver);
>> +}
>> +module_exit(qcom_geni_serial_exit);
>> +
>> +MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("tty:qcom_geni_serial");
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller
  2018-01-17  6:35   ` Bjorn Andersson
@ 2018-02-27 13:25     ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-02-27 13:25 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan



On 1/16/2018 11:35 PM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> Add device tree binding support for GENI based UART Controller in the
>> QUP Wrapper.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> ---
>>   .../devicetree/bindings/serial/qcom,geni-uart.txt  | 29 ++++++++++++++++++++++
>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 13 ++++++++++
>>   2 files changed, 42 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
>>
>> diff --git a/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
>> new file mode 100644
>> index 0000000..e7b9e24
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
>> @@ -0,0 +1,29 @@
>> +Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
>> +
>> +The Generic Interface (GENI) Serial Engine based UART controller supports
>> +console use-cases and is supported only by GENI based Qualcomm Universal
>> +Peripheral (QUP) cores.
>> +
>> +Required properties:
>> +- compatible: should contain "qcom,geni-debug-uart".
> 
> Why is this uart a _debug_ uart? Is there a separate binding for the
> geni-uart?
Yes. It will be uploaded at a later point in time.
> 
> I like that your naming here matches my suggestion with qcom,geni-i2c.
> 
>> +- reg: Should contain UART register location and length.
>> +- reg-names: Should contain "se-phys".
> 
> No need to specify reg-names for a single reg.
Ok.
> 
>> +- interrupts: Should contain UART core interrupts.
>> +- clock-names: Should contain "se-clk".
> 
> Omit the "clk"
Ok.
> 
>> +- clocks: clocks needed for UART, includes the core clock.
> 
> Be more specific.
Ok.
> 
>> +- pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
>> +  Should be "active" and "sleep" for the pin confuguration when core is active
>> +  or when entering sleep state.
> 
> Omit pinctrl information.
Ok.
> 
>> +
>> +Example:
>> +uart0: qcom,serial@a88000 {
> 
> Don't use qcom, in node name. This should be named "serial".
Ok.
> 
>> +	compatible = "qcom,geni-debug-uart";
>> +	reg = <0xa88000 0x7000>;
>> +	reg-names = "se-phys";
>> +	interrupts = <0 355 0>;
> 
> <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>
Ok.
> 
>> +	clock-names = "se-clk";
>> +	clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>;
>> +	pinctrl-names = "default", "sleep";
>> +	pinctrl-0 = <&qup_1_uart_3_active>;
>> +	pinctrl-1 = <&qup_1_uart_3_sleep>;
>> +};
> 
> Regards,
> Bjorn
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-01-19  7:12   ` Bjorn Andersson
@ 2018-02-27 15:07     ` Karthik Ramasubramanian
  0 siblings, 0 replies; 42+ messages in thread
From: Karthik Ramasubramanian @ 2018-02-27 15:07 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
	gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, Girish Mahadevan, Sagar Dharia



On 1/19/2018 12:12 AM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> This driver supports GENI based UART Controller in the Qualcomm SOCs. The
>> Qualcomm Generic Interface (GENI) is a programmable module supporting a
>> wide range of serial interfaces including UART. This driver support console
>> operations using FIFO mode of transfer.
>>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> 
> Please disregard my previous answer to this patch, I hit the wrong key
> combo while stashing my reply.
> 
>> ---
>>   drivers/tty/serial/Kconfig            |   10 +
>>   drivers/tty/serial/Makefile           |    1 +
>>   drivers/tty/serial/qcom_geni_serial.c | 1414 +++++++++++++++++++++++++++++++++
>>   3 files changed, 1425 insertions(+)
>>   create mode 100644 drivers/tty/serial/qcom_geni_serial.c
>>
>> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
>> index b788fee..1be30e5 100644
>> --- a/drivers/tty/serial/Kconfig
>> +++ b/drivers/tty/serial/Kconfig
>> @@ -1098,6 +1098,16 @@ config SERIAL_MSM_CONSOLE
>>   	select SERIAL_CORE_CONSOLE
>>   	select SERIAL_EARLYCON
>>   
>> +config SERIAL_QCOM_GENI
>> +	tristate "QCOM on-chip GENI based serial port support"
>> +	depends on ARCH_QCOM
> 
> depend on the GENI wrapper as well.
Ok.
> 
> [..]
>> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
>> new file mode 100644
>> index 0000000..0dbd329
>> --- /dev/null
>> +++ b/drivers/tty/serial/qcom_geni_serial.c
>> @@ -0,0 +1,1414 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only 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.
>> + */
> 
> SPDX license header.
Ok.
> 
>> +
>> +#include <linux/bitmap.h>
> 
> Unused
> 
>> +#include <linux/bitops.h>
> 
> Unused?
Ok, I will remove the unused header files.
> 
>> +#include <linux/delay.h>
>> +#include <linux/console.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/qcom-geni-se.h>
>> +#include <linux/serial.h>
>> +#include <linux/serial_core.h>
>> +#include <linux/slab.h>
>> +#include <linux/tty.h>
>> +#include <linux/tty_flip.h>
>> +
>> +/* UART specific GENI registers */
>> +#define SE_UART_TX_TRANS_CFG		(0x25C)
> 
> Drop the parenthesis
Ok.
> 
> [..]
>> +static int owr;
>> +module_param(owr, int, 0644);
> 
> What's this?
It is not required. I will drop it.
> 
>> +
>> +struct qcom_geni_serial_port {
>> +	struct uart_port uport;
>> +	char name[20];
>> +	unsigned int tx_fifo_depth;
> 
> size_t is a good type for keeping track of sizes.
Since these are values read from registers, I will keep the type as u32.
> 
>> +	unsigned int tx_fifo_width;
>> +	unsigned int rx_fifo_depth;
>> +	unsigned int tx_wm;
>> +	unsigned int rx_wm;
>> +	unsigned int rx_rfr;
> 
> size_t for sizes.
> 
>> +	int xfer_mode;
>> +	bool port_setup;
>> +	unsigned int *rx_fifo;
> 
> The fifo is typeless, so it should be void *. It is however only ever
> used as a local variable in handle_rx_console() so drop this.
I will drop it for now. It most likely will get re-introduced later with 
the non-console UART patch.
> 
>> +	int (*handle_rx)(struct uart_port *uport,
>> +			unsigned int rx_fifo_wc,
>> +			unsigned int rx_last_byte_valid,
>> +			unsigned int rx_last,
>> +			bool drop_rx);
>> +	struct device *wrapper_dev;
>> +	struct geni_se_rsc serial_rsc;
>> +	unsigned int xmit_size;
> 
> In other drivers we read bytes of the xmit buffer at xmit->tail, write
> to hardware FIFO and update xmit->tail. Why do you keep a going delta
> between the tail and our real tail?
If the console log buffer size is very high, then updating the tail in a 
live manner leads to this driver getting flooded and extremely busy. 
Hence it does not yield the CPU for 10s of seconds. This approach 
introduces some sort of flow control between the console driver and this 
UART controller driver.
> 
>> +	void *rx_buf;
>> +	unsigned int cur_baud;
>> +};
>> +
> [..]
>> +
>> +#define GET_DEV_PORT(uport) \
>> +	container_of(uport, struct qcom_geni_serial_port, uport)
> 
> The idiomatic name for this macro would be something like "to_dev_port".
Ok.
> 
> The use of "uport" as macro parameter makes this only work if the
> parameter is "uport", otherwise the macro will replace the last part of
> the container_of as well.
Ok.
> 
> [..]
>> +static struct qcom_geni_serial_port *get_port_from_line(int line)
>> +{
>> +	struct qcom_geni_serial_port *port = NULL;
>> +
>> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
>> +		port = ERR_PTR(-ENXIO);
> 
> You need to return here as well...
> 
>> +	port = &qcom_geni_console_port;
>> +	return port;
> 
> Drop the "port" and just return ERR_PTR(-ENXIO) in the error case and
> return &qcom_geni_serial_port otherwise.
Ok.
> 
>> +}
>> +
>> +static int qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +				int offset, int bit_field, bool set)
> 
> This function is returning "yes" or "no", so make it return "bool"
Ok.
> 
>> +{
>> +	int iter = 0;
>> +	unsigned int reg;
>> +	bool met = false;
>> +	struct qcom_geni_serial_port *port = NULL;
>> +	bool cond = false;
>> +	unsigned int baud = 115200;
>> +	unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS;
>> +	unsigned long total_iter = 2000;
> 
> Don't initialize things that will be assigned directly again.
Ok.
> 
>> +
>> +
>> +	if (uport->private_data) {
> 
> When is this function being called on a non-initialized uport?
When the support for early console gets added, it will not have private 
data.
> 
>> +		port = GET_DEV_PORT(uport);
>> +		baud = (port->cur_baud ? port->cur_baud : 115200);
> 
> Drop the parenthesis, and consider initializing baud = port->cur_baud
> and then give it 115200 if it's zero separately.
Ok.
> 
>> +		fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
>> +		/*
>> +		 * Total polling iterations based on FIFO worth of bytes to be
>> +		 * sent at current baud .Add a little fluff to the wait.
>> +		 */
>> +		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
>> +		total_iter += 50;
>> +	}
>> +
>> +	while (iter < total_iter) {
>> +		reg = geni_read_reg_nolog(uport->membase, offset);
>> +		cond = reg & bit_field;
>> +		if (cond == set) {
>> +			met = true;
>> +			break;
>> +		}
>> +		udelay(10);
>> +		iter++;
>> +	}
> 
> Use readl_poll_timeout() instead of all this.
Ok.
> 
>> +	return met;
>> +}
>> +
>> +static void qcom_geni_serial_setup_tx(struct uart_port *uport,
>> +				unsigned int xmit_size)
>> +{
>> +	u32 m_cmd = 0;
> 
> Don't initialize this.
Ok.
> 
>> +
>> +	geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
>> +	m_cmd |= (UART_START_TX << M_OPCODE_SHFT);
> 
> Why OR this into 0?
I will assign instead of OR.
> 
>> +	geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
>> +	/*
>> +	 * Writes to enable the primary sequencer should go through before
>> +	 * exiting this function.
>> +	 */
>> +	mb();
>> +}
>> +
>> +static void qcom_geni_serial_poll_cancel_tx(struct uart_port *uport)
> 
> This function polls for command done and if that doesn't happen in time
> it aborts the command. It then clear any interrupts. The function name
> however indicates that we're polling for "cancel of tx".
I will rename the function as qcom_geni_serial_poll_tx_done.
> 
>> +{
>> +	int done = 0;
>> +	unsigned int irq_clear = M_CMD_DONE_EN;
>> +	unsigned int geni_status = 0;
> 
> Don't initialize done and geni_status.
Ok.
> 
>> +
>> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_CMD_DONE_EN, true);
>> +	if (!done) {
>> +		geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
>> +					SE_GENI_M_CMD_CTRL_REG);
>> +		owr++;
> 
> What's owr?
It is not required and will be removed.
> 
>> +		irq_clear |= M_CMD_ABORT_EN;
>> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +							M_CMD_ABORT_EN, true);
>> +	}
>> +	geni_status = geni_read_reg_nolog(uport->membase,
>> +						  SE_GENI_STATUS);
> 
> Why is this line wrapped?
I will fix it.
> 
>> +	if (geni_status & M_GENI_CMD_ACTIVE)
>> +		owr++;
>> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
>> +{
>> +	unsigned int irq_clear = S_CMD_DONE_EN;
>> +
>> +	geni_se_abort_s_cmd(uport->membase);
>> +	/* Ensure this goes through before polling. */
> 
> Move the mb() to the qcom_geni_serial_poll_bit()
Ok.
> 
>> +	mb();
>> +	irq_clear |= S_CMD_ABORT_EN;
>> +	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +					S_GENI_CMD_ABORT, false);
>> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
>> +	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +static int qcom_geni_serial_get_char(struct uart_port *uport)
>> +{
>> +	unsigned int rx_fifo;
>> +	unsigned int m_irq_status;
>> +	unsigned int s_irq_status;
> 
> These are u32 and the variable names would benefit from being shorter.
Ok.
> 
>> +
>> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +			M_SEC_IRQ_EN, true)))
> 
> Drop one parenthesis.
Ok.
> 
>> +		return -ENXIO;
>> +
>> +	m_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_M_IRQ_STATUS);
> 
> Drop the _nolog and rename the variable to reduce the length of this
> line.
Ok.
> 
>> +	s_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_S_IRQ_STATUS);
>> +	geni_write_reg_nolog(m_irq_status, uport->membase,
>> +						SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg_nolog(s_irq_status, uport->membase,
>> +						SE_GENI_S_IRQ_CLEAR);
> 
> Is it necessary to do this interlaced? Or can you just clear M and then
> clear S? I.e. have a single variable named "status".
There is no special mention of interlacing in the programming manual. I 
will check and update accordingly.
> 
>> +
>> +	if (!(qcom_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
>> +			RX_FIFO_WC_MSK, true)))
>> +		return -ENXIO;
>> +
>> +	/*
>> +	 * Read the Rx FIFO only after clearing the interrupt registers and
>> +	 * getting valid RX fifo status.
> 
> Use non-_relaxed readl and you wouldn't have to do this explicitly.
Ok.
> 
>> +	 */
>> +	mb();
>> +	rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
>> +	rx_fifo &= 0xFF;
> 
> return rx_fifo & 0xff; instead of the extra step
Ok.
> 
>> +	return rx_fifo;
>> +}
>> +
>> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
>> +					unsigned char c)
>> +{
>> +	int b = (int) c;
> 
> Why create this alias?
Ok, I will remove it.
> 
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	geni_write_reg_nolog(port->tx_wm, uport->membase,
>> +					SE_GENI_TX_WATERMARK_REG);
>> +	qcom_geni_serial_setup_tx(uport, 1);
>> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +				M_TX_FIFO_WATERMARK_EN, true))
>> +		WARN_ON(1);
> 
> 	WARN_ON(!qcom_geni_serial_poll_bit(...));
Ok.
> 
>> +	geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
>> +	geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
>> +							SE_GENI_M_IRQ_CLEAR);
>> +	/*
>> +	 * Ensure FIFO write goes through before polling for status but.
>> +	 */
> 
> /* This fits in a one-line comment */
> 
> But if this is necessary that means that every time you call
> serial_poll() you need to have this comment and a rmb(). Better move it
> into the poll function then - or just stop using _relaxed()
Ok.
> 
>> +	mb();
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +}
>> +#endif
>> +
>> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
> 
> The Kconfig specifies that we depend on CONFIG_SERIAL_CORE_CONSOLE, so
> no need to check that.
When the non-console usecase is uploaded which can be enabled using an 
independent config, this check helps. Also the CONSOLE_POLL is not 
always enabled. So removing the check for SERIAL_CORE_CONSOLE will keep 
these functions disabled from compiling, but are required.
> 
>> +static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
>> +{
>> +	geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn);
>> +	/*
>> +	 * Ensure FIFO write clear goes through before
>> +	 * next iteration.
> 
> This comment is accurate, but unnecessarily line wrapped.
I will update the comment and line wrappings.
> 
>> +	 */
>> +	mb();
>> +
>> +}
>> +
>> +static void
>> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
>> +				unsigned int count)
>> +{
>> +	int new_line = 0;
>> +	int i;
>> +	int bytes_to_send = count;
>> +	int fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +	int tx_wm = DEF_TX_WM;
>> +
>> +	for (i = 0; i < count; i++) {
>> +		if (s[i] == '\n')
>> +			new_line++;
>> +	}
> 
> Why do we need to deal with this?
The uart_console_write() converts '\n' to '\r\c'. Hence we need to track 
it to ensure the space requirements.
> 
>> +
>> +	bytes_to_send += new_line;
>> +	geni_write_reg_nolog(tx_wm, uport->membase,
>> +					SE_GENI_TX_WATERMARK_REG);
>> +	qcom_geni_serial_setup_tx(uport, bytes_to_send);
>> +	i = 0;
>> +	while (i < count) {
>> +		u32 chars_to_write = 0;
>> +		u32 avail_fifo_bytes = (fifo_depth - tx_wm);
> 
> Use size_t for these.
Ok.
> 
>> +
>> +		/*
>> +		 * If the WM bit never set, then the Tx state machine is not
>> +		 * in a valid state, so break, cancel/abort any existing
>> +		 * command. Unfortunately the current data being written is
>> +		 * lost.
>> +		 */
>> +		while (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_TX_FIFO_WATERMARK_EN, true))
>> +			break;
>> +		chars_to_write = min((unsigned int)(count - i),
>> +							avail_fifo_bytes);
>> +		if ((chars_to_write << 1) > avail_fifo_bytes)
> 
> Write chars_to_write * 2, the compiler will be cleaver for you.
Ok.
> 
>> +			chars_to_write = (avail_fifo_bytes >> 1);
>> +		uart_console_write(uport, (s + i), chars_to_write,
>> +						qcom_geni_serial_wr_char);
>> +		geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
>> +							SE_GENI_M_IRQ_CLEAR);
>> +		/* Ensure this goes through before polling for WM IRQ again.*/
>> +		mb();
> 
> Again, if this is necessary move it into the poll function instead of
> sprinkling it throughout the driver.
Ok.
> 
>> +		i += chars_to_write;
>> +	}
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +}
>> +
>> +static void qcom_geni_serial_console_write(struct console *co, const char *s,
>> +			      unsigned int count)
>> +{
>> +	struct uart_port *uport;
>> +	struct qcom_geni_serial_port *port;
>> +	int locked = 1;
> 
> Use bool for booleans
Ok.
> 
>> +	unsigned long flags;
>> +
>> +	WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
>> +
>> +	port = get_port_from_line(co->index);
>> +	if (IS_ERR_OR_NULL(port))
> 
> port can't be NULL.
Ok.
> 
>> +		return;
>> +
>> +	uport = &port->uport;
>> +	if (oops_in_progress)
>> +		locked = spin_trylock_irqsave(&uport->lock, flags);
>> +	else
>> +		spin_lock_irqsave(&uport->lock, flags);
>> +
>> +	if (locked) {
>> +		__qcom_geni_serial_console_write(uport, s, count);
>> +		spin_unlock_irqrestore(&uport->lock, flags);
>> +	}
>> +}
>> +
>> +static int handle_rx_console(struct uart_port *uport,
>> +			unsigned int rx_fifo_wc,
>> +			unsigned int rx_last_byte_valid,
>> +			unsigned int rx_last,
> 
> You should be able to have a single parameter with a better name stating
> that "this is the last chunk and it's only N bytes", rather than
> encoding this in a bool and a count with obscure names.
Actually rx_last does not indicate it is a last chunk of read. Rather it 
indicates if the last FIFO word is entirely valid or is partially valid. 
If it is partially valid, then rx_last_byte_valid indicates how many 
valid bytes. But I will take your recommendation and update the function 
prototype to pass only the number of bytes to be read.
> 
>> +			bool drop_rx)
>> +{
>> +	int i, c;
>> +	unsigned char *rx_char;
>> +	struct tty_port *tport;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +
>> +	tport = &uport->state->port;
>> +	for (i = 0; i < rx_fifo_wc; i++) {
>> +		int bytes = 4;
>> +
>> +		*(qcom_port->rx_fifo) =
>> +			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
> 
> Just store this value in a local variable, as the only consumer is 3
> lines down.
Ok.
> 
>> +		if (drop_rx)
>> +			continue;
>> +		rx_char = (unsigned char *)qcom_port->rx_fifo;
>> +
>> +		if (i == (rx_fifo_wc - 1)) {
>> +			if (rx_last && rx_last_byte_valid)
> 
> Also, shouldn't rx_last be redundant to the check against rx_fifo_wc?I will update this function so that this check does not happen.
> 
>> +				bytes = rx_last_byte_valid;
>> +		}
>> +		for (c = 0; c < bytes; c++) {
>> +			char flag = TTY_NORMAL;
> 
> No need for a local variable here.
Ok.
> 
>> +			int sysrq;
>> +
>> +			uport->icount.rx++;
>> +			sysrq = uart_handle_sysrq_char(uport, rx_char[c]);
>> +			if (!sysrq)
>> +				tty_insert_flip_char(tport, rx_char[c], flag);
>> +		}
>> +	}
>> +	if (!drop_rx)
>> +		tty_flip_buffer_push(tport);
>> +	return 0;
>> +}
>> +#else
>> +static int handle_rx_console(struct uart_port *uport,
>> +			unsigned int rx_fifo_wc,
>> +			unsigned int rx_last_byte_valid,
>> +			unsigned int rx_last,
>> +			bool drop_rx)
>> +{
>> +	return -EPERM;
>> +}
>> +
>> +#endif /* (CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)) */
>> +
>> +static void qcom_geni_serial_start_tx(struct uart_port *uport)
>> +{
>> +	unsigned int geni_m_irq_en;
> 
> This is a u32, name it "irq_en"
Ok.
> 
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +	unsigned int geni_status;
> 
> geni_status should be of type u32 and there's no lack of descriptiveness
> naming it "status".
Ok.
> 
>> +
>> +	if (qcom_port->xfer_mode == FIFO_MODE) {
>> +		geni_status = geni_read_reg_nolog(uport->membase,
>> +						  SE_GENI_STATUS);
>> +		if (geni_status & M_GENI_CMD_ACTIVE)
>> +			goto exit_start_tx;
> 
> Just return
Ok.
> 
>> +
>> +		if (!qcom_geni_serial_tx_empty(uport))
>> +			goto exit_start_tx;
> 
> Ditto
Ok.
> 
>> +
>> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
>> +						    SE_GENI_M_IRQ_EN);
>> +		geni_m_irq_en |= (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN);
>> +
>> +		geni_write_reg_nolog(qcom_port->tx_wm, uport->membase,
>> +						SE_GENI_TX_WATERMARK_REG);
>> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +		/* Geni command setup should complete before returning.*/
> 
> If that's the case then verify that it's actually done.
I believe there is no ordering issue here. I will double check and drop 
the mb() if not required.
> 
>> +		mb();
>> +	}
>> +exit_start_tx:
>> +	return;
>> +}
>> +
>> +static void stop_tx_sequencer(struct uart_port *uport)
>> +{
>> +	unsigned int geni_m_irq_en;
>> +	unsigned int geni_status;
> 
> These are u32 and can be given more succinct names.
Ok.
> 
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
> 
> Drop the _nolog from all of these to make code easier to read.
Ok.
> 
>> +	geni_m_irq_en &= ~M_CMD_DONE_EN;
>> +	if (port->xfer_mode == FIFO_MODE) {
>> +		geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN;
>> +		geni_write_reg_nolog(0, uport->membase,
>> +				     SE_GENI_TX_WATERMARK_REG);
>> +	}
>> +	port->xmit_size = 0;
>> +	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
>> +	geni_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_STATUS);
>> +	/* Possible stop tx is called multiple times. */
>> +	if (!(geni_status & M_GENI_CMD_ACTIVE))
>> +		return;
>> +
>> +	geni_se_cancel_m_cmd(uport->membase);
>> +	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_CMD_CANCEL_EN, true)) {
>> +		geni_se_abort_m_cmd(uport->membase);
>> +		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +						M_CMD_ABORT_EN, true);
>> +		geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
>> +							SE_GENI_M_IRQ_CLEAR);
>> +	}
>> +	geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
>> +{
>> +	stop_tx_sequencer(uport);
> 
> Inline stop_tx_sequencer here...
Ok.
> 
>> +}
>> +
>> +static void start_rx_sequencer(struct uart_port *uport)
>> +{
>> +	unsigned int geni_s_irq_en;
>> +	unsigned int geni_m_irq_en;
>> +	unsigned int geni_status;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +	if (geni_status & S_GENI_CMD_ACTIVE)
>> +		qcom_geni_serial_stop_rx(uport);
>> +
>> +	geni_se_setup_s_cmd(uport->membase, UART_START_READ, 0);
>> +
>> +	if (port->xfer_mode == FIFO_MODE) {
>> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +
>> +		geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
>> +		geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
>> +
>> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
>> +							SE_GENI_M_IRQ_EN);
> 
> Do you need to do have these two operations interlaced? Can't you take
> care of M and then S using a single variable?
I will check it.
> 
>> +	}
>> +	/*
>> +	 * Ensure the writes to the secondary sequencer and interrupt enables
>> +	 * go through.
> 
> This is not what mb() does.
I will drop it.
> 
>> +	 */
>> +	mb();
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +}
>> +
>> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
>> +{
>> +	start_rx_sequencer(uport);
> 
> Inline start_rx_sequencer
Ok.
> 
>> +}
>> +
>> +static void stop_rx_sequencer(struct uart_port *uport)
>> +{
>> +	unsigned int geni_s_irq_en;
>> +	unsigned int geni_m_irq_en;
>> +	unsigned int geni_status;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +	u32 irq_clear = S_CMD_DONE_EN;
>> +	bool done;
>> +
>> +	if (port->xfer_mode == FIFO_MODE) {
>> +		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_m_irq_en = geni_read_reg_nolog(uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +		geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
>> +		geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
>> +
>> +		geni_write_reg_nolog(geni_s_irq_en, uport->membase,
>> +							SE_GENI_S_IRQ_EN);
>> +		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
>> +							SE_GENI_M_IRQ_EN);
>> +	}
>> +
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +	/* Possible stop rx is called multiple times. */
> 
> Shouldn't this be checked before above writes?
Writing to the above registers does not have impact on the status of 
secondary sequencer. Writing the cancel command has an impact on the 
status and hence the check is placed right above it.
> 
>> +	if (!(geni_status & S_GENI_CMD_ACTIVE))
>> +		return;
>> +	geni_se_cancel_s_cmd(uport->membase);
>> +	/*
>> +	 * Ensure that the cancel goes through before polling for the
>> +	 * cancel control bit.
>> +	 */
>> +	mb();
>> +	done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +					S_GENI_CMD_CANCEL, false);
>> +	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
>> +	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
>> +	if ((geni_status & S_GENI_CMD_ACTIVE))
> 
> Drop the extra parenthesis. Is this checking if we're still active after
> the cancel, or is this redundant?
Ok.
> 
>> +		qcom_geni_serial_abort_rx(uport);
>> +}
>> +
>> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
>> +{
>> +	stop_rx_sequencer(uport);
> 
> Just inline the function here.
Ok.
> 
>> +}
>> +
>> +static int qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
>> +{
>> +	int ret = 0;
>> +	unsigned int rx_fifo_status;
>> +	unsigned int rx_fifo_wc = 0;
>> +	unsigned int rx_last_byte_valid = 0;
>> +	unsigned int rx_last = 0;
> 
> The context of this function gives that we're dealing with rx and
> rx_fifo, so no need to specify that in each local variable.
> 
> Also, they should all be u32 and there's no need to initialize them.
Ok.
> 
>> +	struct tty_port *tport;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +
>> +	tport = &uport->state->port;
>> +	rx_fifo_status = geni_read_reg_nolog(uport->membase,
>> +				SE_GENI_RX_FIFO_STATUS);
>> +	rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK;
>> +	rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >>
>> +						RX_LAST_BYTE_VALID_SHFT);
>> +	rx_last = rx_fifo_status & RX_LAST;
>> +	if (rx_fifo_wc)
>> +		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
>> +							rx_last, drop_rx);
> 
> You're ignoring the return value of handle_rx.
> 
>> +	return ret;
> 
> You don't care about the return value in the caller and you always
> return 0 anyways, so make the function void.
Ok.
> 
>> +}
>> +
>> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
>> +{
>> +	int ret = 0;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +	struct circ_buf *xmit = &uport->state->xmit;
> 
> Use size_t for sizes.
Ok.
> 
>> +	unsigned int avail_fifo_bytes = 0;
> 
> Naming avail_fifo_bytes just "avail" is carrying the same information
> but is shorter.
Ok.
> 
>> +	unsigned int bytes_remaining = 0;
>> +	int i = 0;
>> +	unsigned int tx_fifo_status;
>> +	unsigned int xmit_size;
> 
> This is "the number of bytes we're going to take from the uart xmit
> buffer and push to the hardware FIFO. Call it "chunk" or something,
> because it's not the size of the xmit.
Ok.
> 
>> +	unsigned int fifo_width_bytes = 1;
> 
> If this really is constantly 1 you can remove the variable and remove a
> bit of the complexity in this function.
Ok.
> 
>> +	int temp_tail = 0;
> 
> Local variables are temporary in nature, so no need to name the variable
> temp_tail, it is the "tail" we operate on here.
Ok.
> 
>> +
>> +	xmit_size = uart_circ_chars_pending(xmit);
>> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
>> +					SE_GENI_TX_FIFO_STATUS);
>> +	/* Both FIFO and framework buffer are drained */
>> +	if ((xmit_size == qcom_port->xmit_size) && !tx_fifo_status) {
>> +		qcom_port->xmit_size = 0;
>> +		uart_circ_clear(xmit);
>> +		qcom_geni_serial_stop_tx(uport);
>> +		goto exit_handle_tx;
>> +	}
>> +	xmit_size -= qcom_port->xmit_size;
>> +
>> +	avail_fifo_bytes = (qcom_port->tx_fifo_depth - qcom_port->tx_wm) *
>> +							fifo_width_bytes;
>> +	temp_tail = (xmit->tail + qcom_port->xmit_size) & (UART_XMIT_SIZE - 1);
>> +	if (xmit_size > (UART_XMIT_SIZE - temp_tail))
>> +		xmit_size = (UART_XMIT_SIZE - temp_tail);
>> +	if (xmit_size > avail_fifo_bytes)
>> +		xmit_size = avail_fifo_bytes;
> 
> This section is confusing me, in other drivers we update xmit->tail and
> the uart_circ_chars_pending() returns the number of bytes left to be
> written. But in your driver we maintain qcom_port->xmit_size as a delta
> between the tail and the next byte in the ring buffer to consume.
> 
> I find no place were you're actually updating the tail, so I'm wondering
> how setting qcom_port->xmit_size to zero above will actually work, as it
> doesn't consider that xmit->head might be elsewhere.
> 
> The math that you need to come up with is "how much data is there to be
> written" and "how much FIFO space do I have" and then take the min() of
> those. The prior should be the return value of uart_circ_chars_pending()
> straight off.
When the entire data in the circular buffer is written, uart_circ_clear 
clears the tail. Until then, the tail is not updated. So if the console 
driver fills the entire circular buffer before it can be drained out, 
then this approach controls the flow and allows this driver to yield the 
CPU.
> 
>> +
>> +	if (!xmit_size)
>> +		goto exit_handle_tx;
>> +
>> +	qcom_geni_serial_setup_tx(uport, xmit_size);
>> +
>> +	bytes_remaining = xmit_size;
>> +	while (i < xmit_size) {
>> +		unsigned int tx_bytes;
>> +		unsigned int buf = 0;
>> +		int c;
>> +
>> +		tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
>> +					bytes_remaining : fifo_width_bytes);
> 
> 		tx_bytes = min(remaining, fifo_width);
> 
> But fifo_width_bytes is 1, so this can be simplified.
Ok.
> 
>> +
>> +		for (c = 0; c < tx_bytes ; c++)
>> +			buf |= (xmit->buf[temp_tail + c] << (c * 8));
> 
> As bytes_remaining shouldn't be able to reach 0, tx_bytes should always
> be 1 and as such this is just a matter of writing
> xmit->buf[xmit->tail++] to SE_GENI_TX_FIFOn.
This is written with the non-console UART also in mind, where the number 
of bytes in a FIFO word can be 4 bytes. I will update the code to have 
more clarity.
> 
>> +		geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
>> +		i += tx_bytes;
>> +		temp_tail = (temp_tail + tx_bytes) & (UART_XMIT_SIZE - 1);
>> +		uport->icount.tx += tx_bytes;
>> +		bytes_remaining -= tx_bytes;
>> +		/* Ensure FIFO write goes through */
> 
> Writes to the same register isn't expected to be reordered, so you
> shouldn't need this wmb(). Perhaps it's the same barrier that's needed
> for other polls?
Ok, I will drop this wmb.
> 
>> +		wmb();
>> +	}
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +	qcom_port->xmit_size += xmit_size;
>> +exit_handle_tx:
>> +	uart_write_wakeup(uport);
>> +	return ret;
>> +}
>> +
>> +static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
>> +{
>> +	unsigned int m_irq_status;
>> +	unsigned int s_irq_status;
>> +	struct uart_port *uport = dev;
>> +	unsigned long flags;
>> +	unsigned int m_irq_en;
>> +	bool drop_rx = false;
>> +	struct tty_port *tport = &uport->state->port;
>> +
>> +	spin_lock_irqsave(&uport->lock, flags);
>> +	if (uport->suspended)
>> +		goto exit_geni_serial_isr;
> 
> Do you need to check this within the spinlock?
I will take it outside the spinlock.
> 
> Name your labels based on their action, not the function they are in. So
> a better name would be "out_unlock". But if this can be done outside the
> lock just returning would be better.
Ok.
> 
>> +	m_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_M_IRQ_STATUS);
>> +	s_irq_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_S_IRQ_STATUS);
>> +	m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
>> +	geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
>> +
>> +	if ((m_irq_status & M_ILLEGAL_CMD_EN)) {
> 
> Extra parenthesis, and you can better write this:
> 
> 	if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
> 		goto out_unlock;
Ok.
> 
>> +		WARN_ON(1);
>> +		goto exit_geni_serial_isr;
>> +	}
>> +
>> +	if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
>> +		uport->icount.overrun++;
>> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
>> +	}
>> +
>> +	if ((m_irq_status & m_irq_en) &
> 
> Is M_ILLEGAL_CMD_EN part of m_irq_en? Can you mask the m_irq_status
> based on the irq_en above instead of doing it like this?
I will check and update accordingly.
> 
>> +	    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
>> +		qcom_geni_serial_handle_tx(uport);
>> +
>> +	if ((s_irq_status & S_GP_IRQ_0_EN) || (s_irq_status & S_GP_IRQ_1_EN)) {
> 
> Drop the parenthesis.
Ok.
> 
>> +		if (s_irq_status & S_GP_IRQ_0_EN)
>> +			uport->icount.parity++;
>> +		drop_rx = true;
>> +	} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
>> +		(s_irq_status & S_GP_IRQ_3_EN)) {
> 
> Ditto.
Ok.
> 
>> +		uport->icount.brk++;
>> +	}
>> +
>> +	if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
>> +		(s_irq_status & S_RX_FIFO_LAST_EN))
> 
> Ditto.
Ok.
> 
>> +		qcom_geni_serial_handle_rx(uport, drop_rx);
>> +
>> +exit_geni_serial_isr:
>> +	spin_unlock_irqrestore(&uport->lock, flags);
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
>> +{
>> +	struct uart_port *uport;
>> +
>> +	if (!port)
>> +		return -ENODEV;
>> +
>> +	uport = &port->uport;
>> +	port->tx_fifo_depth = geni_se_get_tx_fifo_depth(uport->membase);
>> +	if (!port->tx_fifo_depth) {
>> +		dev_err(uport->dev, "%s:Invalid TX FIFO depth read\n",
>> +								__func__);
>> +		return -ENXIO;
>> +	}
>> +
>> +	port->tx_fifo_width = geni_se_get_tx_fifo_width(uport->membase);
>> +	if (!port->tx_fifo_width) {
>> +		dev_err(uport->dev, "%s:Invalid TX FIFO width read\n",
>> +								__func__);
>> +		return -ENXIO;
>> +	}
>> +
>> +	port->rx_fifo_depth = geni_se_get_rx_fifo_depth(uport->membase);
>> +	if (!port->rx_fifo_depth) {
>> +		dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
>> +								__func__);
>> +		return -ENXIO;
>> +	}
>> +
>> +	uport->fifosize =
>> +		((port->tx_fifo_depth * port->tx_fifo_width) >> 3);
> 
> This line wrap isn't necessary, use division rather than shifting and
> describe why the fifosize is 1/8 of the depth * width (is the width
> expressed in bits perhaps?)
Ok.
> 
>> +	return 0;
>> +}
>> +
>> +static void set_rfr_wm(struct qcom_geni_serial_port *port)
>> +{
>> +	/*
>> +	 * Set RFR (Flow off) to FIFO_DEPTH - 2.
>> +	 * RX WM level at 10% RX_FIFO_DEPTH.
>> +	 * TX WM level at 10% TX_FIFO_DEPTH.
>> +	 */
>> +	port->rx_rfr = port->rx_fifo_depth - 2;
>> +	port->rx_wm = UART_CONSOLE_RX_WM;
>> +	port->tx_wm = 2;
>> +}
>> +
>> +static void qcom_geni_serial_shutdown(struct uart_port *uport)
>> +{
>> +	unsigned long flags;
>> +
>> +	/* Stop the console before stopping the current tx */
>> +	console_stop(uport->cons);
>> +
>> +	disable_irq(uport->irq);
>> +	free_irq(uport->irq, uport);
>> +	spin_lock_irqsave(&uport->lock, flags);
>> +	qcom_geni_serial_stop_tx(uport);
>> +	qcom_geni_serial_stop_rx(uport);
>> +	spin_unlock_irqrestore(&uport->lock, flags);
>> +}
>> +
>> +static int qcom_geni_serial_port_setup(struct uart_port *uport)
>> +{
>> +	int ret = 0;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +	unsigned long cfg0, cfg1;
>> +	unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
>> +
>> +	set_rfr_wm(qcom_port);
>> +	geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT);
>> +	/*
>> +	 * Make an unconditional cancel on the main sequencer to reset
>> +	 * it else we could end up in data loss scenarios.
>> +	 */
>> +	qcom_port->xfer_mode = FIFO_MODE;
>> +	qcom_geni_serial_poll_cancel_tx(uport);
>> +	geni_se_get_packing_config(8, 1, false, &cfg0, &cfg1);
>> +	geni_write_reg_nolog(cfg0, uport->membase,
>> +					SE_GENI_TX_PACKING_CFG0);
>> +	geni_write_reg_nolog(cfg1, uport->membase,
>> +					SE_GENI_TX_PACKING_CFG1);
>> +	geni_se_get_packing_config(8, 4, false, &cfg0, &cfg1);
>> +	geni_write_reg_nolog(cfg0, uport->membase,
>> +					SE_GENI_RX_PACKING_CFG0);
>> +	geni_write_reg_nolog(cfg1, uport->membase,
>> +					SE_GENI_RX_PACKING_CFG1);
> 
> These aren't above 80 chars, why are you line breaking?
I will fix it.
> 
>> +	ret = geni_se_init(uport->membase, qcom_port->rx_wm, qcom_port->rx_rfr);
>> +	if (ret) {
>> +		dev_err(uport->dev, "%s: Fail\n", __func__);
>> +		goto exit_portsetup;
>> +	}
>> +
>> +	ret = geni_se_select_mode(uport->membase, qcom_port->xfer_mode);
>> +	if (ret)
> 
> Do you need to roll back any init done in the wrapper code? How about
> passing the mode to the init function?
It is not required. They can always be re-initialized if required.
> 
>> +		goto exit_portsetup;
>> +
>> +	qcom_port->port_setup = true;
>> +	/*
>> +	 * Ensure Port setup related IO completes before returning to
>> +	 * framework.
> 
> That's not what mb does.
I will drop mb().
> 
>> +	 */
>> +	mb();
>> +exit_portsetup:
>> +	return ret;
>> +}
>> +
>> +static int qcom_geni_serial_startup(struct uart_port *uport)
>> +{
>> +	int ret = 0;
>> +	struct qcom_geni_serial_port *qcom_port = GET_DEV_PORT(uport);
>> +
>> +	scnprintf(qcom_port->name, sizeof(qcom_port->name),
>> +		  "qcom_serial_geni%d",	uport->line);
>> +
>> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
> 
> Drop the unlikely(), in favour of readable code.
Ok.
> 
>> +		dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
>> +				 __func__, geni_se_get_proto(uport->membase));
> 
> Drop the __func__, the message should describe the issue in itself.
Ok.
> 
>> +		ret = -ENXIO;
>> +		goto exit_startup;
> 
> We haven't done anything, so just return here.
Ok.
> 
>> +	}
>> +
>> +	get_tx_fifo_size(qcom_port);
>> +	if (!qcom_port->port_setup) {
>> +		if (qcom_geni_serial_port_setup(uport))
>> +			goto exit_startup;
>> +	}
>> +
>> +	/*
>> +	 * Ensure that all the port configuration writes complete
>> +	 * before returning to the framework.
> 
> That's not what mb() does.
I will drop the mb().
> 
>> +	 */
>> +	mb();
>> +	ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
>> +			qcom_port->name, uport);
>> +	if (unlikely(ret)) {
>> +		dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n",
>> +							__func__, ret);
> 
> Drop the __func__
Ok.
> 
>> +		goto exit_startup;
>> +	}
>> +
>> +exit_startup:
>> +	return ret;
>> +}
>> +
>> +static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk)
>> +{
>> +	unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
>> +		32000000, 48000000, 64000000, 80000000, 96000000, 100000000};
>> +	int i;
>> +	int match = -1;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
>> +		if (clk_freq > root_freq[i])
>> +			continue;
>> +
>> +		if (!(root_freq[i] % clk_freq)) {
>> +			match = i;
>> +			break;
>> +		}
>> +	}
>> +	if (match != -1)
>> +		*ser_clk = root_freq[match];
>> +	else
>> +		pr_err("clk_freq %ld\n", clk_freq);
> 
> Errors are for humans to consume, so make the message useful for a
> human.
Ok.
> 
>> +	return match;
> 
> The index isn't used, so return the frequency instead of filling in the
> ser_clk reference.
Ok.
> 
>> +}
>> +
>> +static void geni_serial_write_term_regs(struct uart_port *uport,
>> +		u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg,
>> +		u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len,
>> +		u32 s_clk_cfg)
>> +{
>> +	geni_write_reg_nolog(tx_trans_cfg, uport->membase,
>> +							SE_UART_TX_TRANS_CFG);
>> +	geni_write_reg_nolog(tx_parity_cfg, uport->membase,
>> +							SE_UART_TX_PARITY_CFG);
>> +	geni_write_reg_nolog(rx_trans_cfg, uport->membase,
>> +							SE_UART_RX_TRANS_CFG);
>> +	geni_write_reg_nolog(rx_parity_cfg, uport->membase,
>> +							SE_UART_RX_PARITY_CFG);
>> +	geni_write_reg_nolog(bits_per_char, uport->membase,
>> +							SE_UART_TX_WORD_LEN);
>> +	geni_write_reg_nolog(bits_per_char, uport->membase,
>> +							SE_UART_RX_WORD_LEN);
>> +	geni_write_reg_nolog(stop_bit_len, uport->membase,
>> +						SE_UART_TX_STOP_BIT_LEN);
>> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
>> +	geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);
> 
> Drop the _nolog and you should be able to fit these within 80 chars.
Ok.
> 
>> +}
>> +
>> +static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
>> +{
>> +	unsigned long ser_clk;
>> +	int dfs_index;
>> +	int clk_div = 0;
>> +
>> +	*desired_clk_rate = baud * UART_OVERSAMPLING;
>> +	dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk);
>> +	if (dfs_index < 0) {
>> +		pr_err("%s: Can't find matching DFS entry for baud %d\n",
>> +								__func__, baud);
>> +		clk_div = -EINVAL;
> 
> Just return -EINVAL here, instead of using goto.
Ok.
> 
>> +		goto exit_get_clk_div_rate;
>> +	}
>> +
>> +	clk_div = ser_clk / *desired_clk_rate;
>> +	*desired_clk_rate = ser_clk;
>> +exit_get_clk_div_rate:
>> +	return clk_div;
> 
> Clock functions typically return a frequency, so I think it's better to
> return that and pass the divider by reference.
Ok.
> 
>> +}
>> +
>> +static void qcom_geni_serial_set_termios(struct uart_port *uport,
>> +				struct ktermios *termios, struct ktermios *old)
>> +{
>> +	unsigned int baud;
>> +	unsigned int bits_per_char = 0;
>> +	unsigned int tx_trans_cfg;
>> +	unsigned int tx_parity_cfg;
>> +	unsigned int rx_trans_cfg;
>> +	unsigned int rx_parity_cfg;
>> +	unsigned int stop_bit_len;
>> +	unsigned int clk_div;
>> +	unsigned long ser_clk_cfg = 0;
>> +	struct qcom_geni_serial_port *port = GET_DEV_PORT(uport);
>> +	unsigned long clk_rate;
>> +
>> +	qcom_geni_serial_stop_rx(uport);
>> +	/* baud rate */
>> +	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
>> +	port->cur_baud = baud;
>> +	clk_div = get_clk_div_rate(baud, &clk_rate);
>> +	if (clk_div <= 0)
>> +		goto exit_set_termios;
> 
> Name your labels based on their actions; if you name it "out_restart_rx"
> you don't even have to look below to know what will happen when you
> jump.
Ok.
> 
>> +
>> +	uport->uartclk = clk_rate;
>> +	clk_set_rate(port->serial_rsc.se_clk, clk_rate);
>> +	ser_clk_cfg |= SER_CLK_EN;
>> +	ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
>> +
>> +	/* parity */
>> +	tx_trans_cfg = geni_read_reg_nolog(uport->membase,
> 
> Drop the _nolog and you don't need to line wrap
Ok.
> 
>> +							SE_UART_TX_TRANS_CFG);
>> +	tx_parity_cfg = geni_read_reg_nolog(uport->membase,
>> +							SE_UART_TX_PARITY_CFG);
>> +	rx_trans_cfg = geni_read_reg_nolog(uport->membase,
>> +							SE_UART_RX_TRANS_CFG);
>> +	rx_parity_cfg = geni_read_reg_nolog(uport->membase,
>> +							SE_UART_RX_PARITY_CFG);
>> +	if (termios->c_cflag & PARENB) {
>> +		tx_trans_cfg |= UART_TX_PAR_EN;
>> +		rx_trans_cfg |= UART_RX_PAR_EN;
>> +		tx_parity_cfg |= PAR_CALC_EN;
>> +		rx_parity_cfg |= PAR_CALC_EN;
>> +		if (termios->c_cflag & PARODD) {
>> +			tx_parity_cfg |= PAR_ODD;
>> +			rx_parity_cfg |= PAR_ODD;
>> +		} else if (termios->c_cflag & CMSPAR) {
>> +			tx_parity_cfg |= PAR_SPACE;
>> +			rx_parity_cfg |= PAR_SPACE;
>> +		} else {
>> +			tx_parity_cfg |= PAR_EVEN;
>> +			rx_parity_cfg |= PAR_EVEN;
>> +		}
>> +	} else {
>> +		tx_trans_cfg &= ~UART_TX_PAR_EN;
>> +		rx_trans_cfg &= ~UART_RX_PAR_EN;
>> +		tx_parity_cfg &= ~PAR_CALC_EN;
>> +		rx_parity_cfg &= ~PAR_CALC_EN;
>> +	}
>> +
>> +	/* bits per char */
>> +	switch (termios->c_cflag & CSIZE) {
>> +	case CS5:
>> +		bits_per_char = 5;
>> +		break;
>> +	case CS6:
>> +		bits_per_char = 6;
>> +		break;
>> +	case CS7:
>> +		bits_per_char = 7;
>> +		break;
>> +	case CS8:
>> +	default:
>> +		bits_per_char = 8;
>> +		break;
>> +	}
>> +
>> +	/* stop bits */
>> +	if (termios->c_cflag & CSTOPB)
>> +		stop_bit_len = TX_STOP_BIT_LEN_2;
>> +	else
>> +		stop_bit_len = TX_STOP_BIT_LEN_1;
>> +
>> +	/* flow control, clear the CTS_MASK bit if using flow control. */
>> +	if (termios->c_cflag & CRTSCTS)
>> +		tx_trans_cfg &= ~UART_CTS_MASK;
>> +	else
>> +		tx_trans_cfg |= UART_CTS_MASK;
>> +
>> +	if (likely(baud))
> 
> Don't do likely/unlikely.
Ok.
> 
>> +		uart_update_timeout(uport, termios->c_cflag, baud);
>> +
>> +	geni_serial_write_term_regs(uport, tx_trans_cfg, tx_parity_cfg,
>> +		rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len,
>> +								ser_clk_cfg);
> 
> It would likely be fine to just inline the register writes here, instead
> of this long list of parameters.
The same register writes will also be done as part of early console 
setup. Hence it is in a separate function.
> 
>> +exit_set_termios:
>> +	qcom_geni_serial_start_rx(uport);
>> +	return;
> 
> No need to keep an empty return last in the function, and drop the empty
> line following it.
Ok.
> 
>> +
>> +}
>> +
>> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
>> +{
>> +	unsigned int tx_fifo_status;
>> +	unsigned int is_tx_empty = 1;
>> +
>> +	tx_fifo_status = geni_read_reg_nolog(uport->membase,
>> +						SE_GENI_TX_FIFO_STATUS);
> 
> Drop the "_nolog" and naming the result "status" would be just as
> expressive, but keep you below 80 chars.
Ok.
> 
>> +	if (tx_fifo_status)
>> +		is_tx_empty = 0;
>> +
>> +	return is_tx_empty;
> 
> Instead of what you're doing with is_tx_empty, just do:
> 
> 	return !readl(membase + FIFO_STATUS);
Ok.
> 
>> +}
>> +
>> +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
>> +static int __init qcom_geni_console_setup(struct console *co, char *options)
>> +{
>> +	struct uart_port *uport;
>> +	struct qcom_geni_serial_port *dev_port;
>> +	int baud = 115200;
>> +	int bits = 8;
>> +	int parity = 'n';
>> +	int flow = 'n';
>> +	int ret = 0;
> 
> Drop initialization of all these variables.
If the options does not include all the required fields, then they are 
not initialized. Hence initializing them here.
> 
>> +
>> +	if (unlikely(co->index >= GENI_UART_NR_PORTS  || co->index < 0))
> 
> Drop the unlikely.
Ok.
> 
>> +		return -ENXIO;
>> +
>> +	dev_port = get_port_from_line(co->index);
>> +	if (IS_ERR_OR_NULL(dev_port)) {
> 
> port can't be NULL.
Ok.
> 
>> +		ret = PTR_ERR(dev_port);
>> +		pr_err("Invalid line %d(%d)\n", co->index, ret);
>> +		return ret;
> 
> Just return PTR_ERR(dev_port);
Ok.
> 
>> +	}
>> +
>> +	uport = &dev_port->uport;
>> +
>> +	if (unlikely(!uport->membase))
>> +		return -ENXIO;
>> +
>> +	if (geni_se_resources_on(&dev_port->serial_rsc))
>> +		WARN_ON(1);
> 
> If this fails we're likely to access unclocked resources causing the
> system to restart, so we should probably not continue here.
Ok.
> 
>> +
>> +	if (unlikely(geni_se_get_proto(uport->membase) != UART)) {
>> +		geni_se_resources_off(&dev_port->serial_rsc);
>> +		return -ENXIO;
>> +	}
>> +
> [..]
>> +
>> +static const struct of_device_id qcom_geni_serial_match_table[] = {
>> +	{ .compatible = "qcom,geni-debug-uart",
>> +			.data = (void *)&qcom_geni_console_driver},
> 
> Shouldn't need this typecast.
Ok.
> 
> Is it necessary to treat the console uart differently and will there be
> a patch adding support for non-console instances? How will this differ
> and why would that be a different compatible?
Yes, there will be a patch for non-console instances. The Serial Enginee 
that supports non-console use-cases has a much deeper FIFO. Also the 
software uses those Serial Engines in a completely interrupt driven mode 
and there is no polling mode use-cases.
> 
>> +};
>> +
>> +static int qcom_geni_serial_probe(struct platform_device *pdev)
>> +{
>> +	int ret = 0;
>> +	int line = -1;
>> +	struct qcom_geni_serial_port *dev_port;
>> +	struct uart_port *uport;
>> +	struct resource *res;
>> +	struct uart_driver *drv;
>> +	const struct of_device_id *id;
>> +
>> +	id = of_match_device(qcom_geni_serial_match_table, &pdev->dev);
> 
> Use of_device_get_match_data()
Ok.
> 
>> +	if (id) {
>> +		dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
>> +		drv = (struct uart_driver *)id->data;
>> +	} else {
>> +		dev_err(&pdev->dev, "%s: No matching device found", __func__);
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (pdev->dev.of_node) {
>> +		if (drv->cons)
> 
> The one and only uart_driver has a cons.
Ok.
> 
>> +			line = of_alias_get_id(pdev->dev.of_node, "serial");
>> +	} else {
>> +		line = pdev->id;
>> +	}
>> +
>> +	if (line < 0)
>> +		line = atomic_inc_return(&uart_line_id) - 1;
>> +
>> +	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
>> +		return -ENXIO;
>> +	dev_port = get_port_from_line(line);
>> +	if (IS_ERR_OR_NULL(dev_port)) {
> 
> port can't be NULL.
Ok.
> 
>> +		ret = PTR_ERR(dev_port);
>> +		dev_err(&pdev->dev, "Invalid line %d(%d)\n",
>> +					line, ret);
>> +		goto exit_geni_serial_probe;
> 
> You're not doing anything other than returning in
> exit_geni_serial_probe, if you just return here I don't need to jump to
> the bottom of the function to figure this out...
Ok.
> 
>> +	}
>> +
>> +	uport = &dev_port->uport;
>> +
>> +	/* Don't allow 2 drivers to access the same port */
>> +	if (uport->private_data) {
>> +		ret = -ENODEV;
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	uport->dev = &pdev->dev;
>> +	dev_port->wrapper_dev = pdev->dev.parent;
>> +	dev_port->serial_rsc.wrapper_dev = pdev->dev.parent;
>> +	dev_port->serial_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
>> +	if (IS_ERR(dev_port->serial_rsc.se_clk)) {
>> +		ret = PTR_ERR(dev_port->serial_rsc.se_clk);
>> +		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "se-phys");
> 
> Don't grab this by name, just pick the first one.
Ok.
> 
>> +	if (!res) {
>> +		ret = -ENXIO;
>> +		dev_err(&pdev->dev, "Err getting IO region\n");
>> +		goto exit_geni_serial_probe;
>> +	}
> 
> Drop the error handling here.
Ok.
> 
>> +	uport->mapbase = res->start;
> 
> No
I see most drivers are setting it to this value. I am not sure if 
anything needs to be handled differently.
> 
>> +	uport->membase = devm_ioremap(&pdev->dev, res->start,
>> +						resource_size(res));
>> +	if (!uport->membase) {
>> +		ret = -ENOMEM;
>> +		dev_err(&pdev->dev, "Err IO mapping serial iomem");
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
> 
> Drop all of the pinctrl stuff.
Ok.
> 
>> +	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
>> +		dev_err(&pdev->dev, "No pinctrl config specified!\n");
>> +		ret = PTR_ERR(dev_port->serial_rsc.geni_pinctrl);
>> +		goto exit_geni_serial_probe;
>> +	}
> [..]
>> +	dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +	dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +	dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
>> +	uport->fifosize =
>> +		((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3);
> 
> qcom_geni_serial_startup() sets this as well.
Ok.
> 
>> +
>> +	uport->irq = platform_get_irq(pdev, 0);
>> +	if (uport->irq < 0) {
>> +		ret = uport->irq;
>> +		dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret);
>> +		goto exit_geni_serial_probe;
>> +	}
>> +
>> +	uport->private_data = (void *)drv;
> 
> No need to explicitly typecast to void*
Ok.
> 
>> +	platform_set_drvdata(pdev, dev_port);
>> +	dev_port->handle_rx = handle_rx_console;
> 
> Why have a function pointer when you're only referencing a fixed
> function.
It will be used for the non-console use-cases as well.
> 
>> +	dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32),
>> +								GFP_KERNEL);
> 
> It takes 8 bytes to store a pointer and the allocation will have some
> overhead...just to provide storage space for 4 bytes. And you don't have
> any error handling...
Ok. I will drop it for now.
> 
>> +	dev_port->port_setup = false;
>> +	return uart_add_one_port(drv, uport);
>> +
>> +exit_geni_serial_probe:
>> +	return ret;
>> +}
>> +
>> +static int qcom_geni_serial_remove(struct platform_device *pdev)
>> +{
>> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +	struct uart_driver *drv =
>> +			(struct uart_driver *)port->uport.private_data;
> 
> No need to typecast private_data.
Ok.
> 
>> +
>> +	uart_remove_one_port(drv, &port->uport);
>> +	return 0;
>> +}
>> +
>> +
>> +#ifdef CONFIG_PM
> 
> Drop this and mark the functions __maybe_unused.
Ok.
> 
>> +static int qcom_geni_serial_sys_suspend_noirq(struct device *dev)
>> +{
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +	struct uart_port *uport = &port->uport;
>> +
>> +	uart_suspend_port((struct uart_driver *)uport->private_data,
>> +				uport);
> 
> No need to typecast private_data.
Ok.
> 
>> +	return 0;
>> +}
>> +
>> +static int qcom_geni_serial_sys_resume_noirq(struct device *dev)
>> +{
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +	struct uart_port *uport = &port->uport;
>> +
>> +	if (console_suspend_enabled && uport->suspended) {
>> +		uart_resume_port((struct uart_driver *)uport->private_data,
>> +									uport);
> 
> No need to typecast private_data.
Ok.
> 
>> +		disable_irq(uport->irq);
>> +	}
>> +	return 0;
>> +}
> [..]
>> +static int __init qcom_geni_serial_init(void)
>> +{
>> +	int ret = 0;
>> +	int i;
>> +
>> +	for (i = 0; i < GENI_UART_CONS_PORTS; i++) {
> 
> Don't loop over [0,1) to update the one global variable.
Ok.
> 
>> +		qcom_geni_console_port.uport.iotype = UPIO_MEM;
>> +		qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
>> +		qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
>> +		qcom_geni_console_port.uport.line = i;
>> +	}
>> +
>> +	ret = console_register(&qcom_geni_console_driver);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = platform_driver_register(&qcom_geni_serial_platform_driver);
>> +	if (ret) {
>> +		console_unregister(&qcom_geni_console_driver);
>> +		return ret;
>> +	}
>> +	return ret;
> 
> ret is 0 here, drop the return from within the if statement or just
> return 0 for clarity.
Ok.
> 
>> +}
>> +module_init(qcom_geni_serial_init);
>> +
>> +static void __exit qcom_geni_serial_exit(void)
>> +{
>> +	platform_driver_unregister(&qcom_geni_serial_platform_driver);
>> +	console_unregister(&qcom_geni_console_driver);
>> +}
>> +module_exit(qcom_geni_serial_exit);
>> +
>> +MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("tty:qcom_geni_serial");
> 
> What will request the kernel module by this alias?
I will drop it.
> 
> Regards,
> Bjorn
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2018-02-27 15:07 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary Karthikeyan Ramasubramanian
2018-01-16 16:55   ` Bjorn Andersson
2018-01-29 21:52     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
2018-01-17  6:25   ` Bjorn Andersson
2018-01-19 22:53     ` Rob Herring
2018-02-26 21:24     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller Karthikeyan Ramasubramanian
     [not found]   ` <1515805547-22816-5-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-17  6:31     ` Bjorn Andersson
2018-02-26 21:28       ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller Karthikeyan Ramasubramanian
2018-01-17  6:35   ` Bjorn Andersson
2018-02-27 13:25     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
2018-01-18 19:43   ` Bjorn Andersson
2018-01-19  7:12   ` Bjorn Andersson
2018-02-27 15:07     ` Karthik Ramasubramanian
2018-01-24 23:07   ` [v2, " Evan Green
2018-02-14 11:04   ` [PATCH v2 " Amit Kucheria
2018-02-23 18:06   ` [v2, " Guenter Roeck
2018-02-27 13:23     ` Karthik Ramasubramanian
2018-02-23 19:05   ` Doug Anderson
     [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
2018-01-17  6:20     ` Bjorn Andersson
2018-01-18  9:13       ` Rajendra Nayak
2018-01-18 16:57         ` Bjorn Andersson
2018-01-19 22:57           ` Rob Herring
2018-01-31 19:02             ` Karthik Ramasubramanian
     [not found]               ` <1abb0679-1997-9b70-30bd-d3472cea7053-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-02-05 23:53                 ` Bjorn Andersson
2018-01-31 18:58       ` Karthik Ramasubramanian
2018-02-05 23:50         ` Bjorn Andersson
     [not found]     ` <1515805547-22816-4-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-24 23:06       ` [v2,3/7] " Evan Green
2018-01-26 19:38     ` Doug Anderson
2018-02-14 11:07     ` [PATCH v2 3/7] " Amit Kucheria
2018-02-16 20:44       ` Karthik Ramasubramanian
2018-01-13  1:05   ` [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
     [not found]     ` <1515805547-22816-6-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-18  5:23       ` Bjorn Andersson
2018-02-27 13:16         ` Karthik Ramasubramanian
2018-01-24 23:07     ` [v2, " Evan Green
2018-01-19 18:32   ` [PATCH v2 0/7] Introduce GENI SE Controller Driver Randy Dunlap
2018-01-31 18:59     ` Karthik Ramasubramanian

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).