All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Introduce GENI SE Controller Driver
@ 2018-02-28  1:38 Karthikeyan Ramasubramanian
  2018-02-28  1:38 ` [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
                   ` (3 more replies)
  0 siblings, 4 replies; 65+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-02-28  1:38 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, evgreen, acourbot

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.

[v3]
 * Update the driver dependencies
 * Use the SPDX License Expression
 * Squash all the controller device tree bindings together
 * Use kernel doc format for documentation
 * Add additional documentation for packing configuration
 * Use clk_bulk_* API for related clocks
 * Remove driver references to pinctrl and their states
 * Replace magic numbers with appropriate macros
 * Update memory barrier usage and associated comments
 * Reduce interlacing of register reads/writes
 * Fix poll_get_char() operation in console UART driver under polling mode
 * Address other comments from Bjorn Andersson to improve code readability

[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 (4):
  dt-bindings: soc: qcom: Add device tree binding for GENI SE
  soc: qcom: Add GENI based QUP Wrapper driver
  i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C
    controller
  tty: serial: msm_geni_serial: Add serial driver support for GENI based
    QUP

 .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  |   89 ++
 drivers/i2c/busses/Kconfig                         |   11 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-qcom-geni.c                 |  626 +++++++++++
 drivers/soc/qcom/Kconfig                           |    9 +
 drivers/soc/qcom/Makefile                          |    1 +
 drivers/soc/qcom/qcom-geni-se.c                    |  971 ++++++++++++++++
 drivers/tty/serial/Kconfig                         |   11 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/qcom_geni_serial.c              | 1181 ++++++++++++++++++++
 include/linux/qcom-geni-se.h                       |  247 ++++
 11 files changed, 3148 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/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] 65+ messages in thread

* [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-02-28  1:38 [PATCH v3 0/4] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
@ 2018-02-28  1:38 ` Karthikeyan Ramasubramanian
  2018-03-05 23:58     ` Rob Herring
  2018-02-28  1:38 ` [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 65+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-02-28  1:38 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, evgreen, acourbot,
	Sagar Dharia, Girish Mahadevan

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

Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
---
 .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89 ++++++++++++++++++++++
 1 file changed, 89 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..fe6a0c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
@@ -0,0 +1,89 @@
+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> for Serial Engine Address
+- #size-cells: 		Must be <1> for Serial Engine Address Size
+- 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.
+Please refer below the child node definitions for the supported serial
+interface protocols.
+
+Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
+
+Required properties:
+- compatible:		Must be "qcom,geni-i2c".
+- reg: 			Must contain QUP register address and length.
+- interrupts: 		Must contain I2C interrupt.
+- clock-names: 		Must contain "se".
+- clocks: 		Serial engine core clock needed by the device.
+- #address-cells:	Must be <1> for i2c device address.
+- #size-cells:		Must 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.
+
+Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
+
+Required properties:
+- compatible:		Must be "qcom,geni-debug-uart".
+- reg: 			Must contain UART register location and length.
+- interrupts: 		Must contain UART core interrupts.
+- clock-names:		Must contain "se".
+- clocks:		Serial engine core clock needed by the device.
+
+Example:
+	geniqup@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>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		i2c0: i2c@a94000 {
+			compatible = "qcom,geni-i2c";
+			reg = <0xa94000 0x4000>;
+			interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
+			clock-names = "se";
+			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>;
+		};
+
+		uart0: serial@a88000 {
+			compatible = "qcom,geni-debug-uart";
+			reg = <0xa88000 0x7000>;
+			interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
+			clock-names = "se";
+			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] 65+ messages in thread

* [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-02-28  1:38 [PATCH v3 0/4] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
  2018-02-28  1:38 ` [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
@ 2018-02-28  1:38 ` Karthikeyan Ramasubramanian
  2018-03-02 20:41     ` Stephen Boyd
  2018-02-28  1:38 ` [PATCH v3 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
  2018-02-28  1:38 ` [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
  3 siblings, 1 reply; 65+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-02-28  1:38 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, evgreen, acourbot,
	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@codeaurora.org>
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
---
 drivers/soc/qcom/Kconfig        |   9 +
 drivers/soc/qcom/Makefile       |   1 +
 drivers/soc/qcom/qcom-geni-se.c | 971 ++++++++++++++++++++++++++++++++++++++++
 include/linux/qcom-geni-se.h    | 247 ++++++++++
 4 files changed, 1228 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 e050eb8..cc460d0 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -3,6 +3,15 @@
 #
 menu "Qualcomm SoC drivers"
 
+config QCOM_GENI_SE
+	tristate "QCOM GENI Serial Engine Driver"
+	depends 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 dcebf28..959aa74 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..61335b8
--- /dev/null
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -0,0 +1,971 @@
+// 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/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/qcom-geni-se.h>
+
+/**
+ * DOC: Overview
+ *
+ * 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.
+ */
+
+/**
+ * DOC: 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
+ */
+
+/**
+ * DOC: Software description
+ *
+ * GENI SE Wrapper driver is structured into 2 parts:
+ *
+ * geni_wrapper 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 represents Serial Engine. This part of the driver manages Serial
+ * Engine information such as clocks, containing QUP Wrapper etc. 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.
+ */
+
+#define MAX_CLK_PERF_LEVEL 32
+#define NUM_AHB_CLKS 2
+static const char m_ahb_clk[] = "m-ahb";
+static const char s_ahb_clk[] = "s-ahb";
+
+/**
+ * @struct geni_wrapper - 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.
+ * @ahb_clks:		Handle to the primary & secondary AHB clocks.
+ * @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_wrapper {
+	struct device *dev;
+	void __iomem *base;
+	struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
+	struct mutex lock;
+	unsigned int num_clk_levels;
+	unsigned long *clk_perf_tbl;
+};
+
+/* Offset of QUP Hardware Version Register */
+#define QUP_HW_VER_REG			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)
+
+/* Common SE registers */
+#define GENI_INIT_CFG_REVISION		0x0
+#define GENI_S_INIT_CFG_REVISION	0x4
+#define GENI_OUTPUT_CTRL		0x24
+#define GENI_CGC_CTRL			0x28
+#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_BYTE_GRAN		0x254
+#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_GP_LENGTH		0x910
+#define SE_GENI_S_GP_LENGTH		0x914
+#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_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_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_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_MAX_BURST		0xd5c
+#define SE_DMA_RX_FLUSH			0xd60
+#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_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)
+
+/* FW_REVISION_RO fields */
+#define FW_REV_PROTOCOL_MSK		GENMASK(15, 8)
+#define FW_REV_PROTOCOL_SHFT		8
+
+/* 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_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
+
+/**
+ * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
+ * @se:			Pointer to the corresponding Serial Engine.
+ * @major:		Buffer for Major Version field.
+ * @minor:		Buffer for Minor Version field.
+ * @step:		Buffer for Step Version field.
+ */
+void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
+				unsigned int *minor, unsigned int *step)
+{
+	unsigned int version;
+	struct geni_wrapper *wrapper = se->wrapper;
+
+	version = readl_relaxed(wrapper->base + QUP_HW_VER_REG);
+	*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;
+}
+EXPORT_SYMBOL(geni_se_get_qup_hw_version);
+
+/**
+ * geni_se_read_proto() - Read the protocol configured for a Serial Engine
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * Return: Protocol value as configured in the serial engine.
+ */
+u32 geni_se_read_proto(struct geni_se *se)
+{
+	u32 val;
+
+	val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
+
+	return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
+}
+EXPORT_SYMBOL(geni_se_read_proto);
+
+static void geni_se_io_set_mode(void __iomem *base)
+{
+	u32 val;
+
+	val = readl_relaxed(base + SE_IRQ_EN);
+	val |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
+	val |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
+	writel_relaxed(val, base + SE_IRQ_EN);
+
+	val = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
+	val &= ~GENI_DMA_MODE_EN;
+	writel_relaxed(val, base + SE_GENI_DMA_MODE_EN);
+
+	writel_relaxed(0, base + SE_GSI_EVENT_EN);
+}
+
+static void geni_se_io_init(void __iomem *base)
+{
+	u32 val;
+
+	val = readl_relaxed(base + GENI_CGC_CTRL);
+	val |= DEFAULT_CGC_EN;
+	writel_relaxed(val, base + GENI_CGC_CTRL);
+
+	val = readl_relaxed(base + SE_DMA_GENERAL_CFG);
+	val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON;
+	val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON;
+	writel_relaxed(val, base + SE_DMA_GENERAL_CFG);
+
+	writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL);
+	writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG);
+}
+
+/**
+ * geni_se_init() - Initialize the GENI Serial Engine
+ * @se:		Pointer to the concerned Serial Engine.
+ * @rx_wm:	Receive watermark, in units of FIFO words.
+ * @rx_rfr_wm:	Ready-for-receive watermark, in units of 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.
+ */
+int geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr)
+{
+	u32 val;
+
+	geni_se_io_init(se->base);
+	geni_se_io_set_mode(se->base);
+
+	writel_relaxed(rx_wm, se->base + SE_GENI_RX_WATERMARK_REG);
+	writel_relaxed(rx_rfr, se->base + SE_GENI_RX_RFR_WATERMARK_REG);
+
+	val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
+	val |= M_COMMON_GENI_M_IRQ_EN;
+	writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
+
+	val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
+	val |= S_COMMON_GENI_S_IRQ_EN;
+	writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_init);
+
+static void geni_se_select_fifo_mode(struct geni_se *se)
+{
+	u32 proto = geni_se_read_proto(se);
+	u32 val;
+
+	writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
+	writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
+	writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
+	writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
+	writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
+	writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
+
+	val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
+	if (proto != GENI_SE_UART) {
+		val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN;
+		val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
+	}
+	writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
+
+	val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
+	if (proto != GENI_SE_UART)
+		val |= S_CMD_DONE_EN;
+	writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
+
+	val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
+	val &= ~GENI_DMA_MODE_EN;
+	writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
+}
+
+static void geni_se_select_dma_mode(struct geni_se *se)
+{
+	u32 val;
+
+	writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
+	writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
+	writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
+	writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
+	writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
+	writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
+
+	val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
+	val |= GENI_DMA_MODE_EN;
+	writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
+}
+
+/**
+ * geni_se_select_mode() - Select the serial engine transfer mode
+ * @se:		Pointer to the concerned Serial Engine.
+ * @mode:	Transfer mode to be selected.
+ */
+void geni_se_select_mode(struct geni_se *se, int mode)
+{
+	WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA);
+
+	switch (mode) {
+	case GENI_SE_FIFO:
+		geni_se_select_fifo_mode(se);
+		break;
+	case GENI_SE_DMA:
+		geni_se_select_dma_mode(se);
+		break;
+	}
+}
+EXPORT_SYMBOL(geni_se_select_mode);
+
+/**
+ * geni_se_setup_m_cmd() - Setup the primary sequencer
+ * @se:		Pointer to the concerned Serial Engine.
+ * @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 associated parameters.
+ */
+void geni_se_setup_m_cmd(struct geni_se *se, u32 cmd, u32 params)
+{
+	u32 m_cmd;
+
+	m_cmd = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
+	writel_relaxed(m_cmd, se->base + SE_GENI_M_CMD0);
+}
+EXPORT_SYMBOL(geni_se_setup_m_cmd);
+
+/**
+ * geni_se_setup_s_cmd() - Setup the secondary sequencer
+ * @se:		Pointer to the concerned Serial Engine.
+ * @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 associated parameters.
+ */
+void geni_se_setup_s_cmd(struct geni_se *se, u32 cmd, u32 params)
+{
+	u32 s_cmd;
+
+	s_cmd = readl_relaxed(se->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);
+	writel_relaxed(s_cmd, se->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
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * This function is used to cancel the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_cancel_m_cmd(struct geni_se *se)
+{
+	writel_relaxed(M_GENI_CMD_CANCEL, se->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
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * This function is used to cancel the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_cancel_s_cmd(struct geni_se *se)
+{
+	writel_relaxed(S_GENI_CMD_CANCEL, se->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
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * This function is used to force abort the currently configured command in the
+ * primary sequencer.
+ */
+void geni_se_abort_m_cmd(struct geni_se *se)
+{
+	writel_relaxed(M_GENI_CMD_ABORT, se->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
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * This function is used to force abort the currently configured command in the
+ * secondary sequencer.
+ */
+void geni_se_abort_s_cmd(struct geni_se *se)
+{
+	writel_relaxed(S_GENI_CMD_ABORT, se->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
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * 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.
+ */
+u32 geni_se_get_tx_fifo_depth(struct geni_se *se)
+{
+	u32 val;
+
+	val = readl_relaxed(se->base + SE_HW_PARAM_0);
+
+	return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
+}
+EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
+
+/**
+ * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * 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
+ */
+u32 geni_se_get_tx_fifo_width(struct geni_se *se)
+{
+	u32 val;
+
+	val = readl_relaxed(se->base + SE_HW_PARAM_0);
+
+	return (val & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT;
+}
+EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
+
+/**
+ * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * 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
+ */
+u32 geni_se_get_rx_fifo_depth(struct geni_se *se)
+{
+	u32 val;
+
+	val = readl_relaxed(se->base + SE_HW_PARAM_1);
+
+	return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT;
+}
+EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
+
+/**
+ * DOC: Overview
+ *
+ * GENI FIFO packing is highly configurable. TX/RX packing/unpacking consist
+ * of up to 4 operations, each operation represented by 4 configuration vectors
+ * of 10 bits programmed in GENI_TX_PACKING_CFG0 and GENI_TX_PACKING_CFG1 for
+ * TX FIFO and in GENI_RX_PACKING_CFG0 and GENI_RX_PACKING_CFG1 for RX FIFO.
+ * Refer to below examples for detailed bit-field description.
+ *
+ * Example 1: word_size = 7, packing_mode = 4 x 8, msb_to_lsb = 1
+ *
+ *        +-----------+-------+-------+-------+-------+
+ *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
+ *        +-----------+-------+-------+-------+-------+
+ *        | start     | 0x6   | 0xe   | 0x16  | 0x1e  |
+ *        | direction | 1     | 1     | 1     | 1     |
+ *        | length    | 6     | 6     | 6     | 6     |
+ *        | stop      | 0     | 0     | 0     | 1     |
+ *        +-----------+-------+-------+-------+-------+
+ *
+ * Example 2: word_size = 15, packing_mode = 2 x 16, msb_to_lsb = 0
+ *
+ *        +-----------+-------+-------+-------+-------+
+ *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
+ *        +-----------+-------+-------+-------+-------+
+ *        | start     | 0x0   | 0x8   | 0x10  | 0x18  |
+ *        | direction | 0     | 0     | 0     | 0     |
+ *        | length    | 7     | 6     | 7     | 6     |
+ *        | stop      | 0     | 0     | 0     | 1     |
+ *        +-----------+-------+-------+-------+-------+
+ *
+ * Example 3: word_size = 23, packing_mode = 1 x 32, msb_to_lsb = 1
+ *
+ *        +-----------+-------+-------+-------+-------+
+ *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
+ *        +-----------+-------+-------+-------+-------+
+ *        | start     | 0x16  | 0xe   | 0x6   | 0x0   |
+ *        | direction | 1     | 1     | 1     | 1     |
+ *        | length    | 7     | 7     | 6     | 0     |
+ *        | stop      | 0     | 0     | 1     | 0     |
+ *        +-----------+-------+-------+-------+-------+
+ *
+ */
+
+#define NUM_PACKING_VECTORS 4
+#define PACKING_START_SHIFT 5
+#define PACKING_DIR_SHIFT 4
+#define PACKING_LEN_SHIFT 1
+#define PACKING_STOP_BIT BIT(0)
+#define PACKING_VECTOR_SHIFT 10
+/**
+ * geni_se_config_packing() - Packing configuration of the serial engine
+ * @se:		Pointer to the concerned Serial Engine
+ * @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.
+ * @tx_cfg:	Flag to configure the TX Packing.
+ * @rx_cfg:	Flag to configure the RX Packing.
+ *
+ * This function is used to configure the packing rules for the current
+ * transfer.
+ */
+void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words,
+			    bool msb_to_lsb, bool tx_cfg, bool rx_cfg)
+{
+	u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {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)) & ~(BITS_PER_BYTE - 1);
+	int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE;
+	int i;
+
+	if (iter <= 0 || iter > NUM_PACKING_VECTORS)
+		return;
+
+	for (i = 0; i < iter; i++) {
+		if (temp_bpw < BITS_PER_BYTE)
+			len = temp_bpw - 1;
+		else
+			len = BITS_PER_BYTE - 1;
+
+		cfg[i] = idx << PACKING_START_SHIFT;
+		cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT;
+		cfg[i] |= len << PACKING_LEN_SHIFT;
+
+		if (temp_bpw <= BITS_PER_BYTE) {
+			idx = ((i + 1) * BITS_PER_BYTE) + idx_start;
+			temp_bpw = bpw;
+		} else {
+			idx = idx + idx_delta;
+			temp_bpw = temp_bpw - BITS_PER_BYTE;
+		}
+	}
+	cfg[iter - 1] |= PACKING_STOP_BIT;
+	cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT);
+	cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT);
+
+	if (tx_cfg) {
+		writel_relaxed(cfg0, se->base + SE_GENI_TX_PACKING_CFG0);
+		writel_relaxed(cfg1, se->base + SE_GENI_TX_PACKING_CFG1);
+	}
+	if (rx_cfg) {
+		writel_relaxed(cfg0, se->base + SE_GENI_RX_PACKING_CFG0);
+		writel_relaxed(cfg1, se->base + SE_GENI_RX_PACKING_CFG1);
+	}
+
+	/*
+	 * Number of protocol words in each FIFO entry
+	 * 0 - 4x8, four words in each entry, max word size of 8 bits
+	 * 1 - 2x16, two words in each entry, max word size of 16 bits
+	 * 2 - 1x32, one word in each entry, max word size of 32 bits
+	 * 3 - undefined
+	 */
+	if (pack_words || bpw == 32)
+		writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN);
+}
+EXPORT_SYMBOL(geni_se_config_packing);
+
+static void geni_se_clks_off(struct geni_se *se)
+{
+	struct geni_wrapper *wrapper = se->wrapper;
+
+	clk_disable_unprepare(se->clk);
+	clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
+						wrapper->ahb_clks);
+}
+
+/**
+ * geni_se_resources_off() - Turn off resources associated with the serial
+ *                           engine
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_off(struct geni_se *se)
+{
+	int ret;
+
+	ret = pinctrl_pm_select_sleep_state(se->dev);
+	if (ret)
+		return ret;
+
+	geni_se_clks_off(se);
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_resources_off);
+
+static int geni_se_clks_on(struct geni_se *se)
+{
+	int ret;
+	struct geni_wrapper *wrapper = se->wrapper;
+
+	ret = clk_bulk_prepare_enable(ARRAY_SIZE(wrapper->ahb_clks),
+						wrapper->ahb_clks);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(se->clk);
+	if (ret)
+		clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
+							wrapper->ahb_clks);
+	return ret;
+}
+
+/**
+ * geni_se_resources_on() - Turn on resources associated with the serial
+ *                          engine
+ * @se:	Pointer to the concerned Serial Engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int geni_se_resources_on(struct geni_se *se)
+{
+	int ret = 0;
+
+	ret = geni_se_clks_on(se);
+	if (ret)
+		return ret;
+
+	ret = pinctrl_pm_select_default_state(se->dev);
+	if (ret)
+		geni_se_clks_off(se);
+
+	return ret;
+}
+EXPORT_SYMBOL(geni_se_resources_on);
+
+/**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @se:		Pointer to the concerned Serial Engine.
+ * @tbl:	Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
+{
+	struct geni_wrapper *wrapper = se->wrapper;
+	unsigned long freq = 0;
+	int i;
+	int ret = 0;
+
+	mutex_lock(&wrapper->lock);
+	if (wrapper->clk_perf_tbl) {
+		*tbl = wrapper->clk_perf_tbl;
+		ret = wrapper->num_clk_levels;
+		goto out_unlock;
+	}
+
+	wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
+					sizeof(*wrapper->clk_perf_tbl),
+					GFP_KERNEL);
+	if (!wrapper->clk_perf_tbl) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
+		freq = clk_round_rate(se->clk, freq + 1);
+		if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
+			break;
+		wrapper->clk_perf_tbl[i] = freq;
+	}
+	wrapper->num_clk_levels = i;
+	*tbl = wrapper->clk_perf_tbl;
+	ret = wrapper->num_clk_levels;
+out_unlock:
+	mutex_unlock(&wrapper->lock);
+	return ret;
+}
+EXPORT_SYMBOL(geni_se_clk_tbl_get);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @se:		Pointer to the concerned Serial Engine.
+ * @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 exact multiple of the requested frequency, as provided by the Serial
+ * Engine clock in order to meet the performance requirements. If there is
+ * no matching or exact multiple of the requested frequency found, then it
+ * selects the closest floor frequency, if exact flag is not set.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct geni_se *se, 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(se, &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)
+		return -ENOKEY;
+
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_clk_freq_match);
+
+#define GENI_SE_DMA_DONE_EN BIT(0)
+#define GENI_SE_DMA_EOT_EN BIT(1)
+#define GENI_SE_DMA_AHB_ERR_EN BIT(2)
+#define GENI_SE_DMA_EOT_BUF BIT(0)
+/**
+ * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
+ * @se:			Pointer to the concerned Serial Engine.
+ * @buf:		Pointer to the TX buffer.
+ * @len:		Length of the TX buffer.
+ *
+ * This function is used to prepare the buffers for DMA TX.
+ *
+ * Return: Mapped DMA Address of the buffer on success, NULL on failure.
+ */
+dma_addr_t geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len)
+{
+	dma_addr_t iova;
+	struct geni_wrapper *wrapper = se->wrapper;
+	u32 val;
+
+	iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE);
+	if (dma_mapping_error(wrapper->dev, iova))
+		return (dma_addr_t)NULL;
+
+	val = GENI_SE_DMA_DONE_EN;
+	val |= GENI_SE_DMA_EOT_EN;
+	val |= GENI_SE_DMA_AHB_ERR_EN;
+	writel_relaxed(val, se->base + SE_DMA_TX_IRQ_EN_SET);
+	writel_relaxed((u32)iova, se->base + SE_DMA_TX_PTR_L);
+	writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_TX_PTR_H);
+	writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR);
+	writel_relaxed((u32)len, se->base + SE_DMA_TX_LEN);
+	return iova;
+}
+EXPORT_SYMBOL(geni_se_tx_dma_prep);
+
+/**
+ * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
+ * @se:			Pointer to the concerned Serial Engine.
+ * @buf:		Pointer to the RX buffer.
+ * @len:		Length of the RX buffer.
+ *
+ * This function is used to prepare the buffers for DMA RX.
+ *
+ * Return: Mapped DMA Address of the buffer on success, NULL on failure.
+ */
+dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
+{
+	dma_addr_t iova;
+	struct geni_wrapper *wrapper = se->wrapper;
+	u32 val;
+
+	iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
+	if (dma_mapping_error(wrapper->dev, iova))
+		return (dma_addr_t)NULL;
+
+	val = GENI_SE_DMA_DONE_EN;
+	val |= GENI_SE_DMA_EOT_EN;
+	val |= GENI_SE_DMA_AHB_ERR_EN;
+	writel_relaxed(val, se->base + SE_DMA_RX_IRQ_EN_SET);
+	writel_relaxed((u32)iova, se->base + SE_DMA_RX_PTR_L);
+	writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_RX_PTR_H);
+	/* RX does not have EOT buffer type bit. So just reset RX_ATTR */
+	writel_relaxed(0, se->base + SE_DMA_RX_ATTR);
+	writel_relaxed((u32)len, se->base + SE_DMA_RX_LEN);
+	return iova;
+}
+EXPORT_SYMBOL(geni_se_rx_dma_prep);
+
+/**
+ * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
+ * @se:			Pointer to the concerned Serial Engine.
+ * @iova:		DMA address of the TX buffer.
+ * @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 geni_se *se, dma_addr_t iova, size_t len)
+{
+	struct geni_wrapper *wrapper = se->wrapper;
+
+	if (iova)
+		dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE);
+}
+EXPORT_SYMBOL(geni_se_tx_dma_unprep);
+
+/**
+ * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
+ * @se:			Pointer to the concerned Serial Engine.
+ * @iova:		DMA address of the RX buffer.
+ * @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 geni_se *se, dma_addr_t iova, size_t len)
+{
+	struct geni_wrapper *wrapper = se->wrapper;
+
+	if (iova)
+		dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
+}
+EXPORT_SYMBOL(geni_se_rx_dma_unprep);
+
+static int geni_se_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct geni_wrapper *wrapper;
+	int ret;
+
+	wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
+	if (!wrapper)
+		return -ENOMEM;
+
+	wrapper->dev = dev;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	wrapper->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(wrapper->base)) {
+		dev_err(dev, "%s: Error mapping the resource\n", __func__);
+		return -EFAULT;
+	}
+
+	wrapper->ahb_clks[0].id = m_ahb_clk;
+	wrapper->ahb_clks[1].id = s_ahb_clk;
+	ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
+	if (ret) {
+		dev_err(dev, "Err getting AHB clks %d\n", ret);
+		return ret;
+	}
+
+	mutex_init(&wrapper->lock);
+	dev_set_drvdata(dev, wrapper);
+	dev_dbg(dev, "GENI SE Driver probed\n");
+	return devm_of_platform_populate(dev);
+}
+
+static int geni_se_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct geni_wrapper *wrapper = dev_get_drvdata(dev);
+
+	kfree(wrapper->clk_perf_tbl);
+	return 0;
+}
+
+static const struct of_device_id geni_se_dt_match[] = {
+	{ .compatible = "qcom,geni-se-qup", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, geni_se_dt_match);
+
+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,
+};
+module_platform_driver(geni_se_driver);
+
+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..4996de7
--- /dev/null
+++ b/include/linux/qcom-geni-se.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _LINUX_QCOM_GENI_SE
+#define _LINUX_QCOM_GENI_SE
+#include <linux/clk.h>
+#include <linux/dma-direction.h>
+
+/* Transfer mode supported by GENI Serial Engines */
+enum geni_se_xfer_mode {
+	GENI_SE_INVALID,
+	GENI_SE_FIFO,
+	GENI_SE_DMA,
+};
+
+/* Protocols supported by GENI Serial Engines */
+enum geni_se_protocol_types {
+	GENI_SE_NONE,
+	GENI_SE_SPI,
+	GENI_SE_UART,
+	GENI_SE_I2C,
+	GENI_SE_I3C,
+};
+
+/**
+ * struct geni_se - GENI Serial Engine
+ * @base:		Base Address of the Serial Engine's register block.
+ * @dev:		Pointer to the Serial Engine device.
+ * @wrapper:		Pointer to the parent QUP Wrapper core.
+ * @clk:		Handle to the core serial engine clock.
+ */
+struct geni_se {
+	void __iomem *base;
+	struct device *dev;
+	void *wrapper;
+	struct clk *clk;
+};
+
+/* Common SE registers */
+#define GENI_FORCE_DEFAULT_REG		0x20
+#define SE_GENI_STATUS			0x40
+#define GENI_SER_M_CLK_CFG		0x48
+#define GENI_SER_S_CLK_CFG		0x4c
+#define SE_GENI_CLK_SEL			0x7c
+#define SE_GENI_DMA_MODE_EN		0x258
+#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_DMA_TX_IRQ_STAT		0xc40
+#define SE_DMA_TX_IRQ_CLR		0xc44
+#define SE_DMA_TX_FSM_RST		0xc58
+#define SE_DMA_RX_IRQ_STAT		0xd40
+#define SE_DMA_RX_IRQ_CLR		0xd44
+#define SE_DMA_RX_FSM_RST		0xd58
+
+/* GENI_FORCE_DEFAULT_REG fields */
+#define FORCE_DEFAULT	BIT(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
+
+/* 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_GENI_IOS fields */
+#define IO2_DATA_IN			BIT(1)
+#define RX_DATA_IN			BIT(0)
+
+/* 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
+void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
+				unsigned int *minor, unsigned int *step);
+
+u32 geni_se_read_proto(struct geni_se *se);
+
+int geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr);
+
+void geni_se_select_mode(struct geni_se *se, int mode);
+
+void geni_se_setup_m_cmd(struct geni_se *se, u32 cmd, u32 params);
+
+void geni_se_setup_s_cmd(struct geni_se *se, u32 cmd, u32 params);
+
+void geni_se_cancel_m_cmd(struct geni_se *se);
+
+void geni_se_cancel_s_cmd(struct geni_se *se);
+
+void geni_se_abort_m_cmd(struct geni_se *se);
+
+void geni_se_abort_s_cmd(struct geni_se *se);
+
+u32 geni_se_get_tx_fifo_depth(struct geni_se *se);
+
+u32 geni_se_get_tx_fifo_width(struct geni_se *se);
+
+u32 geni_se_get_rx_fifo_depth(struct geni_se *se);
+
+void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words,
+			    bool msb_to_lsb, bool tx_cfg, bool rx_cfg);
+
+int geni_se_resources_off(struct geni_se *se);
+
+int geni_se_resources_on(struct geni_se *se);
+
+int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl);
+
+int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq,
+			   unsigned int *index, unsigned long *res_freq,
+			   bool exact);
+
+dma_addr_t geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len);
+
+dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len);
+
+void geni_se_tx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len);
+
+void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len);
+#endif
+#endif
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* [PATCH v3 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-02-28  1:38 [PATCH v3 0/4] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
  2018-02-28  1:38 ` [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
  2018-02-28  1:38 ` [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
@ 2018-02-28  1:38 ` Karthikeyan Ramasubramanian
  2018-03-07 21:16     ` Doug Anderson
  2018-02-28  1:38 ` [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
  3 siblings, 1 reply; 65+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-02-28  1:38 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, evgreen, acourbot,
	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@codeaurora.org>
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
---
 drivers/i2c/busses/Kconfig         |  11 +
 drivers/i2c/busses/Makefile        |   1 +
 drivers/i2c/busses/i2c-qcom-geni.c | 626 +++++++++++++++++++++++++++++++++++++
 3 files changed, 638 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e2954fb..1ddf5cd 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -848,6 +848,17 @@ 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
+	depends on QCOM_GENI_SE
+	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..e1e4268
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -0,0 +1,626 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.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/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_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		BIT(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
+/* I2C SCL COUNTER fields */
+#define HIGH_COUNTER_MSK	GENMASK(29, 20)
+#define HIGH_COUNTER_SHFT	20
+#define LOW_COUNTER_MSK		GENMASK(19, 10)
+#define LOW_COUNTER_SHFT	10
+#define CYCLE_COUNTER_MSK	GENMASK(9, 0)
+
+#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)
+#define PACKING_BYTES_PW	4
+
+struct geni_i2c_dev {
+	struct geni_se se;
+	u32 tx_wm;
+	int irq;
+	int err;
+	struct i2c_adapter adap;
+	struct completion done;
+	struct i2c_msg *cur;
+	int cur_wr;
+	int cur_rd;
+	u32 clk_freq_out;
+	const struct geni_i2c_clk_fld *clk_fld;
+};
+
+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 const 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;
+	const 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) {
+			gi2c->clk_fld = geni_i2c_clk_map + i;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
+{
+	const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
+	u32 val;
+
+	writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
+
+	val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
+	writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
+
+	val = itr->t_high << HIGH_COUNTER_SHFT;
+	val |= itr->t_low << LOW_COUNTER_SHFT;
+	val |= itr->t_cycle;
+	writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
+	/*
+	 * Ensure later writes/reads to serial engine register block is
+	 * not reordered before this point.
+	 */
+	mb();
+}
+
+static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
+{
+	u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0);
+	u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
+	u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
+	u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS);
+	u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
+	u32 rx_st, tx_st;
+
+	if (dma) {
+		rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
+		tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
+	} else {
+		rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
+		tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS);
+	}
+	dev_dbg(gi2c->se.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->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
+		m_cmd, geni_s, geni_ios);
+}
+
+static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
+{
+	gi2c->err = gi2c_log[err].err;
+	if (gi2c->cur)
+		dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
+			gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
+	dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
+
+	if (err != I2C_NACK && err != GENI_ABORT_DONE)
+		geni_i2c_err_misc(gi2c);
+}
+
+static irqreturn_t geni_i2c_irq(int irq, void *dev)
+{
+	struct geni_i2c_dev *gi2c = dev;
+	int j;
+	u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
+	u32 rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
+	u32 dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
+	u32 dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
+	u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
+	struct i2c_msg *cur = gi2c->cur;
+
+	if (!cur ||
+	    m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
+	    dm_rx_st & (DM_I2C_CB_ERR)) {
+		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);
+
+		/* Disable the TX Watermark interrupt to stop TX */
+		if (!dma)
+			writel_relaxed(0, gi2c->se.base +
+					   SE_GENI_TX_WATERMARK_REG);
+		goto irqret;
+	}
+
+	if (dma) {
+		dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n",
+			dm_tx_st, dm_rx_st);
+		goto irqret;
+	}
+
+	if (cur->flags & I2C_M_RD &&
+	    m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) {
+		u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
+
+		for (j = 0; j < rxcnt; j++) {
+			u32 val;
+			int p = 0;
+
+			val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn);
+			while (gi2c->cur_rd < cur->len && p < sizeof(val)) {
+				cur->buf[gi2c->cur_rd++] = val & 0xff;
+				val >>= 8;
+				p++;
+			}
+			if (gi2c->cur_rd == cur->len)
+				break;
+		}
+	} else if (!(cur->flags & I2C_M_RD) &&
+		   m_stat & M_TX_FIFO_WATERMARK_EN) {
+		for (j = 0; j < gi2c->tx_wm; j++) {
+			u32 temp;
+			u32 val = 0;
+			int p = 0;
+
+			while (gi2c->cur_wr < cur->len && p < sizeof(val)) {
+				temp = (u32)cur->buf[gi2c->cur_wr++];
+				val |= (temp << (p * 8));
+				p++;
+			}
+			writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn);
+			/* TX Complete, Disable the TX Watermark interrupt */
+			if (gi2c->cur_wr == cur->len) {
+				writel_relaxed(0, gi2c->se.base +
+						SE_GENI_TX_WATERMARK_REG);
+				break;
+			}
+		}
+	}
+irqret:
+	if (m_stat)
+		writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR);
+
+	if (dma) {
+		if (dm_tx_st)
+			writel_relaxed(dm_tx_st, gi2c->se.base +
+						SE_DMA_TX_IRQ_CLR);
+		if (dm_rx_st)
+			writel_relaxed(dm_rx_st, gi2c->se.base +
+						SE_DMA_RX_IRQ_CLR);
+	}
+	/* if this is err with done-bit not set, handle that through timeout. */
+	if (m_stat & M_CMD_DONE_EN)
+		complete(&gi2c->done);
+	else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
+		complete(&gi2c->done);
+
+	return IRQ_HANDLED;
+}
+
+static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
+{
+	u32 val;
+	unsigned long timeout = HZ;
+
+	geni_i2c_err(gi2c, GENI_TIMEOUT);
+	gi2c->cur = NULL;
+	geni_se_abort_m_cmd(&gi2c->se);
+	do {
+		timeout = wait_for_completion_timeout(&gi2c->done, timeout);
+		val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
+	} while (!(val & M_CMD_ABORT_EN) && timeout);
+}
+
+static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
+				u32 m_param)
+{
+	dma_addr_t rx_dma;
+	enum geni_se_xfer_mode mode;
+	unsigned long timeout;
+
+	gi2c->cur = msg;
+	mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
+	geni_se_select_mode(&gi2c->se, mode);
+	writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
+	geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
+	if (mode == GENI_SE_DMA) {
+		rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len);
+		if (!rx_dma) {
+			mode = GENI_SE_FIFO;
+			geni_se_select_mode(&gi2c->se, mode);
+		}
+	}
+
+	timeout = wait_for_completion_timeout(&gi2c->done, HZ);
+	if (!timeout)
+		geni_i2c_abort_xfer(gi2c);
+
+	gi2c->cur_rd = 0;
+	if (mode == GENI_SE_DMA) {
+		if (gi2c->err) {
+			writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
+			wait_for_completion_timeout(&gi2c->done, HZ);
+		}
+		geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
+	}
+	if (gi2c->err)
+		dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
+	return gi2c->err;
+}
+
+static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
+				u32 m_param)
+{
+	dma_addr_t tx_dma;
+	enum geni_se_xfer_mode mode;
+	unsigned long timeout;
+
+	gi2c->cur = msg;
+	mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
+	geni_se_select_mode(&gi2c->se, mode);
+	writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
+	geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
+	if (mode == GENI_SE_DMA) {
+		tx_dma = geni_se_tx_dma_prep(&gi2c->se,	msg->buf, msg->len);
+		if (!tx_dma) {
+			mode = GENI_SE_FIFO;
+			geni_se_select_mode(&gi2c->se, mode);
+		}
+	}
+
+	if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
+		writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG);
+
+	timeout = wait_for_completion_timeout(&gi2c->done, HZ);
+	if (!timeout)
+		geni_i2c_abort_xfer(gi2c);
+
+	gi2c->cur_wr = 0;
+	if (mode == GENI_SE_DMA) {
+		if (gi2c->err) {
+			writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
+			wait_for_completion_timeout(&gi2c->done, HZ);
+		}
+		geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
+	}
+	if (gi2c->err)
+		dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
+	return gi2c->err;
+}
+
+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;
+
+	gi2c->err = 0;
+	reinit_completion(&gi2c->done);
+	ret = pm_runtime_get_sync(gi2c->se.dev);
+	if (ret < 0) {
+		dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
+		pm_runtime_put_noidle(gi2c->se.dev);
+		/* Set device in suspended since resume failed */
+		pm_runtime_set_suspended(gi2c->se.dev);
+		return ret;
+	}
+
+	qcom_geni_i2c_conf(gi2c);
+	for (i = 0; i < num; i++) {
+		u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
+
+		m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
+		else
+			ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
+
+		if (ret) {
+			dev_err(gi2c->se.dev, "i2c error %d @ %d\n", ret, i);
+			break;
+		}
+	}
+	if (ret == 0)
+		ret = num;
+
+	pm_runtime_mark_last_busy(gi2c->se.dev);
+	pm_runtime_put_autosuspend(gi2c->se.dev);
+	gi2c->cur = NULL;
+	gi2c->err = 0;
+	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;
+	u32 proto, tx_depth;
+	int ret;
+
+	gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
+	if (!gi2c)
+		return -ENOMEM;
+
+	gi2c->se.dev = &pdev->dev;
+	gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(gi2c->se.base)) {
+		ret = PTR_ERR(gi2c->se.base);
+		dev_err(&pdev->dev, "Err IO Mapping register block %d\n", ret);
+		return ret;
+	}
+
+	gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
+	if (IS_ERR(gi2c->se.clk)) {
+		ret = PTR_ERR(gi2c->se.clk);
+		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+		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(&pdev->dev, "IRQ error for i2c-geni\n");
+		return gi2c->irq;
+	}
+
+	ret = geni_i2c_clk_map_idx(gi2c);
+	if (ret) {
+		dev_err(&pdev->dev, "Invalid clk frequency %d KHz: %d\n",
+			gi2c->clk_freq_out, ret);
+		return ret;
+	}
+
+	gi2c->adap.algo = &geni_i2c_algo;
+	init_completion(&gi2c->done);
+	platform_set_drvdata(pdev, gi2c);
+	ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
+			       IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
+	if (ret) {
+		dev_err(&pdev->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 = &pdev->dev;
+	gi2c->adap.dev.of_node = pdev->dev.of_node;
+	strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
+
+	ret = geni_se_resources_on(&gi2c->se);
+	if (ret) {
+		dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
+		return ret;
+	}
+	proto = geni_se_read_proto(&gi2c->se);
+	tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
+	if (unlikely(proto != GENI_SE_I2C)) {
+		dev_err(&pdev->dev, "Invalid proto %d\n", proto);
+		geni_se_resources_off(&gi2c->se);
+		return -ENXIO;
+	}
+	gi2c->tx_wm = tx_depth - 1;
+	geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
+	geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
+							true, true, true);
+	geni_se_resources_off(&gi2c->se);
+	dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
+
+	pm_runtime_set_suspended(gi2c->se.dev);
+	pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(gi2c->se.dev);
+	pm_runtime_enable(gi2c->se.dev);
+	i2c_add_adapter(&gi2c->adap);
+
+	dev_dbg(&pdev->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->se.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->se);
+	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->se);
+	if (ret)
+		return ret;
+
+	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->se.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,geni-i2c" },
+	{}
+};
+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 = "geni_i2c",
+		.pm = &geni_i2c_pm_ops,
+		.of_match_table = geni_i2c_dt_match,
+	},
+};
+
+module_platform_driver(geni_i2c_driver);
+
+MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores");
+MODULE_LICENSE("GPL v2");
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-02-28  1:38 [PATCH v3 0/4] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
                   ` (2 preceding siblings ...)
  2018-02-28  1:38 ` [PATCH v3 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
@ 2018-02-28  1:38 ` Karthikeyan Ramasubramanian
  2018-03-02 22:11     ` Stephen Boyd
  2018-03-03  0:11     ` Evan Green
  3 siblings, 2 replies; 65+ messages in thread
From: Karthikeyan Ramasubramanian @ 2018-02-28  1:38 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, evgreen, acourbot,
	Girish Mahadevan, Sagar Dharia, Doug Anderson

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>
Signed-off-by: Doug Anderson <dianders@google.com>
---
 drivers/tty/serial/Kconfig            |   11 +
 drivers/tty/serial/Makefile           |    1 +
 drivers/tty/serial/qcom_geni_serial.c | 1181 +++++++++++++++++++++++++++++++++
 3 files changed, 1193 insertions(+)
 create mode 100644 drivers/tty/serial/qcom_geni_serial.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 3682fd3..c6b1500 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1104,6 +1104,17 @@ config SERIAL_MSM_CONSOLE
 	select SERIAL_CORE_CONSOLE
 	select SERIAL_EARLYCON
 
+config SERIAL_QCOM_GENI
+	bool "QCOM on-chip GENI based serial port support"
+	depends on ARCH_QCOM
+	depends on QCOM_GENI_SE
+	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..8536b7d
--- /dev/null
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -0,0 +1,1181 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
+
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/iopoll.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_CONS_PORTS	1
+#define DEF_FIFO_DEPTH_WORDS	16
+#define DEF_TX_WM		2
+#define DEF_FIFO_WIDTH_BITS	32
+#define UART_CONSOLE_RX_WM	2
+
+#ifdef CONFIG_CONSOLE_POLL
+#define RX_BYTES_PW 1
+#else
+#define RX_BYTES_PW 4
+#endif
+
+struct qcom_geni_serial_port {
+	struct uart_port uport;
+	struct geni_se se;
+	char name[20];
+	u32 tx_fifo_depth;
+	u32 tx_fifo_width;
+	u32 rx_fifo_depth;
+	u32 tx_wm;
+	u32 rx_wm;
+	u32 rx_rfr;
+	int xfer_mode;
+	bool port_setup;
+	int (*handle_rx)(struct uart_port *uport,
+				u32 rx_bytes, bool drop_rx);
+	unsigned int xmit_size;
+	unsigned int cur_baud;
+	unsigned int tx_bytes_pw;
+	unsigned int rx_bytes_pw;
+};
+
+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,
+				u32 rx_bytes, bool drop_rx);
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
+static bool 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);
+static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
+					32000000, 48000000, 64000000, 80000000,
+					96000000, 100000000};
+
+#define to_dev_port(ptr, member) \
+		container_of(ptr, struct qcom_geni_serial_port, member)
+
+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)
+{
+	if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
+		return ERR_PTR(-ENXIO);
+	return &qcom_geni_console_port;
+}
+
+static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
+				int offset, int bit_field, bool set)
+{
+	u32 reg;
+	struct qcom_geni_serial_port *port;
+	unsigned int baud;
+	unsigned int fifo_bits;
+	unsigned long timeout_us = 20000;
+
+	/* Ensure polling is not re-ordered before the prior writes/reads */
+	mb();
+
+	if (uport->private_data) {
+		port = to_dev_port(uport, uport);
+		baud = port->cur_baud;
+		if (!baud)
+			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.
+		 */
+		timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
+	}
+
+	return !readl_poll_timeout_atomic(uport->membase + offset, reg,
+			 (bool)(reg & bit_field) == set, 10, timeout_us);
+}
+
+static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
+{
+	u32 m_cmd;
+
+	writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
+	m_cmd = UART_START_TX << M_OPCODE_SHFT;
+	writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
+}
+
+static void qcom_geni_serial_poll_tx_done(struct uart_port *uport)
+{
+	int done;
+	u32 irq_clear = M_CMD_DONE_EN;
+
+	done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_CMD_DONE_EN, true);
+	if (!done) {
+		writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
+						SE_GENI_M_CMD_CTRL_REG);
+		irq_clear |= M_CMD_ABORT_EN;
+		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+							M_CMD_ABORT_EN, true);
+	}
+	writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_abort_rx(struct uart_port *uport)
+{
+	u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
+
+	writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
+	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+					S_GENI_CMD_ABORT, false);
+	writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
+	writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int qcom_geni_serial_get_char(struct uart_port *uport)
+{
+	u32 rx_fifo;
+	u32 status;
+
+	status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
+	writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
+
+	status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
+	writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+
+	/*
+	 * Ensure the writes to clear interrupts is not re-ordered after
+	 * reading the data.
+	 */
+	mb();
+
+	status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
+	if (!(status & RX_FIFO_WC_MSK))
+		return NO_POLL_CHAR;
+
+	rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
+	return rx_fifo & 0xff;
+}
+
+static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
+							unsigned char c)
+{
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+	writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
+	qcom_geni_serial_setup_tx(uport, 1);
+	WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_TX_FIFO_WATERMARK_EN, true));
+	writel_relaxed((u32)c, uport->membase + SE_GENI_TX_FIFOn);
+	writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
+							SE_GENI_M_IRQ_CLEAR);
+	qcom_geni_serial_poll_tx_done(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)
+{
+	writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
+}
+
+static void
+__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
+				 unsigned int count)
+{
+	int new_line = 0;
+	int i;
+	u32 bytes_to_send = count;
+
+	for (i = 0; i < count; i++) {
+		if (s[i] == '\n')
+			new_line++;
+	}
+
+	bytes_to_send += new_line;
+	writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
+	qcom_geni_serial_setup_tx(uport, bytes_to_send);
+	i = 0;
+	while (i < count) {
+		size_t chars_to_write = 0;
+		size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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_t(size_t, (size_t)(count - i), avail / 2);
+		uart_console_write(uport, (s + i), chars_to_write,
+						qcom_geni_serial_wr_char);
+		writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
+							SE_GENI_M_IRQ_CLEAR);
+		i += chars_to_write;
+	}
+	qcom_geni_serial_poll_tx_done(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;
+	bool locked = true;
+	unsigned long flags;
+
+	WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
+
+	port = get_port_from_line(co->index);
+	if (IS_ERR(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, u32 rx_bytes, bool drop)
+{
+	u32 i = rx_bytes;
+	u32 rx_fifo;
+	unsigned char *buf;
+	struct tty_port *tport;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+	tport = &uport->state->port;
+	while (i > 0) {
+		int c;
+		int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;
+
+		rx_fifo = readl_relaxed(uport->membase + SE_GENI_RX_FIFOn);
+		i -= bytes;
+		if (drop)
+			continue;
+		buf = (unsigned char *)&rx_fifo;
+
+		for (c = 0; c < bytes; c++) {
+			int sysrq;
+
+			uport->icount.rx++;
+			sysrq = uart_handle_sysrq_char(uport, buf[c]);
+			if (!sysrq)
+				tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
+		}
+	}
+	if (!drop)
+		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)
+{
+	u32 irq_en;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+	u32 status;
+
+	if (port->xfer_mode == GENI_SE_FIFO) {
+		status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+		if (status & M_GENI_CMD_ACTIVE)
+			return;
+
+		if (!qcom_geni_serial_tx_empty(uport))
+			return;
+
+		/*
+		 * Ensure writing to IRQ_EN & watermark registers are not
+		 * re-ordered before checking the status of the Serial
+		 * Engine and TX FIFO
+		 */
+		mb();
+
+		irq_en = readl_relaxed(uport->membase +	SE_GENI_M_IRQ_EN);
+		irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
+
+		writel_relaxed(port->tx_wm, uport->membase +
+						SE_GENI_TX_WATERMARK_REG);
+		writel_relaxed(irq_en, uport->membase +	SE_GENI_M_IRQ_EN);
+	}
+}
+
+static void qcom_geni_serial_stop_tx(struct uart_port *uport)
+{
+	u32 irq_en;
+	u32 status;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+	irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+	irq_en &= ~M_CMD_DONE_EN;
+	if (port->xfer_mode == GENI_SE_FIFO) {
+		irq_en &= ~M_TX_FIFO_WATERMARK_EN;
+		writel_relaxed(0, uport->membase +
+				     SE_GENI_TX_WATERMARK_REG);
+	}
+	port->xmit_size = 0;
+	writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+	status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+	/* Possible stop tx is called multiple times. */
+	if (!(status & M_GENI_CMD_ACTIVE))
+		return;
+
+	/*
+	 * Ensure cancel command write is not re-ordered before checking
+	 * checking the status of the Primary Sequencer.
+	 */
+	mb();
+
+	geni_se_cancel_m_cmd(&port->se);
+	if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_CMD_CANCEL_EN, true)) {
+		geni_se_abort_m_cmd(&port->se);
+		qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+						M_CMD_ABORT_EN, true);
+		writel_relaxed(M_CMD_ABORT_EN, uport->membase +
+							SE_GENI_M_IRQ_CLEAR);
+	}
+	writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_start_rx(struct uart_port *uport)
+{
+	u32 irq_en;
+	u32 status;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+	status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+	if (status & S_GENI_CMD_ACTIVE)
+		qcom_geni_serial_stop_rx(uport);
+
+	/*
+	 * Ensure setup command write is not re-ordered before checking
+	 * checking the status of the Secondary Sequencer.
+	 */
+	mb();
+
+	geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
+
+	if (port->xfer_mode == GENI_SE_FIFO) {
+		irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
+		irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
+		writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+
+		irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+		irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
+		writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+	}
+}
+
+static void qcom_geni_serial_stop_rx(struct uart_port *uport)
+{
+	u32 irq_en;
+	u32 status;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+	u32 irq_clear = S_CMD_DONE_EN;
+
+	if (port->xfer_mode == GENI_SE_FIFO) {
+		irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
+		irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
+		writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+
+		irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+		irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
+		writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+	}
+
+	status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+	/* Possible stop rx is called multiple times. */
+	if (!(status & S_GENI_CMD_ACTIVE))
+		return;
+
+	/*
+	 * Ensure cancel command write is not re-ordered before checking
+	 * checking the status of the Secondary Sequencer.
+	 */
+	mb();
+
+	geni_se_cancel_s_cmd(&port->se);
+	qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+					S_GENI_CMD_CANCEL, false);
+	status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+	writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
+	if (status & S_GENI_CMD_ACTIVE)
+		qcom_geni_serial_abort_rx(uport);
+}
+
+static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
+{
+	u32 status;
+	u32 word_cnt;
+	u32 last_word_byte_cnt;
+	u32 last_word_partial;
+	u32 total_bytes;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+	status = readl_relaxed(uport->membase +	SE_GENI_RX_FIFO_STATUS);
+	word_cnt = status & RX_FIFO_WC_MSK;
+	last_word_partial = status & RX_LAST;
+	last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
+						RX_LAST_BYTE_VALID_SHFT;
+
+	if (!word_cnt)
+		return;
+	total_bytes = port->rx_bytes_pw * (word_cnt - 1);
+	if (last_word_partial && last_word_byte_cnt)
+		total_bytes += last_word_byte_cnt;
+	else
+		total_bytes += port->rx_bytes_pw;
+	port->handle_rx(uport, total_bytes, drop_rx);
+}
+
+static int qcom_geni_serial_handle_tx(struct uart_port *uport)
+{
+	int ret = 0;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+	struct circ_buf *xmit = &uport->state->xmit;
+	size_t avail;
+	size_t remaining;
+	int i = 0;
+	u32 status;
+	unsigned int chunk;
+	int tail;
+
+	chunk = uart_circ_chars_pending(xmit);
+	status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
+	/* Both FIFO and framework buffer are drained */
+	if ((chunk == port->xmit_size) && !status) {
+		port->xmit_size = 0;
+		uart_circ_clear(xmit);
+		qcom_geni_serial_stop_tx(uport);
+		goto out_write_wakeup;
+	}
+	chunk -= port->xmit_size;
+
+	avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
+	tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
+	if (chunk > (UART_XMIT_SIZE - tail))
+		chunk = UART_XMIT_SIZE - tail;
+	if (chunk > avail)
+		chunk = avail;
+
+	if (!chunk)
+		goto out_write_wakeup;
+
+	qcom_geni_serial_setup_tx(uport, chunk);
+
+	remaining = chunk;
+	while (i < chunk) {
+		unsigned int tx_bytes;
+		unsigned int buf = 0;
+		int c;
+
+		tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
+		for (c = 0; c < tx_bytes ; c++)
+			buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
+
+		writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
+
+		i += tx_bytes;
+		tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
+		uport->icount.tx += tx_bytes;
+		remaining -= tx_bytes;
+	}
+	qcom_geni_serial_poll_tx_done(uport);
+	port->xmit_size += chunk;
+out_write_wakeup:
+	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;
+
+	if (uport->suspended)
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
+	s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
+	m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+	writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
+	writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+
+	if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
+		goto out_unlock;
+
+	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_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
+	    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);
+
+out_unlock:
+	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(&port->se);
+	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(&port->se);
+	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(&port->se);
+	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) / BITS_PER_BYTE;
+	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;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+	unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
+
+	set_rfr_wm(port);
+	writel_relaxed(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.
+	 */
+	port->xfer_mode = GENI_SE_FIFO;
+	qcom_geni_serial_poll_tx_done(uport);
+	geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
+						false, true, false);
+	geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
+						false, false, true);
+	ret = geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
+	if (ret) {
+		dev_err(uport->dev, "%s: Fail\n", __func__);
+		return ret;
+	}
+
+	geni_se_select_mode(&port->se, port->xfer_mode);
+	port->port_setup = true;
+	return ret;
+}
+
+static int qcom_geni_serial_startup(struct uart_port *uport)
+{
+	int ret;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+	scnprintf(port->name, sizeof(port->name),
+		  "qcom_serial_geni%d",	uport->line);
+
+	if (geni_se_read_proto(&port->se) != GENI_SE_UART) {
+		dev_err(uport->dev, "Invalid FW %d loaded.\n",
+				 geni_se_read_proto(&port->se));
+		return -ENXIO;
+	}
+
+	get_tx_fifo_size(port);
+	if (!port->port_setup) {
+		ret = qcom_geni_serial_port_setup(uport);
+		if (ret)
+			return ret;
+	}
+
+	ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
+							port->name, uport);
+	if (ret)
+		dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
+	return ret;
+}
+
+static unsigned long get_clk_cfg(unsigned long clk_freq)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
+		if (!(root_freq[i] % clk_freq))
+			return root_freq[i];
+	}
+	return 0;
+}
+
+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)
+{
+	writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
+	writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
+	writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
+	writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
+	writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
+	writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
+	writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
+	writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
+	writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
+}
+
+static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
+{
+	unsigned long ser_clk;
+	unsigned long desired_clk;
+
+	desired_clk = baud * UART_OVERSAMPLING;
+	ser_clk = get_clk_cfg(desired_clk);
+	if (!ser_clk) {
+		pr_err("%s: Can't find matching DFS entry for baud %d\n",
+								__func__, baud);
+		return ser_clk;
+	}
+
+	*clk_div = ser_clk / desired_clk;
+	return ser_clk;
+}
+
+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;
+	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;
+	struct qcom_geni_serial_port *port = to_dev_port(uport, 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_rate = get_clk_div_rate(baud, &clk_div);
+	if (!clk_rate)
+		goto out_restart_rx;
+
+	uport->uartclk = clk_rate;
+	clk_set_rate(port->se.clk, clk_rate);
+	ser_clk_cfg = SER_CLK_EN;
+	ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
+
+	/* parity */
+	tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
+	tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
+	rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
+	rx_parity_cfg = readl_relaxed(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 (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);
+out_restart_rx:
+	qcom_geni_serial_start_rx(uport);
+}
+
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
+{
+	return !readl_relaxed(uport->membase + SE_GENI_TX_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 *port;
+	int baud;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index >= GENI_UART_CONS_PORTS  || co->index < 0)
+		return -ENXIO;
+
+	port = get_port_from_line(co->index);
+	if (IS_ERR(port)) {
+		pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port));
+		return PTR_ERR(port);
+	}
+
+	uport = &port->uport;
+
+	if (unlikely(!uport->membase))
+		return -ENXIO;
+
+	if (geni_se_resources_on(&port->se)) {
+		dev_err(port->se.dev, "Error turning on resources\n");
+		return -ENXIO;
+	}
+
+	if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {
+		geni_se_resources_off(&port->se);
+		return -ENXIO;
+	}
+
+	if (!port->port_setup) {
+		port->tx_bytes_pw = 1;
+		port->rx_bytes_pw = RX_BYTES_PW;
+		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_CONS_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 *port = to_dev_port(uport, uport);
+
+	if (unlikely(!uart_console(uport)))
+		return;
+
+	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+		geni_se_resources_on(&port->se);
+	else if (new_state == UART_PM_STATE_OFF &&
+			old_state == UART_PM_STATE_ON)
+		geni_se_resources_off(&port->se);
+}
+
+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 int qcom_geni_serial_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int line = -1;
+	struct qcom_geni_serial_port *port;
+	struct uart_port *uport;
+	struct resource *res;
+	struct uart_driver *drv;
+
+	drv = (void *)of_device_get_match_data(&pdev->dev);
+	if (!drv) {
+		dev_err(&pdev->dev, "%s: No matching device found", __func__);
+		return -ENODEV;
+	}
+
+	if (pdev->dev.of_node)
+		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;
+	port = get_port_from_line(line);
+	if (IS_ERR(port)) {
+		ret = PTR_ERR(port);
+		dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret);
+		return ret;
+	}
+
+	uport = &port->uport;
+	/* Don't allow 2 drivers to access the same port */
+	if (uport->private_data)
+		return -ENODEV;
+
+	uport->dev = &pdev->dev;
+	port->se.dev = &pdev->dev;
+	port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
+	port->se.clk = devm_clk_get(&pdev->dev, "se");
+	if (IS_ERR(port->se.clk)) {
+		ret = PTR_ERR(port->se.clk);
+		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+		return ret;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	uport->mapbase = res->start;
+	uport->membase = devm_ioremap_resource(&pdev->dev, res);
+	if (!uport->membase) {
+		dev_err(&pdev->dev, "Err IO mapping serial iomem");
+		return -ENOMEM;
+	}
+	port->se.base = uport->membase;
+
+	port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+	port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+	port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
+
+	uport->irq = platform_get_irq(pdev, 0);
+	if (uport->irq < 0) {
+		dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq);
+		return uport->irq;
+	}
+
+	uport->private_data = drv;
+	platform_set_drvdata(pdev, port);
+	port->handle_rx = handle_rx_console;
+	port->port_setup = false;
+	return uart_add_one_port(drv, uport);
+}
+
+static int qcom_geni_serial_remove(struct platform_device *pdev)
+{
+	struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+	struct uart_driver *drv = port->uport.private_data;
+
+	uart_remove_one_port(drv, &port->uport);
+	return 0;
+}
+
+static int __maybe_unused 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(uport->private_data, uport);
+	return 0;
+}
+
+static int __maybe_unused 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(uport->private_data, uport);
+		disable_irq(uport->irq);
+	}
+	return 0;
+}
+
+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 const struct of_device_id qcom_geni_serial_match_table[] = {
+	{ .compatible = "qcom,geni-debug-uart",
+				.data = &qcom_geni_console_driver, },
+	{}
+};
+MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
+
+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;
+
+	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 = 0;
+
+	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;
+}
+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");
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-02-28  1:38 ` [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
@ 2018-03-02 20:41     ` Stephen Boyd
  0 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-02 20:41 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian, andy.gross, corbet, david.brown,
	gregkh, mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan

Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
> 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        |   9 +
>  drivers/soc/qcom/Makefile       |   1 +
>  drivers/soc/qcom/qcom-geni-se.c | 971 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/qcom-geni-se.h    | 247 ++++++++++
>  4 files changed, 1228 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 e050eb8..cc460d0 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -3,6 +3,15 @@
>  #
>  menu "Qualcomm SoC drivers"
>  
> +config QCOM_GENI_SE
> +       tristate "QCOM GENI Serial Engine Driver"
> +       depends on ARCH_QCOM

Add || COMPILE_TEST?

> +       help
> +         This module is used to manage Generic Interface (GENI) firmware based

s/module/driver?

> +         Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
> +         module is also used to manage the common aspects of multiple Serial
s/module/driver?

> +         Engines present in the QUP.
> +
>  config QCOM_GLINK_SSR
>         tristate "Qualcomm Glink SSR driver"
>         depends on RPMSG
> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> new file mode 100644
> index 0000000..61335b8
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> @@ -0,0 +1,971 @@
> +// 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/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/qcom-geni-se.h>

#include <linux/platform_device.h>

> +
> +/**
> + * DOC: Overview
> + *
> + * 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.
> + */
> +
> +/**
> + * DOC: 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

The code talks about primary and secondary sequencers, but this hardware
description doesn't talk about it. Can you add some more information
here about that aspect too?

> + */
> +
> +/**
> + * DOC: Software description
> + *
> + * GENI SE Wrapper driver is structured into 2 parts:
> + *
> + * geni_wrapper 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 represents Serial Engine. This part of the driver manages Serial
> + * Engine information such as clocks, containing QUP Wrapper etc. This part

Insert a comma here                                           ^

> + * of driver also supports operations(eg. initialize the concerned Serial

Space                                   ^

> + * Engine, select between FIFO and DMA mode of operation etc.) that are
> + * common to all the Serial Engines and are independent of Serial Interfaces.

Why are Serial Interfaces and Serial Engine always capitalized?

> + */
> +
> +#define MAX_CLK_PERF_LEVEL 32
> +#define NUM_AHB_CLKS 2
> +static const char m_ahb_clk[] = "m-ahb";
> +static const char s_ahb_clk[] = "s-ahb";

These are used in one place. Inline them?

> +
> +/**
> + * @struct geni_wrapper - 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.
> + * @ahb_clks:          Handle to the primary & secondary AHB clocks.
> + * @lock:              Lock to protect the device elements.

What does 'device elements' mean?

> + * @num_clk_levels:    Number of valid clock levels in clk_perf_tbl.
> + * @clk_perf_tbl:      Table of clock frequency input to Serial Engine clock.

Kernel-doc normally doesn't have a full-stop on member descriptions.

> + */
> +struct geni_wrapper {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
> +       struct mutex lock;
> +       unsigned int num_clk_levels;
> +       unsigned long *clk_perf_tbl;
> +};
> +
> +/* Offset of QUP Hardware Version Register */

Useless comment?

> +#define QUP_HW_VER_REG                 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)
> +
> +/* Common SE registers */
> +#define GENI_INIT_CFG_REVISION         0x0
> +#define GENI_S_INIT_CFG_REVISION       0x4
> +#define GENI_OUTPUT_CTRL               0x24
> +#define GENI_CGC_CTRL                  0x28
> +#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_BYTE_GRAN              0x254
> +#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_GP_LENGTH            0x910
> +#define SE_GENI_S_GP_LENGTH            0x914
> +#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_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_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_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_MAX_BURST            0xd5c
> +#define SE_DMA_RX_FLUSH                        0xd60
> +#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_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)
> +
> +/* FW_REVISION_RO fields */
> +#define FW_REV_PROTOCOL_MSK            GENMASK(15, 8)
> +#define FW_REV_PROTOCOL_SHFT           8
> +
> +/* 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_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
> +
> +/**
> + * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
> + * @se:                        Pointer to the corresponding Serial Engine.
> + * @major:             Buffer for Major Version field.
> + * @minor:             Buffer for Minor Version field.
> + * @step:              Buffer for Step Version field.
> + */
> +void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
> +                               unsigned int *minor, unsigned int *step)
> +{
> +       unsigned int version;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       version = readl_relaxed(wrapper->base + QUP_HW_VER_REG);
> +       *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;
> +}
> +EXPORT_SYMBOL(geni_se_get_qup_hw_version);

Is this used?

> +
> +/**
> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * Return: Protocol value as configured in the serial engine.
> + */
> +u32 geni_se_read_proto(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
> +
> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_read_proto);

Is this API really needed outside of this file? It would seem like the
drivers that implement the protocol, which are child devices, would only
use this API to confirm that the protocol chosen is for their particular
protocol.


> +
> +static void geni_se_io_set_mode(void __iomem *base)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(base + SE_IRQ_EN);
> +       val |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
> +       val |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);

Drop useless parenthesis please.

> +       writel_relaxed(val, base + SE_IRQ_EN);
> +
> +       val = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
> +       val &= ~GENI_DMA_MODE_EN;
> +       writel_relaxed(val, base + SE_GENI_DMA_MODE_EN);
> +
> +       writel_relaxed(0, base + SE_GSI_EVENT_EN);
> +}
> +
> +static void geni_se_io_init(void __iomem *base)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(base + GENI_CGC_CTRL);
> +       val |= DEFAULT_CGC_EN;
> +       writel_relaxed(val, base + GENI_CGC_CTRL);
> +
> +       val = readl_relaxed(base + SE_DMA_GENERAL_CFG);
> +       val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON;
> +       val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON;
> +       writel_relaxed(val, base + SE_DMA_GENERAL_CFG);
> +
> +       writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL);
> +       writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG);
> +}
> +
> +/**
> + * geni_se_init() - Initialize the GENI Serial Engine
> + * @se:                Pointer to the concerned Serial Engine.
> + * @rx_wm:     Receive watermark, in units of FIFO words.
> + * @rx_rfr_wm: Ready-for-receive watermark, in units of 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.

It never returns an error. Change to void?

> + */
> +int geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr)
> +{
> +       u32 val;
> +
> +       geni_se_io_init(se->base);
> +       geni_se_io_set_mode(se->base);
> +
> +       writel_relaxed(rx_wm, se->base + SE_GENI_RX_WATERMARK_REG);
> +       writel_relaxed(rx_rfr, se->base + SE_GENI_RX_RFR_WATERMARK_REG);
> +
> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
> +       val |= M_COMMON_GENI_M_IRQ_EN;
> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
> +       val |= S_COMMON_GENI_S_IRQ_EN;
> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
> +       return 0;
> +}
> +EXPORT_SYMBOL(geni_se_init);
> +
> +static void geni_se_select_fifo_mode(struct geni_se *se)
> +{
> +       u32 proto = geni_se_read_proto(se);
> +       u32 val;
> +
> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
> +       if (proto != GENI_SE_UART) {
> +               val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN;
> +               val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
> +       }
> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
> +       if (proto != GENI_SE_UART)
> +               val |= S_CMD_DONE_EN;
> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
> +       val &= ~GENI_DMA_MODE_EN;
> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
> +}
> +
> +static void geni_se_select_dma_mode(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
> +       val |= GENI_DMA_MODE_EN;
> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
> +}
> +
> +/**
> + * geni_se_select_mode() - Select the serial engine transfer mode
> + * @se:                Pointer to the concerned Serial Engine.
> + * @mode:      Transfer mode to be selected.
> + */
> +void geni_se_select_mode(struct geni_se *se, int mode)

enum mode?

> +{
> +       WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA);
> +
> +       switch (mode) {
> +       case GENI_SE_FIFO:
> +               geni_se_select_fifo_mode(se);
> +               break;
> +       case GENI_SE_DMA:
> +               geni_se_select_dma_mode(se);
> +               break;
> +       }
> +}
> +EXPORT_SYMBOL(geni_se_select_mode);
> +
> +/**
> + * geni_se_setup_m_cmd() - Setup the primary sequencer
> + * @se:                Pointer to the concerned Serial Engine.
> + * @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 associated parameters.
> + */
> +void geni_se_setup_m_cmd(struct geni_se *se, u32 cmd, u32 params)
> +{
> +       u32 m_cmd;
> +
> +       m_cmd = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
> +       writel_relaxed(m_cmd, se->base + SE_GENI_M_CMD0);
> +}
> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
> +
> +/**
> + * geni_se_setup_s_cmd() - Setup the secondary sequencer
> + * @se:                Pointer to the concerned Serial Engine.
> + * @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 associated parameters.
> + */
> +void geni_se_setup_s_cmd(struct geni_se *se, u32 cmd, u32 params)
> +{
> +       u32 s_cmd;
> +
> +       s_cmd = readl_relaxed(se->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);
> +       writel_relaxed(s_cmd, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to cancel the currently configured command in the
> + * primary sequencer.
> + */
> +void geni_se_cancel_m_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(M_GENI_CMD_CANCEL, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to cancel the currently configured command in the
> + * secondary sequencer.
> + */
> +void geni_se_cancel_s_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(S_GENI_CMD_CANCEL, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to force abort the currently configured command in the
> + * primary sequencer.
> + */
> +void geni_se_abort_m_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(M_GENI_CMD_ABORT, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to force abort the currently configured command in the
> + * secondary sequencer.
> + */
> +void geni_se_abort_s_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(S_GENI_CMD_ABORT, se->base + SE_GENI_S_CMD_CTRL_REG);
> +}
> +EXPORT_SYMBOL(geni_se_abort_s_cmd);

Can these one-liners go into the header file and be marked static
inline? I would guess call-sites already have se->base in hand, so
registers might be reused more efficiently and it may result in a single
store instruction instead of a branch and load/store.

> +
> +/**
> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * 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.
> + */
> +u32 geni_se_get_tx_fifo_depth(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
> +
> +       return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
> +
> +/**
> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * 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
> + */
> +u32 geni_se_get_tx_fifo_width(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
> +
> +       return (val & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
> +
> +/**
> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * 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
> + */
> +u32 geni_se_get_rx_fifo_depth(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + SE_HW_PARAM_1);
> +
> +       return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);

These ones too, can probably just be static inline.

> +
> +/**
> + * DOC: Overview
> + *
> + * GENI FIFO packing is highly configurable. TX/RX packing/unpacking consist
> + * of up to 4 operations, each operation represented by 4 configuration vectors
> + * of 10 bits programmed in GENI_TX_PACKING_CFG0 and GENI_TX_PACKING_CFG1 for
> + * TX FIFO and in GENI_RX_PACKING_CFG0 and GENI_RX_PACKING_CFG1 for RX FIFO.
> + * Refer to below examples for detailed bit-field description.
> + *
> + * Example 1: word_size = 7, packing_mode = 4 x 8, msb_to_lsb = 1
> + *
> + *        +-----------+-------+-------+-------+-------+
> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
> + *        +-----------+-------+-------+-------+-------+
> + *        | start     | 0x6   | 0xe   | 0x16  | 0x1e  |
> + *        | direction | 1     | 1     | 1     | 1     |
> + *        | length    | 6     | 6     | 6     | 6     |
> + *        | stop      | 0     | 0     | 0     | 1     |
> + *        +-----------+-------+-------+-------+-------+
> + *
> + * Example 2: word_size = 15, packing_mode = 2 x 16, msb_to_lsb = 0
> + *
> + *        +-----------+-------+-------+-------+-------+
> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
> + *        +-----------+-------+-------+-------+-------+
> + *        | start     | 0x0   | 0x8   | 0x10  | 0x18  |
> + *        | direction | 0     | 0     | 0     | 0     |
> + *        | length    | 7     | 6     | 7     | 6     |
> + *        | stop      | 0     | 0     | 0     | 1     |
> + *        +-----------+-------+-------+-------+-------+
> + *
> + * Example 3: word_size = 23, packing_mode = 1 x 32, msb_to_lsb = 1
> + *
> + *        +-----------+-------+-------+-------+-------+
> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
> + *        +-----------+-------+-------+-------+-------+
> + *        | start     | 0x16  | 0xe   | 0x6   | 0x0   |
> + *        | direction | 1     | 1     | 1     | 1     |
> + *        | length    | 7     | 7     | 6     | 0     |
> + *        | stop      | 0     | 0     | 1     | 0     |
> + *        +-----------+-------+-------+-------+-------+
> + *
> + */
> +
> +#define NUM_PACKING_VECTORS 4
> +#define PACKING_START_SHIFT 5
> +#define PACKING_DIR_SHIFT 4
> +#define PACKING_LEN_SHIFT 1
> +#define PACKING_STOP_BIT BIT(0)
> +#define PACKING_VECTOR_SHIFT 10
> +/**
> + * geni_se_config_packing() - Packing configuration of the serial engine
> + * @se:                Pointer to the concerned Serial Engine
> + * @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.
> + * @tx_cfg:    Flag to configure the TX Packing.
> + * @rx_cfg:    Flag to configure the RX Packing.
> + *
> + * This function is used to configure the packing rules for the current
> + * transfer.
> + */
> +void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words,
> +                           bool msb_to_lsb, bool tx_cfg, bool rx_cfg)
> +{
> +       u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {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)) & ~(BITS_PER_BYTE - 1);

ALIGN(bpw, BITS_PER_BYTE)?

> +       int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE;
> +       int i;
> +
> +       if (iter <= 0 || iter > NUM_PACKING_VECTORS)
> +               return;
> +
> +       for (i = 0; i < iter; i++) {
> +               if (temp_bpw < BITS_PER_BYTE)
> +                       len = temp_bpw - 1;
> +               else
> +                       len = BITS_PER_BYTE - 1;

len = min(temp_bpw, BITS_PER_BYTE) - 1;

> +
> +               cfg[i] = idx << PACKING_START_SHIFT;
> +               cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT;
> +               cfg[i] |= len << PACKING_LEN_SHIFT;
> +
> +               if (temp_bpw <= BITS_PER_BYTE) {
> +                       idx = ((i + 1) * BITS_PER_BYTE) + idx_start;
> +                       temp_bpw = bpw;
> +               } else {
> +                       idx = idx + idx_delta;
> +                       temp_bpw = temp_bpw - BITS_PER_BYTE;
> +               }
> +       }
> +       cfg[iter - 1] |= PACKING_STOP_BIT;
> +       cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT);
> +       cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT);
> +
> +       if (tx_cfg) {
> +               writel_relaxed(cfg0, se->base + SE_GENI_TX_PACKING_CFG0);
> +               writel_relaxed(cfg1, se->base + SE_GENI_TX_PACKING_CFG1);
> +       }
> +       if (rx_cfg) {
> +               writel_relaxed(cfg0, se->base + SE_GENI_RX_PACKING_CFG0);
> +               writel_relaxed(cfg1, se->base + SE_GENI_RX_PACKING_CFG1);
> +       }
> +
> +       /*
> +        * Number of protocol words in each FIFO entry
> +        * 0 - 4x8, four words in each entry, max word size of 8 bits
> +        * 1 - 2x16, two words in each entry, max word size of 16 bits
> +        * 2 - 1x32, one word in each entry, max word size of 32 bits
> +        * 3 - undefined
> +        */
> +       if (pack_words || bpw == 32)
> +               writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN);
> +}
> +EXPORT_SYMBOL(geni_se_config_packing);
> +
> +static void geni_se_clks_off(struct geni_se *se)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       clk_disable_unprepare(se->clk);
> +       clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
> +                                               wrapper->ahb_clks);
> +}
> +
> +/**
> + * geni_se_resources_off() - Turn off resources associated with the serial
> + *                           engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_off(struct geni_se *se)
> +{
> +       int ret;
> +
> +       ret = pinctrl_pm_select_sleep_state(se->dev);
> +       if (ret)
> +               return ret;
> +
> +       geni_se_clks_off(se);
> +       return 0;
> +}
> +EXPORT_SYMBOL(geni_se_resources_off);
> +
> +static int geni_se_clks_on(struct geni_se *se)
> +{
> +       int ret;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       ret = clk_bulk_prepare_enable(ARRAY_SIZE(wrapper->ahb_clks),
> +                                               wrapper->ahb_clks);
> +       if (ret)
> +               return ret;
> +
> +       ret = clk_prepare_enable(se->clk);
> +       if (ret)
> +               clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
> +                                                       wrapper->ahb_clks);
> +       return ret;
> +}
> +
> +/**
> + * geni_se_resources_on() - Turn on resources associated with the serial
> + *                          engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_on(struct geni_se *se)
> +{
> +       int ret = 0;

Don't assign variables and then reassign them on the next line.

> +
> +       ret = geni_se_clks_on(se);
> +       if (ret)
> +               return ret;
> +
> +       ret = pinctrl_pm_select_default_state(se->dev);
> +       if (ret)
> +               geni_se_clks_off(se);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL(geni_se_resources_on);

IS there a reason why we can't use runtime PM or normal linux PM
infrastructure to power on the wrapper and keep it powered while the
protocol driver is active?

> +
> +/**
> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> + * @se:                Pointer to the concerned Serial Engine.
> + * @tbl:       Table in which the output is returned.
> + *
> + * This function is called by the protocol drivers to determine the different
> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       unsigned long freq = 0;
> +       int i;
> +       int ret = 0;
> +
> +       mutex_lock(&wrapper->lock);
> +       if (wrapper->clk_perf_tbl) {
> +               *tbl = wrapper->clk_perf_tbl;
> +               ret = wrapper->num_clk_levels;
> +               goto out_unlock;
> +       }
> +
> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
> +                                       sizeof(*wrapper->clk_perf_tbl),
> +                                       GFP_KERNEL);
> +       if (!wrapper->clk_perf_tbl) {
> +               ret = -ENOMEM;
> +               goto out_unlock;
> +       }
> +
> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> +               freq = clk_round_rate(se->clk, freq + 1);
> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
> +                       break;
> +               wrapper->clk_perf_tbl[i] = freq;
> +       }
> +       wrapper->num_clk_levels = i;
> +       *tbl = wrapper->clk_perf_tbl;
> +       ret = wrapper->num_clk_levels;
> +out_unlock:
> +       mutex_unlock(&wrapper->lock);

Is this lock actually protecting anything? I mean to say, is any more
than one geni protocol driver calling this function at a time? Or is
the same geni protocol driver calling this from multiple threads at the
same time? The lock looks almost useless.

> +       return ret;
> +}
> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
> +
> +/**
> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
> + * @se:                Pointer to the concerned Serial Engine.
> + * @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 exact multiple of the requested frequency, as provided by the Serial
> + * Engine clock in order to meet the performance requirements. If there is
> + * no matching or exact multiple of the requested frequency found, then it
> + * selects the closest floor frequency, if exact flag is not set.
> + *
> + * Return: 0 on success, standard Linux error codes on failure.
> + */
> +int geni_se_clk_freq_match(struct geni_se *se, 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(se, &tbl);
> +       if (num_clk_levels < 0)
> +               return num_clk_levels;
> +
> +       if (num_clk_levels == 0)
> +               return -EFAULT;

I believe this would mean userspace thought the syscall faulted.
Perhaps -EINVAL instead?

> +
> +       *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)
> +               return -ENOKEY;

Interesting error code. Doubtful this is correct because it seems to be
related to crypto keys.

> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(geni_se_clk_freq_match);
> +
> +#define GENI_SE_DMA_DONE_EN BIT(0)
> +#define GENI_SE_DMA_EOT_EN BIT(1)
> +#define GENI_SE_DMA_AHB_ERR_EN BIT(2)
> +#define GENI_SE_DMA_EOT_BUF BIT(0)
> +/**
> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @buf:               Pointer to the TX buffer.
> + * @len:               Length of the TX buffer.
> + *
> + * This function is used to prepare the buffers for DMA TX.
> + *
> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
> + */
> +dma_addr_t geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len)
> +{
> +       dma_addr_t iova;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       u32 val;
> +
> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE);
> +       if (dma_mapping_error(wrapper->dev, iova))
> +               return (dma_addr_t)NULL;
> +
> +       val = GENI_SE_DMA_DONE_EN;
> +       val |= GENI_SE_DMA_EOT_EN;
> +       val |= GENI_SE_DMA_AHB_ERR_EN;
> +       writel_relaxed(val, se->base + SE_DMA_TX_IRQ_EN_SET);
> +       writel_relaxed((u32)iova, se->base + SE_DMA_TX_PTR_L);

lower_32_bits()

> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_TX_PTR_H);

upper_32_bits()

> +       writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR);
> +       writel_relaxed((u32)len, se->base + SE_DMA_TX_LEN);

Useless cast.

> +       return iova;
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
> +
> +/**
> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @buf:               Pointer to the RX buffer.
> + * @len:               Length of the RX buffer.
> + *
> + * This function is used to prepare the buffers for DMA RX.
> + *
> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
> + */
> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
> +{
> +       dma_addr_t iova;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       u32 val;
> +
> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
> +       if (dma_mapping_error(wrapper->dev, iova))
> +               return (dma_addr_t)NULL;

Can't return a dma_mapping_error address to the caller and have them
figure it out?

> +
> +       val = GENI_SE_DMA_DONE_EN;
> +       val |= GENI_SE_DMA_EOT_EN;
> +       val |= GENI_SE_DMA_AHB_ERR_EN;
> +       writel_relaxed(val, se->base + SE_DMA_RX_IRQ_EN_SET);
> +       writel_relaxed((u32)iova, se->base + SE_DMA_RX_PTR_L);
> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_RX_PTR_H);

upper/lower macros again.

> +       /* RX does not have EOT buffer type bit. So just reset RX_ATTR */
> +       writel_relaxed(0, se->base + SE_DMA_RX_ATTR);
> +       writel_relaxed((u32)len, se->base + SE_DMA_RX_LEN);

Drop cast?

> +       return iova;
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
> +
> +/**
> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @iova:              DMA address of the TX buffer.
> + * @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 geni_se *se, dma_addr_t iova, size_t len)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       if (iova)
> +               dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
> +
> +/**
> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @iova:              DMA address of the RX buffer.
> + * @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 geni_se *se, dma_addr_t iova, size_t len)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       if (iova)
> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);

Instead of having the functions exported, could we set the dma_ops on
all child devices of the wrapper that this driver populates and then
implement the DMA ops for those devices here? I assume that there's
never another DMA master between the wrapper and the serial engine, so I
think it would work.

> +
> +static int geni_se_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +       struct geni_wrapper *wrapper;
> +       int ret;
> +
> +       wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
> +       if (!wrapper)
> +               return -ENOMEM;
> +
> +       wrapper->dev = dev;
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       wrapper->base = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(wrapper->base)) {
> +               dev_err(dev, "%s: Error mapping the resource\n", __func__);

Drop error message, devm_ioremap_resource() already does it.

> +               return -EFAULT;

return PTR_ERR(wrapper->base);

> +       }
> +
> +       wrapper->ahb_clks[0].id = m_ahb_clk;
> +       wrapper->ahb_clks[1].id = s_ahb_clk;
> +       ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
> +       if (ret) {
> +               dev_err(dev, "Err getting AHB clks %d\n", ret);
> +               return ret;
> +       }
> +
> +       mutex_init(&wrapper->lock);
> +       dev_set_drvdata(dev, wrapper);
> +       dev_dbg(dev, "GENI SE Driver probed\n");
> +       return devm_of_platform_populate(dev);
> +}
> +
> +static int geni_se_remove(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct geni_wrapper *wrapper = dev_get_drvdata(dev);
> +
> +       kfree(wrapper->clk_perf_tbl);

Why not devm_kzalloc() this?

> +       return 0;
> +}
> +
> +static const struct of_device_id geni_se_dt_match[] = {
> +       { .compatible = "qcom,geni-se-qup", },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, geni_se_dt_match);
> +
> +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,
> +};
> +module_platform_driver(geni_se_driver);
> +
> +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..4996de7
> --- /dev/null
> +++ b/include/linux/qcom-geni-se.h
> @@ -0,0 +1,247 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef _LINUX_QCOM_GENI_SE
> +#define _LINUX_QCOM_GENI_SE
> +#include <linux/clk.h>

Please forward declare struct clk and drop this include here.

> +#include <linux/dma-direction.h>

Drop?

> +
> +/* Transfer mode supported by GENI Serial Engines */
> +enum geni_se_xfer_mode {
> +       GENI_SE_INVALID,
> +       GENI_SE_FIFO,
> +       GENI_SE_DMA,
> +};
> +
> +/* Protocols supported by GENI Serial Engines */
> +enum geni_se_protocol_types {
> +       GENI_SE_NONE,
> +       GENI_SE_SPI,
> +       GENI_SE_UART,
> +       GENI_SE_I2C,
> +       GENI_SE_I3C,
> +};
> +
> +/**
> + * struct geni_se - GENI Serial Engine
> + * @base:              Base Address of the Serial Engine's register block.
> + * @dev:               Pointer to the Serial Engine device.
> + * @wrapper:           Pointer to the parent QUP Wrapper core.
> + * @clk:               Handle to the core serial engine clock.
> + */
> +struct geni_se {
> +       void __iomem *base;
> +       struct device *dev;
> +       void *wrapper;

Can this get the geni_wrapper type? It could be opaque if you like.

> +       struct clk *clk;
> +};
> +

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-02 20:41     ` Stephen Boyd
  0 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-02 20:41 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian, andy.gross, corbet, david.brown,
	gregkh, mark.rutland, robh+dt, wsa
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, evgreen, acourbot,
	Sagar Dharia, Girish Mahadevan

Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
> 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        |   9 +
>  drivers/soc/qcom/Makefile       |   1 +
>  drivers/soc/qcom/qcom-geni-se.c | 971 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/qcom-geni-se.h    | 247 ++++++++++
>  4 files changed, 1228 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 e050eb8..cc460d0 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -3,6 +3,15 @@
>  #
>  menu "Qualcomm SoC drivers"
>  
> +config QCOM_GENI_SE
> +       tristate "QCOM GENI Serial Engine Driver"
> +       depends on ARCH_QCOM

Add || COMPILE_TEST?

> +       help
> +         This module is used to manage Generic Interface (GENI) firmware based

s/module/driver?

> +         Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
> +         module is also used to manage the common aspects of multiple Serial
s/module/driver?

> +         Engines present in the QUP.
> +
>  config QCOM_GLINK_SSR
>         tristate "Qualcomm Glink SSR driver"
>         depends on RPMSG
> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> new file mode 100644
> index 0000000..61335b8
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> @@ -0,0 +1,971 @@
> +// 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/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/qcom-geni-se.h>

#include <linux/platform_device.h>

> +
> +/**
> + * DOC: Overview
> + *
> + * 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.
> + */
> +
> +/**
> + * DOC: 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

The code talks about primary and secondary sequencers, but this hardware
description doesn't talk about it. Can you add some more information
here about that aspect too?

> + */
> +
> +/**
> + * DOC: Software description
> + *
> + * GENI SE Wrapper driver is structured into 2 parts:
> + *
> + * geni_wrapper 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 represents Serial Engine. This part of the driver manages Serial
> + * Engine information such as clocks, containing QUP Wrapper etc. This part

Insert a comma here                                           ^

> + * of driver also supports operations(eg. initialize the concerned Serial

Space                                   ^

> + * Engine, select between FIFO and DMA mode of operation etc.) that are
> + * common to all the Serial Engines and are independent of Serial Interfaces.

Why are Serial Interfaces and Serial Engine always capitalized?

> + */
> +
> +#define MAX_CLK_PERF_LEVEL 32
> +#define NUM_AHB_CLKS 2
> +static const char m_ahb_clk[] = "m-ahb";
> +static const char s_ahb_clk[] = "s-ahb";

These are used in one place. Inline them?

> +
> +/**
> + * @struct geni_wrapper - 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.
> + * @ahb_clks:          Handle to the primary & secondary AHB clocks.
> + * @lock:              Lock to protect the device elements.

What does 'device elements' mean?

> + * @num_clk_levels:    Number of valid clock levels in clk_perf_tbl.
> + * @clk_perf_tbl:      Table of clock frequency input to Serial Engine clock.

Kernel-doc normally doesn't have a full-stop on member descriptions.

> + */
> +struct geni_wrapper {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
> +       struct mutex lock;
> +       unsigned int num_clk_levels;
> +       unsigned long *clk_perf_tbl;
> +};
> +
> +/* Offset of QUP Hardware Version Register */

Useless comment?

> +#define QUP_HW_VER_REG                 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)
> +
> +/* Common SE registers */
> +#define GENI_INIT_CFG_REVISION         0x0
> +#define GENI_S_INIT_CFG_REVISION       0x4
> +#define GENI_OUTPUT_CTRL               0x24
> +#define GENI_CGC_CTRL                  0x28
> +#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_BYTE_GRAN              0x254
> +#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_GP_LENGTH            0x910
> +#define SE_GENI_S_GP_LENGTH            0x914
> +#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_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_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_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_MAX_BURST            0xd5c
> +#define SE_DMA_RX_FLUSH                        0xd60
> +#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_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)
> +
> +/* FW_REVISION_RO fields */
> +#define FW_REV_PROTOCOL_MSK            GENMASK(15, 8)
> +#define FW_REV_PROTOCOL_SHFT           8
> +
> +/* 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_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
> +
> +/**
> + * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
> + * @se:                        Pointer to the corresponding Serial Engine.
> + * @major:             Buffer for Major Version field.
> + * @minor:             Buffer for Minor Version field.
> + * @step:              Buffer for Step Version field.
> + */
> +void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
> +                               unsigned int *minor, unsigned int *step)
> +{
> +       unsigned int version;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       version = readl_relaxed(wrapper->base + QUP_HW_VER_REG);
> +       *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;
> +}
> +EXPORT_SYMBOL(geni_se_get_qup_hw_version);

Is this used?

> +
> +/**
> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * Return: Protocol value as configured in the serial engine.
> + */
> +u32 geni_se_read_proto(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
> +
> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_read_proto);

Is this API really needed outside of this file? It would seem like the
drivers that implement the protocol, which are child devices, would only
use this API to confirm that the protocol chosen is for their particular
protocol.


> +
> +static void geni_se_io_set_mode(void __iomem *base)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(base + SE_IRQ_EN);
> +       val |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
> +       val |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);

Drop useless parenthesis please.

> +       writel_relaxed(val, base + SE_IRQ_EN);
> +
> +       val = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
> +       val &= ~GENI_DMA_MODE_EN;
> +       writel_relaxed(val, base + SE_GENI_DMA_MODE_EN);
> +
> +       writel_relaxed(0, base + SE_GSI_EVENT_EN);
> +}
> +
> +static void geni_se_io_init(void __iomem *base)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(base + GENI_CGC_CTRL);
> +       val |= DEFAULT_CGC_EN;
> +       writel_relaxed(val, base + GENI_CGC_CTRL);
> +
> +       val = readl_relaxed(base + SE_DMA_GENERAL_CFG);
> +       val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON;
> +       val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON;
> +       writel_relaxed(val, base + SE_DMA_GENERAL_CFG);
> +
> +       writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL);
> +       writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG);
> +}
> +
> +/**
> + * geni_se_init() - Initialize the GENI Serial Engine
> + * @se:                Pointer to the concerned Serial Engine.
> + * @rx_wm:     Receive watermark, in units of FIFO words.
> + * @rx_rfr_wm: Ready-for-receive watermark, in units of 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.

It never returns an error. Change to void?

> + */
> +int geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr)
> +{
> +       u32 val;
> +
> +       geni_se_io_init(se->base);
> +       geni_se_io_set_mode(se->base);
> +
> +       writel_relaxed(rx_wm, se->base + SE_GENI_RX_WATERMARK_REG);
> +       writel_relaxed(rx_rfr, se->base + SE_GENI_RX_RFR_WATERMARK_REG);
> +
> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
> +       val |= M_COMMON_GENI_M_IRQ_EN;
> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
> +       val |= S_COMMON_GENI_S_IRQ_EN;
> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
> +       return 0;
> +}
> +EXPORT_SYMBOL(geni_se_init);
> +
> +static void geni_se_select_fifo_mode(struct geni_se *se)
> +{
> +       u32 proto = geni_se_read_proto(se);
> +       u32 val;
> +
> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
> +       if (proto != GENI_SE_UART) {
> +               val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN;
> +               val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
> +       }
> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
> +       if (proto != GENI_SE_UART)
> +               val |= S_CMD_DONE_EN;
> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
> +       val &= ~GENI_DMA_MODE_EN;
> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
> +}
> +
> +static void geni_se_select_dma_mode(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
> +
> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
> +       val |= GENI_DMA_MODE_EN;
> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
> +}
> +
> +/**
> + * geni_se_select_mode() - Select the serial engine transfer mode
> + * @se:                Pointer to the concerned Serial Engine.
> + * @mode:      Transfer mode to be selected.
> + */
> +void geni_se_select_mode(struct geni_se *se, int mode)

enum mode?

> +{
> +       WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA);
> +
> +       switch (mode) {
> +       case GENI_SE_FIFO:
> +               geni_se_select_fifo_mode(se);
> +               break;
> +       case GENI_SE_DMA:
> +               geni_se_select_dma_mode(se);
> +               break;
> +       }
> +}
> +EXPORT_SYMBOL(geni_se_select_mode);
> +
> +/**
> + * geni_se_setup_m_cmd() - Setup the primary sequencer
> + * @se:                Pointer to the concerned Serial Engine.
> + * @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 associated parameters.
> + */
> +void geni_se_setup_m_cmd(struct geni_se *se, u32 cmd, u32 params)
> +{
> +       u32 m_cmd;
> +
> +       m_cmd = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
> +       writel_relaxed(m_cmd, se->base + SE_GENI_M_CMD0);
> +}
> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
> +
> +/**
> + * geni_se_setup_s_cmd() - Setup the secondary sequencer
> + * @se:                Pointer to the concerned Serial Engine.
> + * @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 associated parameters.
> + */
> +void geni_se_setup_s_cmd(struct geni_se *se, u32 cmd, u32 params)
> +{
> +       u32 s_cmd;
> +
> +       s_cmd = readl_relaxed(se->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);
> +       writel_relaxed(s_cmd, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to cancel the currently configured command in the
> + * primary sequencer.
> + */
> +void geni_se_cancel_m_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(M_GENI_CMD_CANCEL, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to cancel the currently configured command in the
> + * secondary sequencer.
> + */
> +void geni_se_cancel_s_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(S_GENI_CMD_CANCEL, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to force abort the currently configured command in the
> + * primary sequencer.
> + */
> +void geni_se_abort_m_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(M_GENI_CMD_ABORT, se->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
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * This function is used to force abort the currently configured command in the
> + * secondary sequencer.
> + */
> +void geni_se_abort_s_cmd(struct geni_se *se)
> +{
> +       writel_relaxed(S_GENI_CMD_ABORT, se->base + SE_GENI_S_CMD_CTRL_REG);
> +}
> +EXPORT_SYMBOL(geni_se_abort_s_cmd);

Can these one-liners go into the header file and be marked static
inline? I would guess call-sites already have se->base in hand, so
registers might be reused more efficiently and it may result in a single
store instruction instead of a branch and load/store.

> +
> +/**
> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * 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.
> + */
> +u32 geni_se_get_tx_fifo_depth(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
> +
> +       return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
> +
> +/**
> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * 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
> + */
> +u32 geni_se_get_tx_fifo_width(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
> +
> +       return (val & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
> +
> +/**
> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * 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
> + */
> +u32 geni_se_get_rx_fifo_depth(struct geni_se *se)
> +{
> +       u32 val;
> +
> +       val = readl_relaxed(se->base + SE_HW_PARAM_1);
> +
> +       return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT;
> +}
> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);

These ones too, can probably just be static inline.

> +
> +/**
> + * DOC: Overview
> + *
> + * GENI FIFO packing is highly configurable. TX/RX packing/unpacking consist
> + * of up to 4 operations, each operation represented by 4 configuration vectors
> + * of 10 bits programmed in GENI_TX_PACKING_CFG0 and GENI_TX_PACKING_CFG1 for
> + * TX FIFO and in GENI_RX_PACKING_CFG0 and GENI_RX_PACKING_CFG1 for RX FIFO.
> + * Refer to below examples for detailed bit-field description.
> + *
> + * Example 1: word_size = 7, packing_mode = 4 x 8, msb_to_lsb = 1
> + *
> + *        +-----------+-------+-------+-------+-------+
> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
> + *        +-----------+-------+-------+-------+-------+
> + *        | start     | 0x6   | 0xe   | 0x16  | 0x1e  |
> + *        | direction | 1     | 1     | 1     | 1     |
> + *        | length    | 6     | 6     | 6     | 6     |
> + *        | stop      | 0     | 0     | 0     | 1     |
> + *        +-----------+-------+-------+-------+-------+
> + *
> + * Example 2: word_size = 15, packing_mode = 2 x 16, msb_to_lsb = 0
> + *
> + *        +-----------+-------+-------+-------+-------+
> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
> + *        +-----------+-------+-------+-------+-------+
> + *        | start     | 0x0   | 0x8   | 0x10  | 0x18  |
> + *        | direction | 0     | 0     | 0     | 0     |
> + *        | length    | 7     | 6     | 7     | 6     |
> + *        | stop      | 0     | 0     | 0     | 1     |
> + *        +-----------+-------+-------+-------+-------+
> + *
> + * Example 3: word_size = 23, packing_mode = 1 x 32, msb_to_lsb = 1
> + *
> + *        +-----------+-------+-------+-------+-------+
> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
> + *        +-----------+-------+-------+-------+-------+
> + *        | start     | 0x16  | 0xe   | 0x6   | 0x0   |
> + *        | direction | 1     | 1     | 1     | 1     |
> + *        | length    | 7     | 7     | 6     | 0     |
> + *        | stop      | 0     | 0     | 1     | 0     |
> + *        +-----------+-------+-------+-------+-------+
> + *
> + */
> +
> +#define NUM_PACKING_VECTORS 4
> +#define PACKING_START_SHIFT 5
> +#define PACKING_DIR_SHIFT 4
> +#define PACKING_LEN_SHIFT 1
> +#define PACKING_STOP_BIT BIT(0)
> +#define PACKING_VECTOR_SHIFT 10
> +/**
> + * geni_se_config_packing() - Packing configuration of the serial engine
> + * @se:                Pointer to the concerned Serial Engine
> + * @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.
> + * @tx_cfg:    Flag to configure the TX Packing.
> + * @rx_cfg:    Flag to configure the RX Packing.
> + *
> + * This function is used to configure the packing rules for the current
> + * transfer.
> + */
> +void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words,
> +                           bool msb_to_lsb, bool tx_cfg, bool rx_cfg)
> +{
> +       u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {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)) & ~(BITS_PER_BYTE - 1);

ALIGN(bpw, BITS_PER_BYTE)?

> +       int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE;
> +       int i;
> +
> +       if (iter <= 0 || iter > NUM_PACKING_VECTORS)
> +               return;
> +
> +       for (i = 0; i < iter; i++) {
> +               if (temp_bpw < BITS_PER_BYTE)
> +                       len = temp_bpw - 1;
> +               else
> +                       len = BITS_PER_BYTE - 1;

len = min(temp_bpw, BITS_PER_BYTE) - 1;

> +
> +               cfg[i] = idx << PACKING_START_SHIFT;
> +               cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT;
> +               cfg[i] |= len << PACKING_LEN_SHIFT;
> +
> +               if (temp_bpw <= BITS_PER_BYTE) {
> +                       idx = ((i + 1) * BITS_PER_BYTE) + idx_start;
> +                       temp_bpw = bpw;
> +               } else {
> +                       idx = idx + idx_delta;
> +                       temp_bpw = temp_bpw - BITS_PER_BYTE;
> +               }
> +       }
> +       cfg[iter - 1] |= PACKING_STOP_BIT;
> +       cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT);
> +       cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT);
> +
> +       if (tx_cfg) {
> +               writel_relaxed(cfg0, se->base + SE_GENI_TX_PACKING_CFG0);
> +               writel_relaxed(cfg1, se->base + SE_GENI_TX_PACKING_CFG1);
> +       }
> +       if (rx_cfg) {
> +               writel_relaxed(cfg0, se->base + SE_GENI_RX_PACKING_CFG0);
> +               writel_relaxed(cfg1, se->base + SE_GENI_RX_PACKING_CFG1);
> +       }
> +
> +       /*
> +        * Number of protocol words in each FIFO entry
> +        * 0 - 4x8, four words in each entry, max word size of 8 bits
> +        * 1 - 2x16, two words in each entry, max word size of 16 bits
> +        * 2 - 1x32, one word in each entry, max word size of 32 bits
> +        * 3 - undefined
> +        */
> +       if (pack_words || bpw == 32)
> +               writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN);
> +}
> +EXPORT_SYMBOL(geni_se_config_packing);
> +
> +static void geni_se_clks_off(struct geni_se *se)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       clk_disable_unprepare(se->clk);
> +       clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
> +                                               wrapper->ahb_clks);
> +}
> +
> +/**
> + * geni_se_resources_off() - Turn off resources associated with the serial
> + *                           engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_off(struct geni_se *se)
> +{
> +       int ret;
> +
> +       ret = pinctrl_pm_select_sleep_state(se->dev);
> +       if (ret)
> +               return ret;
> +
> +       geni_se_clks_off(se);
> +       return 0;
> +}
> +EXPORT_SYMBOL(geni_se_resources_off);
> +
> +static int geni_se_clks_on(struct geni_se *se)
> +{
> +       int ret;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       ret = clk_bulk_prepare_enable(ARRAY_SIZE(wrapper->ahb_clks),
> +                                               wrapper->ahb_clks);
> +       if (ret)
> +               return ret;
> +
> +       ret = clk_prepare_enable(se->clk);
> +       if (ret)
> +               clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
> +                                                       wrapper->ahb_clks);
> +       return ret;
> +}
> +
> +/**
> + * geni_se_resources_on() - Turn on resources associated with the serial
> + *                          engine
> + * @se:        Pointer to the concerned Serial Engine.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_on(struct geni_se *se)
> +{
> +       int ret = 0;

Don't assign variables and then reassign them on the next line.

> +
> +       ret = geni_se_clks_on(se);
> +       if (ret)
> +               return ret;
> +
> +       ret = pinctrl_pm_select_default_state(se->dev);
> +       if (ret)
> +               geni_se_clks_off(se);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL(geni_se_resources_on);

IS there a reason why we can't use runtime PM or normal linux PM
infrastructure to power on the wrapper and keep it powered while the
protocol driver is active?

> +
> +/**
> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> + * @se:                Pointer to the concerned Serial Engine.
> + * @tbl:       Table in which the output is returned.
> + *
> + * This function is called by the protocol drivers to determine the different
> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       unsigned long freq = 0;
> +       int i;
> +       int ret = 0;
> +
> +       mutex_lock(&wrapper->lock);
> +       if (wrapper->clk_perf_tbl) {
> +               *tbl = wrapper->clk_perf_tbl;
> +               ret = wrapper->num_clk_levels;
> +               goto out_unlock;
> +       }
> +
> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
> +                                       sizeof(*wrapper->clk_perf_tbl),
> +                                       GFP_KERNEL);
> +       if (!wrapper->clk_perf_tbl) {
> +               ret = -ENOMEM;
> +               goto out_unlock;
> +       }
> +
> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> +               freq = clk_round_rate(se->clk, freq + 1);
> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
> +                       break;
> +               wrapper->clk_perf_tbl[i] = freq;
> +       }
> +       wrapper->num_clk_levels = i;
> +       *tbl = wrapper->clk_perf_tbl;
> +       ret = wrapper->num_clk_levels;
> +out_unlock:
> +       mutex_unlock(&wrapper->lock);

Is this lock actually protecting anything? I mean to say, is any more
than one geni protocol driver calling this function at a time? Or is
the same geni protocol driver calling this from multiple threads at the
same time? The lock looks almost useless.

> +       return ret;
> +}
> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
> +
> +/**
> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
> + * @se:                Pointer to the concerned Serial Engine.
> + * @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 exact multiple of the requested frequency, as provided by the Serial
> + * Engine clock in order to meet the performance requirements. If there is
> + * no matching or exact multiple of the requested frequency found, then it
> + * selects the closest floor frequency, if exact flag is not set.
> + *
> + * Return: 0 on success, standard Linux error codes on failure.
> + */
> +int geni_se_clk_freq_match(struct geni_se *se, 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(se, &tbl);
> +       if (num_clk_levels < 0)
> +               return num_clk_levels;
> +
> +       if (num_clk_levels == 0)
> +               return -EFAULT;

I believe this would mean userspace thought the syscall faulted.
Perhaps -EINVAL instead?

> +
> +       *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)
> +               return -ENOKEY;

Interesting error code. Doubtful this is correct because it seems to be
related to crypto keys.

> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(geni_se_clk_freq_match);
> +
> +#define GENI_SE_DMA_DONE_EN BIT(0)
> +#define GENI_SE_DMA_EOT_EN BIT(1)
> +#define GENI_SE_DMA_AHB_ERR_EN BIT(2)
> +#define GENI_SE_DMA_EOT_BUF BIT(0)
> +/**
> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @buf:               Pointer to the TX buffer.
> + * @len:               Length of the TX buffer.
> + *
> + * This function is used to prepare the buffers for DMA TX.
> + *
> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
> + */
> +dma_addr_t geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len)
> +{
> +       dma_addr_t iova;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       u32 val;
> +
> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE);
> +       if (dma_mapping_error(wrapper->dev, iova))
> +               return (dma_addr_t)NULL;
> +
> +       val = GENI_SE_DMA_DONE_EN;
> +       val |= GENI_SE_DMA_EOT_EN;
> +       val |= GENI_SE_DMA_AHB_ERR_EN;
> +       writel_relaxed(val, se->base + SE_DMA_TX_IRQ_EN_SET);
> +       writel_relaxed((u32)iova, se->base + SE_DMA_TX_PTR_L);

lower_32_bits()

> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_TX_PTR_H);

upper_32_bits()

> +       writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR);
> +       writel_relaxed((u32)len, se->base + SE_DMA_TX_LEN);

Useless cast.

> +       return iova;
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
> +
> +/**
> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @buf:               Pointer to the RX buffer.
> + * @len:               Length of the RX buffer.
> + *
> + * This function is used to prepare the buffers for DMA RX.
> + *
> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
> + */
> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
> +{
> +       dma_addr_t iova;
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       u32 val;
> +
> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
> +       if (dma_mapping_error(wrapper->dev, iova))
> +               return (dma_addr_t)NULL;

Can't return a dma_mapping_error address to the caller and have them
figure it out?

> +
> +       val = GENI_SE_DMA_DONE_EN;
> +       val |= GENI_SE_DMA_EOT_EN;
> +       val |= GENI_SE_DMA_AHB_ERR_EN;
> +       writel_relaxed(val, se->base + SE_DMA_RX_IRQ_EN_SET);
> +       writel_relaxed((u32)iova, se->base + SE_DMA_RX_PTR_L);
> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_RX_PTR_H);

upper/lower macros again.

> +       /* RX does not have EOT buffer type bit. So just reset RX_ATTR */
> +       writel_relaxed(0, se->base + SE_DMA_RX_ATTR);
> +       writel_relaxed((u32)len, se->base + SE_DMA_RX_LEN);

Drop cast?

> +       return iova;
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
> +
> +/**
> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @iova:              DMA address of the TX buffer.
> + * @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 geni_se *se, dma_addr_t iova, size_t len)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       if (iova)
> +               dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
> +
> +/**
> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
> + * @se:                        Pointer to the concerned Serial Engine.
> + * @iova:              DMA address of the RX buffer.
> + * @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 geni_se *se, dma_addr_t iova, size_t len)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +
> +       if (iova)
> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);

Instead of having the functions exported, could we set the dma_ops on
all child devices of the wrapper that this driver populates and then
implement the DMA ops for those devices here? I assume that there's
never another DMA master between the wrapper and the serial engine, so I
think it would work.

> +
> +static int geni_se_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +       struct geni_wrapper *wrapper;
> +       int ret;
> +
> +       wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
> +       if (!wrapper)
> +               return -ENOMEM;
> +
> +       wrapper->dev = dev;
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       wrapper->base = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(wrapper->base)) {
> +               dev_err(dev, "%s: Error mapping the resource\n", __func__);

Drop error message, devm_ioremap_resource() already does it.

> +               return -EFAULT;

return PTR_ERR(wrapper->base);

> +       }
> +
> +       wrapper->ahb_clks[0].id = m_ahb_clk;
> +       wrapper->ahb_clks[1].id = s_ahb_clk;
> +       ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
> +       if (ret) {
> +               dev_err(dev, "Err getting AHB clks %d\n", ret);
> +               return ret;
> +       }
> +
> +       mutex_init(&wrapper->lock);
> +       dev_set_drvdata(dev, wrapper);
> +       dev_dbg(dev, "GENI SE Driver probed\n");
> +       return devm_of_platform_populate(dev);
> +}
> +
> +static int geni_se_remove(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct geni_wrapper *wrapper = dev_get_drvdata(dev);
> +
> +       kfree(wrapper->clk_perf_tbl);

Why not devm_kzalloc() this?

> +       return 0;
> +}
> +
> +static const struct of_device_id geni_se_dt_match[] = {
> +       { .compatible = "qcom,geni-se-qup", },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, geni_se_dt_match);
> +
> +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,
> +};
> +module_platform_driver(geni_se_driver);
> +
> +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..4996de7
> --- /dev/null
> +++ b/include/linux/qcom-geni-se.h
> @@ -0,0 +1,247 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef _LINUX_QCOM_GENI_SE
> +#define _LINUX_QCOM_GENI_SE
> +#include <linux/clk.h>

Please forward declare struct clk and drop this include here.

> +#include <linux/dma-direction.h>

Drop?

> +
> +/* Transfer mode supported by GENI Serial Engines */
> +enum geni_se_xfer_mode {
> +       GENI_SE_INVALID,
> +       GENI_SE_FIFO,
> +       GENI_SE_DMA,
> +};
> +
> +/* Protocols supported by GENI Serial Engines */
> +enum geni_se_protocol_types {
> +       GENI_SE_NONE,
> +       GENI_SE_SPI,
> +       GENI_SE_UART,
> +       GENI_SE_I2C,
> +       GENI_SE_I3C,
> +};
> +
> +/**
> + * struct geni_se - GENI Serial Engine
> + * @base:              Base Address of the Serial Engine's register block.
> + * @dev:               Pointer to the Serial Engine device.
> + * @wrapper:           Pointer to the parent QUP Wrapper core.
> + * @clk:               Handle to the core serial engine clock.
> + */
> +struct geni_se {
> +       void __iomem *base;
> +       struct device *dev;
> +       void *wrapper;

Can this get the geni_wrapper type? It could be opaque if you like.

> +       struct clk *clk;
> +};
> +
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-02 20:41     ` Stephen Boyd
@ 2018-03-02 20:58       ` Evan Green
  -1 siblings, 0 replies; 65+ messages in thread
From: Evan Green @ 2018-03-02 20:58 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Karthikeyan Ramasubramanian, Andy Gross, Jonathan Corbet,
	David Brown, gregkh, mark.rutland, robh+dt, wsa, linux-doc,
	linux-arm-msm, devicetree, linux-i2c, linux-serial, jslaby,
	acourbot, Sagar Dharia, Girish Mahadevan

Hi Karthik,

On Tue, Feb 27, 2018 at 5:38 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        |   9 +
>  drivers/soc/qcom/Makefile       |   1 +
>  drivers/soc/qcom/qcom-geni-se.c | 971 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/qcom-geni-se.h    | 247 ++++++++++
>  4 files changed, 1228 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/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> new file mode 100644
> index 0000000..61335b8
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> +
> +/**
> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> + * @se:                Pointer to the concerned Serial Engine.
> + * @tbl:       Table in which the output is returned.
> + *
> + * This function is called by the protocol drivers to determine the different
> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       unsigned long freq = 0;
> +       int i;
> +       int ret = 0;
> +
> +       mutex_lock(&wrapper->lock);
> +       if (wrapper->clk_perf_tbl) {
> +               *tbl = wrapper->clk_perf_tbl;
> +               ret = wrapper->num_clk_levels;
> +               goto out_unlock;
> +       }
> +
> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
> +                                       sizeof(*wrapper->clk_perf_tbl),
> +                                       GFP_KERNEL);
> +       if (!wrapper->clk_perf_tbl) {
> +               ret = -ENOMEM;
> +               goto out_unlock;
> +       }
> +
> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> +               freq = clk_round_rate(se->clk, freq + 1);
> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
> +                       break;
> +               wrapper->clk_perf_tbl[i] = freq;
> +       }
> +       wrapper->num_clk_levels = i;
> +       *tbl = wrapper->clk_perf_tbl;
> +       ret = wrapper->num_clk_levels;
> +out_unlock:
> +       mutex_unlock(&wrapper->lock);
> +       return ret;
> +}
> +EXPORT_SYMBOL(geni_se_clk_tbl_get);

I think Bjorn had this feedback before, but if you did this work in
probe you could remove the mutex altogether.

> +       wrapper->ahb_clks[0].id = m_ahb_clk;
> +       wrapper->ahb_clks[1].id = s_ahb_clk;
> +       ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
> +       if (ret) {
> +               dev_err(dev, "Err getting AHB clks %d\n", ret);
> +               return ret;
> +       }
> +
> +       mutex_init(&wrapper->lock);
> +       dev_set_drvdata(dev, wrapper);
> +       dev_dbg(dev, "GENI SE Driver probed\n");
> +       return devm_of_platform_populate(dev);
> +}
> +
> +static int geni_se_remove(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct geni_wrapper *wrapper = dev_get_drvdata(dev);
> +
> +       kfree(wrapper->clk_perf_tbl);

Maybe null out clk_perf_tbl for safety?

> diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
> new file mode 100644
> index 0000000..4996de7
> --- /dev/null
> +++ b/include/linux/qcom-geni-se.h
[...]
> +/* 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

I think this needs to be #if IS_ENABLED(CONFIG_QCOM_GENI_SE), since
the function prototypes below won't light up if this is built as a
module.

Thanks,
Evan

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-02 20:58       ` Evan Green
  0 siblings, 0 replies; 65+ messages in thread
From: Evan Green @ 2018-03-02 20:58 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Karthikeyan Ramasubramanian, Andy Gross, Jonathan Corbet,
	David Brown, gregkh, mark.rutland, robh+dt, wsa, linux-doc,
	linux-arm-msm, devicetree, linux-i2c, linux-serial, jslaby,
	acourbot, Sagar Dharia, Girish Mahadevan

Hi Karthik,

On Tue, Feb 27, 2018 at 5:38 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        |   9 +
>  drivers/soc/qcom/Makefile       |   1 +
>  drivers/soc/qcom/qcom-geni-se.c | 971 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/qcom-geni-se.h    | 247 ++++++++++
>  4 files changed, 1228 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/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> new file mode 100644
> index 0000000..61335b8
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> +
> +/**
> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> + * @se:                Pointer to the concerned Serial Engine.
> + * @tbl:       Table in which the output is returned.
> + *
> + * This function is called by the protocol drivers to determine the different
> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
> +{
> +       struct geni_wrapper *wrapper = se->wrapper;
> +       unsigned long freq = 0;
> +       int i;
> +       int ret = 0;
> +
> +       mutex_lock(&wrapper->lock);
> +       if (wrapper->clk_perf_tbl) {
> +               *tbl = wrapper->clk_perf_tbl;
> +               ret = wrapper->num_clk_levels;
> +               goto out_unlock;
> +       }
> +
> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
> +                                       sizeof(*wrapper->clk_perf_tbl),
> +                                       GFP_KERNEL);
> +       if (!wrapper->clk_perf_tbl) {
> +               ret = -ENOMEM;
> +               goto out_unlock;
> +       }
> +
> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> +               freq = clk_round_rate(se->clk, freq + 1);
> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
> +                       break;
> +               wrapper->clk_perf_tbl[i] = freq;
> +       }
> +       wrapper->num_clk_levels = i;
> +       *tbl = wrapper->clk_perf_tbl;
> +       ret = wrapper->num_clk_levels;
> +out_unlock:
> +       mutex_unlock(&wrapper->lock);
> +       return ret;
> +}
> +EXPORT_SYMBOL(geni_se_clk_tbl_get);

I think Bjorn had this feedback before, but if you did this work in
probe you could remove the mutex altogether.

> +       wrapper->ahb_clks[0].id = m_ahb_clk;
> +       wrapper->ahb_clks[1].id = s_ahb_clk;
> +       ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
> +       if (ret) {
> +               dev_err(dev, "Err getting AHB clks %d\n", ret);
> +               return ret;
> +       }
> +
> +       mutex_init(&wrapper->lock);
> +       dev_set_drvdata(dev, wrapper);
> +       dev_dbg(dev, "GENI SE Driver probed\n");
> +       return devm_of_platform_populate(dev);
> +}
> +
> +static int geni_se_remove(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct geni_wrapper *wrapper = dev_get_drvdata(dev);
> +
> +       kfree(wrapper->clk_perf_tbl);

Maybe null out clk_perf_tbl for safety?

> diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
> new file mode 100644
> index 0000000..4996de7
> --- /dev/null
> +++ b/include/linux/qcom-geni-se.h
[...]
> +/* 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

I think this needs to be #if IS_ENABLED(CONFIG_QCOM_GENI_SE), since
the function prototypes below won't light up if this is built as a
module.

Thanks,
Evan
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-02-28  1:38 ` [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
@ 2018-03-02 22:11     ` Stephen Boyd
  2018-03-03  0:11     ` Evan Green
  1 sibling, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-02 22:11 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian, andy.gross, corbet, david.brown,
	gregkh, mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index 3682fd3..c6b1500 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1104,6 +1104,17 @@ config SERIAL_MSM_CONSOLE
>         select SERIAL_CORE_CONSOLE
>         select SERIAL_EARLYCON
>  
> +config SERIAL_QCOM_GENI
> +       bool "QCOM on-chip GENI based serial port support"

This can be tristate.

> +       depends on ARCH_QCOM

|| COMPILE_TEST
?

> +       depends on QCOM_GENI_SE
> +       select SERIAL_CORE

This can stay.

> +       select SERIAL_CORE_CONSOLE
> +       select SERIAL_EARLYCON

These two can go to a new config option, like SERIAL_QCOM_GENI_CONSOLE,
and that would be bool. Please take a look at the existing SERIAL_MSM
and SERIAL_MSM_CONSOLE setup to understand how to do it.

> +       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/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> new file mode 100644
> index 0000000..8536b7d
> --- /dev/null
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -0,0 +1,1181 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> +
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.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_CONS_PORTS   1
> +#define DEF_FIFO_DEPTH_WORDS   16
> +#define DEF_TX_WM              2
> +#define DEF_FIFO_WIDTH_BITS    32
> +#define UART_CONSOLE_RX_WM     2
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +#define RX_BYTES_PW 1
> +#else
> +#define RX_BYTES_PW 4
> +#endif
> +
> +struct qcom_geni_serial_port {
> +       struct uart_port uport;
> +       struct geni_se se;
> +       char name[20];
> +       u32 tx_fifo_depth;
> +       u32 tx_fifo_width;
> +       u32 rx_fifo_depth;
> +       u32 tx_wm;
> +       u32 rx_wm;
> +       u32 rx_rfr;
> +       int xfer_mode;

Can this be an enum?

> +       bool port_setup;

Maybe just 'setup'? Port is in the type already.

> +       int (*handle_rx)(struct uart_port *uport,
> +                               u32 rx_bytes, bool drop_rx);

s/rx_bytes/bytes/
s/drop_rx/drop/

> +       unsigned int xmit_size;
> +       unsigned int cur_baud;

s/cur//

> +       unsigned int tx_bytes_pw;
> +       unsigned int rx_bytes_pw;
> +};
> +
> +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,
> +                               u32 rx_bytes, bool drop_rx);

s/rx_bytes/bytes/
s/drop_rx/drop/

> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> +                               int offset, int bit_field, bool set);

No need to forward declare this?

s/bit_// ?

> +static void qcom_geni_serial_stop_rx(struct uart_port *uport);
> +
> +static atomic_t uart_line_id = ATOMIC_INIT(0);

Do we need this? How about rely on DT to always have aliases instead?
Given we only have one port I don't actually understand how this is
supposed to work anyway.

> +static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
> +                                       32000000, 48000000, 64000000, 80000000,
> +                                       96000000, 100000000};
> +
> +#define to_dev_port(ptr, member) \
> +               container_of(ptr, struct qcom_geni_serial_port, member)
> +
> +static struct qcom_geni_serial_port qcom_geni_console_port;

Why singleton? Couldn't there be many?

> +
> +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)
> +{
> +       if ((line < 0) || (line >= GENI_UART_CONS_PORTS))

Drop useless parenthesis please.

> +               return ERR_PTR(-ENXIO);
> +       return &qcom_geni_console_port;
> +}
> +
> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> +                               int offset, int bit_field, bool set)
> +{
> +       u32 reg;
> +       struct qcom_geni_serial_port *port;
> +       unsigned int baud;
> +       unsigned int fifo_bits;
> +       unsigned long timeout_us = 20000;
> +
> +       /* Ensure polling is not re-ordered before the prior writes/reads */
> +       mb();
> +
> +       if (uport->private_data) {
> +               port = to_dev_port(uport, uport);
> +               baud = port->cur_baud;
> +               if (!baud)
> +                       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.

Bad space here                           ^

> +                */
> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
> +       }
> +
> +       return !readl_poll_timeout_atomic(uport->membase + offset, reg,
> +                        (bool)(reg & bit_field) == set, 10, timeout_us);
> +}
> +
> +static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
> +{
> +       u32 m_cmd;
> +
> +       writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
> +       m_cmd = UART_START_TX << M_OPCODE_SHFT;
> +       writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
> +}
> +
> +static void qcom_geni_serial_poll_tx_done(struct uart_port *uport)
> +{
> +       int done;
> +       u32 irq_clear = M_CMD_DONE_EN;
> +
> +       done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_CMD_DONE_EN, true);
> +       if (!done) {
> +               writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
> +                                               SE_GENI_M_CMD_CTRL_REG);
> +               irq_clear |= M_CMD_ABORT_EN;
> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                                       M_CMD_ABORT_EN, true);
> +       }
> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
> +{
> +       u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
> +
> +       writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +                                       S_GENI_CMD_ABORT, false);
> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +       writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
> +}
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +static int qcom_geni_serial_get_char(struct uart_port *uport)
> +{
> +       u32 rx_fifo;
> +       u32 status;
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
> +       writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
> +       writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +
> +       /*
> +        * Ensure the writes to clear interrupts is not re-ordered after
> +        * reading the data.
> +        */
> +       mb();
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
> +       if (!(status & RX_FIFO_WC_MSK))
> +               return NO_POLL_CHAR;
> +
> +       rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
> +       return rx_fifo & 0xff;
> +}
> +
> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
> +                                                       unsigned char c)
> +{
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
> +       qcom_geni_serial_setup_tx(uport, 1);
> +       WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_TX_FIFO_WATERMARK_EN, true));
> +       writel_relaxed((u32)c, uport->membase + SE_GENI_TX_FIFOn);

Drop useless cast.

> +       writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
> +                                                       SE_GENI_M_IRQ_CLEAR);
> +       qcom_geni_serial_poll_tx_done(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)
> +{
> +       writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
> +}
> +
> +static void
> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
> +                                unsigned int count)
> +{
> +       int new_line = 0;

Drop

> +       int i;
> +       u32 bytes_to_send = count;
> +
> +       for (i = 0; i < count; i++) {
> +               if (s[i] == '\n')
> +                       new_line++;

bytes_to_send++;

> +       }
> +
> +       bytes_to_send += new_line;

Drop.

> +       writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
> +       qcom_geni_serial_setup_tx(uport, bytes_to_send);
> +       i = 0;
> +       while (i < count) {

	for (i = 0; i < count; ) {

would be more normal, but ok.

> +               size_t chars_to_write = 0;
> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))

Does this ever timeout? So many nested while loops makes it hard to
follow.

> +                       break;
> +               chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2);
> +               uart_console_write(uport, (s + i), chars_to_write,

Drop useless parenthesis please.

> +                                               qcom_geni_serial_wr_char);
> +               writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
> +                                                       SE_GENI_M_IRQ_CLEAR);
> +               i += chars_to_write;
> +       }
> +       qcom_geni_serial_poll_tx_done(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;
> +       bool locked = true;
> +       unsigned long flags;
> +
> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> +
> +       port = get_port_from_line(co->index);
> +       if (IS_ERR(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);

So if oops is in progress, and we didn't lock here, we don't output
data? I'd think we would always want to write to the fifo, just make the
lock grab/release conditional.

> +               spin_unlock_irqrestore(&uport->lock, flags);
> +       }
> +}
> +
> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
> +{
> +       u32 i = rx_bytes;
> +       u32 rx_fifo;
> +       unsigned char *buf;
> +       struct tty_port *tport;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       tport = &uport->state->port;
> +       while (i > 0) {
> +               int c;
> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;
> +
> +               rx_fifo = readl_relaxed(uport->membase + SE_GENI_RX_FIFOn);

Please use ioread32_rep(..., 1) here.

> +               i -= bytes;
> +               if (drop)
> +                       continue;
> +               buf = (unsigned char *)&rx_fifo;

So that this cast becomes unnecessary, and endian agnostic.

> +
> +               for (c = 0; c < bytes; c++) {
> +                       int sysrq;
> +
> +                       uport->icount.rx++;
> +                       sysrq = uart_handle_sysrq_char(uport, buf[c]);

And so this does the right thing in whatever world we live in.

> +                       if (!sysrq)
> +                               tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
> +               }
> +       }
> +       if (!drop)
> +               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)
> +{
> +       u32 irq_en;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       u32 status;
> +
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +               if (status & M_GENI_CMD_ACTIVE)
> +                       return;
> +
> +               if (!qcom_geni_serial_tx_empty(uport))
> +                       return;
> +
> +               /*
> +                * Ensure writing to IRQ_EN & watermark registers are not
> +                * re-ordered before checking the status of the Serial
> +                * Engine and TX FIFO
> +                */
> +               mb();
> +
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +               irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
> +
> +               writel_relaxed(port->tx_wm, uport->membase +
> +                                               SE_GENI_TX_WATERMARK_REG);
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       }
> +}
> +
> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
> +{
> +       u32 irq_en;
> +       u32 status;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +       irq_en &= ~M_CMD_DONE_EN;
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               irq_en &= ~M_TX_FIFO_WATERMARK_EN;
> +               writel_relaxed(0, uport->membase +
> +                                    SE_GENI_TX_WATERMARK_REG);
> +       }
> +       port->xmit_size = 0;
> +       writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       /* Possible stop tx is called multiple times. */
> +       if (!(status & M_GENI_CMD_ACTIVE))
> +               return;
> +
> +       /*
> +        * Ensure cancel command write is not re-ordered before checking
> +        * checking the status of the Primary Sequencer.
> +        */
> +       mb();
> +
> +       geni_se_cancel_m_cmd(&port->se);
> +       if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_CMD_CANCEL_EN, true)) {
> +               geni_se_abort_m_cmd(&port->se);
> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_CMD_ABORT_EN, true);
> +               writel_relaxed(M_CMD_ABORT_EN, uport->membase +
> +                                                       SE_GENI_M_IRQ_CLEAR);
> +       }
> +       writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
> +{
> +       u32 irq_en;
> +       u32 status;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       if (status & S_GENI_CMD_ACTIVE)
> +               qcom_geni_serial_stop_rx(uport);
> +
> +       /*
> +        * Ensure setup command write is not re-ordered before checking
> +        * checking the status of the Secondary Sequencer.
> +        */
> +       mb();
> +
> +       geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
> +
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
> +               irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
> +
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +               irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       }
> +}
> +
> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
> +{
> +       u32 irq_en;
> +       u32 status;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       u32 irq_clear = S_CMD_DONE_EN;
> +
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
> +               irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
> +
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +               irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       }
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       /* Possible stop rx is called multiple times. */
> +       if (!(status & S_GENI_CMD_ACTIVE))
> +               return;
> +
> +       /*
> +        * Ensure cancel command write is not re-ordered before checking
> +        * checking the status of the Secondary Sequencer.

Each of these comments has 'checking' twice.

> +        */
> +       mb();
> +
> +       geni_se_cancel_s_cmd(&port->se);
> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +                                       S_GENI_CMD_CANCEL, false);
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +       if (status & S_GENI_CMD_ACTIVE)
> +               qcom_geni_serial_abort_rx(uport);
> +}
> +
> +static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)

s/drop_rx/drop/

> +{
> +       u32 status;
> +       u32 word_cnt;
> +       u32 last_word_byte_cnt;
> +       u32 last_word_partial;
> +       u32 total_bytes;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
> +       word_cnt = status & RX_FIFO_WC_MSK;
> +       last_word_partial = status & RX_LAST;
> +       last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
> +                                               RX_LAST_BYTE_VALID_SHFT;
> +
> +       if (!word_cnt)
> +               return;
> +       total_bytes = port->rx_bytes_pw * (word_cnt - 1);
> +       if (last_word_partial && last_word_byte_cnt)
> +               total_bytes += last_word_byte_cnt;
> +       else
> +               total_bytes += port->rx_bytes_pw;
> +       port->handle_rx(uport, total_bytes, drop_rx);
> +}
> +
> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> +{
> +       int ret = 0;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       struct circ_buf *xmit = &uport->state->xmit;
> +       size_t avail;
> +       size_t remaining;
> +       int i = 0;
> +       u32 status;
> +       unsigned int chunk;
> +       int tail;
> +
> +       chunk = uart_circ_chars_pending(xmit);
> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
> +       /* Both FIFO and framework buffer are drained */
> +       if ((chunk == port->xmit_size) && !status) {

Drop useless parenthesis.

> +               port->xmit_size = 0;
> +               uart_circ_clear(xmit);
> +               qcom_geni_serial_stop_tx(uport);
> +               goto out_write_wakeup;
> +       }
> +       chunk -= port->xmit_size;
> +
> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
> +       if (chunk > (UART_XMIT_SIZE - tail))
> +               chunk = UART_XMIT_SIZE - tail;
> +       if (chunk > avail)
> +               chunk = avail;
> +
> +       if (!chunk)
> +               goto out_write_wakeup;
> +
> +       qcom_geni_serial_setup_tx(uport, chunk);
> +
> +       remaining = chunk;
> +       while (i < chunk) {

	for (i = 0; i < chunk; ) {

> +               unsigned int tx_bytes;
> +               unsigned int buf = 0;
> +               int c;
> +
> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
> +               for (c = 0; c < tx_bytes ; c++)
> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
> +
> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
> +
> +               i += tx_bytes;
> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> +               uport->icount.tx += tx_bytes;
> +               remaining -= tx_bytes;
> +       }
> +       qcom_geni_serial_poll_tx_done(uport);
> +       port->xmit_size += chunk;
> +out_write_wakeup:
> +       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;
> +
> +       if (uport->suspended)
> +               return IRQ_HANDLED;
> +
> +       spin_lock_irqsave(&uport->lock, flags);
> +       m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
> +       s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
> +       m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +       writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +       writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +
> +       if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
> +               goto out_unlock;
> +
> +       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_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
> +           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++;

How does break character handling work? I see the accounting here, but
don't see any uart_handle_break() call anywhere.

> +       }
> +
> +       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);
> +
> +out_unlock:
> +       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(&port->se);
> +       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(&port->se);
> +       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(&port->se);
> +       if (!port->rx_fifo_depth) {
> +               dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
> +                                                               __func__);
> +               return -ENXIO;
> +       }

Are these checks verifying the hardware has a proper setting for fifo
depth and width? How is that possible to mess up? Do these ever fail?

> +
> +       uport->fifosize =
> +               (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
> +       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;

port->tx_wm = DEF_TX_WM?

> +}
> +
> +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;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
> +
> +       set_rfr_wm(port);
> +       writel_relaxed(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.
> +        */
> +       port->xfer_mode = GENI_SE_FIFO;
> +       qcom_geni_serial_poll_tx_done(uport);
> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
> +                                               false, true, false);
> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
> +                                               false, false, true);
> +       ret = geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
> +       if (ret) {
> +               dev_err(uport->dev, "%s: Fail\n", __func__);
> +               return ret;
> +       }
> +
> +       geni_se_select_mode(&port->se, port->xfer_mode);
> +       port->port_setup = true;
> +       return ret;
> +}
> +
> +static int qcom_geni_serial_startup(struct uart_port *uport)
> +{
> +       int ret;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       scnprintf(port->name, sizeof(port->name),
> +                 "qcom_serial_geni%d", uport->line);
> +
> +       if (geni_se_read_proto(&port->se) != GENI_SE_UART) {
> +               dev_err(uport->dev, "Invalid FW %d loaded.\n",
> +                                geni_se_read_proto(&port->se));

Please don't read proto twice.

> +               return -ENXIO;
> +       }
> +
> +       get_tx_fifo_size(port);
> +       if (!port->port_setup) {
> +               ret = qcom_geni_serial_port_setup(uport);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
> +                                                       port->name, uport);
> +       if (ret)
> +               dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
> +       return ret;
> +}
> +
> +static unsigned long get_clk_cfg(unsigned long clk_freq)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
> +               if (!(root_freq[i] % clk_freq))
> +                       return root_freq[i];
> +       }
> +       return 0;
> +}
> +
> +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)
> +{
> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);

Can you please inline this function into the caller and put the writels
where the values are calculated? It would reduce the mental work to keep
track of all the variables to find out that they just get written in the
end. Also, this is weirdly placed in the file when get_clk_div_rate()
calls get_clk_cfg() but this function is between them.

> +}
> +
> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
> +{
> +       unsigned long ser_clk;
> +       unsigned long desired_clk;
> +
> +       desired_clk = baud * UART_OVERSAMPLING;
> +       ser_clk = get_clk_cfg(desired_clk);
> +       if (!ser_clk) {
> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
> +                                                               __func__, baud);
> +               return ser_clk;
> +       }
> +
> +       *clk_div = ser_clk / desired_clk;

How wide can clk_div be? It may be better to implement the ser_clk as an
actual clk in the common clk framework, and then have the serial driver
or the i2c driver call clk_set_rate() on that clk and have the CCF
implementation take care of determining the rate that the parent clk can
supply and how it can fit it into the frequency that the divider can
support.

> +       return ser_clk;
> +}
> +
> +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;
> +       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;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, 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_rate = get_clk_div_rate(baud, &clk_div);
> +       if (!clk_rate)
> +               goto out_restart_rx;
> +
> +       uport->uartclk = clk_rate;
> +       clk_set_rate(port->se.clk, clk_rate);
> +       ser_clk_cfg = SER_CLK_EN;
> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);

Drop useless cast.

> +
> +       /* parity */
> +       tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
> +       tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
> +       rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
> +       rx_parity_cfg = readl_relaxed(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 (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);
> +out_restart_rx:
> +       qcom_geni_serial_start_rx(uport);
> +}
> +
> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
> +{
> +       return !readl_relaxed(uport->membase + SE_GENI_TX_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 *port;
> +       int baud;
> +       int bits = 8;
> +       int parity = 'n';
> +       int flow = 'n';
> +
> +       if (co->index >= GENI_UART_CONS_PORTS  || co->index < 0)
> +               return -ENXIO;
> +
> +       port = get_port_from_line(co->index);
> +       if (IS_ERR(port)) {
> +               pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port));
> +               return PTR_ERR(port);
> +       }
> +
> +       uport = &port->uport;
> +
> +       if (unlikely(!uport->membase))
> +               return -ENXIO;
> +
> +       if (geni_se_resources_on(&port->se)) {
> +               dev_err(port->se.dev, "Error turning on resources\n");
> +               return -ENXIO;
> +       }
> +
> +       if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {

Looks like we're validating the configuration of the DT here. Maybe this
can go into the wrapper code and be put behind some DEBUG_KERNEL check
so we can debug bad bootloader configurations if needed? Especially if
this is the only API that's left exposed from the wrapper to the serial
engine/protocol driver.

> +               geni_se_resources_off(&port->se);
> +               return -ENXIO;
> +       }
> +
> +       if (!port->port_setup) {
> +               port->tx_bytes_pw = 1;
> +               port->rx_bytes_pw = RX_BYTES_PW;
> +               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)

__init

> +{
> +       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_CONS_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 *port = to_dev_port(uport, uport);
> +
> +       if (unlikely(!uart_console(uport)))
> +               return;
> +
> +       if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
> +               geni_se_resources_on(&port->se);
> +       else if (new_state == UART_PM_STATE_OFF &&
> +                       old_state == UART_PM_STATE_ON)
> +               geni_se_resources_off(&port->se);
> +}
> +
> +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 int qcom_geni_serial_probe(struct platform_device *pdev)
> +{
> +       int ret = 0;
> +       int line = -1;
> +       struct qcom_geni_serial_port *port;
> +       struct uart_port *uport;
> +       struct resource *res;
> +       struct uart_driver *drv;
> +
> +       drv = (void *)of_device_get_match_data(&pdev->dev);

Useless cast.

> +       if (!drv) {
> +               dev_err(&pdev->dev, "%s: No matching device found", __func__);
> +               return -ENODEV;
> +       }
> +
> +       if (pdev->dev.of_node)
> +               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))

Useless parenthesis.

> +               return -ENXIO;
> +       port = get_port_from_line(line);
> +       if (IS_ERR(port)) {
> +               ret = PTR_ERR(port);
> +               dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret);
> +               return ret;
> +       }
> +
> +       uport = &port->uport;
> +       /* Don't allow 2 drivers to access the same port */
> +       if (uport->private_data)
> +               return -ENODEV;
> +
> +       uport->dev = &pdev->dev;
> +       port->se.dev = &pdev->dev;
> +       port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
> +       port->se.clk = devm_clk_get(&pdev->dev, "se");
> +       if (IS_ERR(port->se.clk)) {
> +               ret = PTR_ERR(port->se.clk);
> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +               return ret;
> +       }
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       uport->mapbase = res->start;
> +       uport->membase = devm_ioremap_resource(&pdev->dev, res);
> +       if (!uport->membase) {

Check for IS_ERR()

> +               dev_err(&pdev->dev, "Err IO mapping serial iomem");

No need for error message with devm_ioremap_resource()

> +               return -ENOMEM;

return PTR_ERR(..)

Also, I see some serial drivers do the mapping when the port is
requested. That can't be done here?

> +       }
> +       port->se.base = uport->membase;
> +
> +       port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +       port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +       port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
> +
> +       uport->irq = platform_get_irq(pdev, 0);
> +       if (uport->irq < 0) {
> +               dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq);
> +               return uport->irq;
> +       }
> +
> +       uport->private_data = drv;
> +       platform_set_drvdata(pdev, port);
> +       port->handle_rx = handle_rx_console;
> +       port->port_setup = false;
> +       return uart_add_one_port(drv, uport);
> +}
> +
> +static int qcom_geni_serial_remove(struct platform_device *pdev)
> +{
> +       struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +       struct uart_driver *drv = port->uport.private_data;
> +
> +       uart_remove_one_port(drv, &port->uport);
> +       return 0;
> +}
> +
> +static int __maybe_unused 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(uport->private_data, uport);
> +       return 0;
> +}
> +
> +static int __maybe_unused 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(uport->private_data, uport);
> +               disable_irq(uport->irq);
> +       }
> +       return 0;
> +}
> +
> +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,

Why are these noirq variants? Please add a comment.

> +};
> +
> +static const struct of_device_id qcom_geni_serial_match_table[] = {
> +       { .compatible = "qcom,geni-debug-uart",
> +                               .data = &qcom_geni_console_driver, },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
> +
> +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;

Drop assignment please.

> +
> +       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 = 0;

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-02 22:11     ` Stephen Boyd
  0 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-02 22:11 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian, andy.gross, corbet, david.brown,
	gregkh, mark.rutland, robh+dt, wsa
  Cc: Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, jslaby, evgreen, acourbot,
	Girish Mahadevan, Sagar Dharia, Doug Anderson

Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index 3682fd3..c6b1500 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1104,6 +1104,17 @@ config SERIAL_MSM_CONSOLE
>         select SERIAL_CORE_CONSOLE
>         select SERIAL_EARLYCON
>  
> +config SERIAL_QCOM_GENI
> +       bool "QCOM on-chip GENI based serial port support"

This can be tristate.

> +       depends on ARCH_QCOM

|| COMPILE_TEST
?

> +       depends on QCOM_GENI_SE
> +       select SERIAL_CORE

This can stay.

> +       select SERIAL_CORE_CONSOLE
> +       select SERIAL_EARLYCON

These two can go to a new config option, like SERIAL_QCOM_GENI_CONSOLE,
and that would be bool. Please take a look at the existing SERIAL_MSM
and SERIAL_MSM_CONSOLE setup to understand how to do it.

> +       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/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> new file mode 100644
> index 0000000..8536b7d
> --- /dev/null
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -0,0 +1,1181 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> +
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.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_CONS_PORTS   1
> +#define DEF_FIFO_DEPTH_WORDS   16
> +#define DEF_TX_WM              2
> +#define DEF_FIFO_WIDTH_BITS    32
> +#define UART_CONSOLE_RX_WM     2
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +#define RX_BYTES_PW 1
> +#else
> +#define RX_BYTES_PW 4
> +#endif
> +
> +struct qcom_geni_serial_port {
> +       struct uart_port uport;
> +       struct geni_se se;
> +       char name[20];
> +       u32 tx_fifo_depth;
> +       u32 tx_fifo_width;
> +       u32 rx_fifo_depth;
> +       u32 tx_wm;
> +       u32 rx_wm;
> +       u32 rx_rfr;
> +       int xfer_mode;

Can this be an enum?

> +       bool port_setup;

Maybe just 'setup'? Port is in the type already.

> +       int (*handle_rx)(struct uart_port *uport,
> +                               u32 rx_bytes, bool drop_rx);

s/rx_bytes/bytes/
s/drop_rx/drop/

> +       unsigned int xmit_size;
> +       unsigned int cur_baud;

s/cur//

> +       unsigned int tx_bytes_pw;
> +       unsigned int rx_bytes_pw;
> +};
> +
> +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,
> +                               u32 rx_bytes, bool drop_rx);

s/rx_bytes/bytes/
s/drop_rx/drop/

> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> +                               int offset, int bit_field, bool set);

No need to forward declare this?

s/bit_// ?

> +static void qcom_geni_serial_stop_rx(struct uart_port *uport);
> +
> +static atomic_t uart_line_id = ATOMIC_INIT(0);

Do we need this? How about rely on DT to always have aliases instead?
Given we only have one port I don't actually understand how this is
supposed to work anyway.

> +static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
> +                                       32000000, 48000000, 64000000, 80000000,
> +                                       96000000, 100000000};
> +
> +#define to_dev_port(ptr, member) \
> +               container_of(ptr, struct qcom_geni_serial_port, member)
> +
> +static struct qcom_geni_serial_port qcom_geni_console_port;

Why singleton? Couldn't there be many?

> +
> +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)
> +{
> +       if ((line < 0) || (line >= GENI_UART_CONS_PORTS))

Drop useless parenthesis please.

> +               return ERR_PTR(-ENXIO);
> +       return &qcom_geni_console_port;
> +}
> +
> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> +                               int offset, int bit_field, bool set)
> +{
> +       u32 reg;
> +       struct qcom_geni_serial_port *port;
> +       unsigned int baud;
> +       unsigned int fifo_bits;
> +       unsigned long timeout_us = 20000;
> +
> +       /* Ensure polling is not re-ordered before the prior writes/reads */
> +       mb();
> +
> +       if (uport->private_data) {
> +               port = to_dev_port(uport, uport);
> +               baud = port->cur_baud;
> +               if (!baud)
> +                       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.

Bad space here                           ^

> +                */
> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
> +       }
> +
> +       return !readl_poll_timeout_atomic(uport->membase + offset, reg,
> +                        (bool)(reg & bit_field) == set, 10, timeout_us);
> +}
> +
> +static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
> +{
> +       u32 m_cmd;
> +
> +       writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
> +       m_cmd = UART_START_TX << M_OPCODE_SHFT;
> +       writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
> +}
> +
> +static void qcom_geni_serial_poll_tx_done(struct uart_port *uport)
> +{
> +       int done;
> +       u32 irq_clear = M_CMD_DONE_EN;
> +
> +       done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_CMD_DONE_EN, true);
> +       if (!done) {
> +               writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
> +                                               SE_GENI_M_CMD_CTRL_REG);
> +               irq_clear |= M_CMD_ABORT_EN;
> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                                       M_CMD_ABORT_EN, true);
> +       }
> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
> +{
> +       u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
> +
> +       writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +                                       S_GENI_CMD_ABORT, false);
> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +       writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
> +}
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +static int qcom_geni_serial_get_char(struct uart_port *uport)
> +{
> +       u32 rx_fifo;
> +       u32 status;
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
> +       writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
> +       writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +
> +       /*
> +        * Ensure the writes to clear interrupts is not re-ordered after
> +        * reading the data.
> +        */
> +       mb();
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
> +       if (!(status & RX_FIFO_WC_MSK))
> +               return NO_POLL_CHAR;
> +
> +       rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
> +       return rx_fifo & 0xff;
> +}
> +
> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
> +                                                       unsigned char c)
> +{
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
> +       qcom_geni_serial_setup_tx(uport, 1);
> +       WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_TX_FIFO_WATERMARK_EN, true));
> +       writel_relaxed((u32)c, uport->membase + SE_GENI_TX_FIFOn);

Drop useless cast.

> +       writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
> +                                                       SE_GENI_M_IRQ_CLEAR);
> +       qcom_geni_serial_poll_tx_done(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)
> +{
> +       writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
> +}
> +
> +static void
> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
> +                                unsigned int count)
> +{
> +       int new_line = 0;

Drop

> +       int i;
> +       u32 bytes_to_send = count;
> +
> +       for (i = 0; i < count; i++) {
> +               if (s[i] == '\n')
> +                       new_line++;

bytes_to_send++;

> +       }
> +
> +       bytes_to_send += new_line;

Drop.

> +       writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
> +       qcom_geni_serial_setup_tx(uport, bytes_to_send);
> +       i = 0;
> +       while (i < count) {

	for (i = 0; i < count; ) {

would be more normal, but ok.

> +               size_t chars_to_write = 0;
> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))

Does this ever timeout? So many nested while loops makes it hard to
follow.

> +                       break;
> +               chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2);
> +               uart_console_write(uport, (s + i), chars_to_write,

Drop useless parenthesis please.

> +                                               qcom_geni_serial_wr_char);
> +               writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
> +                                                       SE_GENI_M_IRQ_CLEAR);
> +               i += chars_to_write;
> +       }
> +       qcom_geni_serial_poll_tx_done(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;
> +       bool locked = true;
> +       unsigned long flags;
> +
> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> +
> +       port = get_port_from_line(co->index);
> +       if (IS_ERR(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);

So if oops is in progress, and we didn't lock here, we don't output
data? I'd think we would always want to write to the fifo, just make the
lock grab/release conditional.

> +               spin_unlock_irqrestore(&uport->lock, flags);
> +       }
> +}
> +
> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
> +{
> +       u32 i = rx_bytes;
> +       u32 rx_fifo;
> +       unsigned char *buf;
> +       struct tty_port *tport;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       tport = &uport->state->port;
> +       while (i > 0) {
> +               int c;
> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;
> +
> +               rx_fifo = readl_relaxed(uport->membase + SE_GENI_RX_FIFOn);

Please use ioread32_rep(..., 1) here.

> +               i -= bytes;
> +               if (drop)
> +                       continue;
> +               buf = (unsigned char *)&rx_fifo;

So that this cast becomes unnecessary, and endian agnostic.

> +
> +               for (c = 0; c < bytes; c++) {
> +                       int sysrq;
> +
> +                       uport->icount.rx++;
> +                       sysrq = uart_handle_sysrq_char(uport, buf[c]);

And so this does the right thing in whatever world we live in.

> +                       if (!sysrq)
> +                               tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
> +               }
> +       }
> +       if (!drop)
> +               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)
> +{
> +       u32 irq_en;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       u32 status;
> +
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +               if (status & M_GENI_CMD_ACTIVE)
> +                       return;
> +
> +               if (!qcom_geni_serial_tx_empty(uport))
> +                       return;
> +
> +               /*
> +                * Ensure writing to IRQ_EN & watermark registers are not
> +                * re-ordered before checking the status of the Serial
> +                * Engine and TX FIFO
> +                */
> +               mb();
> +
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +               irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
> +
> +               writel_relaxed(port->tx_wm, uport->membase +
> +                                               SE_GENI_TX_WATERMARK_REG);
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       }
> +}
> +
> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
> +{
> +       u32 irq_en;
> +       u32 status;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +       irq_en &= ~M_CMD_DONE_EN;
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               irq_en &= ~M_TX_FIFO_WATERMARK_EN;
> +               writel_relaxed(0, uport->membase +
> +                                    SE_GENI_TX_WATERMARK_REG);
> +       }
> +       port->xmit_size = 0;
> +       writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       /* Possible stop tx is called multiple times. */
> +       if (!(status & M_GENI_CMD_ACTIVE))
> +               return;
> +
> +       /*
> +        * Ensure cancel command write is not re-ordered before checking
> +        * checking the status of the Primary Sequencer.
> +        */
> +       mb();
> +
> +       geni_se_cancel_m_cmd(&port->se);
> +       if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_CMD_CANCEL_EN, true)) {
> +               geni_se_abort_m_cmd(&port->se);
> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> +                                               M_CMD_ABORT_EN, true);
> +               writel_relaxed(M_CMD_ABORT_EN, uport->membase +
> +                                                       SE_GENI_M_IRQ_CLEAR);
> +       }
> +       writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
> +{
> +       u32 irq_en;
> +       u32 status;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       if (status & S_GENI_CMD_ACTIVE)
> +               qcom_geni_serial_stop_rx(uport);
> +
> +       /*
> +        * Ensure setup command write is not re-ordered before checking
> +        * checking the status of the Secondary Sequencer.
> +        */
> +       mb();
> +
> +       geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
> +
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
> +               irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
> +
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +               irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       }
> +}
> +
> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
> +{
> +       u32 irq_en;
> +       u32 status;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       u32 irq_clear = S_CMD_DONE_EN;
> +
> +       if (port->xfer_mode == GENI_SE_FIFO) {
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
> +               irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
> +
> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +               irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> +       }
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       /* Possible stop rx is called multiple times. */
> +       if (!(status & S_GENI_CMD_ACTIVE))
> +               return;
> +
> +       /*
> +        * Ensure cancel command write is not re-ordered before checking
> +        * checking the status of the Secondary Sequencer.

Each of these comments has 'checking' twice.

> +        */
> +       mb();
> +
> +       geni_se_cancel_s_cmd(&port->se);
> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
> +                                       S_GENI_CMD_CANCEL, false);
> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +       if (status & S_GENI_CMD_ACTIVE)
> +               qcom_geni_serial_abort_rx(uport);
> +}
> +
> +static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)

s/drop_rx/drop/

> +{
> +       u32 status;
> +       u32 word_cnt;
> +       u32 last_word_byte_cnt;
> +       u32 last_word_partial;
> +       u32 total_bytes;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
> +       word_cnt = status & RX_FIFO_WC_MSK;
> +       last_word_partial = status & RX_LAST;
> +       last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
> +                                               RX_LAST_BYTE_VALID_SHFT;
> +
> +       if (!word_cnt)
> +               return;
> +       total_bytes = port->rx_bytes_pw * (word_cnt - 1);
> +       if (last_word_partial && last_word_byte_cnt)
> +               total_bytes += last_word_byte_cnt;
> +       else
> +               total_bytes += port->rx_bytes_pw;
> +       port->handle_rx(uport, total_bytes, drop_rx);
> +}
> +
> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> +{
> +       int ret = 0;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       struct circ_buf *xmit = &uport->state->xmit;
> +       size_t avail;
> +       size_t remaining;
> +       int i = 0;
> +       u32 status;
> +       unsigned int chunk;
> +       int tail;
> +
> +       chunk = uart_circ_chars_pending(xmit);
> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
> +       /* Both FIFO and framework buffer are drained */
> +       if ((chunk == port->xmit_size) && !status) {

Drop useless parenthesis.

> +               port->xmit_size = 0;
> +               uart_circ_clear(xmit);
> +               qcom_geni_serial_stop_tx(uport);
> +               goto out_write_wakeup;
> +       }
> +       chunk -= port->xmit_size;
> +
> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
> +       if (chunk > (UART_XMIT_SIZE - tail))
> +               chunk = UART_XMIT_SIZE - tail;
> +       if (chunk > avail)
> +               chunk = avail;
> +
> +       if (!chunk)
> +               goto out_write_wakeup;
> +
> +       qcom_geni_serial_setup_tx(uport, chunk);
> +
> +       remaining = chunk;
> +       while (i < chunk) {

	for (i = 0; i < chunk; ) {

> +               unsigned int tx_bytes;
> +               unsigned int buf = 0;
> +               int c;
> +
> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
> +               for (c = 0; c < tx_bytes ; c++)
> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
> +
> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
> +
> +               i += tx_bytes;
> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> +               uport->icount.tx += tx_bytes;
> +               remaining -= tx_bytes;
> +       }
> +       qcom_geni_serial_poll_tx_done(uport);
> +       port->xmit_size += chunk;
> +out_write_wakeup:
> +       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;
> +
> +       if (uport->suspended)
> +               return IRQ_HANDLED;
> +
> +       spin_lock_irqsave(&uport->lock, flags);
> +       m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
> +       s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
> +       m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
> +       writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +       writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
> +
> +       if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
> +               goto out_unlock;
> +
> +       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_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
> +           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++;

How does break character handling work? I see the accounting here, but
don't see any uart_handle_break() call anywhere.

> +       }
> +
> +       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);
> +
> +out_unlock:
> +       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(&port->se);
> +       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(&port->se);
> +       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(&port->se);
> +       if (!port->rx_fifo_depth) {
> +               dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
> +                                                               __func__);
> +               return -ENXIO;
> +       }

Are these checks verifying the hardware has a proper setting for fifo
depth and width? How is that possible to mess up? Do these ever fail?

> +
> +       uport->fifosize =
> +               (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
> +       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;

port->tx_wm = DEF_TX_WM?

> +}
> +
> +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;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
> +
> +       set_rfr_wm(port);
> +       writel_relaxed(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.
> +        */
> +       port->xfer_mode = GENI_SE_FIFO;
> +       qcom_geni_serial_poll_tx_done(uport);
> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
> +                                               false, true, false);
> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
> +                                               false, false, true);
> +       ret = geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
> +       if (ret) {
> +               dev_err(uport->dev, "%s: Fail\n", __func__);
> +               return ret;
> +       }
> +
> +       geni_se_select_mode(&port->se, port->xfer_mode);
> +       port->port_setup = true;
> +       return ret;
> +}
> +
> +static int qcom_geni_serial_startup(struct uart_port *uport)
> +{
> +       int ret;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       scnprintf(port->name, sizeof(port->name),
> +                 "qcom_serial_geni%d", uport->line);
> +
> +       if (geni_se_read_proto(&port->se) != GENI_SE_UART) {
> +               dev_err(uport->dev, "Invalid FW %d loaded.\n",
> +                                geni_se_read_proto(&port->se));

Please don't read proto twice.

> +               return -ENXIO;
> +       }
> +
> +       get_tx_fifo_size(port);
> +       if (!port->port_setup) {
> +               ret = qcom_geni_serial_port_setup(uport);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
> +                                                       port->name, uport);
> +       if (ret)
> +               dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
> +       return ret;
> +}
> +
> +static unsigned long get_clk_cfg(unsigned long clk_freq)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
> +               if (!(root_freq[i] % clk_freq))
> +                       return root_freq[i];
> +       }
> +       return 0;
> +}
> +
> +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)
> +{
> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);

Can you please inline this function into the caller and put the writels
where the values are calculated? It would reduce the mental work to keep
track of all the variables to find out that they just get written in the
end. Also, this is weirdly placed in the file when get_clk_div_rate()
calls get_clk_cfg() but this function is between them.

> +}
> +
> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
> +{
> +       unsigned long ser_clk;
> +       unsigned long desired_clk;
> +
> +       desired_clk = baud * UART_OVERSAMPLING;
> +       ser_clk = get_clk_cfg(desired_clk);
> +       if (!ser_clk) {
> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
> +                                                               __func__, baud);
> +               return ser_clk;
> +       }
> +
> +       *clk_div = ser_clk / desired_clk;

How wide can clk_div be? It may be better to implement the ser_clk as an
actual clk in the common clk framework, and then have the serial driver
or the i2c driver call clk_set_rate() on that clk and have the CCF
implementation take care of determining the rate that the parent clk can
supply and how it can fit it into the frequency that the divider can
support.

> +       return ser_clk;
> +}
> +
> +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;
> +       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;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, 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_rate = get_clk_div_rate(baud, &clk_div);
> +       if (!clk_rate)
> +               goto out_restart_rx;
> +
> +       uport->uartclk = clk_rate;
> +       clk_set_rate(port->se.clk, clk_rate);
> +       ser_clk_cfg = SER_CLK_EN;
> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);

Drop useless cast.

> +
> +       /* parity */
> +       tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
> +       tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
> +       rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
> +       rx_parity_cfg = readl_relaxed(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 (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);
> +out_restart_rx:
> +       qcom_geni_serial_start_rx(uport);
> +}
> +
> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
> +{
> +       return !readl_relaxed(uport->membase + SE_GENI_TX_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 *port;
> +       int baud;
> +       int bits = 8;
> +       int parity = 'n';
> +       int flow = 'n';
> +
> +       if (co->index >= GENI_UART_CONS_PORTS  || co->index < 0)
> +               return -ENXIO;
> +
> +       port = get_port_from_line(co->index);
> +       if (IS_ERR(port)) {
> +               pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port));
> +               return PTR_ERR(port);
> +       }
> +
> +       uport = &port->uport;
> +
> +       if (unlikely(!uport->membase))
> +               return -ENXIO;
> +
> +       if (geni_se_resources_on(&port->se)) {
> +               dev_err(port->se.dev, "Error turning on resources\n");
> +               return -ENXIO;
> +       }
> +
> +       if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {

Looks like we're validating the configuration of the DT here. Maybe this
can go into the wrapper code and be put behind some DEBUG_KERNEL check
so we can debug bad bootloader configurations if needed? Especially if
this is the only API that's left exposed from the wrapper to the serial
engine/protocol driver.

> +               geni_se_resources_off(&port->se);
> +               return -ENXIO;
> +       }
> +
> +       if (!port->port_setup) {
> +               port->tx_bytes_pw = 1;
> +               port->rx_bytes_pw = RX_BYTES_PW;
> +               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)

__init

> +{
> +       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_CONS_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 *port = to_dev_port(uport, uport);
> +
> +       if (unlikely(!uart_console(uport)))
> +               return;
> +
> +       if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
> +               geni_se_resources_on(&port->se);
> +       else if (new_state == UART_PM_STATE_OFF &&
> +                       old_state == UART_PM_STATE_ON)
> +               geni_se_resources_off(&port->se);
> +}
> +
> +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 int qcom_geni_serial_probe(struct platform_device *pdev)
> +{
> +       int ret = 0;
> +       int line = -1;
> +       struct qcom_geni_serial_port *port;
> +       struct uart_port *uport;
> +       struct resource *res;
> +       struct uart_driver *drv;
> +
> +       drv = (void *)of_device_get_match_data(&pdev->dev);

Useless cast.

> +       if (!drv) {
> +               dev_err(&pdev->dev, "%s: No matching device found", __func__);
> +               return -ENODEV;
> +       }
> +
> +       if (pdev->dev.of_node)
> +               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))

Useless parenthesis.

> +               return -ENXIO;
> +       port = get_port_from_line(line);
> +       if (IS_ERR(port)) {
> +               ret = PTR_ERR(port);
> +               dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret);
> +               return ret;
> +       }
> +
> +       uport = &port->uport;
> +       /* Don't allow 2 drivers to access the same port */
> +       if (uport->private_data)
> +               return -ENODEV;
> +
> +       uport->dev = &pdev->dev;
> +       port->se.dev = &pdev->dev;
> +       port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
> +       port->se.clk = devm_clk_get(&pdev->dev, "se");
> +       if (IS_ERR(port->se.clk)) {
> +               ret = PTR_ERR(port->se.clk);
> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +               return ret;
> +       }
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       uport->mapbase = res->start;
> +       uport->membase = devm_ioremap_resource(&pdev->dev, res);
> +       if (!uport->membase) {

Check for IS_ERR()

> +               dev_err(&pdev->dev, "Err IO mapping serial iomem");

No need for error message with devm_ioremap_resource()

> +               return -ENOMEM;

return PTR_ERR(..)

Also, I see some serial drivers do the mapping when the port is
requested. That can't be done here?

> +       }
> +       port->se.base = uport->membase;
> +
> +       port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +       port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
> +       port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
> +
> +       uport->irq = platform_get_irq(pdev, 0);
> +       if (uport->irq < 0) {
> +               dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq);
> +               return uport->irq;
> +       }
> +
> +       uport->private_data = drv;
> +       platform_set_drvdata(pdev, port);
> +       port->handle_rx = handle_rx_console;
> +       port->port_setup = false;
> +       return uart_add_one_port(drv, uport);
> +}
> +
> +static int qcom_geni_serial_remove(struct platform_device *pdev)
> +{
> +       struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
> +       struct uart_driver *drv = port->uport.private_data;
> +
> +       uart_remove_one_port(drv, &port->uport);
> +       return 0;
> +}
> +
> +static int __maybe_unused 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(uport->private_data, uport);
> +       return 0;
> +}
> +
> +static int __maybe_unused 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(uport->private_data, uport);
> +               disable_irq(uport->irq);
> +       }
> +       return 0;
> +}
> +
> +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,

Why are these noirq variants? Please add a comment.

> +};
> +
> +static const struct of_device_id qcom_geni_serial_match_table[] = {
> +       { .compatible = "qcom,geni-debug-uart",
> +                               .data = &qcom_geni_console_driver, },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
> +
> +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;

Drop assignment please.

> +
> +       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 = 0;
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-02-28  1:38 ` [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
@ 2018-03-03  0:11     ` Evan Green
  2018-03-03  0:11     ` Evan Green
  1 sibling, 0 replies; 65+ messages in thread
From: Evan Green @ 2018-03-03  0:11 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: Jonathan Corbet, Andy Gross, David Brown, robh+dt, mark.rutland,
	wsa, gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Hello Karthik,

On Tue, Feb 27, 2018 at 5:38 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>
> Signed-off-by: Doug Anderson <dianders@google.com>
> ---
>  drivers/tty/serial/Kconfig            |   11 +
>  drivers/tty/serial/Makefile           |    1 +
>  drivers/tty/serial/qcom_geni_serial.c | 1181 +++++++++++++++++++++++++++++++++
>  3 files changed, 1193 insertions(+)
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index 3682fd3..c6b1500 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1104,6 +1104,17 @@ config SERIAL_MSM_CONSOLE
>         select SERIAL_CORE_CONSOLE
>         select SERIAL_EARLYCON
>
> +config SERIAL_QCOM_GENI
> +       bool "QCOM on-chip GENI based serial port support"
> +       depends on ARCH_QCOM
> +       depends on QCOM_GENI_SE

My understanding is that this has to be "bool" because there's a
console in there, and consoles cannot be built as modules. Stephen is
suggesting splitting this option up into two, so you could have serial
with or without the console. That's fine, and probably the preferred
way. However, you do want to make sure that if serial (or what's soon
to be serial+console) is enabled, that QCOM_GENI_SE has to be built
=y, and not =m. I'd suggest "select QCOM_GENI_SE" in the new
SERIAL_QCOM_GENI_CONSOLE (or whatever it's called). As it is now, if
SERIAL_QCOM_GENI=y and QCOM_GENI_SE=m, there's a build failure.

GENI_SE is allowed to be built as a module if serial is not enabled
and I2C is built as a module. In order to keep the dependency arrows
going in the same direction, you might want the I2C driver to "select
QCOM_GENI_SE" as well, in order to upgrade GENI_SE to y if I2C is y.

> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> new file mode 100644
> index 0000000..8536b7d
> --- /dev/null
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -0,0 +1,1181 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> +
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.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_CONS_PORTS   1
> +#define DEF_FIFO_DEPTH_WORDS   16
> +#define DEF_TX_WM              2
> +#define DEF_FIFO_WIDTH_BITS    32
> +#define UART_CONSOLE_RX_WM     2
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +#define RX_BYTES_PW 1
> +#else
> +#define RX_BYTES_PW 4
> +#endif

This seems fishy to me. Does either setting work? If so, why not just
have one value?

> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> +                               int offset, int bit_field, bool set)
> +{
> +       u32 reg;
> +       struct qcom_geni_serial_port *port;
> +       unsigned int baud;
> +       unsigned int fifo_bits;
> +       unsigned long timeout_us = 20000;
> +
> +       /* Ensure polling is not re-ordered before the prior writes/reads */
> +       mb();
> +
> +       if (uport->private_data) {
> +               port = to_dev_port(uport, uport);
> +               baud = port->cur_baud;
> +               if (!baud)
> +                       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.
> +                */
> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;

This fluff is a little mysterious, can it be explained at all? Do you
think the fluff factor is in units of time (as you have it) or bits?
Time makes sense I guess if we're worried about clock source
differences.

> +
> +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;
> +       bool locked = true;
> +       unsigned long flags;
> +
> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> +
> +       port = get_port_from_line(co->index);
> +       if (IS_ERR(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);

I too am a little lost on the locking here. What exactly is the lock
protecting? Looks like for the most part it's trying to synchronize
with the ISR? What specifically in the ISR? I just wanted to go
through and check to make sure whatever the shared resource is is
appropriately protected.

> +       }
> +}
> +
> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
> +{
> +       u32 i = rx_bytes;
> +       u32 rx_fifo;
> +       unsigned char *buf;
> +       struct tty_port *tport;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       tport = &uport->state->port;
> +       while (i > 0) {
> +               int c;
> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;

Please replace this with a min macro.

> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> +{
> +       int ret = 0;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       struct circ_buf *xmit = &uport->state->xmit;
> +       size_t avail;
> +       size_t remaining;
> +       int i = 0;
> +       u32 status;
> +       unsigned int chunk;
> +       int tail;
> +
> +       chunk = uart_circ_chars_pending(xmit);
> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
> +       /* Both FIFO and framework buffer are drained */
> +       if ((chunk == port->xmit_size) && !status) {
> +               port->xmit_size = 0;
> +               uart_circ_clear(xmit);
> +               qcom_geni_serial_stop_tx(uport);
> +               goto out_write_wakeup;
> +       }
> +       chunk -= port->xmit_size;
> +
> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
> +       if (chunk > (UART_XMIT_SIZE - tail))
> +               chunk = UART_XMIT_SIZE - tail;
> +       if (chunk > avail)
> +               chunk = avail;
> +
> +       if (!chunk)
> +               goto out_write_wakeup;
> +
> +       qcom_geni_serial_setup_tx(uport, chunk);
> +
> +       remaining = chunk;
> +       while (i < chunk) {
> +               unsigned int tx_bytes;
> +               unsigned int buf = 0;
> +               int c;
> +
> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
> +               for (c = 0; c < tx_bytes ; c++)
> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
> +
> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
> +
> +               i += tx_bytes;
> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> +               uport->icount.tx += tx_bytes;
> +               remaining -= tx_bytes;
> +       }
> +       qcom_geni_serial_poll_tx_done(uport);
> +       port->xmit_size += chunk;
> +out_write_wakeup:
> +       uart_write_wakeup(uport);
> +       return ret;
> +}

This function can't fail, please change the return type to void.

> +
> +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);

This is one part of where I'm confused. What are we protecting here
with the lock? disable_irq waits for any pending ISRs to finish
according to its comment, so you know you're not racing with the ISR.

> +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)
> +{
> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
> +}
> +

I agree with Stephen's comment, this should be inlined into the single
place it's called from.

Thanks Karthik!
-Evan

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-03  0:11     ` Evan Green
  0 siblings, 0 replies; 65+ messages in thread
From: Evan Green @ 2018-03-03  0:11 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: Jonathan Corbet, Andy Gross, David Brown, robh+dt, mark.rutland,
	wsa, gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Hello Karthik,

On Tue, Feb 27, 2018 at 5:38 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>
> Signed-off-by: Doug Anderson <dianders@google.com>
> ---
>  drivers/tty/serial/Kconfig            |   11 +
>  drivers/tty/serial/Makefile           |    1 +
>  drivers/tty/serial/qcom_geni_serial.c | 1181 +++++++++++++++++++++++++++++++++
>  3 files changed, 1193 insertions(+)
>  create mode 100644 drivers/tty/serial/qcom_geni_serial.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index 3682fd3..c6b1500 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1104,6 +1104,17 @@ config SERIAL_MSM_CONSOLE
>         select SERIAL_CORE_CONSOLE
>         select SERIAL_EARLYCON
>
> +config SERIAL_QCOM_GENI
> +       bool "QCOM on-chip GENI based serial port support"
> +       depends on ARCH_QCOM
> +       depends on QCOM_GENI_SE

My understanding is that this has to be "bool" because there's a
console in there, and consoles cannot be built as modules. Stephen is
suggesting splitting this option up into two, so you could have serial
with or without the console. That's fine, and probably the preferred
way. However, you do want to make sure that if serial (or what's soon
to be serial+console) is enabled, that QCOM_GENI_SE has to be built
=y, and not =m. I'd suggest "select QCOM_GENI_SE" in the new
SERIAL_QCOM_GENI_CONSOLE (or whatever it's called). As it is now, if
SERIAL_QCOM_GENI=y and QCOM_GENI_SE=m, there's a build failure.

GENI_SE is allowed to be built as a module if serial is not enabled
and I2C is built as a module. In order to keep the dependency arrows
going in the same direction, you might want the I2C driver to "select
QCOM_GENI_SE" as well, in order to upgrade GENI_SE to y if I2C is y.

> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> new file mode 100644
> index 0000000..8536b7d
> --- /dev/null
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -0,0 +1,1181 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> +
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.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_CONS_PORTS   1
> +#define DEF_FIFO_DEPTH_WORDS   16
> +#define DEF_TX_WM              2
> +#define DEF_FIFO_WIDTH_BITS    32
> +#define UART_CONSOLE_RX_WM     2
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +#define RX_BYTES_PW 1
> +#else
> +#define RX_BYTES_PW 4
> +#endif

This seems fishy to me. Does either setting work? If so, why not just
have one value?

> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> +                               int offset, int bit_field, bool set)
> +{
> +       u32 reg;
> +       struct qcom_geni_serial_port *port;
> +       unsigned int baud;
> +       unsigned int fifo_bits;
> +       unsigned long timeout_us = 20000;
> +
> +       /* Ensure polling is not re-ordered before the prior writes/reads */
> +       mb();
> +
> +       if (uport->private_data) {
> +               port = to_dev_port(uport, uport);
> +               baud = port->cur_baud;
> +               if (!baud)
> +                       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.
> +                */
> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;

This fluff is a little mysterious, can it be explained at all? Do you
think the fluff factor is in units of time (as you have it) or bits?
Time makes sense I guess if we're worried about clock source
differences.

> +
> +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;
> +       bool locked = true;
> +       unsigned long flags;
> +
> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> +
> +       port = get_port_from_line(co->index);
> +       if (IS_ERR(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);

I too am a little lost on the locking here. What exactly is the lock
protecting? Looks like for the most part it's trying to synchronize
with the ISR? What specifically in the ISR? I just wanted to go
through and check to make sure whatever the shared resource is is
appropriately protected.

> +       }
> +}
> +
> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
> +{
> +       u32 i = rx_bytes;
> +       u32 rx_fifo;
> +       unsigned char *buf;
> +       struct tty_port *tport;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +
> +       tport = &uport->state->port;
> +       while (i > 0) {
> +               int c;
> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;

Please replace this with a min macro.

> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> +{
> +       int ret = 0;
> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> +       struct circ_buf *xmit = &uport->state->xmit;
> +       size_t avail;
> +       size_t remaining;
> +       int i = 0;
> +       u32 status;
> +       unsigned int chunk;
> +       int tail;
> +
> +       chunk = uart_circ_chars_pending(xmit);
> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
> +       /* Both FIFO and framework buffer are drained */
> +       if ((chunk == port->xmit_size) && !status) {
> +               port->xmit_size = 0;
> +               uart_circ_clear(xmit);
> +               qcom_geni_serial_stop_tx(uport);
> +               goto out_write_wakeup;
> +       }
> +       chunk -= port->xmit_size;
> +
> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
> +       if (chunk > (UART_XMIT_SIZE - tail))
> +               chunk = UART_XMIT_SIZE - tail;
> +       if (chunk > avail)
> +               chunk = avail;
> +
> +       if (!chunk)
> +               goto out_write_wakeup;
> +
> +       qcom_geni_serial_setup_tx(uport, chunk);
> +
> +       remaining = chunk;
> +       while (i < chunk) {
> +               unsigned int tx_bytes;
> +               unsigned int buf = 0;
> +               int c;
> +
> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
> +               for (c = 0; c < tx_bytes ; c++)
> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
> +
> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
> +
> +               i += tx_bytes;
> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> +               uport->icount.tx += tx_bytes;
> +               remaining -= tx_bytes;
> +       }
> +       qcom_geni_serial_poll_tx_done(uport);
> +       port->xmit_size += chunk;
> +out_write_wakeup:
> +       uart_write_wakeup(uport);
> +       return ret;
> +}

This function can't fail, please change the return type to void.

> +
> +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);

This is one part of where I'm confused. What are we protecting here
with the lock? disable_irq waits for any pending ISRs to finish
according to its comment, so you know you're not racing with the ISR.

> +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)
> +{
> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
> +}
> +

I agree with Stephen's comment, this should be inlined into the single
place it's called from.

Thanks Karthik!
-Evan
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-02 20:41     ` Stephen Boyd
@ 2018-03-03  0:58       ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-03  0:58 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan



On 3/2/2018 1:41 PM, Stephen Boyd wrote:
> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
>> 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        |   9 +
>>   drivers/soc/qcom/Makefile       |   1 +
>>   drivers/soc/qcom/qcom-geni-se.c | 971 ++++++++++++++++++++++++++++++++++++++++
>>   include/linux/qcom-geni-se.h    | 247 ++++++++++
>>   4 files changed, 1228 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 e050eb8..cc460d0 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -3,6 +3,15 @@
>>   #
>>   menu "Qualcomm SoC drivers"
>>   
>> +config QCOM_GENI_SE
>> +       tristate "QCOM GENI Serial Engine Driver"
>> +       depends on ARCH_QCOM
> 
> Add || COMPILE_TEST?
Ok.
> 
>> +       help
>> +         This module is used to manage Generic Interface (GENI) firmware based
> 
> s/module/driver?
> 
>> +         Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
>> +         module is also used to manage the common aspects of multiple Serial
> s/module/driver?
Ok.
> 
>> +         Engines present in the QUP.
>> +
>>   config QCOM_GLINK_SSR
>>          tristate "Qualcomm Glink SSR driver"
>>          depends on RPMSG
>> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
>> new file mode 100644
>> index 0000000..61335b8
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom-geni-se.c
>> @@ -0,0 +1,971 @@
>> +// 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/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/qcom-geni-se.h>
> 
> #include <linux/platform_device.h>
Ok
> 
>> +
>> +/**
>> + * DOC: Overview
>> + *
>> + * 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.
>> + */
>> +
>> +/**
>> + * DOC: 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
> 
> The code talks about primary and secondary sequencers, but this hardware
> description doesn't talk about it. Can you add some more information
> here about that aspect too?
Ok.
> 
>> + */
>> +
>> +/**
>> + * DOC: Software description
>> + *
>> + * GENI SE Wrapper driver is structured into 2 parts:
>> + *
>> + * geni_wrapper 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 represents Serial Engine. This part of the driver manages Serial
>> + * Engine information such as clocks, containing QUP Wrapper etc. This part
> 
> Insert a comma here                                           ^
Ok.
> 
>> + * of driver also supports operations(eg. initialize the concerned Serial
> 
> Space
Ok.
                                    ^
> 
>> + * Engine, select between FIFO and DMA mode of operation etc.) that are
>> + * common to all the Serial Engines and are independent of Serial Interfaces.
> 
> Why are Serial Interfaces and Serial Engine always capitalized?
No special reason. I will change it to small letter.
> 
>> + */
>> +
>> +#define MAX_CLK_PERF_LEVEL 32
>> +#define NUM_AHB_CLKS 2
>> +static const char m_ahb_clk[] = "m-ahb";
>> +static const char s_ahb_clk[] = "s-ahb";
> 
> These are used in one place. Inline them?
Ok.
> 
>> +
>> +/**
>> + * @struct geni_wrapper - 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.
>> + * @ahb_clks:          Handle to the primary & secondary AHB clocks.
>> + * @lock:              Lock to protect the device elements.
> 
> What does 'device elements' mean?
It means members of geni_wrapper structure. I will document that way.
> 
>> + * @num_clk_levels:    Number of valid clock levels in clk_perf_tbl.
>> + * @clk_perf_tbl:      Table of clock frequency input to Serial Engine clock.
> 
> Kernel-doc normally doesn't have a full-stop on member descriptions.
Ok. I will remove the full-stop.
> 
>> + */
>> +struct geni_wrapper {
>> +       struct device *dev;
>> +       void __iomem *base;
>> +       struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
>> +       struct mutex lock;
>> +       unsigned int num_clk_levels;
>> +       unsigned long *clk_perf_tbl;
>> +};
>> +
>> +/* Offset of QUP Hardware Version Register */
> 
> Useless comment?
I will remove it.
> 
>> +#define QUP_HW_VER_REG                 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)
>> +
>> +/* Common SE registers */
>> +#define GENI_INIT_CFG_REVISION         0x0
>> +#define GENI_S_INIT_CFG_REVISION       0x4
>> +#define GENI_OUTPUT_CTRL               0x24
>> +#define GENI_CGC_CTRL                  0x28
>> +#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_BYTE_GRAN              0x254
>> +#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_GP_LENGTH            0x910
>> +#define SE_GENI_S_GP_LENGTH            0x914
>> +#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_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_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_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_MAX_BURST            0xd5c
>> +#define SE_DMA_RX_FLUSH                        0xd60
>> +#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_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)
>> +
>> +/* FW_REVISION_RO fields */
>> +#define FW_REV_PROTOCOL_MSK            GENMASK(15, 8)
>> +#define FW_REV_PROTOCOL_SHFT           8
>> +
>> +/* 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_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
>> +
>> +/**
>> + * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
>> + * @se:                        Pointer to the corresponding Serial Engine.
>> + * @major:             Buffer for Major Version field.
>> + * @minor:             Buffer for Minor Version field.
>> + * @step:              Buffer for Step Version field.
>> + */
>> +void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
>> +                               unsigned int *minor, unsigned int *step)
>> +{
>> +       unsigned int version;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       version = readl_relaxed(wrapper->base + QUP_HW_VER_REG);
>> +       *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;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_qup_hw_version);
> 
> Is this used?
SPI controller driver uses this API and it will be uploaded sooner.
> 
>> +
>> +/**
>> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * Return: Protocol value as configured in the serial engine.
>> + */
>> +u32 geni_se_read_proto(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
>> +
>> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_read_proto);
> 
> Is this API really needed outside of this file? It would seem like the
> drivers that implement the protocol, which are child devices, would only
> use this API to confirm that the protocol chosen is for their particular
> protocol.
No, this API is meant for the protocol drivers to confirm that the 
serial engine is programmed with the firmware for the concerned protocol 
before using the serial engine. If the check fails, the protocol drivers 
stop using the serial engine.
> 
> 
>> +
>> +static void geni_se_io_set_mode(void __iomem *base)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(base + SE_IRQ_EN);
>> +       val |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
>> +       val |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
> 
> Drop useless parenthesis please.
Ok.
> 
>> +       writel_relaxed(val, base + SE_IRQ_EN);
>> +
>> +       val = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
>> +       val &= ~GENI_DMA_MODE_EN;
>> +       writel_relaxed(val, base + SE_GENI_DMA_MODE_EN);
>> +
>> +       writel_relaxed(0, base + SE_GSI_EVENT_EN);
>> +}
>> +
>> +static void geni_se_io_init(void __iomem *base)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(base + GENI_CGC_CTRL);
>> +       val |= DEFAULT_CGC_EN;
>> +       writel_relaxed(val, base + GENI_CGC_CTRL);
>> +
>> +       val = readl_relaxed(base + SE_DMA_GENERAL_CFG);
>> +       val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON;
>> +       val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON;
>> +       writel_relaxed(val, base + SE_DMA_GENERAL_CFG);
>> +
>> +       writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL);
>> +       writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +/**
>> + * geni_se_init() - Initialize the GENI Serial Engine
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @rx_wm:     Receive watermark, in units of FIFO words.
>> + * @rx_rfr_wm: Ready-for-receive watermark, in units of 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.
> 
> It never returns an error. Change to void?
Ok.
> 
>> + */
>> +int geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr)
>> +{
>> +       u32 val;
>> +
>> +       geni_se_io_init(se->base);
>> +       geni_se_io_set_mode(se->base);
>> +
>> +       writel_relaxed(rx_wm, se->base + SE_GENI_RX_WATERMARK_REG);
>> +       writel_relaxed(rx_rfr, se->base + SE_GENI_RX_RFR_WATERMARK_REG);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
>> +       val |= M_COMMON_GENI_M_IRQ_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
>> +       val |= S_COMMON_GENI_S_IRQ_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_init);
>> +
>> +static void geni_se_select_fifo_mode(struct geni_se *se)
>> +{
>> +       u32 proto = geni_se_read_proto(se);
>> +       u32 val;
>> +
>> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
>> +       if (proto != GENI_SE_UART) {
>> +               val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN;
>> +               val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
>> +       }
>> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
>> +       if (proto != GENI_SE_UART)
>> +               val |= S_CMD_DONE_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
>> +       val &= ~GENI_DMA_MODE_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
>> +}
>> +
>> +static void geni_se_select_dma_mode(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
>> +       val |= GENI_DMA_MODE_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
>> +}
>> +
>> +/**
>> + * geni_se_select_mode() - Select the serial engine transfer mode
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @mode:      Transfer mode to be selected.
>> + */
>> +void geni_se_select_mode(struct geni_se *se, int mode)
> 
> enum mode?
Ok.
> 
>> +{
>> +       WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA);
>> +
>> +       switch (mode) {
>> +       case GENI_SE_FIFO:
>> +               geni_se_select_fifo_mode(se);
>> +               break;
>> +       case GENI_SE_DMA:
>> +               geni_se_select_dma_mode(se);
>> +               break;
>> +       }
>> +}
>> +EXPORT_SYMBOL(geni_se_select_mode);
>> +
>> +/**
>> + * geni_se_setup_m_cmd() - Setup the primary sequencer
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @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 associated parameters.
>> + */
>> +void geni_se_setup_m_cmd(struct geni_se *se, u32 cmd, u32 params)
>> +{
>> +       u32 m_cmd;
>> +
>> +       m_cmd = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
>> +       writel_relaxed(m_cmd, se->base + SE_GENI_M_CMD0);
>> +}
>> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
>> +
>> +/**
>> + * geni_se_setup_s_cmd() - Setup the secondary sequencer
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @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 associated parameters.
>> + */
>> +void geni_se_setup_s_cmd(struct geni_se *se, u32 cmd, u32 params)
>> +{
>> +       u32 s_cmd;
>> +
>> +       s_cmd = readl_relaxed(se->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);
>> +       writel_relaxed(s_cmd, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to cancel the currently configured command in the
>> + * primary sequencer.
>> + */
>> +void geni_se_cancel_m_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(M_GENI_CMD_CANCEL, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to cancel the currently configured command in the
>> + * secondary sequencer.
>> + */
>> +void geni_se_cancel_s_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(S_GENI_CMD_CANCEL, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to force abort the currently configured command in the
>> + * primary sequencer.
>> + */
>> +void geni_se_abort_m_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(M_GENI_CMD_ABORT, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to force abort the currently configured command in the
>> + * secondary sequencer.
>> + */
>> +void geni_se_abort_s_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(S_GENI_CMD_ABORT, se->base + SE_GENI_S_CMD_CTRL_REG);
>> +}
>> +EXPORT_SYMBOL(geni_se_abort_s_cmd);
> 
> Can these one-liners go into the header file and be marked static
> inline? I would guess call-sites already have se->base in hand, so
> registers might be reused more efficiently and it may result in a single
> store instruction instead of a branch and load/store.
Ok.
> 
>> +
>> +/**
>> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * 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.
>> + */
>> +u32 geni_se_get_tx_fifo_depth(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
>> +
>> +       return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
>> +
>> +/**
>> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * 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
>> + */
>> +u32 geni_se_get_tx_fifo_width(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
>> +
>> +       return (val & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
>> +
>> +/**
>> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * 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
>> + */
>> +u32 geni_se_get_rx_fifo_depth(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + SE_HW_PARAM_1);
>> +
>> +       return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
> 
> These ones too, can probably just be static inline.
Ok. Just for my knowledge - is there any reference guideline regarding 
when to use static inline myself and when to let the compiler do the 
clever thing?
> 
>> +
>> +/**
>> + * DOC: Overview
>> + *
>> + * GENI FIFO packing is highly configurable. TX/RX packing/unpacking consist
>> + * of up to 4 operations, each operation represented by 4 configuration vectors
>> + * of 10 bits programmed in GENI_TX_PACKING_CFG0 and GENI_TX_PACKING_CFG1 for
>> + * TX FIFO and in GENI_RX_PACKING_CFG0 and GENI_RX_PACKING_CFG1 for RX FIFO.
>> + * Refer to below examples for detailed bit-field description.
>> + *
>> + * Example 1: word_size = 7, packing_mode = 4 x 8, msb_to_lsb = 1
>> + *
>> + *        +-----------+-------+-------+-------+-------+
>> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
>> + *        +-----------+-------+-------+-------+-------+
>> + *        | start     | 0x6   | 0xe   | 0x16  | 0x1e  |
>> + *        | direction | 1     | 1     | 1     | 1     |
>> + *        | length    | 6     | 6     | 6     | 6     |
>> + *        | stop      | 0     | 0     | 0     | 1     |
>> + *        +-----------+-------+-------+-------+-------+
>> + *
>> + * Example 2: word_size = 15, packing_mode = 2 x 16, msb_to_lsb = 0
>> + *
>> + *        +-----------+-------+-------+-------+-------+
>> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
>> + *        +-----------+-------+-------+-------+-------+
>> + *        | start     | 0x0   | 0x8   | 0x10  | 0x18  |
>> + *        | direction | 0     | 0     | 0     | 0     |
>> + *        | length    | 7     | 6     | 7     | 6     |
>> + *        | stop      | 0     | 0     | 0     | 1     |
>> + *        +-----------+-------+-------+-------+-------+
>> + *
>> + * Example 3: word_size = 23, packing_mode = 1 x 32, msb_to_lsb = 1
>> + *
>> + *        +-----------+-------+-------+-------+-------+
>> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
>> + *        +-----------+-------+-------+-------+-------+
>> + *        | start     | 0x16  | 0xe   | 0x6   | 0x0   |
>> + *        | direction | 1     | 1     | 1     | 1     |
>> + *        | length    | 7     | 7     | 6     | 0     |
>> + *        | stop      | 0     | 0     | 1     | 0     |
>> + *        +-----------+-------+-------+-------+-------+
>> + *
>> + */
>> +
>> +#define NUM_PACKING_VECTORS 4
>> +#define PACKING_START_SHIFT 5
>> +#define PACKING_DIR_SHIFT 4
>> +#define PACKING_LEN_SHIFT 1
>> +#define PACKING_STOP_BIT BIT(0)
>> +#define PACKING_VECTOR_SHIFT 10
>> +/**
>> + * geni_se_config_packing() - Packing configuration of the serial engine
>> + * @se:                Pointer to the concerned Serial Engine
>> + * @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.
>> + * @tx_cfg:    Flag to configure the TX Packing.
>> + * @rx_cfg:    Flag to configure the RX Packing.
>> + *
>> + * This function is used to configure the packing rules for the current
>> + * transfer.
>> + */
>> +void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words,
>> +                           bool msb_to_lsb, bool tx_cfg, bool rx_cfg)
>> +{
>> +       u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {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)) & ~(BITS_PER_BYTE - 1);
> 
> ALIGN(bpw, BITS_PER_BYTE)?
Ok.
> 
>> +       int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE;
>> +       int i;
>> +
>> +       if (iter <= 0 || iter > NUM_PACKING_VECTORS)
>> +               return;
>> +
>> +       for (i = 0; i < iter; i++) {
>> +               if (temp_bpw < BITS_PER_BYTE)
>> +                       len = temp_bpw - 1;
>> +               else
>> +                       len = BITS_PER_BYTE - 1;
> 
> len = min(temp_bpw, BITS_PER_BYTE) - 1;
Ok.
> 
>> +
>> +               cfg[i] = idx << PACKING_START_SHIFT;
>> +               cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT;
>> +               cfg[i] |= len << PACKING_LEN_SHIFT;
>> +
>> +               if (temp_bpw <= BITS_PER_BYTE) {
>> +                       idx = ((i + 1) * BITS_PER_BYTE) + idx_start;
>> +                       temp_bpw = bpw;
>> +               } else {
>> +                       idx = idx + idx_delta;
>> +                       temp_bpw = temp_bpw - BITS_PER_BYTE;
>> +               }
>> +       }
>> +       cfg[iter - 1] |= PACKING_STOP_BIT;
>> +       cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT);
>> +       cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT);
>> +
>> +       if (tx_cfg) {
>> +               writel_relaxed(cfg0, se->base + SE_GENI_TX_PACKING_CFG0);
>> +               writel_relaxed(cfg1, se->base + SE_GENI_TX_PACKING_CFG1);
>> +       }
>> +       if (rx_cfg) {
>> +               writel_relaxed(cfg0, se->base + SE_GENI_RX_PACKING_CFG0);
>> +               writel_relaxed(cfg1, se->base + SE_GENI_RX_PACKING_CFG1);
>> +       }
>> +
>> +       /*
>> +        * Number of protocol words in each FIFO entry
>> +        * 0 - 4x8, four words in each entry, max word size of 8 bits
>> +        * 1 - 2x16, two words in each entry, max word size of 16 bits
>> +        * 2 - 1x32, one word in each entry, max word size of 32 bits
>> +        * 3 - undefined
>> +        */
>> +       if (pack_words || bpw == 32)
>> +               writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN);
>> +}
>> +EXPORT_SYMBOL(geni_se_config_packing);
>> +
>> +static void geni_se_clks_off(struct geni_se *se)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       clk_disable_unprepare(se->clk);
>> +       clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
>> +                                               wrapper->ahb_clks);
>> +}
>> +
>> +/**
>> + * geni_se_resources_off() - Turn off resources associated with the serial
>> + *                           engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * Return: 0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_off(struct geni_se *se)
>> +{
>> +       int ret;
>> +
>> +       ret = pinctrl_pm_select_sleep_state(se->dev);
>> +       if (ret)
>> +               return ret;
>> +
>> +       geni_se_clks_off(se);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_off);
>> +
>> +static int geni_se_clks_on(struct geni_se *se)
>> +{
>> +       int ret;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       ret = clk_bulk_prepare_enable(ARRAY_SIZE(wrapper->ahb_clks),
>> +                                               wrapper->ahb_clks);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = clk_prepare_enable(se->clk);
>> +       if (ret)
>> +               clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
>> +                                                       wrapper->ahb_clks);
>> +       return ret;
>> +}
>> +
>> +/**
>> + * geni_se_resources_on() - Turn on resources associated with the serial
>> + *                          engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * Return: 0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_on(struct geni_se *se)
>> +{
>> +       int ret = 0;
> 
> Don't assign variables and then reassign them on the next line.
Ok.
> 
>> +
>> +       ret = geni_se_clks_on(se);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = pinctrl_pm_select_default_state(se->dev);
>> +       if (ret)
>> +               geni_se_clks_off(se);
>> +
>> +       return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_on);
> 
> IS there a reason why we can't use runtime PM or normal linux PM
> infrastructure to power on the wrapper and keep it powered while the
> protocol driver is active?
Besides turning on the clocks & pinctrl settings, wrapper also has to do 
the bus scaling votes. The bus scaling votes depend on the individual 
serial interface bandwidth requirements. The bus scaling votes is not 
present currently. But once the support comes in, this function enables 
adding it.
> 
>> +
>> +/**
>> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @tbl:       Table in which the output is returned.
>> + *
>> + * This function is called by the protocol drivers to determine the different
>> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +       unsigned long freq = 0;
>> +       int i;
>> +       int ret = 0;
>> +
>> +       mutex_lock(&wrapper->lock);
>> +       if (wrapper->clk_perf_tbl) {
>> +               *tbl = wrapper->clk_perf_tbl;
>> +               ret = wrapper->num_clk_levels;
>> +               goto out_unlock;
>> +       }
>> +
>> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
>> +                                       sizeof(*wrapper->clk_perf_tbl),
>> +                                       GFP_KERNEL);
>> +       if (!wrapper->clk_perf_tbl) {
>> +               ret = -ENOMEM;
>> +               goto out_unlock;
>> +       }
>> +
>> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
>> +               freq = clk_round_rate(se->clk, freq + 1);
>> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
>> +                       break;
>> +               wrapper->clk_perf_tbl[i] = freq;
>> +       }
>> +       wrapper->num_clk_levels = i;
>> +       *tbl = wrapper->clk_perf_tbl;
>> +       ret = wrapper->num_clk_levels;
>> +out_unlock:
>> +       mutex_unlock(&wrapper->lock);
> 
> Is this lock actually protecting anything? I mean to say, is any more
> than one geni protocol driver calling this function at a time? Or is
> the same geni protocol driver calling this from multiple threads at the
> same time? The lock looks almost useless.
Yes, there is a possibility of multiple I2C instances within the same 
wrapper trying to get this table simultaneously.

As Evan mentioned in the other thread, Bjorn had the comment to move it 
to the probe and remove the lock. I looked into the possibility of it. 
 From the hardware perspective, this table belongs to the wrapper and is 
shared by all the serial engines within the wrapper. But due to software 
implementation reasons, clk_round_rate can be be performed only on the 
clocks that are tagged as DFS compatible and only the serial engine 
clocks are tagged so. At least this was the understanding based on our 
earlier discussion with the concerned folks. We will revisit it and 
check if anything has changed recently.
> 
>> +       return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
>> +
>> +/**
>> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @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 exact multiple of the requested frequency, as provided by the Serial
>> + * Engine clock in order to meet the performance requirements. If there is
>> + * no matching or exact multiple of the requested frequency found, then it
>> + * selects the closest floor frequency, if exact flag is not set.
>> + *
>> + * Return: 0 on success, standard Linux error codes on failure.
>> + */
>> +int geni_se_clk_freq_match(struct geni_se *se, 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(se, &tbl);
>> +       if (num_clk_levels < 0)
>> +               return num_clk_levels;
>> +
>> +       if (num_clk_levels == 0)
>> +               return -EFAULT;
> 
> I believe this would mean userspace thought the syscall faulted.
> Perhaps -EINVAL instead?
Ok.
> 
>> +
>> +       *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)
>> +               return -ENOKEY;
> 
> Interesting error code. Doubtful this is correct because it seems to be
> related to crypto keys.
Ok.
> 
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_freq_match);
>> +
>> +#define GENI_SE_DMA_DONE_EN BIT(0)
>> +#define GENI_SE_DMA_EOT_EN BIT(1)
>> +#define GENI_SE_DMA_AHB_ERR_EN BIT(2)
>> +#define GENI_SE_DMA_EOT_BUF BIT(0)
>> +/**
>> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @buf:               Pointer to the TX buffer.
>> + * @len:               Length of the TX buffer.
>> + *
>> + * This function is used to prepare the buffers for DMA TX.
>> + *
>> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
>> + */
>> +dma_addr_t geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len)
>> +{
>> +       dma_addr_t iova;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +       u32 val;
>> +
>> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE);
>> +       if (dma_mapping_error(wrapper->dev, iova))
>> +               return (dma_addr_t)NULL;
>> +
>> +       val = GENI_SE_DMA_DONE_EN;
>> +       val |= GENI_SE_DMA_EOT_EN;
>> +       val |= GENI_SE_DMA_AHB_ERR_EN;
>> +       writel_relaxed(val, se->base + SE_DMA_TX_IRQ_EN_SET);
>> +       writel_relaxed((u32)iova, se->base + SE_DMA_TX_PTR_L);
> 
> lower_32_bits()
Ok.
> 
>> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_TX_PTR_H);
> 
> upper_32_bits()
Ok.
> 
>> +       writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR);
>> +       writel_relaxed((u32)len, se->base + SE_DMA_TX_LEN);
> 
> Useless cast.
I will remove the casting.
> 
>> +       return iova;
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>> +
>> +/**
>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @buf:               Pointer to the RX buffer.
>> + * @len:               Length of the RX buffer.
>> + *
>> + * This function is used to prepare the buffers for DMA RX.
>> + *
>> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
>> + */
>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
>> +{
>> +       dma_addr_t iova;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +       u32 val;
>> +
>> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
>> +       if (dma_mapping_error(wrapper->dev, iova))
>> +               return (dma_addr_t)NULL;
> 
> Can't return a dma_mapping_error address to the caller and have them
> figure it out?
Earlier we used to return the DMA_ERROR_CODE which has been removed 
recently in arm64 architecture. If we return the dma_mapping_error, then 
the caller also needs the device which encountered the mapping error. 
The serial interface drivers can use their parent currently to resolve 
the mapping error. Once the wrapper starts mapping using IOMMU context 
bank, then the serial interface drivers do not know which device to use 
to know if there is an error.

Having said that, the dma_ops suggestion might help with handling this 
situation. I will look into it further.
> 
>> +
>> +       val = GENI_SE_DMA_DONE_EN;
>> +       val |= GENI_SE_DMA_EOT_EN;
>> +       val |= GENI_SE_DMA_AHB_ERR_EN;
>> +       writel_relaxed(val, se->base + SE_DMA_RX_IRQ_EN_SET);
>> +       writel_relaxed((u32)iova, se->base + SE_DMA_RX_PTR_L);
>> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_RX_PTR_H);
> 
> upper/lower macros again.
Ok.
> 
>> +       /* RX does not have EOT buffer type bit. So just reset RX_ATTR */
>> +       writel_relaxed(0, se->base + SE_DMA_RX_ATTR);
>> +       writel_relaxed((u32)len, se->base + SE_DMA_RX_LEN);
> 
> Drop cast?
Ok.
> 
>> +       return iova;
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
>> +
>> +/**
>> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @iova:              DMA address of the TX buffer.
>> + * @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 geni_se *se, dma_addr_t iova, size_t len)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       if (iova)
>> +               dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
>> +
>> +/**
>> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @iova:              DMA address of the RX buffer.
>> + * @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 geni_se *se, dma_addr_t iova, size_t len)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       if (iova)
>> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
> 
> Instead of having the functions exported, could we set the dma_ops on
> all child devices of the wrapper that this driver populates and then
> implement the DMA ops for those devices here? I assume that there's
> never another DMA master between the wrapper and the serial engine, so I
> think it would work.
This suggestion looks like it will work.
> 
>> +
>> +static int geni_se_probe(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct resource *res;
>> +       struct geni_wrapper *wrapper;
>> +       int ret;
>> +
>> +       wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
>> +       if (!wrapper)
>> +               return -ENOMEM;
>> +
>> +       wrapper->dev = dev;
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       wrapper->base = devm_ioremap_resource(dev, res);
>> +       if (IS_ERR(wrapper->base)) {
>> +               dev_err(dev, "%s: Error mapping the resource\n", __func__);
> 
> Drop error message, devm_ioremap_resource() already does it.
Ok.
> 
>> +               return -EFAULT;
> 
> return PTR_ERR(wrapper->base);
> 
>> +       }
>> +
>> +       wrapper->ahb_clks[0].id = m_ahb_clk;
>> +       wrapper->ahb_clks[1].id = s_ahb_clk;
>> +       ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
>> +       if (ret) {
>> +               dev_err(dev, "Err getting AHB clks %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       mutex_init(&wrapper->lock);
>> +       dev_set_drvdata(dev, wrapper);
>> +       dev_dbg(dev, "GENI SE Driver probed\n");
>> +       return devm_of_platform_populate(dev);
>> +}
>> +
>> +static int geni_se_remove(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct geni_wrapper *wrapper = dev_get_drvdata(dev);
>> +
>> +       kfree(wrapper->clk_perf_tbl);
> 
> Why not devm_kzalloc() this?
I will check it.
> 
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id geni_se_dt_match[] = {
>> +       { .compatible = "qcom,geni-se-qup", },
>> +       {}
>> +};
>> +MODULE_DEVICE_TABLE(of, geni_se_dt_match);
>> +
>> +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,
>> +};
>> +module_platform_driver(geni_se_driver);
>> +
>> +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..4996de7
>> --- /dev/null
>> +++ b/include/linux/qcom-geni-se.h
>> @@ -0,0 +1,247 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#ifndef _LINUX_QCOM_GENI_SE
>> +#define _LINUX_QCOM_GENI_SE
>> +#include <linux/clk.h>
> 
> Please forward declare struct clk and drop this include here.
Ok.
> 
>> +#include <linux/dma-direction.h>
> 
> Drop?
There was a map function which got dropped in v3 patch series. I will 
drop including this header file.
> 
>> +
>> +/* Transfer mode supported by GENI Serial Engines */
>> +enum geni_se_xfer_mode {
>> +       GENI_SE_INVALID,
>> +       GENI_SE_FIFO,
>> +       GENI_SE_DMA,
>> +};
>> +
>> +/* Protocols supported by GENI Serial Engines */
>> +enum geni_se_protocol_types {
>> +       GENI_SE_NONE,
>> +       GENI_SE_SPI,
>> +       GENI_SE_UART,
>> +       GENI_SE_I2C,
>> +       GENI_SE_I3C,
>> +};
>> +
>> +/**
>> + * struct geni_se - GENI Serial Engine
>> + * @base:              Base Address of the Serial Engine's register block.
>> + * @dev:               Pointer to the Serial Engine device.
>> + * @wrapper:           Pointer to the parent QUP Wrapper core.
>> + * @clk:               Handle to the core serial engine clock.
>> + */
>> +struct geni_se {
>> +       void __iomem *base;
>> +       struct device *dev;
>> +       void *wrapper;
> 
> Can this get the geni_wrapper type? It could be opaque if you like.
I am not sure if it is ok to have the children know the details of the 
parent. That is why it is kept as opaque.
> 
>> +       struct clk *clk;
>> +};
>> +
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-03  0:58       ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-03  0:58 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan



On 3/2/2018 1:41 PM, Stephen Boyd wrote:
> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
>> 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        |   9 +
>>   drivers/soc/qcom/Makefile       |   1 +
>>   drivers/soc/qcom/qcom-geni-se.c | 971 ++++++++++++++++++++++++++++++++++++++++
>>   include/linux/qcom-geni-se.h    | 247 ++++++++++
>>   4 files changed, 1228 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 e050eb8..cc460d0 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -3,6 +3,15 @@
>>   #
>>   menu "Qualcomm SoC drivers"
>>   
>> +config QCOM_GENI_SE
>> +       tristate "QCOM GENI Serial Engine Driver"
>> +       depends on ARCH_QCOM
> 
> Add || COMPILE_TEST?
Ok.
> 
>> +       help
>> +         This module is used to manage Generic Interface (GENI) firmware based
> 
> s/module/driver?
> 
>> +         Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
>> +         module is also used to manage the common aspects of multiple Serial
> s/module/driver?
Ok.
> 
>> +         Engines present in the QUP.
>> +
>>   config QCOM_GLINK_SSR
>>          tristate "Qualcomm Glink SSR driver"
>>          depends on RPMSG
>> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
>> new file mode 100644
>> index 0000000..61335b8
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom-geni-se.c
>> @@ -0,0 +1,971 @@
>> +// 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/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/qcom-geni-se.h>
> 
> #include <linux/platform_device.h>
Ok
> 
>> +
>> +/**
>> + * DOC: Overview
>> + *
>> + * 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.
>> + */
>> +
>> +/**
>> + * DOC: 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
> 
> The code talks about primary and secondary sequencers, but this hardware
> description doesn't talk about it. Can you add some more information
> here about that aspect too?
Ok.
> 
>> + */
>> +
>> +/**
>> + * DOC: Software description
>> + *
>> + * GENI SE Wrapper driver is structured into 2 parts:
>> + *
>> + * geni_wrapper 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 represents Serial Engine. This part of the driver manages Serial
>> + * Engine information such as clocks, containing QUP Wrapper etc. This part
> 
> Insert a comma here                                           ^
Ok.
> 
>> + * of driver also supports operations(eg. initialize the concerned Serial
> 
> Space
Ok.
                                    ^
> 
>> + * Engine, select between FIFO and DMA mode of operation etc.) that are
>> + * common to all the Serial Engines and are independent of Serial Interfaces.
> 
> Why are Serial Interfaces and Serial Engine always capitalized?
No special reason. I will change it to small letter.
> 
>> + */
>> +
>> +#define MAX_CLK_PERF_LEVEL 32
>> +#define NUM_AHB_CLKS 2
>> +static const char m_ahb_clk[] = "m-ahb";
>> +static const char s_ahb_clk[] = "s-ahb";
> 
> These are used in one place. Inline them?
Ok.
> 
>> +
>> +/**
>> + * @struct geni_wrapper - 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.
>> + * @ahb_clks:          Handle to the primary & secondary AHB clocks.
>> + * @lock:              Lock to protect the device elements.
> 
> What does 'device elements' mean?
It means members of geni_wrapper structure. I will document that way.
> 
>> + * @num_clk_levels:    Number of valid clock levels in clk_perf_tbl.
>> + * @clk_perf_tbl:      Table of clock frequency input to Serial Engine clock.
> 
> Kernel-doc normally doesn't have a full-stop on member descriptions.
Ok. I will remove the full-stop.
> 
>> + */
>> +struct geni_wrapper {
>> +       struct device *dev;
>> +       void __iomem *base;
>> +       struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
>> +       struct mutex lock;
>> +       unsigned int num_clk_levels;
>> +       unsigned long *clk_perf_tbl;
>> +};
>> +
>> +/* Offset of QUP Hardware Version Register */
> 
> Useless comment?
I will remove it.
> 
>> +#define QUP_HW_VER_REG                 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)
>> +
>> +/* Common SE registers */
>> +#define GENI_INIT_CFG_REVISION         0x0
>> +#define GENI_S_INIT_CFG_REVISION       0x4
>> +#define GENI_OUTPUT_CTRL               0x24
>> +#define GENI_CGC_CTRL                  0x28
>> +#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_BYTE_GRAN              0x254
>> +#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_GP_LENGTH            0x910
>> +#define SE_GENI_S_GP_LENGTH            0x914
>> +#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_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_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_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_MAX_BURST            0xd5c
>> +#define SE_DMA_RX_FLUSH                        0xd60
>> +#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_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)
>> +
>> +/* FW_REVISION_RO fields */
>> +#define FW_REV_PROTOCOL_MSK            GENMASK(15, 8)
>> +#define FW_REV_PROTOCOL_SHFT           8
>> +
>> +/* 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_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
>> +
>> +/**
>> + * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
>> + * @se:                        Pointer to the corresponding Serial Engine.
>> + * @major:             Buffer for Major Version field.
>> + * @minor:             Buffer for Minor Version field.
>> + * @step:              Buffer for Step Version field.
>> + */
>> +void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
>> +                               unsigned int *minor, unsigned int *step)
>> +{
>> +       unsigned int version;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       version = readl_relaxed(wrapper->base + QUP_HW_VER_REG);
>> +       *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;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_qup_hw_version);
> 
> Is this used?
SPI controller driver uses this API and it will be uploaded sooner.
> 
>> +
>> +/**
>> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * Return: Protocol value as configured in the serial engine.
>> + */
>> +u32 geni_se_read_proto(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
>> +
>> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_read_proto);
> 
> Is this API really needed outside of this file? It would seem like the
> drivers that implement the protocol, which are child devices, would only
> use this API to confirm that the protocol chosen is for their particular
> protocol.
No, this API is meant for the protocol drivers to confirm that the 
serial engine is programmed with the firmware for the concerned protocol 
before using the serial engine. If the check fails, the protocol drivers 
stop using the serial engine.
> 
> 
>> +
>> +static void geni_se_io_set_mode(void __iomem *base)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(base + SE_IRQ_EN);
>> +       val |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
>> +       val |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
> 
> Drop useless parenthesis please.
Ok.
> 
>> +       writel_relaxed(val, base + SE_IRQ_EN);
>> +
>> +       val = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
>> +       val &= ~GENI_DMA_MODE_EN;
>> +       writel_relaxed(val, base + SE_GENI_DMA_MODE_EN);
>> +
>> +       writel_relaxed(0, base + SE_GSI_EVENT_EN);
>> +}
>> +
>> +static void geni_se_io_init(void __iomem *base)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(base + GENI_CGC_CTRL);
>> +       val |= DEFAULT_CGC_EN;
>> +       writel_relaxed(val, base + GENI_CGC_CTRL);
>> +
>> +       val = readl_relaxed(base + SE_DMA_GENERAL_CFG);
>> +       val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON;
>> +       val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON;
>> +       writel_relaxed(val, base + SE_DMA_GENERAL_CFG);
>> +
>> +       writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL);
>> +       writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +/**
>> + * geni_se_init() - Initialize the GENI Serial Engine
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @rx_wm:     Receive watermark, in units of FIFO words.
>> + * @rx_rfr_wm: Ready-for-receive watermark, in units of 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.
> 
> It never returns an error. Change to void?
Ok.
> 
>> + */
>> +int geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr)
>> +{
>> +       u32 val;
>> +
>> +       geni_se_io_init(se->base);
>> +       geni_se_io_set_mode(se->base);
>> +
>> +       writel_relaxed(rx_wm, se->base + SE_GENI_RX_WATERMARK_REG);
>> +       writel_relaxed(rx_rfr, se->base + SE_GENI_RX_RFR_WATERMARK_REG);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
>> +       val |= M_COMMON_GENI_M_IRQ_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
>> +       val |= S_COMMON_GENI_S_IRQ_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_init);
>> +
>> +static void geni_se_select_fifo_mode(struct geni_se *se)
>> +{
>> +       u32 proto = geni_se_read_proto(se);
>> +       u32 val;
>> +
>> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
>> +       if (proto != GENI_SE_UART) {
>> +               val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN;
>> +               val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
>> +       }
>> +       writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN);
>> +       if (proto != GENI_SE_UART)
>> +               val |= S_CMD_DONE_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
>> +       val &= ~GENI_DMA_MODE_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
>> +}
>> +
>> +static void geni_se_select_dma_mode(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       writel_relaxed(0, se->base + SE_GSI_EVENT_EN);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR);
>> +       writel_relaxed(0xffffffff, se->base + SE_IRQ_EN);
>> +
>> +       val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
>> +       val |= GENI_DMA_MODE_EN;
>> +       writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
>> +}
>> +
>> +/**
>> + * geni_se_select_mode() - Select the serial engine transfer mode
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @mode:      Transfer mode to be selected.
>> + */
>> +void geni_se_select_mode(struct geni_se *se, int mode)
> 
> enum mode?
Ok.
> 
>> +{
>> +       WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA);
>> +
>> +       switch (mode) {
>> +       case GENI_SE_FIFO:
>> +               geni_se_select_fifo_mode(se);
>> +               break;
>> +       case GENI_SE_DMA:
>> +               geni_se_select_dma_mode(se);
>> +               break;
>> +       }
>> +}
>> +EXPORT_SYMBOL(geni_se_select_mode);
>> +
>> +/**
>> + * geni_se_setup_m_cmd() - Setup the primary sequencer
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @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 associated parameters.
>> + */
>> +void geni_se_setup_m_cmd(struct geni_se *se, u32 cmd, u32 params)
>> +{
>> +       u32 m_cmd;
>> +
>> +       m_cmd = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
>> +       writel_relaxed(m_cmd, se->base + SE_GENI_M_CMD0);
>> +}
>> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
>> +
>> +/**
>> + * geni_se_setup_s_cmd() - Setup the secondary sequencer
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @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 associated parameters.
>> + */
>> +void geni_se_setup_s_cmd(struct geni_se *se, u32 cmd, u32 params)
>> +{
>> +       u32 s_cmd;
>> +
>> +       s_cmd = readl_relaxed(se->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);
>> +       writel_relaxed(s_cmd, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to cancel the currently configured command in the
>> + * primary sequencer.
>> + */
>> +void geni_se_cancel_m_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(M_GENI_CMD_CANCEL, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to cancel the currently configured command in the
>> + * secondary sequencer.
>> + */
>> +void geni_se_cancel_s_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(S_GENI_CMD_CANCEL, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to force abort the currently configured command in the
>> + * primary sequencer.
>> + */
>> +void geni_se_abort_m_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(M_GENI_CMD_ABORT, se->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
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * This function is used to force abort the currently configured command in the
>> + * secondary sequencer.
>> + */
>> +void geni_se_abort_s_cmd(struct geni_se *se)
>> +{
>> +       writel_relaxed(S_GENI_CMD_ABORT, se->base + SE_GENI_S_CMD_CTRL_REG);
>> +}
>> +EXPORT_SYMBOL(geni_se_abort_s_cmd);
> 
> Can these one-liners go into the header file and be marked static
> inline? I would guess call-sites already have se->base in hand, so
> registers might be reused more efficiently and it may result in a single
> store instruction instead of a branch and load/store.
Ok.
> 
>> +
>> +/**
>> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * 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.
>> + */
>> +u32 geni_se_get_tx_fifo_depth(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
>> +
>> +       return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
>> +
>> +/**
>> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * 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
>> + */
>> +u32 geni_se_get_tx_fifo_width(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + SE_HW_PARAM_0);
>> +
>> +       return (val & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
>> +
>> +/**
>> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * 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
>> + */
>> +u32 geni_se_get_rx_fifo_depth(struct geni_se *se)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(se->base + SE_HW_PARAM_1);
>> +
>> +       return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
> 
> These ones too, can probably just be static inline.
Ok. Just for my knowledge - is there any reference guideline regarding 
when to use static inline myself and when to let the compiler do the 
clever thing?
> 
>> +
>> +/**
>> + * DOC: Overview
>> + *
>> + * GENI FIFO packing is highly configurable. TX/RX packing/unpacking consist
>> + * of up to 4 operations, each operation represented by 4 configuration vectors
>> + * of 10 bits programmed in GENI_TX_PACKING_CFG0 and GENI_TX_PACKING_CFG1 for
>> + * TX FIFO and in GENI_RX_PACKING_CFG0 and GENI_RX_PACKING_CFG1 for RX FIFO.
>> + * Refer to below examples for detailed bit-field description.
>> + *
>> + * Example 1: word_size = 7, packing_mode = 4 x 8, msb_to_lsb = 1
>> + *
>> + *        +-----------+-------+-------+-------+-------+
>> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
>> + *        +-----------+-------+-------+-------+-------+
>> + *        | start     | 0x6   | 0xe   | 0x16  | 0x1e  |
>> + *        | direction | 1     | 1     | 1     | 1     |
>> + *        | length    | 6     | 6     | 6     | 6     |
>> + *        | stop      | 0     | 0     | 0     | 1     |
>> + *        +-----------+-------+-------+-------+-------+
>> + *
>> + * Example 2: word_size = 15, packing_mode = 2 x 16, msb_to_lsb = 0
>> + *
>> + *        +-----------+-------+-------+-------+-------+
>> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
>> + *        +-----------+-------+-------+-------+-------+
>> + *        | start     | 0x0   | 0x8   | 0x10  | 0x18  |
>> + *        | direction | 0     | 0     | 0     | 0     |
>> + *        | length    | 7     | 6     | 7     | 6     |
>> + *        | stop      | 0     | 0     | 0     | 1     |
>> + *        +-----------+-------+-------+-------+-------+
>> + *
>> + * Example 3: word_size = 23, packing_mode = 1 x 32, msb_to_lsb = 1
>> + *
>> + *        +-----------+-------+-------+-------+-------+
>> + *        |           | vec_0 | vec_1 | vec_2 | vec_3 |
>> + *        +-----------+-------+-------+-------+-------+
>> + *        | start     | 0x16  | 0xe   | 0x6   | 0x0   |
>> + *        | direction | 1     | 1     | 1     | 1     |
>> + *        | length    | 7     | 7     | 6     | 0     |
>> + *        | stop      | 0     | 0     | 1     | 0     |
>> + *        +-----------+-------+-------+-------+-------+
>> + *
>> + */
>> +
>> +#define NUM_PACKING_VECTORS 4
>> +#define PACKING_START_SHIFT 5
>> +#define PACKING_DIR_SHIFT 4
>> +#define PACKING_LEN_SHIFT 1
>> +#define PACKING_STOP_BIT BIT(0)
>> +#define PACKING_VECTOR_SHIFT 10
>> +/**
>> + * geni_se_config_packing() - Packing configuration of the serial engine
>> + * @se:                Pointer to the concerned Serial Engine
>> + * @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.
>> + * @tx_cfg:    Flag to configure the TX Packing.
>> + * @rx_cfg:    Flag to configure the RX Packing.
>> + *
>> + * This function is used to configure the packing rules for the current
>> + * transfer.
>> + */
>> +void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words,
>> +                           bool msb_to_lsb, bool tx_cfg, bool rx_cfg)
>> +{
>> +       u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {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)) & ~(BITS_PER_BYTE - 1);
> 
> ALIGN(bpw, BITS_PER_BYTE)?
Ok.
> 
>> +       int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE;
>> +       int i;
>> +
>> +       if (iter <= 0 || iter > NUM_PACKING_VECTORS)
>> +               return;
>> +
>> +       for (i = 0; i < iter; i++) {
>> +               if (temp_bpw < BITS_PER_BYTE)
>> +                       len = temp_bpw - 1;
>> +               else
>> +                       len = BITS_PER_BYTE - 1;
> 
> len = min(temp_bpw, BITS_PER_BYTE) - 1;
Ok.
> 
>> +
>> +               cfg[i] = idx << PACKING_START_SHIFT;
>> +               cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT;
>> +               cfg[i] |= len << PACKING_LEN_SHIFT;
>> +
>> +               if (temp_bpw <= BITS_PER_BYTE) {
>> +                       idx = ((i + 1) * BITS_PER_BYTE) + idx_start;
>> +                       temp_bpw = bpw;
>> +               } else {
>> +                       idx = idx + idx_delta;
>> +                       temp_bpw = temp_bpw - BITS_PER_BYTE;
>> +               }
>> +       }
>> +       cfg[iter - 1] |= PACKING_STOP_BIT;
>> +       cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT);
>> +       cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT);
>> +
>> +       if (tx_cfg) {
>> +               writel_relaxed(cfg0, se->base + SE_GENI_TX_PACKING_CFG0);
>> +               writel_relaxed(cfg1, se->base + SE_GENI_TX_PACKING_CFG1);
>> +       }
>> +       if (rx_cfg) {
>> +               writel_relaxed(cfg0, se->base + SE_GENI_RX_PACKING_CFG0);
>> +               writel_relaxed(cfg1, se->base + SE_GENI_RX_PACKING_CFG1);
>> +       }
>> +
>> +       /*
>> +        * Number of protocol words in each FIFO entry
>> +        * 0 - 4x8, four words in each entry, max word size of 8 bits
>> +        * 1 - 2x16, two words in each entry, max word size of 16 bits
>> +        * 2 - 1x32, one word in each entry, max word size of 32 bits
>> +        * 3 - undefined
>> +        */
>> +       if (pack_words || bpw == 32)
>> +               writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN);
>> +}
>> +EXPORT_SYMBOL(geni_se_config_packing);
>> +
>> +static void geni_se_clks_off(struct geni_se *se)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       clk_disable_unprepare(se->clk);
>> +       clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
>> +                                               wrapper->ahb_clks);
>> +}
>> +
>> +/**
>> + * geni_se_resources_off() - Turn off resources associated with the serial
>> + *                           engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * Return: 0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_off(struct geni_se *se)
>> +{
>> +       int ret;
>> +
>> +       ret = pinctrl_pm_select_sleep_state(se->dev);
>> +       if (ret)
>> +               return ret;
>> +
>> +       geni_se_clks_off(se);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_off);
>> +
>> +static int geni_se_clks_on(struct geni_se *se)
>> +{
>> +       int ret;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       ret = clk_bulk_prepare_enable(ARRAY_SIZE(wrapper->ahb_clks),
>> +                                               wrapper->ahb_clks);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = clk_prepare_enable(se->clk);
>> +       if (ret)
>> +               clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks),
>> +                                                       wrapper->ahb_clks);
>> +       return ret;
>> +}
>> +
>> +/**
>> + * geni_se_resources_on() - Turn on resources associated with the serial
>> + *                          engine
>> + * @se:        Pointer to the concerned Serial Engine.
>> + *
>> + * Return: 0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_on(struct geni_se *se)
>> +{
>> +       int ret = 0;
> 
> Don't assign variables and then reassign them on the next line.
Ok.
> 
>> +
>> +       ret = geni_se_clks_on(se);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = pinctrl_pm_select_default_state(se->dev);
>> +       if (ret)
>> +               geni_se_clks_off(se);
>> +
>> +       return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_on);
> 
> IS there a reason why we can't use runtime PM or normal linux PM
> infrastructure to power on the wrapper and keep it powered while the
> protocol driver is active?
Besides turning on the clocks & pinctrl settings, wrapper also has to do 
the bus scaling votes. The bus scaling votes depend on the individual 
serial interface bandwidth requirements. The bus scaling votes is not 
present currently. But once the support comes in, this function enables 
adding it.
> 
>> +
>> +/**
>> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @tbl:       Table in which the output is returned.
>> + *
>> + * This function is called by the protocol drivers to determine the different
>> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +       unsigned long freq = 0;
>> +       int i;
>> +       int ret = 0;
>> +
>> +       mutex_lock(&wrapper->lock);
>> +       if (wrapper->clk_perf_tbl) {
>> +               *tbl = wrapper->clk_perf_tbl;
>> +               ret = wrapper->num_clk_levels;
>> +               goto out_unlock;
>> +       }
>> +
>> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
>> +                                       sizeof(*wrapper->clk_perf_tbl),
>> +                                       GFP_KERNEL);
>> +       if (!wrapper->clk_perf_tbl) {
>> +               ret = -ENOMEM;
>> +               goto out_unlock;
>> +       }
>> +
>> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
>> +               freq = clk_round_rate(se->clk, freq + 1);
>> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
>> +                       break;
>> +               wrapper->clk_perf_tbl[i] = freq;
>> +       }
>> +       wrapper->num_clk_levels = i;
>> +       *tbl = wrapper->clk_perf_tbl;
>> +       ret = wrapper->num_clk_levels;
>> +out_unlock:
>> +       mutex_unlock(&wrapper->lock);
> 
> Is this lock actually protecting anything? I mean to say, is any more
> than one geni protocol driver calling this function at a time? Or is
> the same geni protocol driver calling this from multiple threads at the
> same time? The lock looks almost useless.
Yes, there is a possibility of multiple I2C instances within the same 
wrapper trying to get this table simultaneously.

As Evan mentioned in the other thread, Bjorn had the comment to move it 
to the probe and remove the lock. I looked into the possibility of it. 
 From the hardware perspective, this table belongs to the wrapper and is 
shared by all the serial engines within the wrapper. But due to software 
implementation reasons, clk_round_rate can be be performed only on the 
clocks that are tagged as DFS compatible and only the serial engine 
clocks are tagged so. At least this was the understanding based on our 
earlier discussion with the concerned folks. We will revisit it and 
check if anything has changed recently.
> 
>> +       return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
>> +
>> +/**
>> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
>> + * @se:                Pointer to the concerned Serial Engine.
>> + * @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 exact multiple of the requested frequency, as provided by the Serial
>> + * Engine clock in order to meet the performance requirements. If there is
>> + * no matching or exact multiple of the requested frequency found, then it
>> + * selects the closest floor frequency, if exact flag is not set.
>> + *
>> + * Return: 0 on success, standard Linux error codes on failure.
>> + */
>> +int geni_se_clk_freq_match(struct geni_se *se, 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(se, &tbl);
>> +       if (num_clk_levels < 0)
>> +               return num_clk_levels;
>> +
>> +       if (num_clk_levels == 0)
>> +               return -EFAULT;
> 
> I believe this would mean userspace thought the syscall faulted.
> Perhaps -EINVAL instead?
Ok.
> 
>> +
>> +       *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)
>> +               return -ENOKEY;
> 
> Interesting error code. Doubtful this is correct because it seems to be
> related to crypto keys.
Ok.
> 
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_freq_match);
>> +
>> +#define GENI_SE_DMA_DONE_EN BIT(0)
>> +#define GENI_SE_DMA_EOT_EN BIT(1)
>> +#define GENI_SE_DMA_AHB_ERR_EN BIT(2)
>> +#define GENI_SE_DMA_EOT_BUF BIT(0)
>> +/**
>> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @buf:               Pointer to the TX buffer.
>> + * @len:               Length of the TX buffer.
>> + *
>> + * This function is used to prepare the buffers for DMA TX.
>> + *
>> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
>> + */
>> +dma_addr_t geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len)
>> +{
>> +       dma_addr_t iova;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +       u32 val;
>> +
>> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE);
>> +       if (dma_mapping_error(wrapper->dev, iova))
>> +               return (dma_addr_t)NULL;
>> +
>> +       val = GENI_SE_DMA_DONE_EN;
>> +       val |= GENI_SE_DMA_EOT_EN;
>> +       val |= GENI_SE_DMA_AHB_ERR_EN;
>> +       writel_relaxed(val, se->base + SE_DMA_TX_IRQ_EN_SET);
>> +       writel_relaxed((u32)iova, se->base + SE_DMA_TX_PTR_L);
> 
> lower_32_bits()
Ok.
> 
>> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_TX_PTR_H);
> 
> upper_32_bits()
Ok.
> 
>> +       writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR);
>> +       writel_relaxed((u32)len, se->base + SE_DMA_TX_LEN);
> 
> Useless cast.
I will remove the casting.
> 
>> +       return iova;
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>> +
>> +/**
>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @buf:               Pointer to the RX buffer.
>> + * @len:               Length of the RX buffer.
>> + *
>> + * This function is used to prepare the buffers for DMA RX.
>> + *
>> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
>> + */
>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
>> +{
>> +       dma_addr_t iova;
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +       u32 val;
>> +
>> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
>> +       if (dma_mapping_error(wrapper->dev, iova))
>> +               return (dma_addr_t)NULL;
> 
> Can't return a dma_mapping_error address to the caller and have them
> figure it out?
Earlier we used to return the DMA_ERROR_CODE which has been removed 
recently in arm64 architecture. If we return the dma_mapping_error, then 
the caller also needs the device which encountered the mapping error. 
The serial interface drivers can use their parent currently to resolve 
the mapping error. Once the wrapper starts mapping using IOMMU context 
bank, then the serial interface drivers do not know which device to use 
to know if there is an error.

Having said that, the dma_ops suggestion might help with handling this 
situation. I will look into it further.
> 
>> +
>> +       val = GENI_SE_DMA_DONE_EN;
>> +       val |= GENI_SE_DMA_EOT_EN;
>> +       val |= GENI_SE_DMA_AHB_ERR_EN;
>> +       writel_relaxed(val, se->base + SE_DMA_RX_IRQ_EN_SET);
>> +       writel_relaxed((u32)iova, se->base + SE_DMA_RX_PTR_L);
>> +       writel_relaxed((u32)(iova >> 32), se->base + SE_DMA_RX_PTR_H);
> 
> upper/lower macros again.
Ok.
> 
>> +       /* RX does not have EOT buffer type bit. So just reset RX_ATTR */
>> +       writel_relaxed(0, se->base + SE_DMA_RX_ATTR);
>> +       writel_relaxed((u32)len, se->base + SE_DMA_RX_LEN);
> 
> Drop cast?
Ok.
> 
>> +       return iova;
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
>> +
>> +/**
>> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @iova:              DMA address of the TX buffer.
>> + * @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 geni_se *se, dma_addr_t iova, size_t len)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       if (iova)
>> +               dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
>> +
>> +/**
>> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
>> + * @se:                        Pointer to the concerned Serial Engine.
>> + * @iova:              DMA address of the RX buffer.
>> + * @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 geni_se *se, dma_addr_t iova, size_t len)
>> +{
>> +       struct geni_wrapper *wrapper = se->wrapper;
>> +
>> +       if (iova)
>> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
> 
> Instead of having the functions exported, could we set the dma_ops on
> all child devices of the wrapper that this driver populates and then
> implement the DMA ops for those devices here? I assume that there's
> never another DMA master between the wrapper and the serial engine, so I
> think it would work.
This suggestion looks like it will work.
> 
>> +
>> +static int geni_se_probe(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct resource *res;
>> +       struct geni_wrapper *wrapper;
>> +       int ret;
>> +
>> +       wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
>> +       if (!wrapper)
>> +               return -ENOMEM;
>> +
>> +       wrapper->dev = dev;
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       wrapper->base = devm_ioremap_resource(dev, res);
>> +       if (IS_ERR(wrapper->base)) {
>> +               dev_err(dev, "%s: Error mapping the resource\n", __func__);
> 
> Drop error message, devm_ioremap_resource() already does it.
Ok.
> 
>> +               return -EFAULT;
> 
> return PTR_ERR(wrapper->base);
> 
>> +       }
>> +
>> +       wrapper->ahb_clks[0].id = m_ahb_clk;
>> +       wrapper->ahb_clks[1].id = s_ahb_clk;
>> +       ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
>> +       if (ret) {
>> +               dev_err(dev, "Err getting AHB clks %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       mutex_init(&wrapper->lock);
>> +       dev_set_drvdata(dev, wrapper);
>> +       dev_dbg(dev, "GENI SE Driver probed\n");
>> +       return devm_of_platform_populate(dev);
>> +}
>> +
>> +static int geni_se_remove(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct geni_wrapper *wrapper = dev_get_drvdata(dev);
>> +
>> +       kfree(wrapper->clk_perf_tbl);
> 
> Why not devm_kzalloc() this?
I will check it.
> 
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id geni_se_dt_match[] = {
>> +       { .compatible = "qcom,geni-se-qup", },
>> +       {}
>> +};
>> +MODULE_DEVICE_TABLE(of, geni_se_dt_match);
>> +
>> +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,
>> +};
>> +module_platform_driver(geni_se_driver);
>> +
>> +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..4996de7
>> --- /dev/null
>> +++ b/include/linux/qcom-geni-se.h
>> @@ -0,0 +1,247 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#ifndef _LINUX_QCOM_GENI_SE
>> +#define _LINUX_QCOM_GENI_SE
>> +#include <linux/clk.h>
> 
> Please forward declare struct clk and drop this include here.
Ok.
> 
>> +#include <linux/dma-direction.h>
> 
> Drop?
There was a map function which got dropped in v3 patch series. I will 
drop including this header file.
> 
>> +
>> +/* Transfer mode supported by GENI Serial Engines */
>> +enum geni_se_xfer_mode {
>> +       GENI_SE_INVALID,
>> +       GENI_SE_FIFO,
>> +       GENI_SE_DMA,
>> +};
>> +
>> +/* Protocols supported by GENI Serial Engines */
>> +enum geni_se_protocol_types {
>> +       GENI_SE_NONE,
>> +       GENI_SE_SPI,
>> +       GENI_SE_UART,
>> +       GENI_SE_I2C,
>> +       GENI_SE_I3C,
>> +};
>> +
>> +/**
>> + * struct geni_se - GENI Serial Engine
>> + * @base:              Base Address of the Serial Engine's register block.
>> + * @dev:               Pointer to the Serial Engine device.
>> + * @wrapper:           Pointer to the parent QUP Wrapper core.
>> + * @clk:               Handle to the core serial engine clock.
>> + */
>> +struct geni_se {
>> +       void __iomem *base;
>> +       struct device *dev;
>> +       void *wrapper;
> 
> Can this get the geni_wrapper type? It could be opaque if you like.
I am not sure if it is ok to have the children know the details of the 
parent. That is why it is kept as opaque.
> 
>> +       struct clk *clk;
>> +};
>> +
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-02-28  1:38 ` [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
@ 2018-03-05 23:58     ` Rob Herring
  0 siblings, 0 replies; 65+ messages in thread
From: Rob Herring @ 2018-03-05 23:58 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, mark.rutland, wsa, gregkh,
	linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan

On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian wrote:
> Add device tree binding support for the QCOM GENI SE driver.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
>  .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89 ++++++++++++++++++++++
>  1 file changed, 89 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..fe6a0c0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> @@ -0,0 +1,89 @@
> +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> for Serial Engine Address
> +- #size-cells: 		Must be <1> for Serial Engine Address Size
> +- 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.

s/spi/SPI/

Where's the SPI binding?

> +Please refer below the child node definitions for the supported serial
> +interface protocols.
> +
> +Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
> +
> +Required properties:
> +- compatible:		Must be "qcom,geni-i2c".
> +- reg: 			Must contain QUP register address and length.
> +- interrupts: 		Must contain I2C interrupt.
> +- clock-names: 		Must contain "se".
> +- clocks: 		Serial engine core clock needed by the device.
> +- #address-cells:	Must be <1> for i2c device address.
> +- #size-cells:		Must 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.
> +
> +Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
> +
> +Required properties:
> +- compatible:		Must be "qcom,geni-debug-uart".
> +- reg: 			Must contain UART register location and length.
> +- interrupts: 		Must contain UART core interrupts.
> +- clock-names:		Must contain "se".
> +- clocks:		Serial engine core clock needed by the device.
> +
> +Example:
> +	geniqup@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>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		i2c0: i2c@a94000 {
> +			compatible = "qcom,geni-i2c";
> +			reg = <0xa94000 0x4000>;
> +			interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
> +			clock-names = "se";
> +			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>;
> +		};
> +
> +		uart0: serial@a88000 {
> +			compatible = "qcom,geni-debug-uart";
> +			reg = <0xa88000 0x7000>;
> +			interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
> +			clock-names = "se";
> +			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	[flat|nested] 65+ messages in thread

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
@ 2018-03-05 23:58     ` Rob Herring
  0 siblings, 0 replies; 65+ messages in thread
From: Rob Herring @ 2018-03-05 23:58 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: corbet, andy.gross, david.brown, mark.rutland, wsa, gregkh,
	linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan

On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian wrote:
> Add device tree binding support for the QCOM GENI SE driver.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
>  .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89 ++++++++++++++++++++++
>  1 file changed, 89 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..fe6a0c0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> @@ -0,0 +1,89 @@
> +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> for Serial Engine Address
> +- #size-cells: 		Must be <1> for Serial Engine Address Size
> +- 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.

s/spi/SPI/

Where's the SPI binding?

> +Please refer below the child node definitions for the supported serial
> +interface protocols.
> +
> +Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
> +
> +Required properties:
> +- compatible:		Must be "qcom,geni-i2c".
> +- reg: 			Must contain QUP register address and length.
> +- interrupts: 		Must contain I2C interrupt.
> +- clock-names: 		Must contain "se".
> +- clocks: 		Serial engine core clock needed by the device.
> +- #address-cells:	Must be <1> for i2c device address.
> +- #size-cells:		Must 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.
> +
> +Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
> +
> +Required properties:
> +- compatible:		Must be "qcom,geni-debug-uart".
> +- reg: 			Must contain UART register location and length.
> +- interrupts: 		Must contain UART core interrupts.
> +- clock-names:		Must contain "se".
> +- clocks:		Serial engine core clock needed by the device.
> +
> +Example:
> +	geniqup@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>;
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		i2c0: i2c@a94000 {
> +			compatible = "qcom,geni-i2c";
> +			reg = <0xa94000 0x4000>;
> +			interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
> +			clock-names = "se";
> +			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>;
> +		};
> +
> +		uart0: serial@a88000 {
> +			compatible = "qcom,geni-debug-uart";
> +			reg = <0xa88000 0x7000>;
> +			interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
> +			clock-names = "se";
> +			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
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-03-02 22:11     ` Stephen Boyd
@ 2018-03-06  0:51       ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-06  0:51 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/2/2018 3:11 PM, Stephen Boyd wrote:
> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
>> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
>> index 3682fd3..c6b1500 100644
>> --- a/drivers/tty/serial/Kconfig
>> +++ b/drivers/tty/serial/Kconfig
>> @@ -1104,6 +1104,17 @@ config SERIAL_MSM_CONSOLE
>>          select SERIAL_CORE_CONSOLE
>>          select SERIAL_EARLYCON
>>   
>> +config SERIAL_QCOM_GENI
>> +       bool "QCOM on-chip GENI based serial port support"
> 
> This can be tristate.
> 
>> +       depends on ARCH_QCOM
> 
> || COMPILE_TEST
> ?
Ok.
> 
>> +       depends on QCOM_GENI_SE
>> +       select SERIAL_CORE
> 
> This can stay.
> 
>> +       select SERIAL_CORE_CONSOLE
>> +       select SERIAL_EARLYCON
> 
> These two can go to a new config option, like SERIAL_QCOM_GENI_CONSOLE,
> and that would be bool. Please take a look at the existing SERIAL_MSM
> and SERIAL_MSM_CONSOLE setup to understand how to do it.
Ok.
> 
>> +       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/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
>> new file mode 100644
>> index 0000000..8536b7d
>> --- /dev/null
>> +++ b/drivers/tty/serial/qcom_geni_serial.c
>> @@ -0,0 +1,1181 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
>> +
>> +#include <linux/console.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.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_CONS_PORTS   1
>> +#define DEF_FIFO_DEPTH_WORDS   16
>> +#define DEF_TX_WM              2
>> +#define DEF_FIFO_WIDTH_BITS    32
>> +#define UART_CONSOLE_RX_WM     2
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +#define RX_BYTES_PW 1
>> +#else
>> +#define RX_BYTES_PW 4
>> +#endif
>> +
>> +struct qcom_geni_serial_port {
>> +       struct uart_port uport;
>> +       struct geni_se se;
>> +       char name[20];
>> +       u32 tx_fifo_depth;
>> +       u32 tx_fifo_width;
>> +       u32 rx_fifo_depth;
>> +       u32 tx_wm;
>> +       u32 rx_wm;
>> +       u32 rx_rfr;
>> +       int xfer_mode;
> 
> Can this be an enum?
Ok.
> 
>> +       bool port_setup;
> 
> Maybe just 'setup'? Port is in the type already.
Ok.
> 
>> +       int (*handle_rx)(struct uart_port *uport,
>> +                               u32 rx_bytes, bool drop_rx);
> 
> s/rx_bytes/bytes/
> s/drop_rx/drop/
Ok.
> 
>> +       unsigned int xmit_size;
>> +       unsigned int cur_baud;
> 
> s/cur//
Ok.
> 
>> +       unsigned int tx_bytes_pw;
>> +       unsigned int rx_bytes_pw;
>> +};
>> +
>> +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,
>> +                               u32 rx_bytes, bool drop_rx);
> 
> s/rx_bytes/bytes/
> s/drop_rx/drop/
Ok.
> 
>> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
>> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +                               int offset, int bit_field, bool set);
> 
> No need to forward declare this?
I will check and remove if not required.
> 
> s/bit_// ?
Ok.
> 
>> +static void qcom_geni_serial_stop_rx(struct uart_port *uport);
>> +
>> +static atomic_t uart_line_id = ATOMIC_INIT(0);
> 
> Do we need this? How about rely on DT to always have aliases instead?
> Given we only have one port I don't actually understand how this is
> supposed to work anyway.
Ok. I will remove it and rely on DT always having alias.
> 
>> +static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
>> +                                       32000000, 48000000, 64000000, 80000000,
>> +                                       96000000, 100000000};
>> +
>> +#define to_dev_port(ptr, member) \
>> +               container_of(ptr, struct qcom_geni_serial_port, member)
>> +
>> +static struct qcom_geni_serial_port qcom_geni_console_port;
> 
> Why singleton? Couldn't there be many?
Our current use-case does not need more than one instance. But more 
instances can be added if desired.
> 
>> +
>> +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)
>> +{
>> +       if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> 
> Drop useless parenthesis please.
Ok.
> 
>> +               return ERR_PTR(-ENXIO);
>> +       return &qcom_geni_console_port;
>> +}
>> +
>> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +                               int offset, int bit_field, bool set)
>> +{
>> +       u32 reg;
>> +       struct qcom_geni_serial_port *port;
>> +       unsigned int baud;
>> +       unsigned int fifo_bits;
>> +       unsigned long timeout_us = 20000;
>> +
>> +       /* Ensure polling is not re-ordered before the prior writes/reads */
>> +       mb();
>> +
>> +       if (uport->private_data) {
>> +               port = to_dev_port(uport, uport);
>> +               baud = port->cur_baud;
>> +               if (!baud)
>> +                       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.
> 
> Bad space here                           ^
> 
I will fix it.
>> +                */
>> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
>> +       }
>> +
>> +       return !readl_poll_timeout_atomic(uport->membase + offset, reg,
>> +                        (bool)(reg & bit_field) == set, 10, timeout_us);
>> +}
>> +
>> +static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
>> +{
>> +       u32 m_cmd;
>> +
>> +       writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
>> +       m_cmd = UART_START_TX << M_OPCODE_SHFT;
>> +       writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
>> +}
>> +
>> +static void qcom_geni_serial_poll_tx_done(struct uart_port *uport)
>> +{
>> +       int done;
>> +       u32 irq_clear = M_CMD_DONE_EN;
>> +
>> +       done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_CMD_DONE_EN, true);
>> +       if (!done) {
>> +               writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
>> +                                               SE_GENI_M_CMD_CTRL_REG);
>> +               irq_clear |= M_CMD_ABORT_EN;
>> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                                       M_CMD_ABORT_EN, true);
>> +       }
>> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
>> +{
>> +       u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
>> +
>> +       writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
>> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +                                       S_GENI_CMD_ABORT, false);
>> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +       writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +static int qcom_geni_serial_get_char(struct uart_port *uport)
>> +{
>> +       u32 rx_fifo;
>> +       u32 status;
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
>> +       writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
>> +       writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +
>> +       /*
>> +        * Ensure the writes to clear interrupts is not re-ordered after
>> +        * reading the data.
>> +        */
>> +       mb();
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
>> +       if (!(status & RX_FIFO_WC_MSK))
>> +               return NO_POLL_CHAR;
>> +
>> +       rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
>> +       return rx_fifo & 0xff;
>> +}
>> +
>> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
>> +                                                       unsigned char c)
>> +{
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
>> +       qcom_geni_serial_setup_tx(uport, 1);
>> +       WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_TX_FIFO_WATERMARK_EN, true));
>> +       writel_relaxed((u32)c, uport->membase + SE_GENI_TX_FIFOn);
> 
> Drop useless cast.
Ok.
> 
>> +       writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
>> +                                                       SE_GENI_M_IRQ_CLEAR);
>> +       qcom_geni_serial_poll_tx_done(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)
>> +{
>> +       writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
>> +}
>> +
>> +static void
>> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
>> +                                unsigned int count)
>> +{
>> +       int new_line = 0;
> 
> Drop
Ok.
> 
>> +       int i;
>> +       u32 bytes_to_send = count;
>> +
>> +       for (i = 0; i < count; i++) {
>> +               if (s[i] == '\n')
>> +                       new_line++;
> 
> bytes_to_send++;
Ok.
> 
>> +       }
>> +
>> +       bytes_to_send += new_line;
> 
> Drop.
Ok.
> 
>> +       writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
>> +       qcom_geni_serial_setup_tx(uport, bytes_to_send);
>> +       i = 0;
>> +       while (i < count) {
> 
> 	for (i = 0; i < count; ) {
> 
> would be more normal, but ok.
> 
>> +               size_t chars_to_write = 0;
>> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))
> 
> Does this ever timeout? So many nested while loops makes it hard to
> follow.
Yes. Based on the baud rate of 115200 and the FIFO Depth & Width of (16 
* 32), the poll should not take more than 5 ms under the timeout scenario.
> 
>> +                       break;
>> +               chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2);
>> +               uart_console_write(uport, (s + i), chars_to_write,
> 
> Drop useless parenthesis please.
Ok.
> 
>> +                                               qcom_geni_serial_wr_char);
>> +               writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
>> +                                                       SE_GENI_M_IRQ_CLEAR);
>> +               i += chars_to_write;
>> +       }
>> +       qcom_geni_serial_poll_tx_done(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;
>> +       bool locked = true;
>> +       unsigned long flags;
>> +
>> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
>> +
>> +       port = get_port_from_line(co->index);
>> +       if (IS_ERR(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);
> 
> So if oops is in progress, and we didn't lock here, we don't output
> data? I'd think we would always want to write to the fifo, just make the
> lock grab/release conditional.
If we fail to grab the lock, then there is another active writer. So 
trying to write to the fifo will put the hardware in bad state because 
writer has programmed the hardware to write 'x' number of words and this 
thread will over-write it with 'y' number of words. Also the data that 
you might see in the console might be garbled.
> 
>> +               spin_unlock_irqrestore(&uport->lock, flags);
>> +       }
>> +}
>> +
>> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
>> +{
>> +       u32 i = rx_bytes;
>> +       u32 rx_fifo;
>> +       unsigned char *buf;
>> +       struct tty_port *tport;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       tport = &uport->state->port;
>> +       while (i > 0) {
>> +               int c;
>> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;
>> +
>> +               rx_fifo = readl_relaxed(uport->membase + SE_GENI_RX_FIFOn);
> 
> Please use ioread32_rep(..., 1) here.
Ok.
> 
>> +               i -= bytes;
>> +               if (drop)
>> +                       continue;
>> +               buf = (unsigned char *)&rx_fifo;
> 
> So that this cast becomes unnecessary, and endian agnostic.
Ok.
> 
>> +
>> +               for (c = 0; c < bytes; c++) {
>> +                       int sysrq;
>> +
>> +                       uport->icount.rx++;
>> +                       sysrq = uart_handle_sysrq_char(uport, buf[c]);
> 
> And so this does the right thing in whatever world we live in.
Ok.
> 
>> +                       if (!sysrq)
>> +                               tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
>> +               }
>> +       }
>> +       if (!drop)
>> +               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)
>> +{
>> +       u32 irq_en;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       u32 status;
>> +
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +               if (status & M_GENI_CMD_ACTIVE)
>> +                       return;
>> +
>> +               if (!qcom_geni_serial_tx_empty(uport))
>> +                       return;
>> +
>> +               /*
>> +                * Ensure writing to IRQ_EN & watermark registers are not
>> +                * re-ordered before checking the status of the Serial
>> +                * Engine and TX FIFO
>> +                */
>> +               mb();
>> +
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +               irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
>> +
>> +               writel_relaxed(port->tx_wm, uport->membase +
>> +                                               SE_GENI_TX_WATERMARK_REG);
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       }
>> +}
>> +
>> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
>> +{
>> +       u32 irq_en;
>> +       u32 status;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +       irq_en &= ~M_CMD_DONE_EN;
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               irq_en &= ~M_TX_FIFO_WATERMARK_EN;
>> +               writel_relaxed(0, uport->membase +
>> +                                    SE_GENI_TX_WATERMARK_REG);
>> +       }
>> +       port->xmit_size = 0;
>> +       writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       /* Possible stop tx is called multiple times. */
>> +       if (!(status & M_GENI_CMD_ACTIVE))
>> +               return;
>> +
>> +       /*
>> +        * Ensure cancel command write is not re-ordered before checking
>> +        * checking the status of the Primary Sequencer.
>> +        */
>> +       mb();
>> +
>> +       geni_se_cancel_m_cmd(&port->se);
>> +       if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_CMD_CANCEL_EN, true)) {
>> +               geni_se_abort_m_cmd(&port->se);
>> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_CMD_ABORT_EN, true);
>> +               writel_relaxed(M_CMD_ABORT_EN, uport->membase +
>> +                                                       SE_GENI_M_IRQ_CLEAR);
>> +       }
>> +       writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
>> +{
>> +       u32 irq_en;
>> +       u32 status;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       if (status & S_GENI_CMD_ACTIVE)
>> +               qcom_geni_serial_stop_rx(uport);
>> +
>> +       /*
>> +        * Ensure setup command write is not re-ordered before checking
>> +        * checking the status of the Secondary Sequencer.
>> +        */
>> +       mb();
>> +
>> +       geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
>> +
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
>> +               irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
>> +
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +               irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       }
>> +}
>> +
>> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
>> +{
>> +       u32 irq_en;
>> +       u32 status;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       u32 irq_clear = S_CMD_DONE_EN;
>> +
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
>> +               irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
>> +
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +               irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       }
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       /* Possible stop rx is called multiple times. */
>> +       if (!(status & S_GENI_CMD_ACTIVE))
>> +               return;
>> +
>> +       /*
>> +        * Ensure cancel command write is not re-ordered before checking
>> +        * checking the status of the Secondary Sequencer.
> 
> Each of these comments has 'checking' twice.
I will fix the comments.
> 
>> +        */
>> +       mb();
>> +
>> +       geni_se_cancel_s_cmd(&port->se);
>> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +                                       S_GENI_CMD_CANCEL, false);
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +       if (status & S_GENI_CMD_ACTIVE)
>> +               qcom_geni_serial_abort_rx(uport);
>> +}
>> +
>> +static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
> 
> s/drop_rx/drop/
Ok.
> 
>> +{
>> +       u32 status;
>> +       u32 word_cnt;
>> +       u32 last_word_byte_cnt;
>> +       u32 last_word_partial;
>> +       u32 total_bytes;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
>> +       word_cnt = status & RX_FIFO_WC_MSK;
>> +       last_word_partial = status & RX_LAST;
>> +       last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
>> +                                               RX_LAST_BYTE_VALID_SHFT;
>> +
>> +       if (!word_cnt)
>> +               return;
>> +       total_bytes = port->rx_bytes_pw * (word_cnt - 1);
>> +       if (last_word_partial && last_word_byte_cnt)
>> +               total_bytes += last_word_byte_cnt;
>> +       else
>> +               total_bytes += port->rx_bytes_pw;
>> +       port->handle_rx(uport, total_bytes, drop_rx);
>> +}
>> +
>> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
>> +{
>> +       int ret = 0;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       struct circ_buf *xmit = &uport->state->xmit;
>> +       size_t avail;
>> +       size_t remaining;
>> +       int i = 0;
>> +       u32 status;
>> +       unsigned int chunk;
>> +       int tail;
>> +
>> +       chunk = uart_circ_chars_pending(xmit);
>> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
>> +       /* Both FIFO and framework buffer are drained */
>> +       if ((chunk == port->xmit_size) && !status) {
> 
> Drop useless parenthesis.
Ok.
> 
>> +               port->xmit_size = 0;
>> +               uart_circ_clear(xmit);
>> +               qcom_geni_serial_stop_tx(uport);
>> +               goto out_write_wakeup;
>> +       }
>> +       chunk -= port->xmit_size;
>> +
>> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
>> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
>> +       if (chunk > (UART_XMIT_SIZE - tail))
>> +               chunk = UART_XMIT_SIZE - tail;
>> +       if (chunk > avail)
>> +               chunk = avail;
>> +
>> +       if (!chunk)
>> +               goto out_write_wakeup;
>> +
>> +       qcom_geni_serial_setup_tx(uport, chunk);
>> +
>> +       remaining = chunk;
>> +       while (i < chunk) {
> 
> 	for (i = 0; i < chunk; ) {
Ok.
> 
>> +               unsigned int tx_bytes;
>> +               unsigned int buf = 0;
>> +               int c;
>> +
>> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
>> +               for (c = 0; c < tx_bytes ; c++)
>> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
>> +
>> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
>> +
>> +               i += tx_bytes;
>> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
>> +               uport->icount.tx += tx_bytes;
>> +               remaining -= tx_bytes;
>> +       }
>> +       qcom_geni_serial_poll_tx_done(uport);
>> +       port->xmit_size += chunk;
>> +out_write_wakeup:
>> +       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;
>> +
>> +       if (uport->suspended)
>> +               return IRQ_HANDLED;
>> +
>> +       spin_lock_irqsave(&uport->lock, flags);
>> +       m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
>> +       s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
>> +       m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +       writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +       writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +
>> +       if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
>> +               goto out_unlock;
>> +
>> +       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_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
>> +           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++;
> 
> How does break character handling work? I see the accounting here, but
> don't see any uart_handle_break() call anywhere.
The reason it is not added is because the hardware does not indicate 
when the break character occured. It can be any one of the FIFO words. 
The statistics is updated to give an idea that the break happened. We 
can add uart_handle_break() but it may not be at an accurate position 
for the above mentioned reason.
> 
>> +       }
>> +
>> +       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);
>> +
>> +out_unlock:
>> +       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(&port->se);
>> +       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(&port->se);
>> +       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(&port->se);
>> +       if (!port->rx_fifo_depth) {
>> +               dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
>> +                                                               __func__);
>> +               return -ENXIO;
>> +       }
> 
> Are these checks verifying the hardware has a proper setting for fifo
> depth and width? How is that possible to mess up? Do these ever fail?
We haven't seen a failure yet. I can drop the check and rely on the fact 
that the hardware is programmed correctly.
> 
>> +
>> +       uport->fifosize =
>> +               (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
>> +       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;
> 
> port->tx_wm = DEF_TX_WM?
Ok.
> 
>> +}
>> +
>> +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;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
>> +
>> +       set_rfr_wm(port);
>> +       writel_relaxed(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.
>> +        */
>> +       port->xfer_mode = GENI_SE_FIFO;
>> +       qcom_geni_serial_poll_tx_done(uport);
>> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
>> +                                               false, true, false);
>> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
>> +                                               false, false, true);
>> +       ret = geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
>> +       if (ret) {
>> +               dev_err(uport->dev, "%s: Fail\n", __func__);
>> +               return ret;
>> +       }
>> +
>> +       geni_se_select_mode(&port->se, port->xfer_mode);
>> +       port->port_setup = true;
>> +       return ret;
>> +}
>> +
>> +static int qcom_geni_serial_startup(struct uart_port *uport)
>> +{
>> +       int ret;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       scnprintf(port->name, sizeof(port->name),
>> +                 "qcom_serial_geni%d", uport->line);
>> +
>> +       if (geni_se_read_proto(&port->se) != GENI_SE_UART) {
>> +               dev_err(uport->dev, "Invalid FW %d loaded.\n",
>> +                                geni_se_read_proto(&port->se));
> 
> Please don't read proto twice.
Ok.
> 
>> +               return -ENXIO;
>> +       }
>> +
>> +       get_tx_fifo_size(port);
>> +       if (!port->port_setup) {
>> +               ret = qcom_geni_serial_port_setup(uport);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
>> +                                                       port->name, uport);
>> +       if (ret)
>> +               dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
>> +       return ret;
>> +}
>> +
>> +static unsigned long get_clk_cfg(unsigned long clk_freq)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
>> +               if (!(root_freq[i] % clk_freq))
>> +                       return root_freq[i];
>> +       }
>> +       return 0;
>> +}
>> +
>> +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)
>> +{
>> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
>> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
>> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
>> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
>> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
> 
> Can you please inline this function into the caller and put the writels
> where the values are calculated? It would reduce the mental work to keep
> track of all the variables to find out that they just get written in the
> end. Also, this is weirdly placed in the file when get_clk_div_rate()
> calls get_clk_cfg() but this function is between them.
Bjorn had a similar comment and there I mentioned that the writes are 
required during early console setup as well. Since the popular vote is 
towards inlining these writes, I will update it accordingly.
> 
>> +}
>> +
>> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
>> +{
>> +       unsigned long ser_clk;
>> +       unsigned long desired_clk;
>> +
>> +       desired_clk = baud * UART_OVERSAMPLING;
>> +       ser_clk = get_clk_cfg(desired_clk);
>> +       if (!ser_clk) {
>> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
>> +                                                               __func__, baud);
>> +               return ser_clk;
>> +       }
>> +
>> +       *clk_div = ser_clk / desired_clk;
> 
> How wide can clk_div be? It may be better to implement the ser_clk as an
> actual clk in the common clk framework, and then have the serial driver
> or the i2c driver call clk_set_rate() on that clk and have the CCF
> implementation take care of determining the rate that the parent clk can
> supply and how it can fit it into the frequency that the divider can
> support.
Based on my current expertise with the CCF, I will address this comment 
in a later patchset than the next one.
> 
>> +       return ser_clk;
>> +}
>> +
>> +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;
>> +       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;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, 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_rate = get_clk_div_rate(baud, &clk_div);
>> +       if (!clk_rate)
>> +               goto out_restart_rx;
>> +
>> +       uport->uartclk = clk_rate;
>> +       clk_set_rate(port->se.clk, clk_rate);
>> +       ser_clk_cfg = SER_CLK_EN;
>> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
> 
> Drop useless cast.
I think you mean parenthesis. I do not see casting here.
> 
>> +
>> +       /* parity */
>> +       tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
>> +       tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
>> +       rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
>> +       rx_parity_cfg = readl_relaxed(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 (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);
>> +out_restart_rx:
>> +       qcom_geni_serial_start_rx(uport);
>> +}
>> +
>> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
>> +{
>> +       return !readl_relaxed(uport->membase + SE_GENI_TX_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 *port;
>> +       int baud;
>> +       int bits = 8;
>> +       int parity = 'n';
>> +       int flow = 'n';
>> +
>> +       if (co->index >= GENI_UART_CONS_PORTS  || co->index < 0)
>> +               return -ENXIO;
>> +
>> +       port = get_port_from_line(co->index);
>> +       if (IS_ERR(port)) {
>> +               pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port));
>> +               return PTR_ERR(port);
>> +       }
>> +
>> +       uport = &port->uport;
>> +
>> +       if (unlikely(!uport->membase))
>> +               return -ENXIO;
>> +
>> +       if (geni_se_resources_on(&port->se)) {
>> +               dev_err(port->se.dev, "Error turning on resources\n");
>> +               return -ENXIO;
>> +       }
>> +
>> +       if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {
> 
> Looks like we're validating the configuration of the DT here. Maybe this
> can go into the wrapper code and be put behind some DEBUG_KERNEL check
> so we can debug bad bootloader configurations if needed? Especially if
> this is the only API that's left exposed from the wrapper to the serial
> engine/protocol driver.
Ok.
> 
>> +               geni_se_resources_off(&port->se);
>> +               return -ENXIO;
>> +       }
>> +
>> +       if (!port->port_setup) {
>> +               port->tx_bytes_pw = 1;
>> +               port->rx_bytes_pw = RX_BYTES_PW;
>> +               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)
> 
> __init
Ok.
> 
>> +{
>> +       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_CONS_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 *port = to_dev_port(uport, uport);
>> +
>> +       if (unlikely(!uart_console(uport)))
>> +               return;
>> +
>> +       if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
>> +               geni_se_resources_on(&port->se);
>> +       else if (new_state == UART_PM_STATE_OFF &&
>> +                       old_state == UART_PM_STATE_ON)
>> +               geni_se_resources_off(&port->se);
>> +}
>> +
>> +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 int qcom_geni_serial_probe(struct platform_device *pdev)
>> +{
>> +       int ret = 0;
>> +       int line = -1;
>> +       struct qcom_geni_serial_port *port;
>> +       struct uart_port *uport;
>> +       struct resource *res;
>> +       struct uart_driver *drv;
>> +
>> +       drv = (void *)of_device_get_match_data(&pdev->dev);
> 
> Useless cast.
There were compiler warnings assigning a const void * to a void *. That 
is why I have the cast in place.
> 
>> +       if (!drv) {
>> +               dev_err(&pdev->dev, "%s: No matching device found", __func__);
>> +               return -ENODEV;
>> +       }
>> +
>> +       if (pdev->dev.of_node)
>> +               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))
> 
> Useless parenthesis.
I will drop it.
> 
>> +               return -ENXIO;
>> +       port = get_port_from_line(line);
>> +       if (IS_ERR(port)) {
>> +               ret = PTR_ERR(port);
>> +               dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret);
>> +               return ret;
>> +       }
>> +
>> +       uport = &port->uport;
>> +       /* Don't allow 2 drivers to access the same port */
>> +       if (uport->private_data)
>> +               return -ENODEV;
>> +
>> +       uport->dev = &pdev->dev;
>> +       port->se.dev = &pdev->dev;
>> +       port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
>> +       port->se.clk = devm_clk_get(&pdev->dev, "se");
>> +       if (IS_ERR(port->se.clk)) {
>> +               ret = PTR_ERR(port->se.clk);
>> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       uport->mapbase = res->start;
>> +       uport->membase = devm_ioremap_resource(&pdev->dev, res);
>> +       if (!uport->membase) {
> 
> Check for IS_ERR()
Ok.
> 
>> +               dev_err(&pdev->dev, "Err IO mapping serial iomem");
> 
> No need for error message with devm_ioremap_resource()
Ok.
> 
>> +               return -ENOMEM;
> 
> return PTR_ERR(..)
Ok.
> 
> Also, I see some serial drivers do the mapping when the port is
> requested. That can't be done here?
By "when the port is requested" do you mean console's setup operation. 
It can be done, but given that it is a one-time operation I am not sure 
if it makes any difference between mapping here and there.
> 
>> +       }
>> +       port->se.base = uport->membase;
>> +
>> +       port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +       port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +       port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
>> +
>> +       uport->irq = platform_get_irq(pdev, 0);
>> +       if (uport->irq < 0) {
>> +               dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq);
>> +               return uport->irq;
>> +       }
>> +
>> +       uport->private_data = drv;
>> +       platform_set_drvdata(pdev, port);
>> +       port->handle_rx = handle_rx_console;
>> +       port->port_setup = false;
>> +       return uart_add_one_port(drv, uport);
>> +}
>> +
>> +static int qcom_geni_serial_remove(struct platform_device *pdev)
>> +{
>> +       struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +       struct uart_driver *drv = port->uport.private_data;
>> +
>> +       uart_remove_one_port(drv, &port->uport);
>> +       return 0;
>> +}
>> +
>> +static int __maybe_unused 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(uport->private_data, uport);
>> +       return 0;
>> +}
>> +
>> +static int __maybe_unused 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(uport->private_data, uport);
>> +               disable_irq(uport->irq);
>> +       }
>> +       return 0;
>> +}
>> +
>> +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,
> 
> Why are these noirq variants? Please add a comment.
The intention is to enable the console UART port usage as late as 
possible in the suspend cycle. Hence noirq variants. I will add this as 
a comment. Please let me know if the usage does not match the intention.
> 
>> +};
>> +
>> +static const struct of_device_id qcom_geni_serial_match_table[] = {
>> +       { .compatible = "qcom,geni-debug-uart",
>> +                               .data = &qcom_geni_console_driver, },
>> +       {}
>> +};
>> +MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
>> +
>> +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;
> 
> Drop assignment please.
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 = 0;
> --
> 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-06  0:51       ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-06  0:51 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/2/2018 3:11 PM, Stephen Boyd wrote:
> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
>> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
>> index 3682fd3..c6b1500 100644
>> --- a/drivers/tty/serial/Kconfig
>> +++ b/drivers/tty/serial/Kconfig
>> @@ -1104,6 +1104,17 @@ config SERIAL_MSM_CONSOLE
>>          select SERIAL_CORE_CONSOLE
>>          select SERIAL_EARLYCON
>>   
>> +config SERIAL_QCOM_GENI
>> +       bool "QCOM on-chip GENI based serial port support"
> 
> This can be tristate.
> 
>> +       depends on ARCH_QCOM
> 
> || COMPILE_TEST
> ?
Ok.
> 
>> +       depends on QCOM_GENI_SE
>> +       select SERIAL_CORE
> 
> This can stay.
> 
>> +       select SERIAL_CORE_CONSOLE
>> +       select SERIAL_EARLYCON
> 
> These two can go to a new config option, like SERIAL_QCOM_GENI_CONSOLE,
> and that would be bool. Please take a look at the existing SERIAL_MSM
> and SERIAL_MSM_CONSOLE setup to understand how to do it.
Ok.
> 
>> +       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/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
>> new file mode 100644
>> index 0000000..8536b7d
>> --- /dev/null
>> +++ b/drivers/tty/serial/qcom_geni_serial.c
>> @@ -0,0 +1,1181 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
>> +
>> +#include <linux/console.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.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_CONS_PORTS   1
>> +#define DEF_FIFO_DEPTH_WORDS   16
>> +#define DEF_TX_WM              2
>> +#define DEF_FIFO_WIDTH_BITS    32
>> +#define UART_CONSOLE_RX_WM     2
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +#define RX_BYTES_PW 1
>> +#else
>> +#define RX_BYTES_PW 4
>> +#endif
>> +
>> +struct qcom_geni_serial_port {
>> +       struct uart_port uport;
>> +       struct geni_se se;
>> +       char name[20];
>> +       u32 tx_fifo_depth;
>> +       u32 tx_fifo_width;
>> +       u32 rx_fifo_depth;
>> +       u32 tx_wm;
>> +       u32 rx_wm;
>> +       u32 rx_rfr;
>> +       int xfer_mode;
> 
> Can this be an enum?
Ok.
> 
>> +       bool port_setup;
> 
> Maybe just 'setup'? Port is in the type already.
Ok.
> 
>> +       int (*handle_rx)(struct uart_port *uport,
>> +                               u32 rx_bytes, bool drop_rx);
> 
> s/rx_bytes/bytes/
> s/drop_rx/drop/
Ok.
> 
>> +       unsigned int xmit_size;
>> +       unsigned int cur_baud;
> 
> s/cur//
Ok.
> 
>> +       unsigned int tx_bytes_pw;
>> +       unsigned int rx_bytes_pw;
>> +};
>> +
>> +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,
>> +                               u32 rx_bytes, bool drop_rx);
> 
> s/rx_bytes/bytes/
> s/drop_rx/drop/
Ok.
> 
>> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
>> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +                               int offset, int bit_field, bool set);
> 
> No need to forward declare this?
I will check and remove if not required.
> 
> s/bit_// ?
Ok.
> 
>> +static void qcom_geni_serial_stop_rx(struct uart_port *uport);
>> +
>> +static atomic_t uart_line_id = ATOMIC_INIT(0);
> 
> Do we need this? How about rely on DT to always have aliases instead?
> Given we only have one port I don't actually understand how this is
> supposed to work anyway.
Ok. I will remove it and rely on DT always having alias.
> 
>> +static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
>> +                                       32000000, 48000000, 64000000, 80000000,
>> +                                       96000000, 100000000};
>> +
>> +#define to_dev_port(ptr, member) \
>> +               container_of(ptr, struct qcom_geni_serial_port, member)
>> +
>> +static struct qcom_geni_serial_port qcom_geni_console_port;
> 
> Why singleton? Couldn't there be many?
Our current use-case does not need more than one instance. But more 
instances can be added if desired.
> 
>> +
>> +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)
>> +{
>> +       if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
> 
> Drop useless parenthesis please.
Ok.
> 
>> +               return ERR_PTR(-ENXIO);
>> +       return &qcom_geni_console_port;
>> +}
>> +
>> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +                               int offset, int bit_field, bool set)
>> +{
>> +       u32 reg;
>> +       struct qcom_geni_serial_port *port;
>> +       unsigned int baud;
>> +       unsigned int fifo_bits;
>> +       unsigned long timeout_us = 20000;
>> +
>> +       /* Ensure polling is not re-ordered before the prior writes/reads */
>> +       mb();
>> +
>> +       if (uport->private_data) {
>> +               port = to_dev_port(uport, uport);
>> +               baud = port->cur_baud;
>> +               if (!baud)
>> +                       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.
> 
> Bad space here                           ^
> 
I will fix it.
>> +                */
>> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
>> +       }
>> +
>> +       return !readl_poll_timeout_atomic(uport->membase + offset, reg,
>> +                        (bool)(reg & bit_field) == set, 10, timeout_us);
>> +}
>> +
>> +static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
>> +{
>> +       u32 m_cmd;
>> +
>> +       writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
>> +       m_cmd = UART_START_TX << M_OPCODE_SHFT;
>> +       writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
>> +}
>> +
>> +static void qcom_geni_serial_poll_tx_done(struct uart_port *uport)
>> +{
>> +       int done;
>> +       u32 irq_clear = M_CMD_DONE_EN;
>> +
>> +       done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_CMD_DONE_EN, true);
>> +       if (!done) {
>> +               writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
>> +                                               SE_GENI_M_CMD_CTRL_REG);
>> +               irq_clear |= M_CMD_ABORT_EN;
>> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                                       M_CMD_ABORT_EN, true);
>> +       }
>> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_abort_rx(struct uart_port *uport)
>> +{
>> +       u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
>> +
>> +       writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
>> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +                                       S_GENI_CMD_ABORT, false);
>> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +       writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +static int qcom_geni_serial_get_char(struct uart_port *uport)
>> +{
>> +       u32 rx_fifo;
>> +       u32 status;
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
>> +       writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
>> +       writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +
>> +       /*
>> +        * Ensure the writes to clear interrupts is not re-ordered after
>> +        * reading the data.
>> +        */
>> +       mb();
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
>> +       if (!(status & RX_FIFO_WC_MSK))
>> +               return NO_POLL_CHAR;
>> +
>> +       rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
>> +       return rx_fifo & 0xff;
>> +}
>> +
>> +static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
>> +                                                       unsigned char c)
>> +{
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
>> +       qcom_geni_serial_setup_tx(uport, 1);
>> +       WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_TX_FIFO_WATERMARK_EN, true));
>> +       writel_relaxed((u32)c, uport->membase + SE_GENI_TX_FIFOn);
> 
> Drop useless cast.
Ok.
> 
>> +       writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
>> +                                                       SE_GENI_M_IRQ_CLEAR);
>> +       qcom_geni_serial_poll_tx_done(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)
>> +{
>> +       writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
>> +}
>> +
>> +static void
>> +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
>> +                                unsigned int count)
>> +{
>> +       int new_line = 0;
> 
> Drop
Ok.
> 
>> +       int i;
>> +       u32 bytes_to_send = count;
>> +
>> +       for (i = 0; i < count; i++) {
>> +               if (s[i] == '\n')
>> +                       new_line++;
> 
> bytes_to_send++;
Ok.
> 
>> +       }
>> +
>> +       bytes_to_send += new_line;
> 
> Drop.
Ok.
> 
>> +       writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
>> +       qcom_geni_serial_setup_tx(uport, bytes_to_send);
>> +       i = 0;
>> +       while (i < count) {
> 
> 	for (i = 0; i < count; ) {
> 
> would be more normal, but ok.
> 
>> +               size_t chars_to_write = 0;
>> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))
> 
> Does this ever timeout? So many nested while loops makes it hard to
> follow.
Yes. Based on the baud rate of 115200 and the FIFO Depth & Width of (16 
* 32), the poll should not take more than 5 ms under the timeout scenario.
> 
>> +                       break;
>> +               chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2);
>> +               uart_console_write(uport, (s + i), chars_to_write,
> 
> Drop useless parenthesis please.
Ok.
> 
>> +                                               qcom_geni_serial_wr_char);
>> +               writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
>> +                                                       SE_GENI_M_IRQ_CLEAR);
>> +               i += chars_to_write;
>> +       }
>> +       qcom_geni_serial_poll_tx_done(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;
>> +       bool locked = true;
>> +       unsigned long flags;
>> +
>> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
>> +
>> +       port = get_port_from_line(co->index);
>> +       if (IS_ERR(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);
> 
> So if oops is in progress, and we didn't lock here, we don't output
> data? I'd think we would always want to write to the fifo, just make the
> lock grab/release conditional.
If we fail to grab the lock, then there is another active writer. So 
trying to write to the fifo will put the hardware in bad state because 
writer has programmed the hardware to write 'x' number of words and this 
thread will over-write it with 'y' number of words. Also the data that 
you might see in the console might be garbled.
> 
>> +               spin_unlock_irqrestore(&uport->lock, flags);
>> +       }
>> +}
>> +
>> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
>> +{
>> +       u32 i = rx_bytes;
>> +       u32 rx_fifo;
>> +       unsigned char *buf;
>> +       struct tty_port *tport;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       tport = &uport->state->port;
>> +       while (i > 0) {
>> +               int c;
>> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;
>> +
>> +               rx_fifo = readl_relaxed(uport->membase + SE_GENI_RX_FIFOn);
> 
> Please use ioread32_rep(..., 1) here.
Ok.
> 
>> +               i -= bytes;
>> +               if (drop)
>> +                       continue;
>> +               buf = (unsigned char *)&rx_fifo;
> 
> So that this cast becomes unnecessary, and endian agnostic.
Ok.
> 
>> +
>> +               for (c = 0; c < bytes; c++) {
>> +                       int sysrq;
>> +
>> +                       uport->icount.rx++;
>> +                       sysrq = uart_handle_sysrq_char(uport, buf[c]);
> 
> And so this does the right thing in whatever world we live in.
Ok.
> 
>> +                       if (!sysrq)
>> +                               tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
>> +               }
>> +       }
>> +       if (!drop)
>> +               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)
>> +{
>> +       u32 irq_en;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       u32 status;
>> +
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +               if (status & M_GENI_CMD_ACTIVE)
>> +                       return;
>> +
>> +               if (!qcom_geni_serial_tx_empty(uport))
>> +                       return;
>> +
>> +               /*
>> +                * Ensure writing to IRQ_EN & watermark registers are not
>> +                * re-ordered before checking the status of the Serial
>> +                * Engine and TX FIFO
>> +                */
>> +               mb();
>> +
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +               irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
>> +
>> +               writel_relaxed(port->tx_wm, uport->membase +
>> +                                               SE_GENI_TX_WATERMARK_REG);
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       }
>> +}
>> +
>> +static void qcom_geni_serial_stop_tx(struct uart_port *uport)
>> +{
>> +       u32 irq_en;
>> +       u32 status;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +       irq_en &= ~M_CMD_DONE_EN;
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               irq_en &= ~M_TX_FIFO_WATERMARK_EN;
>> +               writel_relaxed(0, uport->membase +
>> +                                    SE_GENI_TX_WATERMARK_REG);
>> +       }
>> +       port->xmit_size = 0;
>> +       writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       /* Possible stop tx is called multiple times. */
>> +       if (!(status & M_GENI_CMD_ACTIVE))
>> +               return;
>> +
>> +       /*
>> +        * Ensure cancel command write is not re-ordered before checking
>> +        * checking the status of the Primary Sequencer.
>> +        */
>> +       mb();
>> +
>> +       geni_se_cancel_m_cmd(&port->se);
>> +       if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_CMD_CANCEL_EN, true)) {
>> +               geni_se_abort_m_cmd(&port->se);
>> +               qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
>> +                                               M_CMD_ABORT_EN, true);
>> +               writel_relaxed(M_CMD_ABORT_EN, uport->membase +
>> +                                                       SE_GENI_M_IRQ_CLEAR);
>> +       }
>> +       writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +}
>> +
>> +static void qcom_geni_serial_start_rx(struct uart_port *uport)
>> +{
>> +       u32 irq_en;
>> +       u32 status;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       if (status & S_GENI_CMD_ACTIVE)
>> +               qcom_geni_serial_stop_rx(uport);
>> +
>> +       /*
>> +        * Ensure setup command write is not re-ordered before checking
>> +        * checking the status of the Secondary Sequencer.
>> +        */
>> +       mb();
>> +
>> +       geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
>> +
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
>> +               irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
>> +
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +               irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       }
>> +}
>> +
>> +static void qcom_geni_serial_stop_rx(struct uart_port *uport)
>> +{
>> +       u32 irq_en;
>> +       u32 status;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       u32 irq_clear = S_CMD_DONE_EN;
>> +
>> +       if (port->xfer_mode == GENI_SE_FIFO) {
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
>> +               irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
>> +
>> +               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +               irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
>> +               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
>> +       }
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       /* Possible stop rx is called multiple times. */
>> +       if (!(status & S_GENI_CMD_ACTIVE))
>> +               return;
>> +
>> +       /*
>> +        * Ensure cancel command write is not re-ordered before checking
>> +        * checking the status of the Secondary Sequencer.
> 
> Each of these comments has 'checking' twice.
I will fix the comments.
> 
>> +        */
>> +       mb();
>> +
>> +       geni_se_cancel_s_cmd(&port->se);
>> +       qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
>> +                                       S_GENI_CMD_CANCEL, false);
>> +       status = readl_relaxed(uport->membase + SE_GENI_STATUS);
>> +       writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +       if (status & S_GENI_CMD_ACTIVE)
>> +               qcom_geni_serial_abort_rx(uport);
>> +}
>> +
>> +static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
> 
> s/drop_rx/drop/
Ok.
> 
>> +{
>> +       u32 status;
>> +       u32 word_cnt;
>> +       u32 last_word_byte_cnt;
>> +       u32 last_word_partial;
>> +       u32 total_bytes;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
>> +       word_cnt = status & RX_FIFO_WC_MSK;
>> +       last_word_partial = status & RX_LAST;
>> +       last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
>> +                                               RX_LAST_BYTE_VALID_SHFT;
>> +
>> +       if (!word_cnt)
>> +               return;
>> +       total_bytes = port->rx_bytes_pw * (word_cnt - 1);
>> +       if (last_word_partial && last_word_byte_cnt)
>> +               total_bytes += last_word_byte_cnt;
>> +       else
>> +               total_bytes += port->rx_bytes_pw;
>> +       port->handle_rx(uport, total_bytes, drop_rx);
>> +}
>> +
>> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
>> +{
>> +       int ret = 0;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       struct circ_buf *xmit = &uport->state->xmit;
>> +       size_t avail;
>> +       size_t remaining;
>> +       int i = 0;
>> +       u32 status;
>> +       unsigned int chunk;
>> +       int tail;
>> +
>> +       chunk = uart_circ_chars_pending(xmit);
>> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
>> +       /* Both FIFO and framework buffer are drained */
>> +       if ((chunk == port->xmit_size) && !status) {
> 
> Drop useless parenthesis.
Ok.
> 
>> +               port->xmit_size = 0;
>> +               uart_circ_clear(xmit);
>> +               qcom_geni_serial_stop_tx(uport);
>> +               goto out_write_wakeup;
>> +       }
>> +       chunk -= port->xmit_size;
>> +
>> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
>> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
>> +       if (chunk > (UART_XMIT_SIZE - tail))
>> +               chunk = UART_XMIT_SIZE - tail;
>> +       if (chunk > avail)
>> +               chunk = avail;
>> +
>> +       if (!chunk)
>> +               goto out_write_wakeup;
>> +
>> +       qcom_geni_serial_setup_tx(uport, chunk);
>> +
>> +       remaining = chunk;
>> +       while (i < chunk) {
> 
> 	for (i = 0; i < chunk; ) {
Ok.
> 
>> +               unsigned int tx_bytes;
>> +               unsigned int buf = 0;
>> +               int c;
>> +
>> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
>> +               for (c = 0; c < tx_bytes ; c++)
>> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
>> +
>> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
>> +
>> +               i += tx_bytes;
>> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
>> +               uport->icount.tx += tx_bytes;
>> +               remaining -= tx_bytes;
>> +       }
>> +       qcom_geni_serial_poll_tx_done(uport);
>> +       port->xmit_size += chunk;
>> +out_write_wakeup:
>> +       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;
>> +
>> +       if (uport->suspended)
>> +               return IRQ_HANDLED;
>> +
>> +       spin_lock_irqsave(&uport->lock, flags);
>> +       m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
>> +       s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
>> +       m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
>> +       writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
>> +       writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
>> +
>> +       if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
>> +               goto out_unlock;
>> +
>> +       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_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
>> +           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++;
> 
> How does break character handling work? I see the accounting here, but
> don't see any uart_handle_break() call anywhere.
The reason it is not added is because the hardware does not indicate 
when the break character occured. It can be any one of the FIFO words. 
The statistics is updated to give an idea that the break happened. We 
can add uart_handle_break() but it may not be at an accurate position 
for the above mentioned reason.
> 
>> +       }
>> +
>> +       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);
>> +
>> +out_unlock:
>> +       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(&port->se);
>> +       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(&port->se);
>> +       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(&port->se);
>> +       if (!port->rx_fifo_depth) {
>> +               dev_err(uport->dev, "%s:Invalid RX FIFO depth read\n",
>> +                                                               __func__);
>> +               return -ENXIO;
>> +       }
> 
> Are these checks verifying the hardware has a proper setting for fifo
> depth and width? How is that possible to mess up? Do these ever fail?
We haven't seen a failure yet. I can drop the check and rely on the fact 
that the hardware is programmed correctly.
> 
>> +
>> +       uport->fifosize =
>> +               (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
>> +       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;
> 
> port->tx_wm = DEF_TX_WM?
Ok.
> 
>> +}
>> +
>> +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;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
>> +
>> +       set_rfr_wm(port);
>> +       writel_relaxed(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.
>> +        */
>> +       port->xfer_mode = GENI_SE_FIFO;
>> +       qcom_geni_serial_poll_tx_done(uport);
>> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
>> +                                               false, true, false);
>> +       geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
>> +                                               false, false, true);
>> +       ret = geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
>> +       if (ret) {
>> +               dev_err(uport->dev, "%s: Fail\n", __func__);
>> +               return ret;
>> +       }
>> +
>> +       geni_se_select_mode(&port->se, port->xfer_mode);
>> +       port->port_setup = true;
>> +       return ret;
>> +}
>> +
>> +static int qcom_geni_serial_startup(struct uart_port *uport)
>> +{
>> +       int ret;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       scnprintf(port->name, sizeof(port->name),
>> +                 "qcom_serial_geni%d", uport->line);
>> +
>> +       if (geni_se_read_proto(&port->se) != GENI_SE_UART) {
>> +               dev_err(uport->dev, "Invalid FW %d loaded.\n",
>> +                                geni_se_read_proto(&port->se));
> 
> Please don't read proto twice.
Ok.
> 
>> +               return -ENXIO;
>> +       }
>> +
>> +       get_tx_fifo_size(port);
>> +       if (!port->port_setup) {
>> +               ret = qcom_geni_serial_port_setup(uport);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
>> +                                                       port->name, uport);
>> +       if (ret)
>> +               dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
>> +       return ret;
>> +}
>> +
>> +static unsigned long get_clk_cfg(unsigned long clk_freq)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
>> +               if (!(root_freq[i] % clk_freq))
>> +                       return root_freq[i];
>> +       }
>> +       return 0;
>> +}
>> +
>> +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)
>> +{
>> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
>> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
>> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
>> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
>> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
> 
> Can you please inline this function into the caller and put the writels
> where the values are calculated? It would reduce the mental work to keep
> track of all the variables to find out that they just get written in the
> end. Also, this is weirdly placed in the file when get_clk_div_rate()
> calls get_clk_cfg() but this function is between them.
Bjorn had a similar comment and there I mentioned that the writes are 
required during early console setup as well. Since the popular vote is 
towards inlining these writes, I will update it accordingly.
> 
>> +}
>> +
>> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
>> +{
>> +       unsigned long ser_clk;
>> +       unsigned long desired_clk;
>> +
>> +       desired_clk = baud * UART_OVERSAMPLING;
>> +       ser_clk = get_clk_cfg(desired_clk);
>> +       if (!ser_clk) {
>> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
>> +                                                               __func__, baud);
>> +               return ser_clk;
>> +       }
>> +
>> +       *clk_div = ser_clk / desired_clk;
> 
> How wide can clk_div be? It may be better to implement the ser_clk as an
> actual clk in the common clk framework, and then have the serial driver
> or the i2c driver call clk_set_rate() on that clk and have the CCF
> implementation take care of determining the rate that the parent clk can
> supply and how it can fit it into the frequency that the divider can
> support.
Based on my current expertise with the CCF, I will address this comment 
in a later patchset than the next one.
> 
>> +       return ser_clk;
>> +}
>> +
>> +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;
>> +       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;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, 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_rate = get_clk_div_rate(baud, &clk_div);
>> +       if (!clk_rate)
>> +               goto out_restart_rx;
>> +
>> +       uport->uartclk = clk_rate;
>> +       clk_set_rate(port->se.clk, clk_rate);
>> +       ser_clk_cfg = SER_CLK_EN;
>> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
> 
> Drop useless cast.
I think you mean parenthesis. I do not see casting here.
> 
>> +
>> +       /* parity */
>> +       tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
>> +       tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
>> +       rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
>> +       rx_parity_cfg = readl_relaxed(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 (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);
>> +out_restart_rx:
>> +       qcom_geni_serial_start_rx(uport);
>> +}
>> +
>> +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
>> +{
>> +       return !readl_relaxed(uport->membase + SE_GENI_TX_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 *port;
>> +       int baud;
>> +       int bits = 8;
>> +       int parity = 'n';
>> +       int flow = 'n';
>> +
>> +       if (co->index >= GENI_UART_CONS_PORTS  || co->index < 0)
>> +               return -ENXIO;
>> +
>> +       port = get_port_from_line(co->index);
>> +       if (IS_ERR(port)) {
>> +               pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port));
>> +               return PTR_ERR(port);
>> +       }
>> +
>> +       uport = &port->uport;
>> +
>> +       if (unlikely(!uport->membase))
>> +               return -ENXIO;
>> +
>> +       if (geni_se_resources_on(&port->se)) {
>> +               dev_err(port->se.dev, "Error turning on resources\n");
>> +               return -ENXIO;
>> +       }
>> +
>> +       if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {
> 
> Looks like we're validating the configuration of the DT here. Maybe this
> can go into the wrapper code and be put behind some DEBUG_KERNEL check
> so we can debug bad bootloader configurations if needed? Especially if
> this is the only API that's left exposed from the wrapper to the serial
> engine/protocol driver.
Ok.
> 
>> +               geni_se_resources_off(&port->se);
>> +               return -ENXIO;
>> +       }
>> +
>> +       if (!port->port_setup) {
>> +               port->tx_bytes_pw = 1;
>> +               port->rx_bytes_pw = RX_BYTES_PW;
>> +               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)
> 
> __init
Ok.
> 
>> +{
>> +       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_CONS_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 *port = to_dev_port(uport, uport);
>> +
>> +       if (unlikely(!uart_console(uport)))
>> +               return;
>> +
>> +       if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
>> +               geni_se_resources_on(&port->se);
>> +       else if (new_state == UART_PM_STATE_OFF &&
>> +                       old_state == UART_PM_STATE_ON)
>> +               geni_se_resources_off(&port->se);
>> +}
>> +
>> +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 int qcom_geni_serial_probe(struct platform_device *pdev)
>> +{
>> +       int ret = 0;
>> +       int line = -1;
>> +       struct qcom_geni_serial_port *port;
>> +       struct uart_port *uport;
>> +       struct resource *res;
>> +       struct uart_driver *drv;
>> +
>> +       drv = (void *)of_device_get_match_data(&pdev->dev);
> 
> Useless cast.
There were compiler warnings assigning a const void * to a void *. That 
is why I have the cast in place.
> 
>> +       if (!drv) {
>> +               dev_err(&pdev->dev, "%s: No matching device found", __func__);
>> +               return -ENODEV;
>> +       }
>> +
>> +       if (pdev->dev.of_node)
>> +               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))
> 
> Useless parenthesis.
I will drop it.
> 
>> +               return -ENXIO;
>> +       port = get_port_from_line(line);
>> +       if (IS_ERR(port)) {
>> +               ret = PTR_ERR(port);
>> +               dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret);
>> +               return ret;
>> +       }
>> +
>> +       uport = &port->uport;
>> +       /* Don't allow 2 drivers to access the same port */
>> +       if (uport->private_data)
>> +               return -ENODEV;
>> +
>> +       uport->dev = &pdev->dev;
>> +       port->se.dev = &pdev->dev;
>> +       port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
>> +       port->se.clk = devm_clk_get(&pdev->dev, "se");
>> +       if (IS_ERR(port->se.clk)) {
>> +               ret = PTR_ERR(port->se.clk);
>> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       uport->mapbase = res->start;
>> +       uport->membase = devm_ioremap_resource(&pdev->dev, res);
>> +       if (!uport->membase) {
> 
> Check for IS_ERR()
Ok.
> 
>> +               dev_err(&pdev->dev, "Err IO mapping serial iomem");
> 
> No need for error message with devm_ioremap_resource()
Ok.
> 
>> +               return -ENOMEM;
> 
> return PTR_ERR(..)
Ok.
> 
> Also, I see some serial drivers do the mapping when the port is
> requested. That can't be done here?
By "when the port is requested" do you mean console's setup operation. 
It can be done, but given that it is a one-time operation I am not sure 
if it makes any difference between mapping here and there.
> 
>> +       }
>> +       port->se.base = uport->membase;
>> +
>> +       port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +       port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
>> +       port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
>> +
>> +       uport->irq = platform_get_irq(pdev, 0);
>> +       if (uport->irq < 0) {
>> +               dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq);
>> +               return uport->irq;
>> +       }
>> +
>> +       uport->private_data = drv;
>> +       platform_set_drvdata(pdev, port);
>> +       port->handle_rx = handle_rx_console;
>> +       port->port_setup = false;
>> +       return uart_add_one_port(drv, uport);
>> +}
>> +
>> +static int qcom_geni_serial_remove(struct platform_device *pdev)
>> +{
>> +       struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
>> +       struct uart_driver *drv = port->uport.private_data;
>> +
>> +       uart_remove_one_port(drv, &port->uport);
>> +       return 0;
>> +}
>> +
>> +static int __maybe_unused 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(uport->private_data, uport);
>> +       return 0;
>> +}
>> +
>> +static int __maybe_unused 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(uport->private_data, uport);
>> +               disable_irq(uport->irq);
>> +       }
>> +       return 0;
>> +}
>> +
>> +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,
> 
> Why are these noirq variants? Please add a comment.
The intention is to enable the console UART port usage as late as 
possible in the suspend cycle. Hence noirq variants. I will add this as 
a comment. Please let me know if the usage does not match the intention.
> 
>> +};
>> +
>> +static const struct of_device_id qcom_geni_serial_match_table[] = {
>> +       { .compatible = "qcom,geni-debug-uart",
>> +                               .data = &qcom_geni_console_driver, },
>> +       {}
>> +};
>> +MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
>> +
>> +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;
> 
> Drop assignment please.
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 = 0;
> --
> 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
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-03-05 23:58     ` Rob Herring
@ 2018-03-06  0:55       ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-06  0:55 UTC (permalink / raw)
  To: Rob Herring
  Cc: corbet, andy.gross, david.brown, mark.rutland, wsa, gregkh,
	linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan



On 3/5/2018 4:58 PM, Rob Herring wrote:
> On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian wrote:
>> Add device tree binding support for the QCOM GENI SE driver.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> ---
>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89 ++++++++++++++++++++++
>>   1 file changed, 89 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..fe6a0c0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>> @@ -0,0 +1,89 @@
>> +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> for Serial Engine Address
>> +- #size-cells: 		Must be <1> for Serial Engine Address Size
>> +- 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.
> 
> s/spi/SPI/
> 
> Where's the SPI binding?
Since the patch series introduces UART and I2C drivers, I added the 
bindings only for them. I thought about adding the SPI binding when the 
SPI controller driver is introduced. Please let me know if you want me 
to add the bindings for SPI in this patch series itself.
> 
>> +Please refer below the child node definitions for the supported serial
>> +interface protocols.
>> +
>> +Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
>> +
>> +Required properties:
>> +- compatible:		Must be "qcom,geni-i2c".
>> +- reg: 			Must contain QUP register address and length.
>> +- interrupts: 		Must contain I2C interrupt.
>> +- clock-names: 		Must contain "se".
>> +- clocks: 		Serial engine core clock needed by the device.
>> +- #address-cells:	Must be <1> for i2c device address.
>> +- #size-cells:		Must 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.
>> +
>> +Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
>> +
>> +Required properties:
>> +- compatible:		Must be "qcom,geni-debug-uart".
>> +- reg: 			Must contain UART register location and length.
>> +- interrupts: 		Must contain UART core interrupts.
>> +- clock-names:		Must contain "se".
>> +- clocks:		Serial engine core clock needed by the device.
>> +
>> +Example:
>> +	geniqup@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>;
>> +		#address-cells = <1>;
>> +		#size-cells = <1>;
>> +		ranges;
>> +
>> +		i2c0: i2c@a94000 {
>> +			compatible = "qcom,geni-i2c";
>> +			reg = <0xa94000 0x4000>;
>> +			interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
>> +			clock-names = "se";
>> +			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>;
>> +		};
>> +
>> +		uart0: serial@a88000 {
>> +			compatible = "qcom,geni-debug-uart";
>> +			reg = <0xa88000 0x7000>;
>> +			interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
>> +			clock-names = "se";
>> +			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
>>
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
@ 2018-03-06  0:55       ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-06  0:55 UTC (permalink / raw)
  To: Rob Herring
  Cc: corbet, andy.gross, david.brown, mark.rutland, wsa, gregkh,
	linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan



On 3/5/2018 4:58 PM, Rob Herring wrote:
> On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian wrote:
>> Add device tree binding support for the QCOM GENI SE driver.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> ---
>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89 ++++++++++++++++++++++
>>   1 file changed, 89 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..fe6a0c0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>> @@ -0,0 +1,89 @@
>> +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> for Serial Engine Address
>> +- #size-cells: 		Must be <1> for Serial Engine Address Size
>> +- 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.
> 
> s/spi/SPI/
> 
> Where's the SPI binding?
Since the patch series introduces UART and I2C drivers, I added the 
bindings only for them. I thought about adding the SPI binding when the 
SPI controller driver is introduced. Please let me know if you want me 
to add the bindings for SPI in this patch series itself.
> 
>> +Please refer below the child node definitions for the supported serial
>> +interface protocols.
>> +
>> +Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
>> +
>> +Required properties:
>> +- compatible:		Must be "qcom,geni-i2c".
>> +- reg: 			Must contain QUP register address and length.
>> +- interrupts: 		Must contain I2C interrupt.
>> +- clock-names: 		Must contain "se".
>> +- clocks: 		Serial engine core clock needed by the device.
>> +- #address-cells:	Must be <1> for i2c device address.
>> +- #size-cells:		Must 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.
>> +
>> +Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
>> +
>> +Required properties:
>> +- compatible:		Must be "qcom,geni-debug-uart".
>> +- reg: 			Must contain UART register location and length.
>> +- interrupts: 		Must contain UART core interrupts.
>> +- clock-names:		Must contain "se".
>> +- clocks:		Serial engine core clock needed by the device.
>> +
>> +Example:
>> +	geniqup@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>;
>> +		#address-cells = <1>;
>> +		#size-cells = <1>;
>> +		ranges;
>> +
>> +		i2c0: i2c@a94000 {
>> +			compatible = "qcom,geni-i2c";
>> +			reg = <0xa94000 0x4000>;
>> +			interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
>> +			clock-names = "se";
>> +			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>;
>> +		};
>> +
>> +		uart0: serial@a88000 {
>> +			compatible = "qcom,geni-debug-uart";
>> +			reg = <0xa88000 0x7000>;
>> +			interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
>> +			clock-names = "se";
>> +			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
>>
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-03-06  0:55       ` Karthik Ramasubramanian
@ 2018-03-06 13:22         ` Rob Herring
  -1 siblings, 0 replies; 65+ messages in thread
From: Rob Herring @ 2018-03-06 13:22 UTC (permalink / raw)
  To: Karthik Ramasubramanian
  Cc: Jonathan Corbet, Andy Gross, David Brown, Mark Rutland,
	Wolfram Sang, Greg Kroah-Hartman, linux-doc, linux-arm-msm,
	devicetree, Linux I2C, linux-serial, Jiri Slaby, evgreen,
	acourbot, Sagar Dharia, Girish Mahadevan

On Mon, Mar 5, 2018 at 6:55 PM, Karthik Ramasubramanian
<kramasub@codeaurora.org> wrote:
>
>
> On 3/5/2018 4:58 PM, Rob Herring wrote:
>>
>> On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian
>> wrote:
>>>
>>> Add device tree binding support for the QCOM GENI SE driver.
>>>
>>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>>> ---
>>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89
>>> ++++++++++++++++++++++
>>>   1 file changed, 89 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..fe6a0c0
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>>> @@ -0,0 +1,89 @@
>>> +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> for Serial Engine Address
>>> +- #size-cells:                 Must be <1> for Serial Engine Address
>>> Size
>>> +- 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.
>>
>>
>> s/spi/SPI/
>>
>> Where's the SPI binding?
>
> Since the patch series introduces UART and I2C drivers, I added the bindings
> only for them. I thought about adding the SPI binding when the SPI
> controller driver is introduced. Please let me know if you want me to add
> the bindings for SPI in this patch series itself.

There's no requirement to have the driver and I prefer bindings be as
complete as possible.

Rob

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

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
@ 2018-03-06 13:22         ` Rob Herring
  0 siblings, 0 replies; 65+ messages in thread
From: Rob Herring @ 2018-03-06 13:22 UTC (permalink / raw)
  To: Karthik Ramasubramanian
  Cc: Jonathan Corbet, Andy Gross, David Brown, Mark Rutland,
	Wolfram Sang, Greg Kroah-Hartman, linux-doc, linux-arm-msm,
	devicetree, Linux I2C, linux-serial, Jiri Slaby, evgreen,
	acourbot, Sagar Dharia, Girish Mahadevan

On Mon, Mar 5, 2018 at 6:55 PM, Karthik Ramasubramanian
<kramasub@codeaurora.org> wrote:
>
>
> On 3/5/2018 4:58 PM, Rob Herring wrote:
>>
>> On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian
>> wrote:
>>>
>>> Add device tree binding support for the QCOM GENI SE driver.
>>>
>>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>>> ---
>>>   .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89
>>> ++++++++++++++++++++++
>>>   1 file changed, 89 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..fe6a0c0
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>>> @@ -0,0 +1,89 @@
>>> +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> for Serial Engine Address
>>> +- #size-cells:                 Must be <1> for Serial Engine Address
>>> Size
>>> +- 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.
>>
>>
>> s/spi/SPI/
>>
>> Where's the SPI binding?
>
> Since the patch series introduces UART and I2C drivers, I added the bindings
> only for them. I thought about adding the SPI binding when the SPI
> controller driver is introduced. Please let me know if you want me to add
> the bindings for SPI in this patch series itself.

There's no requirement to have the driver and I prefer bindings be as
complete as possible.

Rob
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
  2018-03-06 13:22         ` Rob Herring
@ 2018-03-06 17:13           ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-06 17:13 UTC (permalink / raw)
  To: Rob Herring
  Cc: Jonathan Corbet, Andy Gross, David Brown, Mark Rutland,
	Wolfram Sang, Greg Kroah-Hartman, linux-doc, linux-arm-msm,
	devicetree, Linux I2C, linux-serial, Jiri Slaby, evgreen,
	acourbot, Sagar Dharia, Girish Mahadevan



On 3/6/2018 6:22 AM, Rob Herring wrote:
> On Mon, Mar 5, 2018 at 6:55 PM, Karthik Ramasubramanian
> <kramasub@codeaurora.org> wrote:
>>
>>
>> On 3/5/2018 4:58 PM, Rob Herring wrote:
>>>
>>> On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian
>>> wrote:
>>>>
>>>> Add device tree binding support for the QCOM GENI SE driver.
>>>>
>>>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>>>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>>>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>>>> ---
>>>>    .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89
>>>> ++++++++++++++++++++++
>>>>    1 file changed, 89 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..fe6a0c0
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>>>> @@ -0,0 +1,89 @@
>>>> +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> for Serial Engine Address
>>>> +- #size-cells:                 Must be <1> for Serial Engine Address
>>>> Size
>>>> +- 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.
>>>
>>>
>>> s/spi/SPI/
>>>
>>> Where's the SPI binding?
>>
>> Since the patch series introduces UART and I2C drivers, I added the bindings
>> only for them. I thought about adding the SPI binding when the SPI
>> controller driver is introduced. Please let me know if you want me to add
>> the bindings for SPI in this patch series itself.
> 
> There's no requirement to have the driver and I prefer bindings be as
> complete as possible.
Ok, I will add the bindings for SPI controller in the next posting.
> 
> 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] 65+ messages in thread

* Re: [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE
@ 2018-03-06 17:13           ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-06 17:13 UTC (permalink / raw)
  To: Rob Herring
  Cc: Jonathan Corbet, Andy Gross, David Brown, Mark Rutland,
	Wolfram Sang, Greg Kroah-Hartman, linux-doc, linux-arm-msm,
	devicetree, Linux I2C, linux-serial, Jiri Slaby, evgreen,
	acourbot, Sagar Dharia, Girish Mahadevan



On 3/6/2018 6:22 AM, Rob Herring wrote:
> On Mon, Mar 5, 2018 at 6:55 PM, Karthik Ramasubramanian
> <kramasub@codeaurora.org> wrote:
>>
>>
>> On 3/5/2018 4:58 PM, Rob Herring wrote:
>>>
>>> On Tue, Feb 27, 2018 at 06:38:06PM -0700, Karthikeyan Ramasubramanian
>>> wrote:
>>>>
>>>> Add device tree binding support for the QCOM GENI SE driver.
>>>>
>>>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>>>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>>>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>>>> ---
>>>>    .../devicetree/bindings/soc/qcom/qcom,geni-se.txt  | 89
>>>> ++++++++++++++++++++++
>>>>    1 file changed, 89 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..fe6a0c0
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>>>> @@ -0,0 +1,89 @@
>>>> +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> for Serial Engine Address
>>>> +- #size-cells:                 Must be <1> for Serial Engine Address
>>>> Size
>>>> +- 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.
>>>
>>>
>>> s/spi/SPI/
>>>
>>> Where's the SPI binding?
>>
>> Since the patch series introduces UART and I2C drivers, I added the bindings
>> only for them. I thought about adding the SPI binding when the SPI
>> controller driver is introduced. Please let me know if you want me to add
>> the bindings for SPI in this patch series itself.
> 
> There's no requirement to have the driver and I prefer bindings be as
> complete as possible.
Ok, I will add the bindings for SPI controller in the next posting.
> 
> Rob
> 
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-03-06  0:51       ` Karthik Ramasubramanian
@ 2018-03-06 21:45         ` Stephen Boyd
  -1 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-06 21:45 UTC (permalink / raw)
  To: Karthik Ramasubramanian, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
> > Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
> > 
> >> +               size_t chars_to_write = 0;
> >> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))
> > 
> > Does this ever timeout? So many nested while loops makes it hard to
> > follow.
> Yes. Based on the baud rate of 115200 and the FIFO Depth & Width of (16 
> * 32), the poll should not take more than 5 ms under the timeout scenario.

Sure, but I'm asking if this has any sort of timeout associated with it.
It looks to be a while loop that could possibly go forever?

> >> +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;
> >> +       bool locked = true;
> >> +       unsigned long flags;
> >> +
> >> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> >> +
> >> +       port = get_port_from_line(co->index);
> >> +       if (IS_ERR(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);
> > 
> > So if oops is in progress, and we didn't lock here, we don't output
> > data? I'd think we would always want to write to the fifo, just make the
> > lock grab/release conditional.
> If we fail to grab the lock, then there is another active writer. So 
> trying to write to the fifo will put the hardware in bad state because 
> writer has programmed the hardware to write 'x' number of words and this 
> thread will over-write it with 'y' number of words. Also the data that 
> you might see in the console might be garbled.

I suspect that because this is the serial console, and we want it to
always output stuff even when we're going down in flames, we may want to
ignore that case and just wait for the fifo to free up and then
overwrite the number of words that we want to output and push out more
characters.

I always get confused though because there seem to be lots of different
ways to do things in serial drivers and not too much clear documentation
that I've read describing what to do.

> > 
> >> +               spin_unlock_irqrestore(&uport->lock, flags);
> >> +       }
> >> +}
[...]
> >> +
> >> +       if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
> >> +           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++;
> > 
> > How does break character handling work? I see the accounting here, but
> > don't see any uart_handle_break() call anywhere.
> The reason it is not added is because the hardware does not indicate 
> when the break character occured. It can be any one of the FIFO words. 
> The statistics is updated to give an idea that the break happened. We 
> can add uart_handle_break() but it may not be at an accurate position 
> for the above mentioned reason.

Sounds like the previous uart design. We would want uart_handle_break()
support for kgdb to work over serial. Do we still get an interrupt
signal that a break character is somewhere in the fifo? If we get that,
then does it also put a NUL (0) character into the fifo that we can
find? I had to do something like that before, where we detect the irq
and then we go through the fifo a character at a time to find the break
character that looks like a NUL, and then let sysrq core handle the
characters after that break character comes in.

> > 
> > 
> >> +}
> >> +
> >> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
> >> +{
> >> +       unsigned long ser_clk;
> >> +       unsigned long desired_clk;
> >> +
> >> +       desired_clk = baud * UART_OVERSAMPLING;
> >> +       ser_clk = get_clk_cfg(desired_clk);
> >> +       if (!ser_clk) {
> >> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
> >> +                                                               __func__, baud);
> >> +               return ser_clk;
> >> +       }
> >> +
> >> +       *clk_div = ser_clk / desired_clk;
> > 
> > How wide can clk_div be? It may be better to implement the ser_clk as an
> > actual clk in the common clk framework, and then have the serial driver
> > or the i2c driver call clk_set_rate() on that clk and have the CCF
> > implementation take care of determining the rate that the parent clk can
> > supply and how it can fit it into the frequency that the divider can
> > support.
> Based on my current expertise with the CCF, I will address this comment 
> in a later patchset than the next one.

Ok. I've seen similar designs in some mmc drivers. For example, you can
look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
clk_ops and then just start using that clk directly from the driver.
There are also some helper functions for dividers that would hopefully
make the job of calculating the best divider easier.

> >> +
> >> +       uport->uartclk = clk_rate;
> >> +       clk_set_rate(port->se.clk, clk_rate);
> >> +       ser_clk_cfg = SER_CLK_EN;
> >> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
> > 
> > Drop useless cast.
> I think you mean parenthesis. I do not see casting here.

Yes! You got it.

> >> +#endif
> >> +       .pm = qcom_geni_serial_cons_pm,
> >> +};
> >> +
> >> +static int qcom_geni_serial_probe(struct platform_device *pdev)
> >> +{
> >> +       int ret = 0;
> >> +       int line = -1;
> >> +       struct qcom_geni_serial_port *port;
> >> +       struct uart_port *uport;
> >> +       struct resource *res;
> >> +       struct uart_driver *drv;
> >> +
> >> +       drv = (void *)of_device_get_match_data(&pdev->dev);
> > 
> > Useless cast.
> There were compiler warnings assigning a const void * to a void *. That 
> is why I have the cast in place.

Oh. Yes you shouldn't cast away the const. Please make the compiler
warning go away without casting it away.

> > 
> > 
> > Also, I see some serial drivers do the mapping when the port is
> > requested. That can't be done here?
> By "when the port is requested" do you mean console's setup operation. 
> It can be done, but given that it is a one-time operation I am not sure 
> if it makes any difference between mapping here and there.

No. I meant the uart_ops::uart_request() function and also
uart_ops::config_port(). Take a look at msm_config_port() and
msm_request_port() for how it was done on pre-geni qcom uarts.

I suppose we should try to probe this as a module and run a getty on it
and then remove and probe the module a couple times after that.
That should shake out some bugs.

> >> +
> >> +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,
> > 
> > Why are these noirq variants? Please add a comment.
> The intention is to enable the console UART port usage as late as 
> possible in the suspend cycle. Hence noirq variants. I will add this as 
> a comment. Please let me know if the usage does not match the intention.

Hm.. Does no_console_suspend not work? Or not work well enough?

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-06 21:45         ` Stephen Boyd
  0 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-06 21:45 UTC (permalink / raw)
  To: Karthik Ramasubramanian, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
> > Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
> > 
> >> +               size_t chars_to_write = 0;
> >> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))
> > 
> > Does this ever timeout? So many nested while loops makes it hard to
> > follow.
> Yes. Based on the baud rate of 115200 and the FIFO Depth & Width of (16 
> * 32), the poll should not take more than 5 ms under the timeout scenario.

Sure, but I'm asking if this has any sort of timeout associated with it.
It looks to be a while loop that could possibly go forever?

> >> +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;
> >> +       bool locked = true;
> >> +       unsigned long flags;
> >> +
> >> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> >> +
> >> +       port = get_port_from_line(co->index);
> >> +       if (IS_ERR(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);
> > 
> > So if oops is in progress, and we didn't lock here, we don't output
> > data? I'd think we would always want to write to the fifo, just make the
> > lock grab/release conditional.
> If we fail to grab the lock, then there is another active writer. So 
> trying to write to the fifo will put the hardware in bad state because 
> writer has programmed the hardware to write 'x' number of words and this 
> thread will over-write it with 'y' number of words. Also the data that 
> you might see in the console might be garbled.

I suspect that because this is the serial console, and we want it to
always output stuff even when we're going down in flames, we may want to
ignore that case and just wait for the fifo to free up and then
overwrite the number of words that we want to output and push out more
characters.

I always get confused though because there seem to be lots of different
ways to do things in serial drivers and not too much clear documentation
that I've read describing what to do.

> > 
> >> +               spin_unlock_irqrestore(&uport->lock, flags);
> >> +       }
> >> +}
[...]
> >> +
> >> +       if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
> >> +           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++;
> > 
> > How does break character handling work? I see the accounting here, but
> > don't see any uart_handle_break() call anywhere.
> The reason it is not added is because the hardware does not indicate 
> when the break character occured. It can be any one of the FIFO words. 
> The statistics is updated to give an idea that the break happened. We 
> can add uart_handle_break() but it may not be at an accurate position 
> for the above mentioned reason.

Sounds like the previous uart design. We would want uart_handle_break()
support for kgdb to work over serial. Do we still get an interrupt
signal that a break character is somewhere in the fifo? If we get that,
then does it also put a NUL (0) character into the fifo that we can
find? I had to do something like that before, where we detect the irq
and then we go through the fifo a character at a time to find the break
character that looks like a NUL, and then let sysrq core handle the
characters after that break character comes in.

> > 
> > 
> >> +}
> >> +
> >> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
> >> +{
> >> +       unsigned long ser_clk;
> >> +       unsigned long desired_clk;
> >> +
> >> +       desired_clk = baud * UART_OVERSAMPLING;
> >> +       ser_clk = get_clk_cfg(desired_clk);
> >> +       if (!ser_clk) {
> >> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
> >> +                                                               __func__, baud);
> >> +               return ser_clk;
> >> +       }
> >> +
> >> +       *clk_div = ser_clk / desired_clk;
> > 
> > How wide can clk_div be? It may be better to implement the ser_clk as an
> > actual clk in the common clk framework, and then have the serial driver
> > or the i2c driver call clk_set_rate() on that clk and have the CCF
> > implementation take care of determining the rate that the parent clk can
> > supply and how it can fit it into the frequency that the divider can
> > support.
> Based on my current expertise with the CCF, I will address this comment 
> in a later patchset than the next one.

Ok. I've seen similar designs in some mmc drivers. For example, you can
look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
clk_ops and then just start using that clk directly from the driver.
There are also some helper functions for dividers that would hopefully
make the job of calculating the best divider easier.

> >> +
> >> +       uport->uartclk = clk_rate;
> >> +       clk_set_rate(port->se.clk, clk_rate);
> >> +       ser_clk_cfg = SER_CLK_EN;
> >> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
> > 
> > Drop useless cast.
> I think you mean parenthesis. I do not see casting here.

Yes! You got it.

> >> +#endif
> >> +       .pm = qcom_geni_serial_cons_pm,
> >> +};
> >> +
> >> +static int qcom_geni_serial_probe(struct platform_device *pdev)
> >> +{
> >> +       int ret = 0;
> >> +       int line = -1;
> >> +       struct qcom_geni_serial_port *port;
> >> +       struct uart_port *uport;
> >> +       struct resource *res;
> >> +       struct uart_driver *drv;
> >> +
> >> +       drv = (void *)of_device_get_match_data(&pdev->dev);
> > 
> > Useless cast.
> There were compiler warnings assigning a const void * to a void *. That 
> is why I have the cast in place.

Oh. Yes you shouldn't cast away the const. Please make the compiler
warning go away without casting it away.

> > 
> > 
> > Also, I see some serial drivers do the mapping when the port is
> > requested. That can't be done here?
> By "when the port is requested" do you mean console's setup operation. 
> It can be done, but given that it is a one-time operation I am not sure 
> if it makes any difference between mapping here and there.

No. I meant the uart_ops::uart_request() function and also
uart_ops::config_port(). Take a look at msm_config_port() and
msm_request_port() for how it was done on pre-geni qcom uarts.

I suppose we should try to probe this as a module and run a getty on it
and then remove and probe the module a couple times after that.
That should shake out some bugs.

> >> +
> >> +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,
> > 
> > Why are these noirq variants? Please add a comment.
> The intention is to enable the console UART port usage as late as 
> possible in the suspend cycle. Hence noirq variants. I will add this as 
> a comment. Please let me know if the usage does not match the intention.

Hm.. Does no_console_suspend not work? Or not work well enough?

--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-03  0:58       ` Karthik Ramasubramanian
@ 2018-03-06 21:56         ` Stephen Boyd
  -1 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-06 21:56 UTC (permalink / raw)
  To: Karthik Ramasubramanian, Stephen Boyd, andy.gross, corbet,
	david.brown, gregkh, mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan

Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
> 
> 
> On 3/2/2018 1:41 PM, Stephen Boyd wrote:
> > Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
> >> +
> >> +/**
> >> + * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
> >> + * @se:                        Pointer to the corresponding Serial Engine.
> >> + * @major:             Buffer for Major Version field.
> >> + * @minor:             Buffer for Minor Version field.
> >> + * @step:              Buffer for Step Version field.
> >> + */
> >> +void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
> >> +                               unsigned int *minor, unsigned int *step)
> >> +{
> >> +       unsigned int version;
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +
> >> +       version = readl_relaxed(wrapper->base + QUP_HW_VER_REG);
> >> +       *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;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_get_qup_hw_version);
> > 
> > Is this used?
> SPI controller driver uses this API and it will be uploaded sooner.

Ok. Maybe it can also be a macro to get the u32 and then some more
macros on top of that to pick out the major/minor/step out of the u32
that you read.

> > 
> >> +
> >> +/**
> >> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
> >> + * @se:        Pointer to the concerned Serial Engine.
> >> + *
> >> + * Return: Protocol value as configured in the serial engine.
> >> + */
> >> +u32 geni_se_read_proto(struct geni_se *se)
> >> +{
> >> +       u32 val;
> >> +
> >> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
> >> +
> >> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_read_proto);
> > 
> > Is this API really needed outside of this file? It would seem like the
> > drivers that implement the protocol, which are child devices, would only
> > use this API to confirm that the protocol chosen is for their particular
> > protocol.
> No, this API is meant for the protocol drivers to confirm that the 
> serial engine is programmed with the firmware for the concerned protocol 
> before using the serial engine. If the check fails, the protocol drivers 
> stop using the serial engine.

Ok maybe we don't really need it then?

> >> + * RX fifo of the serial engine.
> >> + *
> >> + * Return: RX fifo depth in units of FIFO words
> >> + */
> >> +u32 geni_se_get_rx_fifo_depth(struct geni_se *se)
> >> +{
> >> +       u32 val;
> >> +
> >> +       val = readl_relaxed(se->base + SE_HW_PARAM_1);
> >> +
> >> +       return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
> > 
> > These ones too, can probably just be static inline.
> Ok. Just for my knowledge - is there any reference guideline regarding 
> when to use static inline myself and when to let the compiler do the 
> clever thing?

Not that I'm aware of. It's really up to you to decide.

> > 
> >> +
> >> +       ret = geni_se_clks_on(se);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       ret = pinctrl_pm_select_default_state(se->dev);
> >> +       if (ret)
> >> +               geni_se_clks_off(se);
> >> +
> >> +       return ret;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_resources_on);
> > 
> > IS there a reason why we can't use runtime PM or normal linux PM
> > infrastructure to power on the wrapper and keep it powered while the
> > protocol driver is active?
> Besides turning on the clocks & pinctrl settings, wrapper also has to do 
> the bus scaling votes. The bus scaling votes depend on the individual 
> serial interface bandwidth requirements. The bus scaling votes is not 
> present currently. But once the support comes in, this function enables 
> adding it.

Ok, but that would basically be some code consolidation around picking a
bandwidth and enabling/disabling? It sounds like it could go into either
the serial interface drivers or into the runtime PM path of the wrapper.

> > 
> >> +
> >> +/**
> >> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> >> + * @se:                Pointer to the concerned Serial Engine.
> >> + * @tbl:       Table in which the output is returned.
> >> + *
> >> + * This function is called by the protocol drivers to determine the different
> >> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
> >> +{
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +       unsigned long freq = 0;
> >> +       int i;
> >> +       int ret = 0;
> >> +
> >> +       mutex_lock(&wrapper->lock);
> >> +       if (wrapper->clk_perf_tbl) {
> >> +               *tbl = wrapper->clk_perf_tbl;
> >> +               ret = wrapper->num_clk_levels;
> >> +               goto out_unlock;
> >> +       }
> >> +
> >> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
> >> +                                       sizeof(*wrapper->clk_perf_tbl),
> >> +                                       GFP_KERNEL);
> >> +       if (!wrapper->clk_perf_tbl) {
> >> +               ret = -ENOMEM;
> >> +               goto out_unlock;
> >> +       }
> >> +
> >> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> >> +               freq = clk_round_rate(se->clk, freq + 1);
> >> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
> >> +                       break;
> >> +               wrapper->clk_perf_tbl[i] = freq;
> >> +       }
> >> +       wrapper->num_clk_levels = i;
> >> +       *tbl = wrapper->clk_perf_tbl;
> >> +       ret = wrapper->num_clk_levels;
> >> +out_unlock:
> >> +       mutex_unlock(&wrapper->lock);
> > 
> > Is this lock actually protecting anything? I mean to say, is any more
> > than one geni protocol driver calling this function at a time? Or is
> > the same geni protocol driver calling this from multiple threads at the
> > same time? The lock looks almost useless.
> Yes, there is a possibility of multiple I2C instances within the same 
> wrapper trying to get this table simultaneously.
> 
> As Evan mentioned in the other thread, Bjorn had the comment to move it 
> to the probe and remove the lock. I looked into the possibility of it. 
>  From the hardware perspective, this table belongs to the wrapper and is 
> shared by all the serial engines within the wrapper. But due to software 
> implementation reasons, clk_round_rate can be be performed only on the 
> clocks that are tagged as DFS compatible and only the serial engine 
> clocks are tagged so. At least this was the understanding based on our 
> earlier discussion with the concerned folks. We will revisit it and 
> check if anything has changed recently.

Hmm sounds like the round rate should happen on the parent of the
se_clk, and this wrapper DT binding should get the clk for the parent of
the se->clk to run round_rate() on. Then it could all be done in probe,
which sounds good.

> >> +       return iova;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
> >> +
> >> +/**
> >> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
> >> + * @se:                        Pointer to the concerned Serial Engine.
> >> + * @buf:               Pointer to the RX buffer.
> >> + * @len:               Length of the RX buffer.
> >> + *
> >> + * This function is used to prepare the buffers for DMA RX.
> >> + *
> >> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
> >> + */
> >> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
> >> +{
> >> +       dma_addr_t iova;
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +       u32 val;
> >> +
> >> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
> >> +       if (dma_mapping_error(wrapper->dev, iova))
> >> +               return (dma_addr_t)NULL;
> > 
> > Can't return a dma_mapping_error address to the caller and have them
> > figure it out?
> Earlier we used to return the DMA_ERROR_CODE which has been removed 
> recently in arm64 architecture. If we return the dma_mapping_error, then 
> the caller also needs the device which encountered the mapping error. 
> The serial interface drivers can use their parent currently to resolve 
> the mapping error. Once the wrapper starts mapping using IOMMU context 
> bank, then the serial interface drivers do not know which device to use 
> to know if there is an error.
> 
> Having said that, the dma_ops suggestion might help with handling this 
> situation. I will look into it further.

Ok, thanks.

> >> +{
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +
> >> +       if (iova)
> >> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
> >> +}
> >> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
> > 
> > Instead of having the functions exported, could we set the dma_ops on
> > all child devices of the wrapper that this driver populates and then
> > implement the DMA ops for those devices here? I assume that there's
> > never another DMA master between the wrapper and the serial engine, so I
> > think it would work.
> This suggestion looks like it will work.

It would be a good idea to check with some other people on the dma_ops
suggestion. Maybe add the DMA mapping subsystem folks to help out here

DMA MAPPING HELPERS
M:      Christoph Hellwig <hch@lst.de>
M:      Marek Szyprowski <m.szyprowski@samsung.com>
R:      Robin Murphy <robin.murphy@arm.com>
L:      iommu@lists.linux-foundation.org

> > 
> >> +
> >> +/* Transfer mode supported by GENI Serial Engines */
> >> +enum geni_se_xfer_mode {
> >> +       GENI_SE_INVALID,
> >> +       GENI_SE_FIFO,
> >> +       GENI_SE_DMA,
> >> +};
> >> +
> >> +/* Protocols supported by GENI Serial Engines */
> >> +enum geni_se_protocol_types {
> >> +       GENI_SE_NONE,
> >> +       GENI_SE_SPI,
> >> +       GENI_SE_UART,
> >> +       GENI_SE_I2C,
> >> +       GENI_SE_I3C,
> >> +};
> >> +
> >> +/**
> >> + * struct geni_se - GENI Serial Engine
> >> + * @base:              Base Address of the Serial Engine's register block.
> >> + * @dev:               Pointer to the Serial Engine device.
> >> + * @wrapper:           Pointer to the parent QUP Wrapper core.
> >> + * @clk:               Handle to the core serial engine clock.
> >> + */
> >> +struct geni_se {
> >> +       void __iomem *base;
> >> +       struct device *dev;
> >> +       void *wrapper;
> > 
> > Can this get the geni_wrapper type? It could be opaque if you like.
> I am not sure if it is ok to have the children know the details of the 
> parent. That is why it is kept as opaque.

That's fine, but I mean to have struct geni_wrapper *wrapper, and then
struct geni_wrapper; in this file. Children won't know details and we
get slightly more type safety.

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-06 21:56         ` Stephen Boyd
  0 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-06 21:56 UTC (permalink / raw)
  To: Karthik Ramasubramanian, Stephen Boyd, andy.gross, corbet,
	david.brown, gregkh, mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan

Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
> 
> 
> On 3/2/2018 1:41 PM, Stephen Boyd wrote:
> > Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
> >> +
> >> +/**
> >> + * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
> >> + * @se:                        Pointer to the corresponding Serial Engine.
> >> + * @major:             Buffer for Major Version field.
> >> + * @minor:             Buffer for Minor Version field.
> >> + * @step:              Buffer for Step Version field.
> >> + */
> >> +void geni_se_get_qup_hw_version(struct geni_se *se, unsigned int *major,
> >> +                               unsigned int *minor, unsigned int *step)
> >> +{
> >> +       unsigned int version;
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +
> >> +       version = readl_relaxed(wrapper->base + QUP_HW_VER_REG);
> >> +       *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;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_get_qup_hw_version);
> > 
> > Is this used?
> SPI controller driver uses this API and it will be uploaded sooner.

Ok. Maybe it can also be a macro to get the u32 and then some more
macros on top of that to pick out the major/minor/step out of the u32
that you read.

> > 
> >> +
> >> +/**
> >> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
> >> + * @se:        Pointer to the concerned Serial Engine.
> >> + *
> >> + * Return: Protocol value as configured in the serial engine.
> >> + */
> >> +u32 geni_se_read_proto(struct geni_se *se)
> >> +{
> >> +       u32 val;
> >> +
> >> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
> >> +
> >> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_read_proto);
> > 
> > Is this API really needed outside of this file? It would seem like the
> > drivers that implement the protocol, which are child devices, would only
> > use this API to confirm that the protocol chosen is for their particular
> > protocol.
> No, this API is meant for the protocol drivers to confirm that the 
> serial engine is programmed with the firmware for the concerned protocol 
> before using the serial engine. If the check fails, the protocol drivers 
> stop using the serial engine.

Ok maybe we don't really need it then?

> >> + * RX fifo of the serial engine.
> >> + *
> >> + * Return: RX fifo depth in units of FIFO words
> >> + */
> >> +u32 geni_se_get_rx_fifo_depth(struct geni_se *se)
> >> +{
> >> +       u32 val;
> >> +
> >> +       val = readl_relaxed(se->base + SE_HW_PARAM_1);
> >> +
> >> +       return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
> > 
> > These ones too, can probably just be static inline.
> Ok. Just for my knowledge - is there any reference guideline regarding 
> when to use static inline myself and when to let the compiler do the 
> clever thing?

Not that I'm aware of. It's really up to you to decide.

> > 
> >> +
> >> +       ret = geni_se_clks_on(se);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       ret = pinctrl_pm_select_default_state(se->dev);
> >> +       if (ret)
> >> +               geni_se_clks_off(se);
> >> +
> >> +       return ret;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_resources_on);
> > 
> > IS there a reason why we can't use runtime PM or normal linux PM
> > infrastructure to power on the wrapper and keep it powered while the
> > protocol driver is active?
> Besides turning on the clocks & pinctrl settings, wrapper also has to do 
> the bus scaling votes. The bus scaling votes depend on the individual 
> serial interface bandwidth requirements. The bus scaling votes is not 
> present currently. But once the support comes in, this function enables 
> adding it.

Ok, but that would basically be some code consolidation around picking a
bandwidth and enabling/disabling? It sounds like it could go into either
the serial interface drivers or into the runtime PM path of the wrapper.

> > 
> >> +
> >> +/**
> >> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> >> + * @se:                Pointer to the concerned Serial Engine.
> >> + * @tbl:       Table in which the output is returned.
> >> + *
> >> + * This function is called by the protocol drivers to determine the different
> >> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
> >> +{
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +       unsigned long freq = 0;
> >> +       int i;
> >> +       int ret = 0;
> >> +
> >> +       mutex_lock(&wrapper->lock);
> >> +       if (wrapper->clk_perf_tbl) {
> >> +               *tbl = wrapper->clk_perf_tbl;
> >> +               ret = wrapper->num_clk_levels;
> >> +               goto out_unlock;
> >> +       }
> >> +
> >> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
> >> +                                       sizeof(*wrapper->clk_perf_tbl),
> >> +                                       GFP_KERNEL);
> >> +       if (!wrapper->clk_perf_tbl) {
> >> +               ret = -ENOMEM;
> >> +               goto out_unlock;
> >> +       }
> >> +
> >> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> >> +               freq = clk_round_rate(se->clk, freq + 1);
> >> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
> >> +                       break;
> >> +               wrapper->clk_perf_tbl[i] = freq;
> >> +       }
> >> +       wrapper->num_clk_levels = i;
> >> +       *tbl = wrapper->clk_perf_tbl;
> >> +       ret = wrapper->num_clk_levels;
> >> +out_unlock:
> >> +       mutex_unlock(&wrapper->lock);
> > 
> > Is this lock actually protecting anything? I mean to say, is any more
> > than one geni protocol driver calling this function at a time? Or is
> > the same geni protocol driver calling this from multiple threads at the
> > same time? The lock looks almost useless.
> Yes, there is a possibility of multiple I2C instances within the same 
> wrapper trying to get this table simultaneously.
> 
> As Evan mentioned in the other thread, Bjorn had the comment to move it 
> to the probe and remove the lock. I looked into the possibility of it. 
>  From the hardware perspective, this table belongs to the wrapper and is 
> shared by all the serial engines within the wrapper. But due to software 
> implementation reasons, clk_round_rate can be be performed only on the 
> clocks that are tagged as DFS compatible and only the serial engine 
> clocks are tagged so. At least this was the understanding based on our 
> earlier discussion with the concerned folks. We will revisit it and 
> check if anything has changed recently.

Hmm sounds like the round rate should happen on the parent of the
se_clk, and this wrapper DT binding should get the clk for the parent of
the se->clk to run round_rate() on. Then it could all be done in probe,
which sounds good.

> >> +       return iova;
> >> +}
> >> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
> >> +
> >> +/**
> >> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
> >> + * @se:                        Pointer to the concerned Serial Engine.
> >> + * @buf:               Pointer to the RX buffer.
> >> + * @len:               Length of the RX buffer.
> >> + *
> >> + * This function is used to prepare the buffers for DMA RX.
> >> + *
> >> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
> >> + */
> >> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
> >> +{
> >> +       dma_addr_t iova;
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +       u32 val;
> >> +
> >> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
> >> +       if (dma_mapping_error(wrapper->dev, iova))
> >> +               return (dma_addr_t)NULL;
> > 
> > Can't return a dma_mapping_error address to the caller and have them
> > figure it out?
> Earlier we used to return the DMA_ERROR_CODE which has been removed 
> recently in arm64 architecture. If we return the dma_mapping_error, then 
> the caller also needs the device which encountered the mapping error. 
> The serial interface drivers can use their parent currently to resolve 
> the mapping error. Once the wrapper starts mapping using IOMMU context 
> bank, then the serial interface drivers do not know which device to use 
> to know if there is an error.
> 
> Having said that, the dma_ops suggestion might help with handling this 
> situation. I will look into it further.

Ok, thanks.

> >> +{
> >> +       struct geni_wrapper *wrapper = se->wrapper;
> >> +
> >> +       if (iova)
> >> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
> >> +}
> >> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
> > 
> > Instead of having the functions exported, could we set the dma_ops on
> > all child devices of the wrapper that this driver populates and then
> > implement the DMA ops for those devices here? I assume that there's
> > never another DMA master between the wrapper and the serial engine, so I
> > think it would work.
> This suggestion looks like it will work.

It would be a good idea to check with some other people on the dma_ops
suggestion. Maybe add the DMA mapping subsystem folks to help out here

DMA MAPPING HELPERS
M:      Christoph Hellwig <hch@lst.de>
M:      Marek Szyprowski <m.szyprowski@samsung.com>
R:      Robin Murphy <robin.murphy@arm.com>
L:      iommu@lists.linux-foundation.org

> > 
> >> +
> >> +/* Transfer mode supported by GENI Serial Engines */
> >> +enum geni_se_xfer_mode {
> >> +       GENI_SE_INVALID,
> >> +       GENI_SE_FIFO,
> >> +       GENI_SE_DMA,
> >> +};
> >> +
> >> +/* Protocols supported by GENI Serial Engines */
> >> +enum geni_se_protocol_types {
> >> +       GENI_SE_NONE,
> >> +       GENI_SE_SPI,
> >> +       GENI_SE_UART,
> >> +       GENI_SE_I2C,
> >> +       GENI_SE_I3C,
> >> +};
> >> +
> >> +/**
> >> + * struct geni_se - GENI Serial Engine
> >> + * @base:              Base Address of the Serial Engine's register block.
> >> + * @dev:               Pointer to the Serial Engine device.
> >> + * @wrapper:           Pointer to the parent QUP Wrapper core.
> >> + * @clk:               Handle to the core serial engine clock.
> >> + */
> >> +struct geni_se {
> >> +       void __iomem *base;
> >> +       struct device *dev;
> >> +       void *wrapper;
> > 
> > Can this get the geni_wrapper type? It could be opaque if you like.
> I am not sure if it is ok to have the children know the details of the 
> parent. That is why it is kept as opaque.

That's fine, but I mean to have struct geni_wrapper *wrapper, and then
struct geni_wrapper; in this file. Children won't know details and we
get slightly more type safety.

--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-02-28  1:38 ` [PATCH v3 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
@ 2018-03-07 21:16     ` Doug Anderson
  0 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-07 21:16 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, evgreen,
	acourbot, Sagar Dharia, Girish Mahadevan, swboyd

Hi,

On Tue, Feb 27, 2018 at 5:38 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         |  11 +
>  drivers/i2c/busses/Makefile        |   1 +
>  drivers/i2c/busses/i2c-qcom-geni.c | 626 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 638 insertions(+)

I'm not an expert on geni (and, to be honest, I haven't read the main
geni patch yet).  ...but I figured I could at least add my $0.02 since
I've stared at i2c bus drivers a lot in the past.  Feel free to tell
me if I'm full or crap...


>  create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index e2954fb..1ddf5cd 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -848,6 +848,17 @@ 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
> +       depends on QCOM_GENI_SE
> +       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.

Kind of a generic description and this driver is only for new SoCs,
right?  Maybe make it a little more specific?


> +
> +         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..e1e4268
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
> @@ -0,0 +1,626 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> +
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.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/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_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           BIT(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
> +/* I2C SCL COUNTER fields */
> +#define HIGH_COUNTER_MSK       GENMASK(29, 20)
> +#define HIGH_COUNTER_SHFT      20
> +#define LOW_COUNTER_MSK                GENMASK(19, 10)
> +#define LOW_COUNTER_SHFT       10
> +#define CYCLE_COUNTER_MSK      GENMASK(9, 0)
> +
> +#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

Above should be an enum; then use the enum type as the parameter to
geni_i2c_err() so it's obvious that "err" is not a normal linux error
code.


> +#define I2C_NACK               GP_IRQ1
> +#define I2C_BUS_PROTO          GP_IRQ3
> +#define I2C_ARB_LOST           GP_IRQ4

Get rid of definition of GP_IRQ1, 3, and 4 and just define I2C_NACK,
I2C_BUS_PROTO, and I2C_ARB_LOST directly.


> +#define DM_I2C_CB_ERR          ((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
> +                                                                       << 5)

Should these really be using "GP_IRQ1", "GP_IRQ3", and "GP_IRQ4".
Does this use of those numbers have anything to do with the other use
of them?  Seems like this should just be BIT(1) | BIT(3) | BIT(4).

Said another way: does bit 1 in this field coorespond to NACK, bit 3
correspond to BUS_PROTO, and bit 4 correspond to ARB_LOST?  If not
then I see no reason to try to tie them together.  If they do
correspond then use BIT(I2C_NACK), etc...


> +
> +#define I2C_AUTO_SUSPEND_DELAY 250

Why 250 ms?  That seems like an eternity.  Is it really that expensive
to turn resources off and on?  I would sorta just expect clocks and
stuff to get turned off right after a transaction finished unless
another one was pending right behind it...


> +#define KHz(freq)              (1000 * freq)

I probably wouldn't define KHz macro and just used numbers like 100000
like all the other i2c drivers, but I guess it's OK.  Should be all
caps, though?


> +#define PACKING_BYTES_PW       4
> +
> +struct geni_i2c_dev {
> +       struct geni_se se;
> +       u32 tx_wm;
> +       int irq;
> +       int err;
> +       struct i2c_adapter adap;
> +       struct completion done;
> +       struct i2c_msg *cur;
> +       int cur_wr;
> +       int cur_rd;
> +       u32 clk_freq_out;
> +       const struct geni_i2c_clk_fld *clk_fld;
> +};
> +
> +struct geni_i2c_err_log {
> +       int err;
> +       const char *msg;
> +};
> +
> +static struct geni_i2c_err_log gi2c_log[] = {

static const?


> +       [GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
> +       [I2C_NACK] = {-ENOTCONN, "NACK: slv unresponsive, check its power/reset-ln"},

Longer than 80 characters; don't split the string, but you could still
wrap better.


> +       [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 const 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},

So I guess this is all relying on an input serial clock of 19.2MHz?
Maybe document that?

Assuming I'm understanding the math here, is it really OK for your
100kHz and 1MHz mode to be running slightly fast?

19200. / 2 / 24
>>> 400.0

19200. / 7 / 26
>>> 105.49450549450549

19200. / 1 / 18
>>> 1066.6666666666667

It seems like you'd want the fastest clock that you can make that's
_less than_ the spec.


It would also be interesting to know if it's expected that boards
might need to tweak the t_high / t_low depending on their electrical
characteristics.  In the past I've had lots of requests from board
makers to tweak things because they've got a long trace, or a stronger
or weaker pull, or ...  If so we might later need to add some dts
properties like "i2c-scl-rising-time-ns" and make the math more
dynamic here, unless your hardware somehow automatically adjusts for
this type of thing...


> +};
> +
> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
> +{
> +       int i;
> +       const 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) {
> +                       gi2c->clk_fld = geni_i2c_clk_map + i;

Isn't "geni_i2c_clk_map + i" just "itr"?


> +                       return 0;
> +               }
> +       }
> +       return -EINVAL;
> +}
> +
> +static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
> +{
> +       const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
> +       u32 val;
> +
> +       writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
> +
> +       val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
> +       writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
> +
> +       val = itr->t_high << HIGH_COUNTER_SHFT;
> +       val |= itr->t_low << LOW_COUNTER_SHFT;
> +       val |= itr->t_cycle;
> +       writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
> +       /*
> +        * Ensure later writes/reads to serial engine register block is
> +        * not reordered before this point.
> +        */
> +       mb();

This mb() is to make sure that later writes to "gi2c->se.base" are not
reordered to be above the ones in this function?  You don't need a
mb().  writel_relaxed() already enforces this.


> +}
> +
> +static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
> +{
> +       u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0);
> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
> +       u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
> +       u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS);
> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
> +       u32 rx_st, tx_st;
> +
> +       if (dma) {
> +               rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
> +               tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
> +       } else {
> +               rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
> +               tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS);
> +       }
> +       dev_dbg(gi2c->se.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->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
> +               m_cmd, geni_s, geni_ios);
> +}
> +
> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
> +{
> +       gi2c->err = gi2c_log[err].err;

You should only set gi2c->err if it was 0 to start with.  You want
"err" to contain the first error, not the last one.  This is
especially important due to the comment elsewhere in this patch "if
this is err with done-bit not set, handle that through timeout".  You
don't want the timeout to clobber the true error.


On a separate note: I wonder if it makes sense to couch the rest of
this function in something that will compile to a no-op if DEBUG and
DYNAMIC_DEBUG aren't defined?  Then you can avoid including code for
all these readl calls.

> +       if (gi2c->cur)
> +               dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
> +                       gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
> +       dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
> +
> +       if (err != I2C_NACK && err != GENI_ABORT_DONE)
> +               geni_i2c_err_misc(gi2c);
> +}
> +
> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
> +{
> +       struct geni_i2c_dev *gi2c = dev;
> +       int j;
> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
> +       u32 rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
> +       u32 dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
> +       u32 dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
> +       struct i2c_msg *cur = gi2c->cur;
> +
> +       if (!cur ||
> +           m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
> +           dm_rx_st & (DM_I2C_CB_ERR)) {
> +               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);
> +
> +               /* Disable the TX Watermark interrupt to stop TX */
> +               if (!dma)
> +                       writel_relaxed(0, gi2c->se.base +
> +                                          SE_GENI_TX_WATERMARK_REG);
> +               goto irqret;
> +       }
> +
> +       if (dma) {
> +               dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n",
> +                       dm_tx_st, dm_rx_st);
> +               goto irqret;
> +       }
> +
> +       if (cur->flags & I2C_M_RD &&
> +           m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) {
> +               u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
> +
> +               for (j = 0; j < rxcnt; j++) {
> +                       u32 val;
> +                       int p = 0;
> +
> +                       val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn);
> +                       while (gi2c->cur_rd < cur->len && p < sizeof(val)) {
> +                               cur->buf[gi2c->cur_rd++] = val & 0xff;
> +                               val >>= 8;
> +                               p++;
> +                       }
> +                       if (gi2c->cur_rd == cur->len)
> +                               break;
> +               }
> +       } else if (!(cur->flags & I2C_M_RD) &&
> +                  m_stat & M_TX_FIFO_WATERMARK_EN) {
> +               for (j = 0; j < gi2c->tx_wm; j++) {
> +                       u32 temp;
> +                       u32 val = 0;
> +                       int p = 0;
> +
> +                       while (gi2c->cur_wr < cur->len && p < sizeof(val)) {
> +                               temp = (u32)cur->buf[gi2c->cur_wr++];

What is the (u32) cast doing here?


> +                               val |= (temp << (p * 8));

Get rid of extra parenthesis.


> +                               p++;
> +                       }
> +                       writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn);
> +                       /* TX Complete, Disable the TX Watermark interrupt */
> +                       if (gi2c->cur_wr == cur->len) {
> +                               writel_relaxed(0, gi2c->se.base +
> +                                               SE_GENI_TX_WATERMARK_REG);
> +                               break;
> +                       }
> +               }
> +       }
> +irqret:
> +       if (m_stat)
> +               writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR);
> +
> +       if (dma) {
> +               if (dm_tx_st)
> +                       writel_relaxed(dm_tx_st, gi2c->se.base +
> +                                               SE_DMA_TX_IRQ_CLR);
> +               if (dm_rx_st)
> +                       writel_relaxed(dm_rx_st, gi2c->se.base +
> +                                               SE_DMA_RX_IRQ_CLR);
> +       }
> +       /* if this is err with done-bit not set, handle that through timeout. */
> +       if (m_stat & M_CMD_DONE_EN)
> +               complete(&gi2c->done);
> +       else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
> +               complete(&gi2c->done);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
> +{
> +       u32 val;
> +       unsigned long timeout = HZ;

Rename to time_left?  ...and maybe use a #define for the init value?


> +
> +       geni_i2c_err(gi2c, GENI_TIMEOUT);
> +       gi2c->cur = NULL;

Don't you need a spinlock or something?  In most of the other cases
you could get away with no locking because the irq isn't happening at
the same time as other code that's mucking with stuff, but in the
timeout case we may be mucking with stuff at the same time as the irq.


> +       geni_se_abort_m_cmd(&gi2c->se);
> +       do {
> +               timeout = wait_for_completion_timeout(&gi2c->done, timeout);
> +               val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
> +       } while (!(val & M_CMD_ABORT_EN) && timeout);

Print an error if there was a timeout aborting?


> +}
> +
> +static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
> +                               u32 m_param)
> +{
> +       dma_addr_t rx_dma;
> +       enum geni_se_xfer_mode mode;
> +       unsigned long timeout;
> +
> +       gi2c->cur = msg;
> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;

DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
a lot by transferring i2c commands over DMA compared to a FIFO?
Enough to justify the code complexity and the set of bugs that will
show up?  I'm sure it will be a controversial assertion given that the
code's already written, but personally I'd be supportive of ripping
DMA mode out to simplify the driver.  I'd be curious if anyone else
agrees.  To me it seems like premature optimization.


> +       geni_se_select_mode(&gi2c->se, mode);
> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
> +       if (mode == GENI_SE_DMA) {
> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len);

Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
check this flag before using msg->buf for DMA?  ...or use
i2c_get_dma_safe_msg_buf()?

...btw: the relative lack of people doing this in the kernel is
further evidence of DMA not really being worth it for i2c busses.


> +               if (!rx_dma) {
> +                       mode = GENI_SE_FIFO;
> +                       geni_se_select_mode(&gi2c->se, mode);
> +               }
> +       }
> +
> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);

Perhaps make a #define for the timeout instead of just hardcoding HZ (1 second).


> +       if (!timeout)

Can you rename "timeout" to "time_left"?  Otherwise this read like "if
there wasn't a timeout then abort".


> +               geni_i2c_abort_xfer(gi2c);
> +
> +       gi2c->cur_rd = 0;
> +       if (mode == GENI_SE_DMA) {
> +               if (gi2c->err) {
> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
> +                       wait_for_completion_timeout(&gi2c->done, HZ);

Worth printing an error if this one times out?  Seems like we'd be in
bad shape...

...also: to be paranoid do you need a re_init_completion before you
reset things?  In theory one could conceive of the concept that the
earlier completion timed out and then the DMA interrupt came right
after.  Now there will be a completion already on the books so your
wait will return instantly even though the reset hasn't been done.


> +               }
> +               geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
> +       }
> +       if (gi2c->err)
> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);

OK, so I'm a bit baffled.  You've got all these tables in this driver
that give you nice/informative error messages.  Then those nice error
messages are just calling dev_dbg() and here you print out an arcane
linux error?

Also: seems like you wouldn't want to print errors for NACKs, right?
Otherwise i2cdetect is going to be spewing isn't it?


> +       return gi2c->err;
> +}
> +
> +static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
> +                               u32 m_param)
> +{
> +       dma_addr_t tx_dma;
> +       enum geni_se_xfer_mode mode;
> +       unsigned long timeout;
> +
> +       gi2c->cur = msg;
> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
> +       geni_se_select_mode(&gi2c->se, mode);
> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
> +       geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
> +       if (mode == GENI_SE_DMA) {
> +               tx_dma = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len);
> +               if (!tx_dma) {
> +                       mode = GENI_SE_FIFO;
> +                       geni_se_select_mode(&gi2c->se, mode);
> +               }
> +       }
> +
> +       if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
> +               writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG);
> +
> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);
> +       if (!timeout)
> +               geni_i2c_abort_xfer(gi2c);
> +
> +       gi2c->cur_wr = 0;
> +       if (mode == GENI_SE_DMA) {
> +               if (gi2c->err) {
> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
> +                       wait_for_completion_timeout(&gi2c->done, HZ);
> +               }
> +               geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
> +       }
> +       if (gi2c->err)
> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
> +       return gi2c->err;
> +}
> +
> +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;
> +
> +       gi2c->err = 0;
> +       reinit_completion(&gi2c->done);
> +       ret = pm_runtime_get_sync(gi2c->se.dev);
> +       if (ret < 0) {
> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
> +               pm_runtime_put_noidle(gi2c->se.dev);
> +               /* Set device in suspended since resume failed */
> +               pm_runtime_set_suspended(gi2c->se.dev);
> +               return ret;

Wow, that's a cluster of arcane calls to handle a call that probably
will never fail (it just enables clocks and sets pinctrl).  Sigh.
...but as far as I can tell the whole sequence is right.  You
definitely need a "put" after a failed get and it looks like
pm_runtime_set_suspended() has a special exception where it can be
called if you got a runtime error...


> +       }
> +
> +       qcom_geni_i2c_conf(gi2c);
> +       for (i = 0; i < num; i++) {
> +               u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
> +
> +               m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
> +
> +               if (msgs[i].flags & I2C_M_RD)
> +                       ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
> +               else
> +                       ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
> +
> +               if (ret) {
> +                       dev_err(gi2c->se.dev, "i2c error %d @ %d\n", ret, i);
> +                       break;
> +               }
> +       }
> +       if (ret == 0)
> +               ret = num;
> +
> +       pm_runtime_mark_last_busy(gi2c->se.dev);
> +       pm_runtime_put_autosuspend(gi2c->se.dev);
> +       gi2c->cur = NULL;
> +       gi2c->err = 0;
> +       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;
> +       u32 proto, tx_depth;
> +       int ret;
> +
> +       gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
> +       if (!gi2c)
> +               return -ENOMEM;
> +
> +       gi2c->se.dev = &pdev->dev;
> +       gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(gi2c->se.base)) {
> +               ret = PTR_ERR(gi2c->se.base);
> +               dev_err(&pdev->dev, "Err IO Mapping register block %d\n", ret);

No need for error message with devm_ioremap_resource().


> +               return ret;
> +       }
> +
> +       gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
> +       if (IS_ERR(gi2c->se.clk)) {
> +               ret = PTR_ERR(gi2c->se.clk);
> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +               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);
> +       }

I feel like it should default to 100KHz.  i2c_parse_fw_timings()
defaults to this and to me the wording "New drivers almost always
should use the defaults" makes me feel this should be the defaults.

> +
> +       gi2c->irq = platform_get_irq(pdev, 0);
> +       if (gi2c->irq < 0) {
> +               dev_err(&pdev->dev, "IRQ error for i2c-geni\n");
> +               return gi2c->irq;
> +       }
> +
> +       ret = geni_i2c_clk_map_idx(gi2c);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Invalid clk frequency %d KHz: %d\n",
> +                       gi2c->clk_freq_out, ret);

Need a divide by 1000 since your printout includes "KHz".  Also note
that the proper Si units is kHz not KHz, isn't it?


> +               return ret;
> +       }
> +
> +       gi2c->adap.algo = &geni_i2c_algo;
> +       init_completion(&gi2c->done);
> +       platform_set_drvdata(pdev, gi2c);
> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
> +                       gi2c->irq, ret);
> +               return ret;
> +       }
> +       disable_irq(gi2c->irq);

Can you explain the goal of the disable_irq() here.  Is it actually
needed for something or does it somehow save power?  From drivers I've
reviewed in the past this doesn't seem like a common thing to do, so
I'm curious what it's supposed to gain for you.  I'd be inclined to
just delete the whole disable/enable of the irq from this driver.


> +       i2c_set_adapdata(&gi2c->adap, gi2c);
> +       gi2c->adap.dev.parent = &pdev->dev;
> +       gi2c->adap.dev.of_node = pdev->dev.of_node;
> +       strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
> +
> +       ret = geni_se_resources_on(&gi2c->se);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
> +               return ret;
> +       }
> +       proto = geni_se_read_proto(&gi2c->se);
> +       tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
> +       if (unlikely(proto != GENI_SE_I2C)) {

Avoid compiler hints like unlikely() unless you're really truly
optimizing a tight inner loop.  Otherwise let the compiler do its job.


> +               dev_err(&pdev->dev, "Invalid proto %d\n", proto);
> +               geni_se_resources_off(&gi2c->se);
> +               return -ENXIO;
> +       }
> +       gi2c->tx_wm = tx_depth - 1;
> +       geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
> +       geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
> +                                                       true, true, true);
> +       geni_se_resources_off(&gi2c->se);
> +       dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
> +
> +       pm_runtime_set_suspended(gi2c->se.dev);
> +       pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
> +       pm_runtime_use_autosuspend(gi2c->se.dev);
> +       pm_runtime_enable(gi2c->se.dev);
> +       i2c_add_adapter(&gi2c->adap);
> +
> +       dev_dbg(&pdev->dev, "I2C probed\n");

Is this really a useful dev_dbg()?  Just turn on initcall debugging...


> +       return 0;
> +}
> +
> +static int geni_i2c_remove(struct platform_device *pdev)
> +{
> +       struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
> +
> +       pm_runtime_disable(gi2c->se.dev);

Is this right?  You don't want to disable PM Runtime transitions but
you actually want to force the adapter into suspended state, don't
you?  I don't see anything that does that...


> +       i2c_del_adapter(&gi2c->adap);
> +       return 0;
> +}
> +
> +static int geni_i2c_resume_noirq(struct device *device)
> +{
> +       return 0;
> +}

No need for a dummy function; just stick NULL in the structure, no?

> +
> +#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->se);
> +       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->se);
> +       if (ret)
> +               return ret;
> +
> +       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->se.dev, "late I2C transaction request\n");
> +               return -EBUSY;
> +       }

Does this happen?  How?

Nothing about this code looks special for your hardware.  If this is
really needed, why is it not part of the i2c core since there's
nothing specific about your driver here?


> +       if (!pm_runtime_status_suspended(device)) {
> +               geni_i2c_runtime_suspend(device);
> +               pm_runtime_disable(device);
> +               pm_runtime_set_suspended(device);
> +               pm_runtime_enable(device);
> +       }

Similar question.  Why do you need this special case code?  Are there
cases where we're all the way at suspend_noirq and yet we still
haven't runtime suspended?  Can you please document how we get into
this state?


> +       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,

Please use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS and SET_RUNTIME_PM_OPS.  Then
you can get rid of all the dummy functions.  AKA something like:

static const struct dev_pm_ops geni_i2c_pm_ops = {
  SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL)
  SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, NULL)
};


> +};
> +
> +static const struct of_device_id geni_i2c_dt_match[] = {
> +       { .compatible = "qcom,geni-i2c" },
> +       {}
> +};
> +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 = "geni_i2c",
> +               .pm = &geni_i2c_pm_ops,
> +               .of_match_table = geni_i2c_dt_match,
> +       },
> +};
> +
> +module_platform_driver(geni_i2c_driver);
> +
> +MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores");
> +MODULE_LICENSE("GPL v2");

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

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-07 21:16     ` Doug Anderson
  0 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-07 21:16 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, evgreen,
	acourbot, Sagar Dharia, Girish Mahadevan, swboyd

Hi,

On Tue, Feb 27, 2018 at 5:38 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         |  11 +
>  drivers/i2c/busses/Makefile        |   1 +
>  drivers/i2c/busses/i2c-qcom-geni.c | 626 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 638 insertions(+)

I'm not an expert on geni (and, to be honest, I haven't read the main
geni patch yet).  ...but I figured I could at least add my $0.02 since
I've stared at i2c bus drivers a lot in the past.  Feel free to tell
me if I'm full or crap...


>  create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index e2954fb..1ddf5cd 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -848,6 +848,17 @@ 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
> +       depends on QCOM_GENI_SE
> +       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.

Kind of a generic description and this driver is only for new SoCs,
right?  Maybe make it a little more specific?


> +
> +         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..e1e4268
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
> @@ -0,0 +1,626 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> +
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.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/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_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           BIT(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
> +/* I2C SCL COUNTER fields */
> +#define HIGH_COUNTER_MSK       GENMASK(29, 20)
> +#define HIGH_COUNTER_SHFT      20
> +#define LOW_COUNTER_MSK                GENMASK(19, 10)
> +#define LOW_COUNTER_SHFT       10
> +#define CYCLE_COUNTER_MSK      GENMASK(9, 0)
> +
> +#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

Above should be an enum; then use the enum type as the parameter to
geni_i2c_err() so it's obvious that "err" is not a normal linux error
code.


> +#define I2C_NACK               GP_IRQ1
> +#define I2C_BUS_PROTO          GP_IRQ3
> +#define I2C_ARB_LOST           GP_IRQ4

Get rid of definition of GP_IRQ1, 3, and 4 and just define I2C_NACK,
I2C_BUS_PROTO, and I2C_ARB_LOST directly.


> +#define DM_I2C_CB_ERR          ((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
> +                                                                       << 5)

Should these really be using "GP_IRQ1", "GP_IRQ3", and "GP_IRQ4".
Does this use of those numbers have anything to do with the other use
of them?  Seems like this should just be BIT(1) | BIT(3) | BIT(4).

Said another way: does bit 1 in this field coorespond to NACK, bit 3
correspond to BUS_PROTO, and bit 4 correspond to ARB_LOST?  If not
then I see no reason to try to tie them together.  If they do
correspond then use BIT(I2C_NACK), etc...


> +
> +#define I2C_AUTO_SUSPEND_DELAY 250

Why 250 ms?  That seems like an eternity.  Is it really that expensive
to turn resources off and on?  I would sorta just expect clocks and
stuff to get turned off right after a transaction finished unless
another one was pending right behind it...


> +#define KHz(freq)              (1000 * freq)

I probably wouldn't define KHz macro and just used numbers like 100000
like all the other i2c drivers, but I guess it's OK.  Should be all
caps, though?


> +#define PACKING_BYTES_PW       4
> +
> +struct geni_i2c_dev {
> +       struct geni_se se;
> +       u32 tx_wm;
> +       int irq;
> +       int err;
> +       struct i2c_adapter adap;
> +       struct completion done;
> +       struct i2c_msg *cur;
> +       int cur_wr;
> +       int cur_rd;
> +       u32 clk_freq_out;
> +       const struct geni_i2c_clk_fld *clk_fld;
> +};
> +
> +struct geni_i2c_err_log {
> +       int err;
> +       const char *msg;
> +};
> +
> +static struct geni_i2c_err_log gi2c_log[] = {

static const?


> +       [GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
> +       [I2C_NACK] = {-ENOTCONN, "NACK: slv unresponsive, check its power/reset-ln"},

Longer than 80 characters; don't split the string, but you could still
wrap better.


> +       [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 const 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},

So I guess this is all relying on an input serial clock of 19.2MHz?
Maybe document that?

Assuming I'm understanding the math here, is it really OK for your
100kHz and 1MHz mode to be running slightly fast?

19200. / 2 / 24
>>> 400.0

19200. / 7 / 26
>>> 105.49450549450549

19200. / 1 / 18
>>> 1066.6666666666667

It seems like you'd want the fastest clock that you can make that's
_less than_ the spec.


It would also be interesting to know if it's expected that boards
might need to tweak the t_high / t_low depending on their electrical
characteristics.  In the past I've had lots of requests from board
makers to tweak things because they've got a long trace, or a stronger
or weaker pull, or ...  If so we might later need to add some dts
properties like "i2c-scl-rising-time-ns" and make the math more
dynamic here, unless your hardware somehow automatically adjusts for
this type of thing...


> +};
> +
> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
> +{
> +       int i;
> +       const 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) {
> +                       gi2c->clk_fld = geni_i2c_clk_map + i;

Isn't "geni_i2c_clk_map + i" just "itr"?


> +                       return 0;
> +               }
> +       }
> +       return -EINVAL;
> +}
> +
> +static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
> +{
> +       const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
> +       u32 val;
> +
> +       writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
> +
> +       val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
> +       writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
> +
> +       val = itr->t_high << HIGH_COUNTER_SHFT;
> +       val |= itr->t_low << LOW_COUNTER_SHFT;
> +       val |= itr->t_cycle;
> +       writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
> +       /*
> +        * Ensure later writes/reads to serial engine register block is
> +        * not reordered before this point.
> +        */
> +       mb();

This mb() is to make sure that later writes to "gi2c->se.base" are not
reordered to be above the ones in this function?  You don't need a
mb().  writel_relaxed() already enforces this.


> +}
> +
> +static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
> +{
> +       u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0);
> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
> +       u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
> +       u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS);
> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
> +       u32 rx_st, tx_st;
> +
> +       if (dma) {
> +               rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
> +               tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
> +       } else {
> +               rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
> +               tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS);
> +       }
> +       dev_dbg(gi2c->se.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->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
> +               m_cmd, geni_s, geni_ios);
> +}
> +
> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
> +{
> +       gi2c->err = gi2c_log[err].err;

You should only set gi2c->err if it was 0 to start with.  You want
"err" to contain the first error, not the last one.  This is
especially important due to the comment elsewhere in this patch "if
this is err with done-bit not set, handle that through timeout".  You
don't want the timeout to clobber the true error.


On a separate note: I wonder if it makes sense to couch the rest of
this function in something that will compile to a no-op if DEBUG and
DYNAMIC_DEBUG aren't defined?  Then you can avoid including code for
all these readl calls.

> +       if (gi2c->cur)
> +               dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
> +                       gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
> +       dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
> +
> +       if (err != I2C_NACK && err != GENI_ABORT_DONE)
> +               geni_i2c_err_misc(gi2c);
> +}
> +
> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
> +{
> +       struct geni_i2c_dev *gi2c = dev;
> +       int j;
> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
> +       u32 rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
> +       u32 dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
> +       u32 dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
> +       struct i2c_msg *cur = gi2c->cur;
> +
> +       if (!cur ||
> +           m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
> +           dm_rx_st & (DM_I2C_CB_ERR)) {
> +               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);
> +
> +               /* Disable the TX Watermark interrupt to stop TX */
> +               if (!dma)
> +                       writel_relaxed(0, gi2c->se.base +
> +                                          SE_GENI_TX_WATERMARK_REG);
> +               goto irqret;
> +       }
> +
> +       if (dma) {
> +               dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n",
> +                       dm_tx_st, dm_rx_st);
> +               goto irqret;
> +       }
> +
> +       if (cur->flags & I2C_M_RD &&
> +           m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) {
> +               u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
> +
> +               for (j = 0; j < rxcnt; j++) {
> +                       u32 val;
> +                       int p = 0;
> +
> +                       val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn);
> +                       while (gi2c->cur_rd < cur->len && p < sizeof(val)) {
> +                               cur->buf[gi2c->cur_rd++] = val & 0xff;
> +                               val >>= 8;
> +                               p++;
> +                       }
> +                       if (gi2c->cur_rd == cur->len)
> +                               break;
> +               }
> +       } else if (!(cur->flags & I2C_M_RD) &&
> +                  m_stat & M_TX_FIFO_WATERMARK_EN) {
> +               for (j = 0; j < gi2c->tx_wm; j++) {
> +                       u32 temp;
> +                       u32 val = 0;
> +                       int p = 0;
> +
> +                       while (gi2c->cur_wr < cur->len && p < sizeof(val)) {
> +                               temp = (u32)cur->buf[gi2c->cur_wr++];

What is the (u32) cast doing here?


> +                               val |= (temp << (p * 8));

Get rid of extra parenthesis.


> +                               p++;
> +                       }
> +                       writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn);
> +                       /* TX Complete, Disable the TX Watermark interrupt */
> +                       if (gi2c->cur_wr == cur->len) {
> +                               writel_relaxed(0, gi2c->se.base +
> +                                               SE_GENI_TX_WATERMARK_REG);
> +                               break;
> +                       }
> +               }
> +       }
> +irqret:
> +       if (m_stat)
> +               writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR);
> +
> +       if (dma) {
> +               if (dm_tx_st)
> +                       writel_relaxed(dm_tx_st, gi2c->se.base +
> +                                               SE_DMA_TX_IRQ_CLR);
> +               if (dm_rx_st)
> +                       writel_relaxed(dm_rx_st, gi2c->se.base +
> +                                               SE_DMA_RX_IRQ_CLR);
> +       }
> +       /* if this is err with done-bit not set, handle that through timeout. */
> +       if (m_stat & M_CMD_DONE_EN)
> +               complete(&gi2c->done);
> +       else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
> +               complete(&gi2c->done);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
> +{
> +       u32 val;
> +       unsigned long timeout = HZ;

Rename to time_left?  ...and maybe use a #define for the init value?


> +
> +       geni_i2c_err(gi2c, GENI_TIMEOUT);
> +       gi2c->cur = NULL;

Don't you need a spinlock or something?  In most of the other cases
you could get away with no locking because the irq isn't happening at
the same time as other code that's mucking with stuff, but in the
timeout case we may be mucking with stuff at the same time as the irq.


> +       geni_se_abort_m_cmd(&gi2c->se);
> +       do {
> +               timeout = wait_for_completion_timeout(&gi2c->done, timeout);
> +               val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
> +       } while (!(val & M_CMD_ABORT_EN) && timeout);

Print an error if there was a timeout aborting?


> +}
> +
> +static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
> +                               u32 m_param)
> +{
> +       dma_addr_t rx_dma;
> +       enum geni_se_xfer_mode mode;
> +       unsigned long timeout;
> +
> +       gi2c->cur = msg;
> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;

DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
a lot by transferring i2c commands over DMA compared to a FIFO?
Enough to justify the code complexity and the set of bugs that will
show up?  I'm sure it will be a controversial assertion given that the
code's already written, but personally I'd be supportive of ripping
DMA mode out to simplify the driver.  I'd be curious if anyone else
agrees.  To me it seems like premature optimization.


> +       geni_se_select_mode(&gi2c->se, mode);
> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
> +       if (mode == GENI_SE_DMA) {
> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len);

Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
check this flag before using msg->buf for DMA?  ...or use
i2c_get_dma_safe_msg_buf()?

...btw: the relative lack of people doing this in the kernel is
further evidence of DMA not really being worth it for i2c busses.


> +               if (!rx_dma) {
> +                       mode = GENI_SE_FIFO;
> +                       geni_se_select_mode(&gi2c->se, mode);
> +               }
> +       }
> +
> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);

Perhaps make a #define for the timeout instead of just hardcoding HZ (1 second).


> +       if (!timeout)

Can you rename "timeout" to "time_left"?  Otherwise this read like "if
there wasn't a timeout then abort".


> +               geni_i2c_abort_xfer(gi2c);
> +
> +       gi2c->cur_rd = 0;
> +       if (mode == GENI_SE_DMA) {
> +               if (gi2c->err) {
> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
> +                       wait_for_completion_timeout(&gi2c->done, HZ);

Worth printing an error if this one times out?  Seems like we'd be in
bad shape...

...also: to be paranoid do you need a re_init_completion before you
reset things?  In theory one could conceive of the concept that the
earlier completion timed out and then the DMA interrupt came right
after.  Now there will be a completion already on the books so your
wait will return instantly even though the reset hasn't been done.


> +               }
> +               geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
> +       }
> +       if (gi2c->err)
> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);

OK, so I'm a bit baffled.  You've got all these tables in this driver
that give you nice/informative error messages.  Then those nice error
messages are just calling dev_dbg() and here you print out an arcane
linux error?

Also: seems like you wouldn't want to print errors for NACKs, right?
Otherwise i2cdetect is going to be spewing isn't it?


> +       return gi2c->err;
> +}
> +
> +static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
> +                               u32 m_param)
> +{
> +       dma_addr_t tx_dma;
> +       enum geni_se_xfer_mode mode;
> +       unsigned long timeout;
> +
> +       gi2c->cur = msg;
> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
> +       geni_se_select_mode(&gi2c->se, mode);
> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
> +       geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
> +       if (mode == GENI_SE_DMA) {
> +               tx_dma = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len);
> +               if (!tx_dma) {
> +                       mode = GENI_SE_FIFO;
> +                       geni_se_select_mode(&gi2c->se, mode);
> +               }
> +       }
> +
> +       if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
> +               writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG);
> +
> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);
> +       if (!timeout)
> +               geni_i2c_abort_xfer(gi2c);
> +
> +       gi2c->cur_wr = 0;
> +       if (mode == GENI_SE_DMA) {
> +               if (gi2c->err) {
> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
> +                       wait_for_completion_timeout(&gi2c->done, HZ);
> +               }
> +               geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
> +       }
> +       if (gi2c->err)
> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
> +       return gi2c->err;
> +}
> +
> +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;
> +
> +       gi2c->err = 0;
> +       reinit_completion(&gi2c->done);
> +       ret = pm_runtime_get_sync(gi2c->se.dev);
> +       if (ret < 0) {
> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
> +               pm_runtime_put_noidle(gi2c->se.dev);
> +               /* Set device in suspended since resume failed */
> +               pm_runtime_set_suspended(gi2c->se.dev);
> +               return ret;

Wow, that's a cluster of arcane calls to handle a call that probably
will never fail (it just enables clocks and sets pinctrl).  Sigh.
...but as far as I can tell the whole sequence is right.  You
definitely need a "put" after a failed get and it looks like
pm_runtime_set_suspended() has a special exception where it can be
called if you got a runtime error...


> +       }
> +
> +       qcom_geni_i2c_conf(gi2c);
> +       for (i = 0; i < num; i++) {
> +               u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
> +
> +               m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
> +
> +               if (msgs[i].flags & I2C_M_RD)
> +                       ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
> +               else
> +                       ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
> +
> +               if (ret) {
> +                       dev_err(gi2c->se.dev, "i2c error %d @ %d\n", ret, i);
> +                       break;
> +               }
> +       }
> +       if (ret == 0)
> +               ret = num;
> +
> +       pm_runtime_mark_last_busy(gi2c->se.dev);
> +       pm_runtime_put_autosuspend(gi2c->se.dev);
> +       gi2c->cur = NULL;
> +       gi2c->err = 0;
> +       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;
> +       u32 proto, tx_depth;
> +       int ret;
> +
> +       gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
> +       if (!gi2c)
> +               return -ENOMEM;
> +
> +       gi2c->se.dev = &pdev->dev;
> +       gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(gi2c->se.base)) {
> +               ret = PTR_ERR(gi2c->se.base);
> +               dev_err(&pdev->dev, "Err IO Mapping register block %d\n", ret);

No need for error message with devm_ioremap_resource().


> +               return ret;
> +       }
> +
> +       gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
> +       if (IS_ERR(gi2c->se.clk)) {
> +               ret = PTR_ERR(gi2c->se.clk);
> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +               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);
> +       }

I feel like it should default to 100KHz.  i2c_parse_fw_timings()
defaults to this and to me the wording "New drivers almost always
should use the defaults" makes me feel this should be the defaults.

> +
> +       gi2c->irq = platform_get_irq(pdev, 0);
> +       if (gi2c->irq < 0) {
> +               dev_err(&pdev->dev, "IRQ error for i2c-geni\n");
> +               return gi2c->irq;
> +       }
> +
> +       ret = geni_i2c_clk_map_idx(gi2c);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Invalid clk frequency %d KHz: %d\n",
> +                       gi2c->clk_freq_out, ret);

Need a divide by 1000 since your printout includes "KHz".  Also note
that the proper Si units is kHz not KHz, isn't it?


> +               return ret;
> +       }
> +
> +       gi2c->adap.algo = &geni_i2c_algo;
> +       init_completion(&gi2c->done);
> +       platform_set_drvdata(pdev, gi2c);
> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
> +                       gi2c->irq, ret);
> +               return ret;
> +       }
> +       disable_irq(gi2c->irq);

Can you explain the goal of the disable_irq() here.  Is it actually
needed for something or does it somehow save power?  From drivers I've
reviewed in the past this doesn't seem like a common thing to do, so
I'm curious what it's supposed to gain for you.  I'd be inclined to
just delete the whole disable/enable of the irq from this driver.


> +       i2c_set_adapdata(&gi2c->adap, gi2c);
> +       gi2c->adap.dev.parent = &pdev->dev;
> +       gi2c->adap.dev.of_node = pdev->dev.of_node;
> +       strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
> +
> +       ret = geni_se_resources_on(&gi2c->se);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
> +               return ret;
> +       }
> +       proto = geni_se_read_proto(&gi2c->se);
> +       tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
> +       if (unlikely(proto != GENI_SE_I2C)) {

Avoid compiler hints like unlikely() unless you're really truly
optimizing a tight inner loop.  Otherwise let the compiler do its job.


> +               dev_err(&pdev->dev, "Invalid proto %d\n", proto);
> +               geni_se_resources_off(&gi2c->se);
> +               return -ENXIO;
> +       }
> +       gi2c->tx_wm = tx_depth - 1;
> +       geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
> +       geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
> +                                                       true, true, true);
> +       geni_se_resources_off(&gi2c->se);
> +       dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
> +
> +       pm_runtime_set_suspended(gi2c->se.dev);
> +       pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
> +       pm_runtime_use_autosuspend(gi2c->se.dev);
> +       pm_runtime_enable(gi2c->se.dev);
> +       i2c_add_adapter(&gi2c->adap);
> +
> +       dev_dbg(&pdev->dev, "I2C probed\n");

Is this really a useful dev_dbg()?  Just turn on initcall debugging...


> +       return 0;
> +}
> +
> +static int geni_i2c_remove(struct platform_device *pdev)
> +{
> +       struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
> +
> +       pm_runtime_disable(gi2c->se.dev);

Is this right?  You don't want to disable PM Runtime transitions but
you actually want to force the adapter into suspended state, don't
you?  I don't see anything that does that...


> +       i2c_del_adapter(&gi2c->adap);
> +       return 0;
> +}
> +
> +static int geni_i2c_resume_noirq(struct device *device)
> +{
> +       return 0;
> +}

No need for a dummy function; just stick NULL in the structure, no?

> +
> +#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->se);
> +       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->se);
> +       if (ret)
> +               return ret;
> +
> +       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->se.dev, "late I2C transaction request\n");
> +               return -EBUSY;
> +       }

Does this happen?  How?

Nothing about this code looks special for your hardware.  If this is
really needed, why is it not part of the i2c core since there's
nothing specific about your driver here?


> +       if (!pm_runtime_status_suspended(device)) {
> +               geni_i2c_runtime_suspend(device);
> +               pm_runtime_disable(device);
> +               pm_runtime_set_suspended(device);
> +               pm_runtime_enable(device);
> +       }

Similar question.  Why do you need this special case code?  Are there
cases where we're all the way at suspend_noirq and yet we still
haven't runtime suspended?  Can you please document how we get into
this state?


> +       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,

Please use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS and SET_RUNTIME_PM_OPS.  Then
you can get rid of all the dummy functions.  AKA something like:

static const struct dev_pm_ops geni_i2c_pm_ops = {
  SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL)
  SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, NULL)
};


> +};
> +
> +static const struct of_device_id geni_i2c_dt_match[] = {
> +       { .compatible = "qcom,geni-i2c" },
> +       {}
> +};
> +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 = "geni_i2c",
> +               .pm = &geni_i2c_pm_ops,
> +               .of_match_table = geni_i2c_dt_match,
> +       },
> +};
> +
> +module_platform_driver(geni_i2c_driver);
> +
> +MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores");
> +MODULE_LICENSE("GPL v2");
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-03-07 21:16     ` Doug Anderson
@ 2018-03-08  2:42       ` Sagar Dharia
  -1 siblings, 0 replies; 65+ messages in thread
From: Sagar Dharia @ 2018-03-08  2:42 UTC (permalink / raw)
  To: Doug Anderson, 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, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi Doug,
Thank you for reviewing the patch. I will take a stab at a few comments 
below. We will address most of the other comments in next version of I2C 
patch.
> 
> 
>> +
>> +#define I2C_AUTO_SUSPEND_DELAY 250
> 
> Why 250 ms?  That seems like an eternity.  Is it really that expensive
> to turn resources off and on?  I would sorta just expect clocks and
> stuff to get turned off right after a transaction finished unless
> another one was pending right behind it...
> 
The response from RPMh to turn on/off shared resources also take quite a 
few msecs. The QUP serial bus block sits quite a few shared-NOCs away 
from the memory and runtime-PM is used a bandwidth vote/NOC vote for 
these NOCs from QUP to memory. Also the RPC between apps and RPMh can 
sometimes take longer depending on other tasks running on apps. This 250 
msec avoids thrashing of these RPCs between apps and RPMh.
If you plan to keep these NOCs on forever, then your are right: 
runtime-PM will be only used to turn on/off local clocks and we won't 
even need autosuspend. that's not true on products where this driver is 
currently deployed.
>> +
>> +static const 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},
> 
> So I guess this is all relying on an input serial clock of 19.2MHz?
> Maybe document that?
> 
> Assuming I'm understanding the math here, is it really OK for your
> 100kHz and 1MHz mode to be running slightly fast?
> 
> 19200. / 2 / 24
>>>> 400.0
> 
> 19200. / 7 / 26
>>>> 105.49450549450549
> 
> 19200. / 1 / 18
>>>> 1066.6666666666667
> 
> It seems like you'd want the fastest clock that you can make that's
> _less than_ the spec.
> 
> 
> It would also be interesting to know if it's expected that boards
> might need to tweak the t_high / t_low depending on their electrical
> characteristics.  In the past I've had lots of requests from board
> makers to tweak things because they've got a long trace, or a stronger
> or weaker pull, or ...  If so we might later need to add some dts
> properties like "i2c-scl-rising-time-ns" and make the math more
> dynamic here, unless your hardware somehow automatically adjusts for
> this type of thing...
>These values are derived by our HW team to comply with the t-high and 
t-low specs of I2C. We have confirmed on scope that the frequency of SCL 
is indeed less than/equal to the spec. We have not come across slaves 
who have needed to tweak these things. We are open to adding these 
properties in dts if you have any such slaves not conforming due to 
board-layout of other reasons.
>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
> 
> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
> a lot by transferring i2c commands over DMA compared to a FIFO?
> Enough to justify the code complexity and the set of bugs that will
> show up?  I'm sure it will be a controversial assertion given that the
> code's already written, but personally I'd be supportive of ripping
> DMA mode out to simplify the driver.  I'd be curious if anyone else
> agrees.  To me it seems like premature optimization.

Yes, we have multiple clients (e.g. touch, NFC) using I2C for data 
transfers bigger than 32 bytes (some transfers are 100s of bytes). The 
fifo size is 32, so we can definitely avoid at least 1 interrupt when 
DMA mode is used with data size > 32.
> 
> 
>> +       geni_se_select_mode(&gi2c->se, mode);
>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
>> +       if (mode == GENI_SE_DMA) {
>> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len);
> 
> Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
> check this flag before using msg->buf for DMA?  ...or use
> i2c_get_dma_safe_msg_buf()?
> 
> ...btw: the relative lack of people doing this in the kernel is
> further evidence of DMA not really being worth it for i2c busses.
I cannot comment about other drivers here using or not using DMA since 
they may not be exercised with slaves like NFC?
>> +       ret = pm_runtime_get_sync(gi2c->se.dev);
>> +       if (ret < 0) {
>> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
>> +               pm_runtime_put_noidle(gi2c->se.dev);
>> +               /* Set device in suspended since resume failed */
>> +               pm_runtime_set_suspended(gi2c->se.dev);
>> +               return ret;
> 
> Wow, that's a cluster of arcane calls to handle a call that probably
> will never fail (it just enables clocks and sets pinctrl).  Sigh.
> ...but as far as I can tell the whole sequence is right.  You
> definitely need a "put" after a failed get and it looks like
> pm_runtime_set_suspended() has a special exception where it can be
> called if you got a runtime error...
We didn't have this in before either. But this condition is somewhat 
frequent if I2C transactions are called on cusp of exiting system 
suspend. (e.g. PMIC slave getting a wakeup-IRQ and trying to read from 
PMIC through I2C to read its status as to what caused that wake-up. At 
that time, get_sync doesn't really enable resources (kernel 4.9) since 
the runtime-pm ref-count isn't decremented. We run the risk of unclocked 
access if these arcane calls aren't present. You can go through 
runtime-pm documentation chapter 6 for more details.

>> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
>> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
>> +                       gi2c->irq, ret);
>> +               return ret;
>> +       }
>> +       disable_irq(gi2c->irq);
> 
> Can you explain the goal of the disable_irq() here.  Is it actually
> needed for something or does it somehow save power?  From drivers I've
> reviewed in the past this doesn't seem like a common thing to do, so
> I'm curious what it's supposed to gain for you.  I'd be inclined to
> just delete the whole disable/enable of the irq from this driver.

Qualcomm's power team suggests we enable/disable unused IRQs. Otherwise 
they can block apps from entering some low-power mode (unless the 
interrupt is in some list?) I will confirm again with them and let you know.
>> +       /* Make sure no transactions are pending */
>> +       ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
>> +       if (!ret) {
>> +               dev_err(gi2c->se.dev, "late I2C transaction request\n");
>> +               return -EBUSY;
>> +       }
> 
> Does this happen?  How?
> 
> Nothing about this code looks special for your hardware.  If this is
> really needed, why is it not part of the i2c core since there's
> nothing specific about your driver here?
> 
There have been some clients that don't implement sys-suspend/resume 
callbacks (so i2c adapter has no clue they are done with their 
transactions) and this allows us to be flexible when they call I2C 
transactions extremely late.

> 
>> +       if (!pm_runtime_status_suspended(device)) {
>> +               geni_i2c_runtime_suspend(device);
>> +               pm_runtime_disable(device);
>> +               pm_runtime_set_suspended(device);
>> +               pm_runtime_enable(device);
>> +       }
> 
> Similar question.  Why do you need this special case code?  Are there
> cases where we're all the way at suspend_noirq and yet we still
> haven't runtime suspended?  Can you please document how we get into
> this state?
> This is when transaction happens less-than 250 msec of the 
system-suspend. PM-runtime has not gotten a chance to auto-suspend us 
since timer hasn't expired before system-suspend is attempted. These 
calls make sure that we truly turn off driver resources and make 
runtime-PM state consistent with the HW state. We can document this.


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

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

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-08  2:42       ` Sagar Dharia
  0 siblings, 0 replies; 65+ messages in thread
From: Sagar Dharia @ 2018-03-08  2:42 UTC (permalink / raw)
  To: Doug Anderson, 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, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi Doug,
Thank you for reviewing the patch. I will take a stab at a few comments 
below. We will address most of the other comments in next version of I2C 
patch.
> 
> 
>> +
>> +#define I2C_AUTO_SUSPEND_DELAY 250
> 
> Why 250 ms?  That seems like an eternity.  Is it really that expensive
> to turn resources off and on?  I would sorta just expect clocks and
> stuff to get turned off right after a transaction finished unless
> another one was pending right behind it...
> 
The response from RPMh to turn on/off shared resources also take quite a 
few msecs. The QUP serial bus block sits quite a few shared-NOCs away 
from the memory and runtime-PM is used a bandwidth vote/NOC vote for 
these NOCs from QUP to memory. Also the RPC between apps and RPMh can 
sometimes take longer depending on other tasks running on apps. This 250 
msec avoids thrashing of these RPCs between apps and RPMh.
If you plan to keep these NOCs on forever, then your are right: 
runtime-PM will be only used to turn on/off local clocks and we won't 
even need autosuspend. that's not true on products where this driver is 
currently deployed.
>> +
>> +static const 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},
> 
> So I guess this is all relying on an input serial clock of 19.2MHz?
> Maybe document that?
> 
> Assuming I'm understanding the math here, is it really OK for your
> 100kHz and 1MHz mode to be running slightly fast?
> 
> 19200. / 2 / 24
>>>> 400.0
> 
> 19200. / 7 / 26
>>>> 105.49450549450549
> 
> 19200. / 1 / 18
>>>> 1066.6666666666667
> 
> It seems like you'd want the fastest clock that you can make that's
> _less than_ the spec.
> 
> 
> It would also be interesting to know if it's expected that boards
> might need to tweak the t_high / t_low depending on their electrical
> characteristics.  In the past I've had lots of requests from board
> makers to tweak things because they've got a long trace, or a stronger
> or weaker pull, or ...  If so we might later need to add some dts
> properties like "i2c-scl-rising-time-ns" and make the math more
> dynamic here, unless your hardware somehow automatically adjusts for
> this type of thing...
>These values are derived by our HW team to comply with the t-high and 
t-low specs of I2C. We have confirmed on scope that the frequency of SCL 
is indeed less than/equal to the spec. We have not come across slaves 
who have needed to tweak these things. We are open to adding these 
properties in dts if you have any such slaves not conforming due to 
board-layout of other reasons.
>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
> 
> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
> a lot by transferring i2c commands over DMA compared to a FIFO?
> Enough to justify the code complexity and the set of bugs that will
> show up?  I'm sure it will be a controversial assertion given that the
> code's already written, but personally I'd be supportive of ripping
> DMA mode out to simplify the driver.  I'd be curious if anyone else
> agrees.  To me it seems like premature optimization.

Yes, we have multiple clients (e.g. touch, NFC) using I2C for data 
transfers bigger than 32 bytes (some transfers are 100s of bytes). The 
fifo size is 32, so we can definitely avoid at least 1 interrupt when 
DMA mode is used with data size > 32.
> 
> 
>> +       geni_se_select_mode(&gi2c->se, mode);
>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
>> +       if (mode == GENI_SE_DMA) {
>> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len);
> 
> Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
> check this flag before using msg->buf for DMA?  ...or use
> i2c_get_dma_safe_msg_buf()?
> 
> ...btw: the relative lack of people doing this in the kernel is
> further evidence of DMA not really being worth it for i2c busses.
I cannot comment about other drivers here using or not using DMA since 
they may not be exercised with slaves like NFC?
>> +       ret = pm_runtime_get_sync(gi2c->se.dev);
>> +       if (ret < 0) {
>> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
>> +               pm_runtime_put_noidle(gi2c->se.dev);
>> +               /* Set device in suspended since resume failed */
>> +               pm_runtime_set_suspended(gi2c->se.dev);
>> +               return ret;
> 
> Wow, that's a cluster of arcane calls to handle a call that probably
> will never fail (it just enables clocks and sets pinctrl).  Sigh.
> ...but as far as I can tell the whole sequence is right.  You
> definitely need a "put" after a failed get and it looks like
> pm_runtime_set_suspended() has a special exception where it can be
> called if you got a runtime error...
We didn't have this in before either. But this condition is somewhat 
frequent if I2C transactions are called on cusp of exiting system 
suspend. (e.g. PMIC slave getting a wakeup-IRQ and trying to read from 
PMIC through I2C to read its status as to what caused that wake-up. At 
that time, get_sync doesn't really enable resources (kernel 4.9) since 
the runtime-pm ref-count isn't decremented. We run the risk of unclocked 
access if these arcane calls aren't present. You can go through 
runtime-pm documentation chapter 6 for more details.

>> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
>> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
>> +                       gi2c->irq, ret);
>> +               return ret;
>> +       }
>> +       disable_irq(gi2c->irq);
> 
> Can you explain the goal of the disable_irq() here.  Is it actually
> needed for something or does it somehow save power?  From drivers I've
> reviewed in the past this doesn't seem like a common thing to do, so
> I'm curious what it's supposed to gain for you.  I'd be inclined to
> just delete the whole disable/enable of the irq from this driver.

Qualcomm's power team suggests we enable/disable unused IRQs. Otherwise 
they can block apps from entering some low-power mode (unless the 
interrupt is in some list?) I will confirm again with them and let you know.
>> +       /* Make sure no transactions are pending */
>> +       ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
>> +       if (!ret) {
>> +               dev_err(gi2c->se.dev, "late I2C transaction request\n");
>> +               return -EBUSY;
>> +       }
> 
> Does this happen?  How?
> 
> Nothing about this code looks special for your hardware.  If this is
> really needed, why is it not part of the i2c core since there's
> nothing specific about your driver here?
> 
There have been some clients that don't implement sys-suspend/resume 
callbacks (so i2c adapter has no clue they are done with their 
transactions) and this allows us to be flexible when they call I2C 
transactions extremely late.

> 
>> +       if (!pm_runtime_status_suspended(device)) {
>> +               geni_i2c_runtime_suspend(device);
>> +               pm_runtime_disable(device);
>> +               pm_runtime_set_suspended(device);
>> +               pm_runtime_enable(device);
>> +       }
> 
> Similar question.  Why do you need this special case code?  Are there
> cases where we're all the way at suspend_noirq and yet we still
> haven't runtime suspended?  Can you please document how we get into
> this state?
> This is when transaction happens less-than 250 msec of the 
system-suspend. PM-runtime has not gotten a chance to auto-suspend us 
since timer hasn't expired before system-suspend is attempted. These 
calls make sure that we truly turn off driver resources and make 
runtime-PM state consistent with the HW state. We can document this.


Thanks
Sagar
-- 
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-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-03-08  2:42       ` Sagar Dharia
@ 2018-03-08  5:19         ` Doug Anderson
  -1 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-08  5:19 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi,

On Wed, Mar 7, 2018 at 6:42 PM, Sagar Dharia <sdharia@codeaurora.org> wrote:
> Hi Doug,
> Thank you for reviewing the patch. I will take a stab at a few comments
> below. We will address most of the other comments in next version of I2C
> patch.
>>
>>
>>
>>> +
>>> +#define I2C_AUTO_SUSPEND_DELAY 250
>>
>>
>> Why 250 ms?  That seems like an eternity.  Is it really that expensive
>> to turn resources off and on?  I would sorta just expect clocks and
>> stuff to get turned off right after a transaction finished unless
>> another one was pending right behind it...
>>
> The response from RPMh to turn on/off shared resources also take quite a few
> msecs. The QUP serial bus block sits quite a few shared-NOCs away from the
> memory and runtime-PM is used a bandwidth vote/NOC vote for these NOCs from
> QUP to memory. Also the RPC between apps and RPMh can sometimes take longer
> depending on other tasks running on apps. This 250 msec avoids thrashing of
> these RPCs between apps and RPMh.
> If you plan to keep these NOCs on forever, then your are right: runtime-PM
> will be only used to turn on/off local clocks and we won't even need
> autosuspend. that's not true on products where this driver is currently
> deployed.

OK, fair enough.  I don't know how RPMh works well enough to argue.
It does seem odd that you'd want to design things such that it takes a
few msecs to pull it out of runtime suspend, especially for touch.


>>> +
>>> +static const 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},
>>
>>
>> So I guess this is all relying on an input serial clock of 19.2MHz?
>> Maybe document that?
>>
>> Assuming I'm understanding the math here, is it really OK for your
>> 100kHz and 1MHz mode to be running slightly fast?
>>
>> 19200. / 2 / 24
>>>>>
>>>>> 400.0
>>
>>
>> 19200. / 7 / 26
>>>>>
>>>>> 105.49450549450549
>>
>>
>> 19200. / 1 / 18
>>>>>
>>>>> 1066.6666666666667
>>
>>
>> It seems like you'd want the fastest clock that you can make that's
>> _less than_ the spec.
>>
>>
>> It would also be interesting to know if it's expected that boards
>> might need to tweak the t_high / t_low depending on their electrical
>> characteristics.  In the past I've had lots of requests from board
>> makers to tweak things because they've got a long trace, or a stronger
>> or weaker pull, or ...  If so we might later need to add some dts
>> properties like "i2c-scl-rising-time-ns" and make the math more
>> dynamic here, unless your hardware somehow automatically adjusts for
>> this type of thing...
>> These values are derived by our HW team to comply with the t-high and
>
> t-low specs of I2C. We have confirmed on scope that the frequency of SCL is
> indeed less than/equal to the spec. We have not come across slaves who have
> needed to tweak these things. We are open to adding these properties in dts
> if you have any such slaves not conforming due to board-layout of other
> reasons.

OK, I'm fine with leaving something like this till later if/when it
comes up.  Documenting a little bit more about how these timings work
seems like it would be nice, though, at least mentioning what the
source clock is...


>>>
>>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
>>
>>
>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>> a lot by transferring i2c commands over DMA compared to a FIFO?
>> Enough to justify the code complexity and the set of bugs that will
>> show up?  I'm sure it will be a controversial assertion given that the
>> code's already written, but personally I'd be supportive of ripping
>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>> agrees.  To me it seems like premature optimization.
>
>
> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data transfers
> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size is
> 32, so we can definitely avoid at least 1 interrupt when DMA mode is used
> with data size > 32.

Does that 1-2 interrupts make any real difference, though?  In theory
it really shouldn't affect the transfer rate.  We should be able to
service the interrupt plenty fast and if we were concerned we would
tweak the watermark code a little bit.  So I guess we're worried about
the extra CPU cycles (and power cost) to service those extra couple
interrupts?

In theory when touch data is coming in or NFC data is coming in then
we're probably not in a super low power state to begin with.  If it's
touch data we likely want to have the CPU boosted a bunch to respond
to the user quickly.  If we've got 8 cores available all of which can
run at 1GHz or more a few interrupts won't kill us.  NFC data is
probably not common enough that we need to optimize power/CPU
utilizatoin for that.


So while i can believe that you do save an interrupt or two, I still
am not convinced that those interrupts are worth a bunch of complex
code (and a whole second code path) to save.


...also note that above you said that coming out of runtime suspend
can take several msec.  That seems like it dwarfs any slight
difference in timing between a FIFO-based operation and DMA.


>>> +       geni_se_select_mode(&gi2c->se, mode);
>>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
>>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
>>> +       if (mode == GENI_SE_DMA) {
>>> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf,
>>> msg->len);
>>
>>
>> Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
>> check this flag before using msg->buf for DMA?  ...or use
>> i2c_get_dma_safe_msg_buf()?
>>
>> ...btw: the relative lack of people doing this in the kernel is
>> further evidence of DMA not really being worth it for i2c busses.
>
> I cannot comment about other drivers here using or not using DMA since they
> may not be exercised with slaves like NFC?
>>>
>>> +       ret = pm_runtime_get_sync(gi2c->se.dev);
>>> +       if (ret < 0) {
>>> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n",
>>> ret);
>>> +               pm_runtime_put_noidle(gi2c->se.dev);
>>> +               /* Set device in suspended since resume failed */
>>> +               pm_runtime_set_suspended(gi2c->se.dev);
>>> +               return ret;
>>
>>
>> Wow, that's a cluster of arcane calls to handle a call that probably
>> will never fail (it just enables clocks and sets pinctrl).  Sigh.
>> ...but as far as I can tell the whole sequence is right.  You
>> definitely need a "put" after a failed get and it looks like
>> pm_runtime_set_suspended() has a special exception where it can be
>> called if you got a runtime error...
>
> We didn't have this in before either. But this condition is somewhat
> frequent if I2C transactions are called on cusp of exiting system suspend.
> (e.g. PMIC slave getting a wakeup-IRQ and trying to read from PMIC through
> I2C to read its status as to what caused that wake-up. At that time,
> get_sync doesn't really enable resources (kernel 4.9) since the runtime-pm
> ref-count isn't decremented. We run the risk of unclocked access if these
> arcane calls aren't present. You can go through runtime-pm documentation
> chapter 6 for more details.

Yeah, I certainly agree that the calls are needed if
pm_runtime_get_sync() and I'm not suggesting removing them.  Hence the
"as far as I can tell the whole sequence is right".

...but I'm actually kinda worried if you're saying that you actually
ran into this case.  Hopefully that got fixed and code no longer tries
to read from the PMIC at a bad time anymore?  That code should be
fixed not to be running so late in suspend.


>>> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
>>> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
>>> +       if (ret) {
>>> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
>>> +                       gi2c->irq, ret);
>>> +               return ret;
>>> +       }
>>> +       disable_irq(gi2c->irq);
>>
>>
>> Can you explain the goal of the disable_irq() here.  Is it actually
>> needed for something or does it somehow save power?  From drivers I've
>> reviewed in the past this doesn't seem like a common thing to do, so
>> I'm curious what it's supposed to gain for you.  I'd be inclined to
>> just delete the whole disable/enable of the irq from this driver.
>
>
> Qualcomm's power team suggests we enable/disable unused IRQs. Otherwise they
> can block apps from entering some low-power mode (unless the interrupt is in
> some list?) I will confirm again with them and let you know.

Since this is weird (to me anyway), please document w/ a comment.


>>>
>>> +       /* Make sure no transactions are pending */
>>> +       ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
>>> +       if (!ret) {
>>> +               dev_err(gi2c->se.dev, "late I2C transaction request\n");
>>> +               return -EBUSY;
>>> +       }
>>
>>
>> Does this happen?  How?
>>
>> Nothing about this code looks special for your hardware.  If this is
>> really needed, why is it not part of the i2c core since there's
>> nothing specific about your driver here?
>>
> There have been some clients that don't implement sys-suspend/resume
> callbacks (so i2c adapter has no clue they are done with their transactions)
> and this allows us to be flexible when they call I2C transactions extremely
> late.

Still feels like this belongs in the i2c core, not your driver.  Maybe
you should send a patch for the core and remove it from here?

...and also, it seems like any i2c clients that don't implement the
suspend/resume callbacks and try to do i2c transactions late in the
game need to be fixed.  It should be documented that this isn't a
valid thing for a driver to do and if we end up in this error case
then it's not an i2c issue but it's a bad driver somewhere.

>
>>
>>> +       if (!pm_runtime_status_suspended(device)) {
>>> +               geni_i2c_runtime_suspend(device);
>>> +               pm_runtime_disable(device);
>>> +               pm_runtime_set_suspended(device);
>>> +               pm_runtime_enable(device);
>>> +       }
>>
>>
>> Similar question.  Why do you need this special case code?  Are there
>> cases where we're all the way at suspend_noirq and yet we still
>> haven't runtime suspended?  Can you please document how we get into
>> this state?
>> This is when transaction happens less-than 250 msec of the
>
> system-suspend. PM-runtime has not gotten a chance to auto-suspend us since
> timer hasn't expired before system-suspend is attempted. These calls make
> sure that we truly turn off driver resources and make runtime-PM state
> consistent with the HW state. We can document this.

OK.  PM Runtime always gets me mixed up.  Seems really strange that it
wouldn't autosuspend all devices (regardless of timeout) at system
suspend time.

-Doug

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

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-08  5:19         ` Doug Anderson
  0 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-08  5:19 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi,

On Wed, Mar 7, 2018 at 6:42 PM, Sagar Dharia <sdharia@codeaurora.org> wrote:
> Hi Doug,
> Thank you for reviewing the patch. I will take a stab at a few comments
> below. We will address most of the other comments in next version of I2C
> patch.
>>
>>
>>
>>> +
>>> +#define I2C_AUTO_SUSPEND_DELAY 250
>>
>>
>> Why 250 ms?  That seems like an eternity.  Is it really that expensive
>> to turn resources off and on?  I would sorta just expect clocks and
>> stuff to get turned off right after a transaction finished unless
>> another one was pending right behind it...
>>
> The response from RPMh to turn on/off shared resources also take quite a few
> msecs. The QUP serial bus block sits quite a few shared-NOCs away from the
> memory and runtime-PM is used a bandwidth vote/NOC vote for these NOCs from
> QUP to memory. Also the RPC between apps and RPMh can sometimes take longer
> depending on other tasks running on apps. This 250 msec avoids thrashing of
> these RPCs between apps and RPMh.
> If you plan to keep these NOCs on forever, then your are right: runtime-PM
> will be only used to turn on/off local clocks and we won't even need
> autosuspend. that's not true on products where this driver is currently
> deployed.

OK, fair enough.  I don't know how RPMh works well enough to argue.
It does seem odd that you'd want to design things such that it takes a
few msecs to pull it out of runtime suspend, especially for touch.


>>> +
>>> +static const 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},
>>
>>
>> So I guess this is all relying on an input serial clock of 19.2MHz?
>> Maybe document that?
>>
>> Assuming I'm understanding the math here, is it really OK for your
>> 100kHz and 1MHz mode to be running slightly fast?
>>
>> 19200. / 2 / 24
>>>>>
>>>>> 400.0
>>
>>
>> 19200. / 7 / 26
>>>>>
>>>>> 105.49450549450549
>>
>>
>> 19200. / 1 / 18
>>>>>
>>>>> 1066.6666666666667
>>
>>
>> It seems like you'd want the fastest clock that you can make that's
>> _less than_ the spec.
>>
>>
>> It would also be interesting to know if it's expected that boards
>> might need to tweak the t_high / t_low depending on their electrical
>> characteristics.  In the past I've had lots of requests from board
>> makers to tweak things because they've got a long trace, or a stronger
>> or weaker pull, or ...  If so we might later need to add some dts
>> properties like "i2c-scl-rising-time-ns" and make the math more
>> dynamic here, unless your hardware somehow automatically adjusts for
>> this type of thing...
>> These values are derived by our HW team to comply with the t-high and
>
> t-low specs of I2C. We have confirmed on scope that the frequency of SCL is
> indeed less than/equal to the spec. We have not come across slaves who have
> needed to tweak these things. We are open to adding these properties in dts
> if you have any such slaves not conforming due to board-layout of other
> reasons.

OK, I'm fine with leaving something like this till later if/when it
comes up.  Documenting a little bit more about how these timings work
seems like it would be nice, though, at least mentioning what the
source clock is...


>>>
>>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
>>
>>
>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>> a lot by transferring i2c commands over DMA compared to a FIFO?
>> Enough to justify the code complexity and the set of bugs that will
>> show up?  I'm sure it will be a controversial assertion given that the
>> code's already written, but personally I'd be supportive of ripping
>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>> agrees.  To me it seems like premature optimization.
>
>
> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data transfers
> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size is
> 32, so we can definitely avoid at least 1 interrupt when DMA mode is used
> with data size > 32.

Does that 1-2 interrupts make any real difference, though?  In theory
it really shouldn't affect the transfer rate.  We should be able to
service the interrupt plenty fast and if we were concerned we would
tweak the watermark code a little bit.  So I guess we're worried about
the extra CPU cycles (and power cost) to service those extra couple
interrupts?

In theory when touch data is coming in or NFC data is coming in then
we're probably not in a super low power state to begin with.  If it's
touch data we likely want to have the CPU boosted a bunch to respond
to the user quickly.  If we've got 8 cores available all of which can
run at 1GHz or more a few interrupts won't kill us.  NFC data is
probably not common enough that we need to optimize power/CPU
utilizatoin for that.


So while i can believe that you do save an interrupt or two, I still
am not convinced that those interrupts are worth a bunch of complex
code (and a whole second code path) to save.


...also note that above you said that coming out of runtime suspend
can take several msec.  That seems like it dwarfs any slight
difference in timing between a FIFO-based operation and DMA.


>>> +       geni_se_select_mode(&gi2c->se, mode);
>>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
>>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
>>> +       if (mode == GENI_SE_DMA) {
>>> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf,
>>> msg->len);
>>
>>
>> Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
>> check this flag before using msg->buf for DMA?  ...or use
>> i2c_get_dma_safe_msg_buf()?
>>
>> ...btw: the relative lack of people doing this in the kernel is
>> further evidence of DMA not really being worth it for i2c busses.
>
> I cannot comment about other drivers here using or not using DMA since they
> may not be exercised with slaves like NFC?
>>>
>>> +       ret = pm_runtime_get_sync(gi2c->se.dev);
>>> +       if (ret < 0) {
>>> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n",
>>> ret);
>>> +               pm_runtime_put_noidle(gi2c->se.dev);
>>> +               /* Set device in suspended since resume failed */
>>> +               pm_runtime_set_suspended(gi2c->se.dev);
>>> +               return ret;
>>
>>
>> Wow, that's a cluster of arcane calls to handle a call that probably
>> will never fail (it just enables clocks and sets pinctrl).  Sigh.
>> ...but as far as I can tell the whole sequence is right.  You
>> definitely need a "put" after a failed get and it looks like
>> pm_runtime_set_suspended() has a special exception where it can be
>> called if you got a runtime error...
>
> We didn't have this in before either. But this condition is somewhat
> frequent if I2C transactions are called on cusp of exiting system suspend.
> (e.g. PMIC slave getting a wakeup-IRQ and trying to read from PMIC through
> I2C to read its status as to what caused that wake-up. At that time,
> get_sync doesn't really enable resources (kernel 4.9) since the runtime-pm
> ref-count isn't decremented. We run the risk of unclocked access if these
> arcane calls aren't present. You can go through runtime-pm documentation
> chapter 6 for more details.

Yeah, I certainly agree that the calls are needed if
pm_runtime_get_sync() and I'm not suggesting removing them.  Hence the
"as far as I can tell the whole sequence is right".

...but I'm actually kinda worried if you're saying that you actually
ran into this case.  Hopefully that got fixed and code no longer tries
to read from the PMIC at a bad time anymore?  That code should be
fixed not to be running so late in suspend.


>>> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
>>> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
>>> +       if (ret) {
>>> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
>>> +                       gi2c->irq, ret);
>>> +               return ret;
>>> +       }
>>> +       disable_irq(gi2c->irq);
>>
>>
>> Can you explain the goal of the disable_irq() here.  Is it actually
>> needed for something or does it somehow save power?  From drivers I've
>> reviewed in the past this doesn't seem like a common thing to do, so
>> I'm curious what it's supposed to gain for you.  I'd be inclined to
>> just delete the whole disable/enable of the irq from this driver.
>
>
> Qualcomm's power team suggests we enable/disable unused IRQs. Otherwise they
> can block apps from entering some low-power mode (unless the interrupt is in
> some list?) I will confirm again with them and let you know.

Since this is weird (to me anyway), please document w/ a comment.


>>>
>>> +       /* Make sure no transactions are pending */
>>> +       ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
>>> +       if (!ret) {
>>> +               dev_err(gi2c->se.dev, "late I2C transaction request\n");
>>> +               return -EBUSY;
>>> +       }
>>
>>
>> Does this happen?  How?
>>
>> Nothing about this code looks special for your hardware.  If this is
>> really needed, why is it not part of the i2c core since there's
>> nothing specific about your driver here?
>>
> There have been some clients that don't implement sys-suspend/resume
> callbacks (so i2c adapter has no clue they are done with their transactions)
> and this allows us to be flexible when they call I2C transactions extremely
> late.

Still feels like this belongs in the i2c core, not your driver.  Maybe
you should send a patch for the core and remove it from here?

...and also, it seems like any i2c clients that don't implement the
suspend/resume callbacks and try to do i2c transactions late in the
game need to be fixed.  It should be documented that this isn't a
valid thing for a driver to do and if we end up in this error case
then it's not an i2c issue but it's a bad driver somewhere.

>
>>
>>> +       if (!pm_runtime_status_suspended(device)) {
>>> +               geni_i2c_runtime_suspend(device);
>>> +               pm_runtime_disable(device);
>>> +               pm_runtime_set_suspended(device);
>>> +               pm_runtime_enable(device);
>>> +       }
>>
>>
>> Similar question.  Why do you need this special case code?  Are there
>> cases where we're all the way at suspend_noirq and yet we still
>> haven't runtime suspended?  Can you please document how we get into
>> this state?
>> This is when transaction happens less-than 250 msec of the
>
> system-suspend. PM-runtime has not gotten a chance to auto-suspend us since
> timer hasn't expired before system-suspend is attempted. These calls make
> sure that we truly turn off driver resources and make runtime-PM state
> consistent with the HW state. We can document this.

OK.  PM Runtime always gets me mixed up.  Seems really strange that it
wouldn't autosuspend all devices (regardless of timeout) at system
suspend time.

-Doug
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-03-06 21:45         ` Stephen Boyd
@ 2018-03-08  6:06           ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-08  6:06 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/6/2018 2:45 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
>> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
>>> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
>>>
>>>> +               size_t chars_to_write = 0;
>>>> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))
>>>
>>> Does this ever timeout? So many nested while loops makes it hard to
>>> follow.
>> Yes. Based on the baud rate of 115200 and the FIFO Depth & Width of (16
>> * 32), the poll should not take more than 5 ms under the timeout scenario.
> 
> Sure, but I'm asking if this has any sort of timeout associated with it.
> It looks to be a while loop that could possibly go forever?
I will change it from a while loop to if condition to make it clear.
> 
>>>> +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;
>>>> +       bool locked = true;
>>>> +       unsigned long flags;
>>>> +
>>>> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
>>>> +
>>>> +       port = get_port_from_line(co->index);
>>>> +       if (IS_ERR(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);
>>>
>>> So if oops is in progress, and we didn't lock here, we don't output
>>> data? I'd think we would always want to write to the fifo, just make the
>>> lock grab/release conditional.
>> If we fail to grab the lock, then there is another active writer. So
>> trying to write to the fifo will put the hardware in bad state because
>> writer has programmed the hardware to write 'x' number of words and this
>> thread will over-write it with 'y' number of words. Also the data that
>> you might see in the console might be garbled.
> 
> I suspect that because this is the serial console, and we want it to
> always output stuff even when we're going down in flames, we may want to
> ignore that case and just wait for the fifo to free up and then
> overwrite the number of words that we want to output and push out more
> characters.
> 
> I always get confused though because there seem to be lots of different
> ways to do things in serial drivers and not too much clear documentation
> that I've read describing what to do.
Ok. If the active writer is interrupted due to OOPS handler, then the 
interrupted write can be cancelled and the write from OOPS handler can 
be performed.
> 
>>>
>>>> +               spin_unlock_irqrestore(&uport->lock, flags);
>>>> +       }
>>>> +}
> [...]
>>>> +
>>>> +       if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
>>>> +           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++;
>>>
>>> How does break character handling work? I see the accounting here, but
>>> don't see any uart_handle_break() call anywhere.
>> The reason it is not added is because the hardware does not indicate
>> when the break character occured. It can be any one of the FIFO words.
>> The statistics is updated to give an idea that the break happened. We
>> can add uart_handle_break() but it may not be at an accurate position
>> for the above mentioned reason.
> 
> Sounds like the previous uart design. We would want uart_handle_break()
> support for kgdb to work over serial. Do we still get an interrupt
> signal that a break character is somewhere in the fifo? If we get that,
> then does it also put a NUL (0) character into the fifo that we can
> find? I had to do something like that before, where we detect the irq
> and then we go through the fifo a character at a time to find the break
> character that looks like a NUL, and then let sysrq core handle the
> characters after that break character comes in.
I will use the same logic as in blsp2 serial to catch the break 
character, same as NULL and push the break character to the framework.
> 
>>>
>>>
>>>> +}
>>>> +
>>>> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
>>>> +{
>>>> +       unsigned long ser_clk;
>>>> +       unsigned long desired_clk;
>>>> +
>>>> +       desired_clk = baud * UART_OVERSAMPLING;
>>>> +       ser_clk = get_clk_cfg(desired_clk);
>>>> +       if (!ser_clk) {
>>>> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
>>>> +                                                               __func__, baud);
>>>> +               return ser_clk;
>>>> +       }
>>>> +
>>>> +       *clk_div = ser_clk / desired_clk;
>>>
>>> How wide can clk_div be? It may be better to implement the ser_clk as an
>>> actual clk in the common clk framework, and then have the serial driver
>>> or the i2c driver call clk_set_rate() on that clk and have the CCF
>>> implementation take care of determining the rate that the parent clk can
>>> supply and how it can fit it into the frequency that the divider can
>>> support.
>> Based on my current expertise with the CCF, I will address this comment
>> in a later patchset than the next one.
> 
> Ok. I've seen similar designs in some mmc drivers. For example, you can
> look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
> clk_ops and then just start using that clk directly from the driver.
> There are also some helper functions for dividers that would hopefully
> make the job of calculating the best divider easier.
Thanks for the pointers. I will take a look at it. In the meanwhile I 
had discussions with our clock team. They pointed out that the register 
to write the divider value here is outside the scope of clock controller 
which makes it trickier to implement your suggestion. They are already 
in the mailing list and we will discuss further and get back to you in 
this regard.
> 
>>>> +
>>>> +       uport->uartclk = clk_rate;
>>>> +       clk_set_rate(port->se.clk, clk_rate);
>>>> +       ser_clk_cfg = SER_CLK_EN;
>>>> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
>>>
>>> Drop useless cast.
>> I think you mean parenthesis. I do not see casting here.
> 
> Yes! You got it.
> 
>>>> +#endif
>>>> +       .pm = qcom_geni_serial_cons_pm,
>>>> +};
>>>> +
>>>> +static int qcom_geni_serial_probe(struct platform_device *pdev)
>>>> +{
>>>> +       int ret = 0;
>>>> +       int line = -1;
>>>> +       struct qcom_geni_serial_port *port;
>>>> +       struct uart_port *uport;
>>>> +       struct resource *res;
>>>> +       struct uart_driver *drv;
>>>> +
>>>> +       drv = (void *)of_device_get_match_data(&pdev->dev);
>>>
>>> Useless cast.
>> There were compiler warnings assigning a const void * to a void *. That
>> is why I have the cast in place.
> 
> Oh. Yes you shouldn't cast away the const. Please make the compiler
> warning go away without casting it away.
Ok. I will figure out an alternative to this one.
> 
>>>
>>>
>>> Also, I see some serial drivers do the mapping when the port is
>>> requested. That can't be done here?
>> By "when the port is requested" do you mean console's setup operation.
>> It can be done, but given that it is a one-time operation I am not sure
>> if it makes any difference between mapping here and there.
> 
> No. I meant the uart_ops::uart_request() function and also
> uart_ops::config_port(). Take a look at msm_config_port() and
> msm_request_port() for how it was done on pre-geni qcom uarts.
> 
I will take a look at it and update accordingly.
> I suppose we should try to probe this as a module and run a getty on it
> and then remove and probe the module a couple times after that.
> That should shake out some bugs.
> 
>>>> +
>>>> +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,
>>>
>>> Why are these noirq variants? Please add a comment.
>> The intention is to enable the console UART port usage as late as
>> possible in the suspend cycle. Hence noirq variants. I will add this as
>> a comment. Please let me know if the usage does not match the intention.
> 
> Hm.. Does no_console_suspend not work? Or not work well enough?
It works. When console suspend is disabled, the suspend operation does 
not get triggered and the resume operation checks if the console suspend 
is disabled and performs the needed thing.
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-08  6:06           ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-08  6:06 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/6/2018 2:45 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
>> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
>>> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:09)
>>>
>>>> +               size_t chars_to_write = 0;
>>>> +               size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_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))
>>>
>>> Does this ever timeout? So many nested while loops makes it hard to
>>> follow.
>> Yes. Based on the baud rate of 115200 and the FIFO Depth & Width of (16
>> * 32), the poll should not take more than 5 ms under the timeout scenario.
> 
> Sure, but I'm asking if this has any sort of timeout associated with it.
> It looks to be a while loop that could possibly go forever?
I will change it from a while loop to if condition to make it clear.
> 
>>>> +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;
>>>> +       bool locked = true;
>>>> +       unsigned long flags;
>>>> +
>>>> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
>>>> +
>>>> +       port = get_port_from_line(co->index);
>>>> +       if (IS_ERR(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);
>>>
>>> So if oops is in progress, and we didn't lock here, we don't output
>>> data? I'd think we would always want to write to the fifo, just make the
>>> lock grab/release conditional.
>> If we fail to grab the lock, then there is another active writer. So
>> trying to write to the fifo will put the hardware in bad state because
>> writer has programmed the hardware to write 'x' number of words and this
>> thread will over-write it with 'y' number of words. Also the data that
>> you might see in the console might be garbled.
> 
> I suspect that because this is the serial console, and we want it to
> always output stuff even when we're going down in flames, we may want to
> ignore that case and just wait for the fifo to free up and then
> overwrite the number of words that we want to output and push out more
> characters.
> 
> I always get confused though because there seem to be lots of different
> ways to do things in serial drivers and not too much clear documentation
> that I've read describing what to do.
Ok. If the active writer is interrupted due to OOPS handler, then the 
interrupted write can be cancelled and the write from OOPS handler can 
be performed.
> 
>>>
>>>> +               spin_unlock_irqrestore(&uport->lock, flags);
>>>> +       }
>>>> +}
> [...]
>>>> +
>>>> +       if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
>>>> +           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++;
>>>
>>> How does break character handling work? I see the accounting here, but
>>> don't see any uart_handle_break() call anywhere.
>> The reason it is not added is because the hardware does not indicate
>> when the break character occured. It can be any one of the FIFO words.
>> The statistics is updated to give an idea that the break happened. We
>> can add uart_handle_break() but it may not be at an accurate position
>> for the above mentioned reason.
> 
> Sounds like the previous uart design. We would want uart_handle_break()
> support for kgdb to work over serial. Do we still get an interrupt
> signal that a break character is somewhere in the fifo? If we get that,
> then does it also put a NUL (0) character into the fifo that we can
> find? I had to do something like that before, where we detect the irq
> and then we go through the fifo a character at a time to find the break
> character that looks like a NUL, and then let sysrq core handle the
> characters after that break character comes in.
I will use the same logic as in blsp2 serial to catch the break 
character, same as NULL and push the break character to the framework.
> 
>>>
>>>
>>>> +}
>>>> +
>>>> +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
>>>> +{
>>>> +       unsigned long ser_clk;
>>>> +       unsigned long desired_clk;
>>>> +
>>>> +       desired_clk = baud * UART_OVERSAMPLING;
>>>> +       ser_clk = get_clk_cfg(desired_clk);
>>>> +       if (!ser_clk) {
>>>> +               pr_err("%s: Can't find matching DFS entry for baud %d\n",
>>>> +                                                               __func__, baud);
>>>> +               return ser_clk;
>>>> +       }
>>>> +
>>>> +       *clk_div = ser_clk / desired_clk;
>>>
>>> How wide can clk_div be? It may be better to implement the ser_clk as an
>>> actual clk in the common clk framework, and then have the serial driver
>>> or the i2c driver call clk_set_rate() on that clk and have the CCF
>>> implementation take care of determining the rate that the parent clk can
>>> supply and how it can fit it into the frequency that the divider can
>>> support.
>> Based on my current expertise with the CCF, I will address this comment
>> in a later patchset than the next one.
> 
> Ok. I've seen similar designs in some mmc drivers. For example, you can
> look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
> clk_ops and then just start using that clk directly from the driver.
> There are also some helper functions for dividers that would hopefully
> make the job of calculating the best divider easier.
Thanks for the pointers. I will take a look at it. In the meanwhile I 
had discussions with our clock team. They pointed out that the register 
to write the divider value here is outside the scope of clock controller 
which makes it trickier to implement your suggestion. They are already 
in the mailing list and we will discuss further and get back to you in 
this regard.
> 
>>>> +
>>>> +       uport->uartclk = clk_rate;
>>>> +       clk_set_rate(port->se.clk, clk_rate);
>>>> +       ser_clk_cfg = SER_CLK_EN;
>>>> +       ser_clk_cfg |= (clk_div << CLK_DIV_SHFT);
>>>
>>> Drop useless cast.
>> I think you mean parenthesis. I do not see casting here.
> 
> Yes! You got it.
> 
>>>> +#endif
>>>> +       .pm = qcom_geni_serial_cons_pm,
>>>> +};
>>>> +
>>>> +static int qcom_geni_serial_probe(struct platform_device *pdev)
>>>> +{
>>>> +       int ret = 0;
>>>> +       int line = -1;
>>>> +       struct qcom_geni_serial_port *port;
>>>> +       struct uart_port *uport;
>>>> +       struct resource *res;
>>>> +       struct uart_driver *drv;
>>>> +
>>>> +       drv = (void *)of_device_get_match_data(&pdev->dev);
>>>
>>> Useless cast.
>> There were compiler warnings assigning a const void * to a void *. That
>> is why I have the cast in place.
> 
> Oh. Yes you shouldn't cast away the const. Please make the compiler
> warning go away without casting it away.
Ok. I will figure out an alternative to this one.
> 
>>>
>>>
>>> Also, I see some serial drivers do the mapping when the port is
>>> requested. That can't be done here?
>> By "when the port is requested" do you mean console's setup operation.
>> It can be done, but given that it is a one-time operation I am not sure
>> if it makes any difference between mapping here and there.
> 
> No. I meant the uart_ops::uart_request() function and also
> uart_ops::config_port(). Take a look at msm_config_port() and
> msm_request_port() for how it was done on pre-geni qcom uarts.
> 
I will take a look at it and update accordingly.
> I suppose we should try to probe this as a module and run a getty on it
> and then remove and probe the module a couple times after that.
> That should shake out some bugs.
> 
>>>> +
>>>> +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,
>>>
>>> Why are these noirq variants? Please add a comment.
>> The intention is to enable the console UART port usage as late as
>> possible in the suspend cycle. Hence noirq variants. I will add this as
>> a comment. Please let me know if the usage does not match the intention.
> 
> Hm.. Does no_console_suspend not work? Or not work well enough?
It works. When console suspend is disabled, the suspend operation does 
not get triggered and the resume operation checks if the console suspend 
is disabled and performs the needed thing.
> 
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-06 21:56         ` Stephen Boyd
@ 2018-03-08  6:46             ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-08  6:46 UTC (permalink / raw)
  To: Stephen Boyd, Stephen Boyd, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	corbet-T1hC0tSOHrs, david.brown-QSEj5FYQhm4dnm+yROfE0A,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	wsa-z923LK4zBo2bacvFa/9K2g, hch-jcswGhMUV9g,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ, robin.murphy-5wv7dgnIgG8
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Girish Mahadevan,
	acourbot-F7+t8E8rja9g9hUCZPvPmw,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	evgreen-F7+t8E8rja9g9hUCZPvPmw,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia



On 3/6/2018 2:56 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
> 
>>>> +       return iova;
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>>>> +
>>>> +/**
>>>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
>>>> + * @se:                        Pointer to the concerned Serial Engine.
>>>> + * @buf:               Pointer to the RX buffer.
>>>> + * @len:               Length of the RX buffer.
>>>> + *
>>>> + * This function is used to prepare the buffers for DMA RX.
>>>> + *
>>>> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
>>>> + */
>>>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
>>>> +{
>>>> +       dma_addr_t iova;
>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>> +       u32 val;
>>>> +
>>>> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
>>>> +       if (dma_mapping_error(wrapper->dev, iova))
>>>> +               return (dma_addr_t)NULL;
>>>
>>> Can't return a dma_mapping_error address to the caller and have them
>>> figure it out?
>> Earlier we used to return the DMA_ERROR_CODE which has been removed
>> recently in arm64 architecture. If we return the dma_mapping_error, then
>> the caller also needs the device which encountered the mapping error.
>> The serial interface drivers can use their parent currently to resolve
>> the mapping error. Once the wrapper starts mapping using IOMMU context
>> bank, then the serial interface drivers do not know which device to use
>> to know if there is an error.
>>
>> Having said that, the dma_ops suggestion might help with handling this
>> situation. I will look into it further.
> 
> Ok, thanks.
> 
>>>> +{
>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>> +
>>>> +       if (iova)
>>>> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>>>
>>> Instead of having the functions exported, could we set the dma_ops on
>>> all child devices of the wrapper that this driver populates and then
>>> implement the DMA ops for those devices here? I assume that there's
>>> never another DMA master between the wrapper and the serial engine, so I
>>> think it would work.
>> This suggestion looks like it will work.
> 
> It would be a good idea to check with some other people on the dma_ops
> suggestion. Maybe add the DMA mapping subsystem folks to help out here
I have added the DMA mapping subsystem folks to help out here.

To present an overview, we have a wrapper controller which is composed 
of several serial engines. The serial engines are programmed with UART, 
I2C or SPI protocol and support DMA transfer. When the serial engines 
perform DMA transfer, the wrapper controller device is used to perform 
the mapping. The reason wrapper device is used is because of IOMMU and 
there is only one IOMMU context bank to perform the translation for the 
entire wrapper controller. So the wrapper controller exports map and 
unmap functions to the individual protocol drivers.

There is a suggestion to make the parent wrapper controller implement 
the dma_map_ops, instead of exported map/unmap functions and populate 
those dma_map_ops on all the children serial engines. Can you please 
provide your inputs regarding this suggestion?
> 
> DMA MAPPING HELPERS
> M:      Christoph Hellwig <hch-jcswGhMUV9g@public.gmane.org>
> M:      Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> R:      Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
> L:      iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
> 
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-08  6:46             ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-08  6:46 UTC (permalink / raw)
  To: Stephen Boyd, Stephen Boyd, andy.gross, corbet, david.brown,
	gregkh, mark.rutland, robh+dt, wsa, hch, m.szyprowski,
	robin.murphy
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan, iommu



On 3/6/2018 2:56 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
> 
>>>> +       return iova;
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>>>> +
>>>> +/**
>>>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
>>>> + * @se:                        Pointer to the concerned Serial Engine.
>>>> + * @buf:               Pointer to the RX buffer.
>>>> + * @len:               Length of the RX buffer.
>>>> + *
>>>> + * This function is used to prepare the buffers for DMA RX.
>>>> + *
>>>> + * Return: Mapped DMA Address of the buffer on success, NULL on failure.
>>>> + */
>>>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len)
>>>> +{
>>>> +       dma_addr_t iova;
>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>> +       u32 val;
>>>> +
>>>> +       iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE);
>>>> +       if (dma_mapping_error(wrapper->dev, iova))
>>>> +               return (dma_addr_t)NULL;
>>>
>>> Can't return a dma_mapping_error address to the caller and have them
>>> figure it out?
>> Earlier we used to return the DMA_ERROR_CODE which has been removed
>> recently in arm64 architecture. If we return the dma_mapping_error, then
>> the caller also needs the device which encountered the mapping error.
>> The serial interface drivers can use their parent currently to resolve
>> the mapping error. Once the wrapper starts mapping using IOMMU context
>> bank, then the serial interface drivers do not know which device to use
>> to know if there is an error.
>>
>> Having said that, the dma_ops suggestion might help with handling this
>> situation. I will look into it further.
> 
> Ok, thanks.
> 
>>>> +{
>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>> +
>>>> +       if (iova)
>>>> +               dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>>>
>>> Instead of having the functions exported, could we set the dma_ops on
>>> all child devices of the wrapper that this driver populates and then
>>> implement the DMA ops for those devices here? I assume that there's
>>> never another DMA master between the wrapper and the serial engine, so I
>>> think it would work.
>> This suggestion looks like it will work.
> 
> It would be a good idea to check with some other people on the dma_ops
> suggestion. Maybe add the DMA mapping subsystem folks to help out here
I have added the DMA mapping subsystem folks to help out here.

To present an overview, we have a wrapper controller which is composed 
of several serial engines. The serial engines are programmed with UART, 
I2C or SPI protocol and support DMA transfer. When the serial engines 
perform DMA transfer, the wrapper controller device is used to perform 
the mapping. The reason wrapper device is used is because of IOMMU and 
there is only one IOMMU context bank to perform the translation for the 
entire wrapper controller. So the wrapper controller exports map and 
unmap functions to the individual protocol drivers.

There is a suggestion to make the parent wrapper controller implement 
the dma_map_ops, instead of exported map/unmap functions and populate 
those dma_map_ops on all the children serial engines. Can you please 
provide your inputs regarding this suggestion?
> 
> DMA MAPPING HELPERS
> M:      Christoph Hellwig <hch@lst.de>
> M:      Marek Szyprowski <m.szyprowski@samsung.com>
> R:      Robin Murphy <robin.murphy@arm.com>
> L:      iommu@lists.linux-foundation.org
> 
> 
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-08  6:46             ` Karthik Ramasubramanian
@ 2018-03-08 13:24                 ` Robin Murphy
  -1 siblings, 0 replies; 65+ messages in thread
From: Robin Murphy @ 2018-03-08 13:24 UTC (permalink / raw)
  To: Karthik Ramasubramanian, Stephen Boyd, Stephen Boyd,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A, corbet-T1hC0tSOHrs,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	wsa-z923LK4zBo2bacvFa/9K2g, hch-jcswGhMUV9g,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Girish Mahadevan,
	acourbot-F7+t8E8rja9g9hUCZPvPmw,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	evgreen-F7+t8E8rja9g9hUCZPvPmw,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia

On 08/03/18 06:46, Karthik Ramasubramanian wrote:
> 
> 
> On 3/6/2018 2:56 PM, Stephen Boyd wrote:
>> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
>>
>>>>> +       return iova;
>>>>> +}
>>>>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>>>>> +
>>>>> +/**
>>>>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA 
>>>>> transfer
>>>>> + * @se:                        Pointer to the concerned Serial 
>>>>> Engine.
>>>>> + * @buf:               Pointer to the RX buffer.
>>>>> + * @len:               Length of the RX buffer.
>>>>> + *
>>>>> + * This function is used to prepare the buffers for DMA RX.
>>>>> + *
>>>>> + * Return: Mapped DMA Address of the buffer on success, NULL on 
>>>>> failure.
>>>>> + */
>>>>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, 
>>>>> size_t len)
>>>>> +{
>>>>> +       dma_addr_t iova;
>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>> +       u32 val;
>>>>> +
>>>>> +       iova = dma_map_single(wrapper->dev, buf, len, 
>>>>> DMA_FROM_DEVICE);
>>>>> +       if (dma_mapping_error(wrapper->dev, iova))
>>>>> +               return (dma_addr_t)NULL;
>>>>
>>>> Can't return a dma_mapping_error address to the caller and have them
>>>> figure it out?
>>> Earlier we used to return the DMA_ERROR_CODE which has been removed
>>> recently in arm64 architecture. If we return the dma_mapping_error, then
>>> the caller also needs the device which encountered the mapping error.
>>> The serial interface drivers can use their parent currently to resolve
>>> the mapping error. Once the wrapper starts mapping using IOMMU context
>>> bank, then the serial interface drivers do not know which device to use
>>> to know if there is an error.
>>>
>>> Having said that, the dma_ops suggestion might help with handling this
>>> situation. I will look into it further.
>>
>> Ok, thanks.
>>
>>>>> +{
>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>> +
>>>>> +       if (iova)
>>>>> +               dma_unmap_single(wrapper->dev, iova, len, 
>>>>> DMA_FROM_DEVICE);
>>>>> +}
>>>>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>>>>
>>>> Instead of having the functions exported, could we set the dma_ops on
>>>> all child devices of the wrapper that this driver populates and then
>>>> implement the DMA ops for those devices here? I assume that there's
>>>> never another DMA master between the wrapper and the serial engine, 
>>>> so I
>>>> think it would work.
>>> This suggestion looks like it will work.
>>
>> It would be a good idea to check with some other people on the dma_ops
>> suggestion. Maybe add the DMA mapping subsystem folks to help out here
> I have added the DMA mapping subsystem folks to help out here.
> 
> To present an overview, we have a wrapper controller which is composed 
> of several serial engines. The serial engines are programmed with UART, 
> I2C or SPI protocol and support DMA transfer. When the serial engines 
> perform DMA transfer, the wrapper controller device is used to perform 
> the mapping. The reason wrapper device is used is because of IOMMU and 
> there is only one IOMMU context bank to perform the translation for the 
> entire wrapper controller. So the wrapper controller exports map and 
> unmap functions to the individual protocol drivers.
> 
> There is a suggestion to make the parent wrapper controller implement 
> the dma_map_ops, instead of exported map/unmap functions and populate 
> those dma_map_ops on all the children serial engines. Can you please 
> provide your inputs regarding this suggestion?

Implementing dma_map_ops inside a driver for real hardware is almost 
always the wrong thing to do.

Based on what I could infer about the hardware from looking through the 
whole series in the linux-arm-msm archive, this is probably more like a 
multi-channel DMA controller where each "channel" has a configurable 
serial interface on the other end, as opposed to an actual bus where the 
serial engines are individually distinct AHB masters routed through the 
wrapper. If that's true, then using the QUP platform device for DMA API 
calls is the appropriate thing to do. Personally I'd be inclined not to 
abstract the dma_{map,unmap} calls at all, and just have the protocol 
drivers make them directly using dev->parent/wrapper->dev/whatever, but 
if you do want to abstract those then just give the abstraction a saner 
interface, i.e. pass the DMA handle by reference and return a regular 
int for error/success status.

Robin.
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-08 13:24                 ` Robin Murphy
  0 siblings, 0 replies; 65+ messages in thread
From: Robin Murphy @ 2018-03-08 13:24 UTC (permalink / raw)
  To: Karthik Ramasubramanian, Stephen Boyd, Stephen Boyd, andy.gross,
	corbet, david.brown, gregkh, mark.rutland, robh+dt, wsa, hch,
	m.szyprowski
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan, iommu

On 08/03/18 06:46, Karthik Ramasubramanian wrote:
> 
> 
> On 3/6/2018 2:56 PM, Stephen Boyd wrote:
>> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
>>
>>>>> +       return iova;
>>>>> +}
>>>>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>>>>> +
>>>>> +/**
>>>>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA 
>>>>> transfer
>>>>> + * @se:                        Pointer to the concerned Serial 
>>>>> Engine.
>>>>> + * @buf:               Pointer to the RX buffer.
>>>>> + * @len:               Length of the RX buffer.
>>>>> + *
>>>>> + * This function is used to prepare the buffers for DMA RX.
>>>>> + *
>>>>> + * Return: Mapped DMA Address of the buffer on success, NULL on 
>>>>> failure.
>>>>> + */
>>>>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, 
>>>>> size_t len)
>>>>> +{
>>>>> +       dma_addr_t iova;
>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>> +       u32 val;
>>>>> +
>>>>> +       iova = dma_map_single(wrapper->dev, buf, len, 
>>>>> DMA_FROM_DEVICE);
>>>>> +       if (dma_mapping_error(wrapper->dev, iova))
>>>>> +               return (dma_addr_t)NULL;
>>>>
>>>> Can't return a dma_mapping_error address to the caller and have them
>>>> figure it out?
>>> Earlier we used to return the DMA_ERROR_CODE which has been removed
>>> recently in arm64 architecture. If we return the dma_mapping_error, then
>>> the caller also needs the device which encountered the mapping error.
>>> The serial interface drivers can use their parent currently to resolve
>>> the mapping error. Once the wrapper starts mapping using IOMMU context
>>> bank, then the serial interface drivers do not know which device to use
>>> to know if there is an error.
>>>
>>> Having said that, the dma_ops suggestion might help with handling this
>>> situation. I will look into it further.
>>
>> Ok, thanks.
>>
>>>>> +{
>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>> +
>>>>> +       if (iova)
>>>>> +               dma_unmap_single(wrapper->dev, iova, len, 
>>>>> DMA_FROM_DEVICE);
>>>>> +}
>>>>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>>>>
>>>> Instead of having the functions exported, could we set the dma_ops on
>>>> all child devices of the wrapper that this driver populates and then
>>>> implement the DMA ops for those devices here? I assume that there's
>>>> never another DMA master between the wrapper and the serial engine, 
>>>> so I
>>>> think it would work.
>>> This suggestion looks like it will work.
>>
>> It would be a good idea to check with some other people on the dma_ops
>> suggestion. Maybe add the DMA mapping subsystem folks to help out here
> I have added the DMA mapping subsystem folks to help out here.
> 
> To present an overview, we have a wrapper controller which is composed 
> of several serial engines. The serial engines are programmed with UART, 
> I2C or SPI protocol and support DMA transfer. When the serial engines 
> perform DMA transfer, the wrapper controller device is used to perform 
> the mapping. The reason wrapper device is used is because of IOMMU and 
> there is only one IOMMU context bank to perform the translation for the 
> entire wrapper controller. So the wrapper controller exports map and 
> unmap functions to the individual protocol drivers.
> 
> There is a suggestion to make the parent wrapper controller implement 
> the dma_map_ops, instead of exported map/unmap functions and populate 
> those dma_map_ops on all the children serial engines. Can you please 
> provide your inputs regarding this suggestion?

Implementing dma_map_ops inside a driver for real hardware is almost 
always the wrong thing to do.

Based on what I could infer about the hardware from looking through the 
whole series in the linux-arm-msm archive, this is probably more like a 
multi-channel DMA controller where each "channel" has a configurable 
serial interface on the other end, as opposed to an actual bus where the 
serial engines are individually distinct AHB masters routed through the 
wrapper. If that's true, then using the QUP platform device for DMA API 
calls is the appropriate thing to do. Personally I'd be inclined not to 
abstract the dma_{map,unmap} calls at all, and just have the protocol 
drivers make them directly using dev->parent/wrapper->dev/whatever, but 
if you do want to abstract those then just give the abstraction a saner 
interface, i.e. pass the DMA handle by reference and return a regular 
int for error/success status.

Robin.
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-08 13:24                 ` Robin Murphy
@ 2018-03-08 14:41                     ` Christoph Hellwig
  -1 siblings, 0 replies; 65+ messages in thread
From: Christoph Hellwig @ 2018-03-08 14:41 UTC (permalink / raw)
  To: Robin Murphy
  Cc: mark.rutland-5wv7dgnIgG8, wsa-z923LK4zBo2bacvFa/9K2g,
	david.brown-QSEj5FYQhm4dnm+yROfE0A, Karthik Ramasubramanian,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, hch-jcswGhMUV9g,
	corbet-T1hC0tSOHrs, linux-doc-u79uwXL29TY76Z2rM5mHXA,
	evgreen-F7+t8E8rja9g9hUCZPvPmw,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A, Stephen Boyd,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, Stephen Boyd,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Sagar Dharia, Girish Mahadevan,
	acourbot-F7+t8E8rja9g9hUCZPvPmw,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

On Thu, Mar 08, 2018 at 01:24:45PM +0000, Robin Murphy wrote:
> Implementing dma_map_ops inside a driver for real hardware is almost always 
> the wrong thing to do.

Agreed.  dma_map_ops should be a platform decision based on the bus.

Even our dma_virt_ops basically just works around bad driver layering.

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-08 14:41                     ` Christoph Hellwig
  0 siblings, 0 replies; 65+ messages in thread
From: Christoph Hellwig @ 2018-03-08 14:41 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Karthik Ramasubramanian, Stephen Boyd, Stephen Boyd, andy.gross,
	corbet, david.brown, gregkh, mark.rutland, robh+dt, wsa, hch,
	m.szyprowski, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, evgreen, acourbot, Sagar Dharia,
	Girish Mahadevan, iommu

On Thu, Mar 08, 2018 at 01:24:45PM +0000, Robin Murphy wrote:
> Implementing dma_map_ops inside a driver for real hardware is almost always 
> the wrong thing to do.

Agreed.  dma_map_ops should be a platform decision based on the bus.

Even our dma_virt_ops basically just works around bad driver layering.
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-08 13:24                 ` Robin Murphy
@ 2018-03-08 18:18                     ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-08 18:18 UTC (permalink / raw)
  To: Robin Murphy, Stephen Boyd, Stephen Boyd,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A, corbet-T1hC0tSOHrs,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	wsa-z923LK4zBo2bacvFa/9K2g, hch-jcswGhMUV9g,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Girish Mahadevan,
	acourbot-F7+t8E8rja9g9hUCZPvPmw,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	evgreen-F7+t8E8rja9g9hUCZPvPmw,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
	Sagar Dharia



On 3/8/2018 6:24 AM, Robin Murphy wrote:
> On 08/03/18 06:46, Karthik Ramasubramanian wrote:
>>
>>
>> On 3/6/2018 2:56 PM, Stephen Boyd wrote:
>>> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
>>>
>>>>>> +       return iova;
>>>>>> +}
>>>>>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>>>>>> +
>>>>>> +/**
>>>>>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA 
>>>>>> transfer
>>>>>> + * @se:                        Pointer to the concerned Serial 
>>>>>> Engine.
>>>>>> + * @buf:               Pointer to the RX buffer.
>>>>>> + * @len:               Length of the RX buffer.
>>>>>> + *
>>>>>> + * This function is used to prepare the buffers for DMA RX.
>>>>>> + *
>>>>>> + * Return: Mapped DMA Address of the buffer on success, NULL on 
>>>>>> failure.
>>>>>> + */
>>>>>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, 
>>>>>> size_t len)
>>>>>> +{
>>>>>> +       dma_addr_t iova;
>>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>>> +       u32 val;
>>>>>> +
>>>>>> +       iova = dma_map_single(wrapper->dev, buf, len, 
>>>>>> DMA_FROM_DEVICE);
>>>>>> +       if (dma_mapping_error(wrapper->dev, iova))
>>>>>> +               return (dma_addr_t)NULL;
>>>>>
>>>>> Can't return a dma_mapping_error address to the caller and have them
>>>>> figure it out?
>>>> Earlier we used to return the DMA_ERROR_CODE which has been removed
>>>> recently in arm64 architecture. If we return the dma_mapping_error, 
>>>> then
>>>> the caller also needs the device which encountered the mapping error.
>>>> The serial interface drivers can use their parent currently to resolve
>>>> the mapping error. Once the wrapper starts mapping using IOMMU context
>>>> bank, then the serial interface drivers do not know which device to use
>>>> to know if there is an error.
>>>>
>>>> Having said that, the dma_ops suggestion might help with handling this
>>>> situation. I will look into it further.
>>>
>>> Ok, thanks.
>>>
>>>>>> +{
>>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>>> +
>>>>>> +       if (iova)
>>>>>> +               dma_unmap_single(wrapper->dev, iova, len, 
>>>>>> DMA_FROM_DEVICE);
>>>>>> +}
>>>>>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>>>>>
>>>>> Instead of having the functions exported, could we set the dma_ops on
>>>>> all child devices of the wrapper that this driver populates and then
>>>>> implement the DMA ops for those devices here? I assume that there's
>>>>> never another DMA master between the wrapper and the serial engine, 
>>>>> so I
>>>>> think it would work.
>>>> This suggestion looks like it will work.
>>>
>>> It would be a good idea to check with some other people on the dma_ops
>>> suggestion. Maybe add the DMA mapping subsystem folks to help out here
>> I have added the DMA mapping subsystem folks to help out here.
>>
>> To present an overview, we have a wrapper controller which is composed 
>> of several serial engines. The serial engines are programmed with 
>> UART, I2C or SPI protocol and support DMA transfer. When the serial 
>> engines perform DMA transfer, the wrapper controller device is used to 
>> perform the mapping. The reason wrapper device is used is because of 
>> IOMMU and there is only one IOMMU context bank to perform the 
>> translation for the entire wrapper controller. So the wrapper 
>> controller exports map and unmap functions to the individual protocol 
>> drivers.
>>
>> There is a suggestion to make the parent wrapper controller implement 
>> the dma_map_ops, instead of exported map/unmap functions and populate 
>> those dma_map_ops on all the children serial engines. Can you please 
>> provide your inputs regarding this suggestion?
> 
> Implementing dma_map_ops inside a driver for real hardware is almost 
> always the wrong thing to do.
> 
> Based on what I could infer about the hardware from looking through the 
> whole series in the linux-arm-msm archive, this is probably more like a 
> multi-channel DMA controller where each "channel" has a configurable 
> serial interface on the other end, as opposed to an actual bus where the 
> serial engines are individually distinct AHB masters routed through the 
> wrapper. If that's true, then using the QUP platform device for DMA API 
> calls is the appropriate thing to do. Personally I'd be inclined not to 
> abstract the dma_{map,unmap} calls at all, and just have the protocol 
> drivers make them directly using dev->parent/wrapper->dev/whatever, but 
> if you do want to abstract those then just give the abstraction a saner 
> interface, i.e. pass the DMA handle by reference and return a regular 
> int for error/success status.
> 
> Robin.
Thank you Robin & Christoph for your inputs. The wrapper driver used to 
provide the recommended abstraction until v2 of this patch series. In v3 
it was tweaked to address a comment. If there are no objections, I will 
revive it back.

Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-08 18:18                     ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-08 18:18 UTC (permalink / raw)
  To: Robin Murphy, Stephen Boyd, Stephen Boyd, andy.gross, corbet,
	david.brown, gregkh, mark.rutland, robh+dt, wsa, hch,
	m.szyprowski
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan, iommu



On 3/8/2018 6:24 AM, Robin Murphy wrote:
> On 08/03/18 06:46, Karthik Ramasubramanian wrote:
>>
>>
>> On 3/6/2018 2:56 PM, Stephen Boyd wrote:
>>> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
>>>
>>>>>> +       return iova;
>>>>>> +}
>>>>>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>>>>>> +
>>>>>> +/**
>>>>>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA 
>>>>>> transfer
>>>>>> + * @se:                        Pointer to the concerned Serial 
>>>>>> Engine.
>>>>>> + * @buf:               Pointer to the RX buffer.
>>>>>> + * @len:               Length of the RX buffer.
>>>>>> + *
>>>>>> + * This function is used to prepare the buffers for DMA RX.
>>>>>> + *
>>>>>> + * Return: Mapped DMA Address of the buffer on success, NULL on 
>>>>>> failure.
>>>>>> + */
>>>>>> +dma_addr_t geni_se_rx_dma_prep(struct geni_se *se, void *buf, 
>>>>>> size_t len)
>>>>>> +{
>>>>>> +       dma_addr_t iova;
>>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>>> +       u32 val;
>>>>>> +
>>>>>> +       iova = dma_map_single(wrapper->dev, buf, len, 
>>>>>> DMA_FROM_DEVICE);
>>>>>> +       if (dma_mapping_error(wrapper->dev, iova))
>>>>>> +               return (dma_addr_t)NULL;
>>>>>
>>>>> Can't return a dma_mapping_error address to the caller and have them
>>>>> figure it out?
>>>> Earlier we used to return the DMA_ERROR_CODE which has been removed
>>>> recently in arm64 architecture. If we return the dma_mapping_error, 
>>>> then
>>>> the caller also needs the device which encountered the mapping error.
>>>> The serial interface drivers can use their parent currently to resolve
>>>> the mapping error. Once the wrapper starts mapping using IOMMU context
>>>> bank, then the serial interface drivers do not know which device to use
>>>> to know if there is an error.
>>>>
>>>> Having said that, the dma_ops suggestion might help with handling this
>>>> situation. I will look into it further.
>>>
>>> Ok, thanks.
>>>
>>>>>> +{
>>>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>>>> +
>>>>>> +       if (iova)
>>>>>> +               dma_unmap_single(wrapper->dev, iova, len, 
>>>>>> DMA_FROM_DEVICE);
>>>>>> +}
>>>>>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>>>>>
>>>>> Instead of having the functions exported, could we set the dma_ops on
>>>>> all child devices of the wrapper that this driver populates and then
>>>>> implement the DMA ops for those devices here? I assume that there's
>>>>> never another DMA master between the wrapper and the serial engine, 
>>>>> so I
>>>>> think it would work.
>>>> This suggestion looks like it will work.
>>>
>>> It would be a good idea to check with some other people on the dma_ops
>>> suggestion. Maybe add the DMA mapping subsystem folks to help out here
>> I have added the DMA mapping subsystem folks to help out here.
>>
>> To present an overview, we have a wrapper controller which is composed 
>> of several serial engines. The serial engines are programmed with 
>> UART, I2C or SPI protocol and support DMA transfer. When the serial 
>> engines perform DMA transfer, the wrapper controller device is used to 
>> perform the mapping. The reason wrapper device is used is because of 
>> IOMMU and there is only one IOMMU context bank to perform the 
>> translation for the entire wrapper controller. So the wrapper 
>> controller exports map and unmap functions to the individual protocol 
>> drivers.
>>
>> There is a suggestion to make the parent wrapper controller implement 
>> the dma_map_ops, instead of exported map/unmap functions and populate 
>> those dma_map_ops on all the children serial engines. Can you please 
>> provide your inputs regarding this suggestion?
> 
> Implementing dma_map_ops inside a driver for real hardware is almost 
> always the wrong thing to do.
> 
> Based on what I could infer about the hardware from looking through the 
> whole series in the linux-arm-msm archive, this is probably more like a 
> multi-channel DMA controller where each "channel" has a configurable 
> serial interface on the other end, as opposed to an actual bus where the 
> serial engines are individually distinct AHB masters routed through the 
> wrapper. If that's true, then using the QUP platform device for DMA API 
> calls is the appropriate thing to do. Personally I'd be inclined not to 
> abstract the dma_{map,unmap} calls at all, and just have the protocol 
> drivers make them directly using dev->parent/wrapper->dev/whatever, but 
> if you do want to abstract those then just give the abstraction a saner 
> interface, i.e. pass the DMA handle by reference and return a regular 
> int for error/success status.
> 
> Robin.
Thank you Robin & Christoph for your inputs. The wrapper driver used to 
provide the recommended abstraction until v2 of this patch series. In v3 
it was tweaked to address a comment. If there are no objections, I will 
revive it back.

Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-03-08  5:19         ` Doug Anderson
@ 2018-03-08 21:12           ` Doug Anderson
  -1 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-08 21:12 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi,

On Wed, Mar 7, 2018 at 9:19 PM, Doug Anderson <dianders@chromium.org> wrote:
>>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>>> a lot by transferring i2c commands over DMA compared to a FIFO?
>>> Enough to justify the code complexity and the set of bugs that will
>>> show up?  I'm sure it will be a controversial assertion given that the
>>> code's already written, but personally I'd be supportive of ripping
>>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>>> agrees.  To me it seems like premature optimization.
>>
>>
>> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data transfers
>> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size is
>> 32, so we can definitely avoid at least 1 interrupt when DMA mode is used
>> with data size > 32.
>
> Does that 1-2 interrupts make any real difference, though?  In theory
> it really shouldn't affect the transfer rate.  We should be able to
> service the interrupt plenty fast and if we were concerned we would
> tweak the watermark code a little bit.  So I guess we're worried about
> the extra CPU cycles (and power cost) to service those extra couple
> interrupts?
>
> In theory when touch data is coming in or NFC data is coming in then
> we're probably not in a super low power state to begin with.  If it's
> touch data we likely want to have the CPU boosted a bunch to respond
> to the user quickly.  If we've got 8 cores available all of which can
> run at 1GHz or more a few interrupts won't kill us.  NFC data is
> probably not common enough that we need to optimize power/CPU
> utilizatoin for that.
>
>
> So while i can believe that you do save an interrupt or two, I still
> am not convinced that those interrupts are worth a bunch of complex
> code (and a whole second code path) to save.
>
>
> ...also note that above you said that coming out of runtime suspend
> can take several msec.  That seems like it dwarfs any slight
> difference in timing between a FIFO-based operation and DMA.

One last note here (sorry for not thinking of this last night) is that
I would also be interested in considering _only_ supporting the DMA
path.  Is it somehow intrinsically bad to use the DMA flow for a
1-byte transfer?  Is there a bunch of extra overhead or power draw?

Specifically my main point is that maintaining two separate flows (the
FIFO flow vs the DMA flow) adds complexity, code size, and bugs.  If
there's a really good reason to maintain both flows then fine, but we
should really consider if this is something that's really giving us
value before we agree to it.


-Doug

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

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-08 21:12           ` Doug Anderson
  0 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-08 21:12 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi,

On Wed, Mar 7, 2018 at 9:19 PM, Doug Anderson <dianders@chromium.org> wrote:
>>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>>> a lot by transferring i2c commands over DMA compared to a FIFO?
>>> Enough to justify the code complexity and the set of bugs that will
>>> show up?  I'm sure it will be a controversial assertion given that the
>>> code's already written, but personally I'd be supportive of ripping
>>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>>> agrees.  To me it seems like premature optimization.
>>
>>
>> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data transfers
>> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size is
>> 32, so we can definitely avoid at least 1 interrupt when DMA mode is used
>> with data size > 32.
>
> Does that 1-2 interrupts make any real difference, though?  In theory
> it really shouldn't affect the transfer rate.  We should be able to
> service the interrupt plenty fast and if we were concerned we would
> tweak the watermark code a little bit.  So I guess we're worried about
> the extra CPU cycles (and power cost) to service those extra couple
> interrupts?
>
> In theory when touch data is coming in or NFC data is coming in then
> we're probably not in a super low power state to begin with.  If it's
> touch data we likely want to have the CPU boosted a bunch to respond
> to the user quickly.  If we've got 8 cores available all of which can
> run at 1GHz or more a few interrupts won't kill us.  NFC data is
> probably not common enough that we need to optimize power/CPU
> utilizatoin for that.
>
>
> So while i can believe that you do save an interrupt or two, I still
> am not convinced that those interrupts are worth a bunch of complex
> code (and a whole second code path) to save.
>
>
> ...also note that above you said that coming out of runtime suspend
> can take several msec.  That seems like it dwarfs any slight
> difference in timing between a FIFO-based operation and DMA.

One last note here (sorry for not thinking of this last night) is that
I would also be interested in considering _only_ supporting the DMA
path.  Is it somehow intrinsically bad to use the DMA flow for a
1-byte transfer?  Is there a bunch of extra overhead or power draw?

Specifically my main point is that maintaining two separate flows (the
FIFO flow vs the DMA flow) adds complexity, code size, and bugs.  If
there's a really good reason to maintain both flows then fine, but we
should really consider if this is something that's really giving us
value before we agree to it.


-Doug
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-03-08  6:06           ` Karthik Ramasubramanian
@ 2018-03-08 22:32             ` Stephen Boyd
  -1 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-08 22:32 UTC (permalink / raw)
  To: Karthik Ramasubramanian, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Quoting Karthik Ramasubramanian (2018-03-07 22:06:29)
> 
> 
> On 3/6/2018 2:45 PM, Stephen Boyd wrote:
> > Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
> >> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
> > 
> > Ok. I've seen similar designs in some mmc drivers. For example, you can
> > look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
> > clk_ops and then just start using that clk directly from the driver.
> > There are also some helper functions for dividers that would hopefully
> > make the job of calculating the best divider easier.
> Thanks for the pointers. I will take a look at it. In the meanwhile I 
> had discussions with our clock team. They pointed out that the register 
> to write the divider value here is outside the scope of clock controller 
> which makes it trickier to implement your suggestion. They are already 
> in the mailing list and we will discuss further and get back to you in 
> this regard.

Ok. Let me know if I can help answer any questions.

> >>>
> >>> Why are these noirq variants? Please add a comment.
> >> The intention is to enable the console UART port usage as late as
> >> possible in the suspend cycle. Hence noirq variants. I will add this as
> >> a comment. Please let me know if the usage does not match the intention.
> > 
> > Hm.. Does no_console_suspend not work? Or not work well enough?
> It works. When console suspend is disabled, the suspend operation does 
> not get triggered and the resume operation checks if the console suspend 
> is disabled and performs the needed thing.

Ok so then do we need the noirq variants? Or console suspend is special
enough for this to not matter?

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-08 22:32             ` Stephen Boyd
  0 siblings, 0 replies; 65+ messages in thread
From: Stephen Boyd @ 2018-03-08 22:32 UTC (permalink / raw)
  To: Karthik Ramasubramanian, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Quoting Karthik Ramasubramanian (2018-03-07 22:06:29)
> 
> 
> On 3/6/2018 2:45 PM, Stephen Boyd wrote:
> > Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
> >> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
> > 
> > Ok. I've seen similar designs in some mmc drivers. For example, you can
> > look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
> > clk_ops and then just start using that clk directly from the driver.
> > There are also some helper functions for dividers that would hopefully
> > make the job of calculating the best divider easier.
> Thanks for the pointers. I will take a look at it. In the meanwhile I 
> had discussions with our clock team. They pointed out that the register 
> to write the divider value here is outside the scope of clock controller 
> which makes it trickier to implement your suggestion. They are already 
> in the mailing list and we will discuss further and get back to you in 
> this regard.

Ok. Let me know if I can help answer any questions.

> >>>
> >>> Why are these noirq variants? Please add a comment.
> >> The intention is to enable the console UART port usage as late as
> >> possible in the suspend cycle. Hence noirq variants. I will add this as
> >> a comment. Please let me know if the usage does not match the intention.
> > 
> > Hm.. Does no_console_suspend not work? Or not work well enough?
> It works. When console suspend is disabled, the suspend operation does 
> not get triggered and the resume operation checks if the console suspend 
> is disabled and performs the needed thing.

Ok so then do we need the noirq variants? Or console suspend is special
enough for this to not matter?
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-03-08 21:12           ` Doug Anderson
@ 2018-03-09  1:06             ` Sagar Dharia
  -1 siblings, 0 replies; 65+ messages in thread
From: Sagar Dharia @ 2018-03-09  1:06 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi Doug

On 3/8/2018 2:12 PM, Doug Anderson wrote:
> Hi,
> 
> On Wed, Mar 7, 2018 at 9:19 PM, Doug Anderson <dianders@chromium.org> wrote:
>>>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>>>> a lot by transferring i2c commands over DMA compared to a FIFO?
>>>> Enough to justify the code complexity and the set of bugs that will
>>>> show up?  I'm sure it will be a controversial assertion given that the
>>>> code's already written, but personally I'd be supportive of ripping
>>>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>>>> agrees.  To me it seems like premature optimization.
>>>
>>>
>>> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data transfers
>>> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size is
>>> 32, so we can definitely avoid at least 1 interrupt when DMA mode is used
>>> with data size > 32.
>>
>> Does that 1-2 interrupts make any real difference, though?  In theory
>> it really shouldn't affect the transfer rate.  We should be able to
>> service the interrupt plenty fast and if we were concerned we would
>> tweak the watermark code a little bit.  So I guess we're worried about
>> the extra CPU cycles (and power cost) to service those extra couple
>> interrupts?
>>
>> In theory when touch data is coming in or NFC data is coming in then
>> we're probably not in a super low power state to begin with.  If it's
>> touch data we likely want to have the CPU boosted a bunch to respond
>> to the user quickly.  If we've got 8 cores available all of which can
>> run at 1GHz or more a few interrupts won't kill us.  NFC data is
>> probably not common enough that we need to optimize power/CPU
>> utilizatoin for that.
>>
>>
>> So while i can believe that you do save an interrupt or two, I still
>> am not convinced that those interrupts are worth a bunch of complex
>> code (and a whole second code path) to save.
>>
>>
>> ...also note that above you said that coming out of runtime suspend
>> can take several msec.  That seems like it dwarfs any slight
>> difference in timing between a FIFO-based operation and DMA.
> 
> One last note here (sorry for not thinking of this last night) is that
> I would also be interested in considering _only_ supporting the DMA
> path.  Is it somehow intrinsically bad to use the DMA flow for a
> 1-byte transfer?  Is there a bunch of extra overhead or power draw?
> 
> Specifically my main point is that maintaining two separate flows (the
> FIFO flow vs the DMA flow) adds complexity, code size, and bugs.  If
> there's a really good reason to maintain both flows then fine, but we
> should really consider if this is something that's really giving us
> value before we agree to it.
> 

FIFO mode gives us 2 advantages:
1. small transfers don't have to go through 'dma-map/unmap penalties.
Some small buffers come from the stack of client caller and the
dma-map/unmap may fail.
2. bring-ups are 'less eventful' (with temp. change to just not have DMA
mode at all during bring-ups) since SMMU translation/DMA path from QUP
(master) to memory slave may not always available when critical I2C
peripherals need to be brought up (e.g. PMIC). CPU to QUP (slave) path
is usually available.

On the other side, DMA mode has other advantages:
1. Multiple android clients are still heavily using I2C in spite of
faster peripheral buses being available in industry.
As an example, some multi-finger Touch screens use I2C and the data to
be transferred per transaction over the bus grows well beyond 70-100
bytes based on number of fingers. These transactions are very frequent
when touch is being used, and in an environment where other heavy system
users are also running (MM/graphics).
Another example is, NFC uses I2C (as of now) to transfer as much as 700+
bytes. This can save us 20+ interrupts per transfer.

These transfers are mostly in burst. So the RPMh penalty to resume the
shared resources is only experienced for very first transfer. Remaining
transfers in the burst benefit from DMA if they are too big.

Goal here is to have common driver for upstream targets and android and
android has seen proven advantages with both modes.
If we end up keeping DMA only for downstream (or FIFO only for
downstream), then we lose the advantage of having code in upstream since
we have to maintain downstream patch with other mode.

Thanks
Sagar

> 
> -Doug
> --
> 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-09  1:06             ` Sagar Dharia
  0 siblings, 0 replies; 65+ messages in thread
From: Sagar Dharia @ 2018-03-09  1:06 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi Doug

On 3/8/2018 2:12 PM, Doug Anderson wrote:
> Hi,
> 
> On Wed, Mar 7, 2018 at 9:19 PM, Doug Anderson <dianders@chromium.org> wrote:
>>>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>>>> a lot by transferring i2c commands over DMA compared to a FIFO?
>>>> Enough to justify the code complexity and the set of bugs that will
>>>> show up?  I'm sure it will be a controversial assertion given that the
>>>> code's already written, but personally I'd be supportive of ripping
>>>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>>>> agrees.  To me it seems like premature optimization.
>>>
>>>
>>> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data transfers
>>> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size is
>>> 32, so we can definitely avoid at least 1 interrupt when DMA mode is used
>>> with data size > 32.
>>
>> Does that 1-2 interrupts make any real difference, though?  In theory
>> it really shouldn't affect the transfer rate.  We should be able to
>> service the interrupt plenty fast and if we were concerned we would
>> tweak the watermark code a little bit.  So I guess we're worried about
>> the extra CPU cycles (and power cost) to service those extra couple
>> interrupts?
>>
>> In theory when touch data is coming in or NFC data is coming in then
>> we're probably not in a super low power state to begin with.  If it's
>> touch data we likely want to have the CPU boosted a bunch to respond
>> to the user quickly.  If we've got 8 cores available all of which can
>> run at 1GHz or more a few interrupts won't kill us.  NFC data is
>> probably not common enough that we need to optimize power/CPU
>> utilizatoin for that.
>>
>>
>> So while i can believe that you do save an interrupt or two, I still
>> am not convinced that those interrupts are worth a bunch of complex
>> code (and a whole second code path) to save.
>>
>>
>> ...also note that above you said that coming out of runtime suspend
>> can take several msec.  That seems like it dwarfs any slight
>> difference in timing between a FIFO-based operation and DMA.
> 
> One last note here (sorry for not thinking of this last night) is that
> I would also be interested in considering _only_ supporting the DMA
> path.  Is it somehow intrinsically bad to use the DMA flow for a
> 1-byte transfer?  Is there a bunch of extra overhead or power draw?
> 
> Specifically my main point is that maintaining two separate flows (the
> FIFO flow vs the DMA flow) adds complexity, code size, and bugs.  If
> there's a really good reason to maintain both flows then fine, but we
> should really consider if this is something that's really giving us
> value before we agree to it.
> 

FIFO mode gives us 2 advantages:
1. small transfers don't have to go through 'dma-map/unmap penalties.
Some small buffers come from the stack of client caller and the
dma-map/unmap may fail.
2. bring-ups are 'less eventful' (with temp. change to just not have DMA
mode at all during bring-ups) since SMMU translation/DMA path from QUP
(master) to memory slave may not always available when critical I2C
peripherals need to be brought up (e.g. PMIC). CPU to QUP (slave) path
is usually available.

On the other side, DMA mode has other advantages:
1. Multiple android clients are still heavily using I2C in spite of
faster peripheral buses being available in industry.
As an example, some multi-finger Touch screens use I2C and the data to
be transferred per transaction over the bus grows well beyond 70-100
bytes based on number of fingers. These transactions are very frequent
when touch is being used, and in an environment where other heavy system
users are also running (MM/graphics).
Another example is, NFC uses I2C (as of now) to transfer as much as 700+
bytes. This can save us 20+ interrupts per transfer.

These transfers are mostly in burst. So the RPMh penalty to resume the
shared resources is only experienced for very first transfer. Remaining
transfers in the burst benefit from DMA if they are too big.

Goal here is to have common driver for upstream targets and android and
android has seen proven advantages with both modes.
If we end up keeping DMA only for downstream (or FIFO only for
downstream), then we lose the advantage of having code in upstream since
we have to maintain downstream patch with other mode.

Thanks
Sagar

> 
> -Doug
> --
> 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
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-03-08  5:19         ` Doug Anderson
@ 2018-03-09  1:27           ` Sagar Dharia
  -1 siblings, 0 replies; 65+ messages in thread
From: Sagar Dharia @ 2018-03-09  1:27 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd, harryy, adharmap

Hi Doug,

On 3/7/2018 10:19 PM, Doug Anderson wrote:
> Hi,
> 
> On Wed, Mar 7, 2018 at 6:42 PM, Sagar Dharia <sdharia@codeaurora.org> wrote:
>> Hi Doug,
>> Thank you for reviewing the patch. I will take a stab at a few comments
>> below. We will address most of the other comments in next version of I2C
>> patch.
>>>
>>>> +
>>>> +static const 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},
>>>
>>>
>>> So I guess this is all relying on an input serial clock of 19.2MHz?
>>> Maybe document that?
>>>
>>> Assuming I'm understanding the math here, is it really OK for your
>>> 100kHz and 1MHz mode to be running slightly fast?
>>>
>>> 19200. / 2 / 24
>>>>>>
>>>>>> 400.0
>>>
>>>
>>> 19200. / 7 / 26
>>>>>>
>>>>>> 105.49450549450549
>>>
>>>
>>> 19200. / 1 / 18
>>>>>>
>>>>>> 1066.6666666666667
>>>
>>>
>>> It seems like you'd want the fastest clock that you can make that's
>>> _less than_ the spec.
>>>
>>>
>>> It would also be interesting to know if it's expected that boards
>>> might need to tweak the t_high / t_low depending on their electrical
>>> characteristics.  In the past I've had lots of requests from board
>>> makers to tweak things because they've got a long trace, or a stronger
>>> or weaker pull, or ...  If so we might later need to add some dts
>>> properties like "i2c-scl-rising-time-ns" and make the math more
>>> dynamic here, unless your hardware somehow automatically adjusts for
>>> this type of thing...
>>> These values are derived by our HW team to comply with the t-high and
>>
>> t-low specs of I2C. We have confirmed on scope that the frequency of SCL is
>> indeed less than/equal to the spec. We have not come across slaves who have
>> needed to tweak these things. We are open to adding these properties in dts
>> if you have any such slaves not conforming due to board-layout of other
>> reasons.
> 
> OK, I'm fine with leaving something like this till later if/when it
> comes up.  Documenting a little bit more about how these timings work
> seems like it would be nice, though, at least mentioning what the
> source clock is...
>

Yes, we will document how t-high and t-low is derived from source.

>>> Wow, that's a cluster of arcane calls to handle a call that probably
>>> will never fail (it just enables clocks and sets pinctrl).  Sigh.
>>> ...but as far as I can tell the whole sequence is right.  You
>>> definitely need a "put" after a failed get and it looks like
>>> pm_runtime_set_suspended() has a special exception where it can be
>>> called if you got a runtime error...
>>
>> We didn't have this in before either. But this condition is somewhat
>> frequent if I2C transactions are called on cusp of exiting system suspend.
>> (e.g. PMIC slave getting a wakeup-IRQ and trying to read from PMIC through
>> I2C to read its status as to what caused that wake-up. At that time,
>> get_sync doesn't really enable resources (kernel 4.9) since the runtime-pm
>> ref-count isn't decremented. We run the risk of unclocked access if these
>> arcane calls aren't present. You can go through runtime-pm documentation
>> chapter 6 for more details.
> 
> Yeah, I certainly agree that the calls are needed if
> pm_runtime_get_sync() and I'm not suggesting removing them.  Hence the
> "as far as I can tell the whole sequence is right".
> 
> ...but I'm actually kinda worried if you're saying that you actually
> ran into this case.  Hopefully that got fixed and code no longer tries
> to read from the PMIC at a bad time anymore?  That code should be
> fixed not to be running so late in suspend.
> 

I have added Harry Y and Abhijeet D (developers for PMIC I2C client
team). They can comment if there is still a usecase of very late
transaction during suspend and/or very early transaction during resume.

> 
>>>>
>>>> +       /* Make sure no transactions are pending */
>>>> +       ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
>>>> +       if (!ret) {
>>>> +               dev_err(gi2c->se.dev, "late I2C transaction request\n");
>>>> +               return -EBUSY;
>>>> +       }
>>>
>>>
>>> Does this happen?  How?
>>>
>>> Nothing about this code looks special for your hardware.  If this is
>>> really needed, why is it not part of the i2c core since there's
>>> nothing specific about your driver here?
>>>
>> There have been some clients that don't implement sys-suspend/resume
>> callbacks (so i2c adapter has no clue they are done with their transactions)
>> and this allows us to be flexible when they call I2C transactions extremely
>> late.
> 
> Still feels like this belongs in the i2c core, not your driver.  Maybe
> you should send a patch for the core and remove it from here?
> 
> ...and also, it seems like any i2c clients that don't implement the
> suspend/resume callbacks and try to do i2c transactions late in the
> game need to be fixed.  It should be documented that this isn't a
> valid thing for a driver to do and if we end up in this error case
> then it's not an i2c issue but it's a bad driver somewhere.
> 
You are right: this check is special for our HW due to usecases
mentioned above.
This check can go in i2c-core, but then it will not be necessary if
all adapters and clients that we work with are upstreamed (and all
implement system suspend/resume).
We will remove this in next version of geni i2c-adapter driver.

Thanks
Sagar

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

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

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-09  1:27           ` Sagar Dharia
  0 siblings, 0 replies; 65+ messages in thread
From: Sagar Dharia @ 2018-03-09  1:27 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd, harryy, adharmap

Hi Doug,

On 3/7/2018 10:19 PM, Doug Anderson wrote:
> Hi,
> 
> On Wed, Mar 7, 2018 at 6:42 PM, Sagar Dharia <sdharia@codeaurora.org> wrote:
>> Hi Doug,
>> Thank you for reviewing the patch. I will take a stab at a few comments
>> below. We will address most of the other comments in next version of I2C
>> patch.
>>>
>>>> +
>>>> +static const 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},
>>>
>>>
>>> So I guess this is all relying on an input serial clock of 19.2MHz?
>>> Maybe document that?
>>>
>>> Assuming I'm understanding the math here, is it really OK for your
>>> 100kHz and 1MHz mode to be running slightly fast?
>>>
>>> 19200. / 2 / 24
>>>>>>
>>>>>> 400.0
>>>
>>>
>>> 19200. / 7 / 26
>>>>>>
>>>>>> 105.49450549450549
>>>
>>>
>>> 19200. / 1 / 18
>>>>>>
>>>>>> 1066.6666666666667
>>>
>>>
>>> It seems like you'd want the fastest clock that you can make that's
>>> _less than_ the spec.
>>>
>>>
>>> It would also be interesting to know if it's expected that boards
>>> might need to tweak the t_high / t_low depending on their electrical
>>> characteristics.  In the past I've had lots of requests from board
>>> makers to tweak things because they've got a long trace, or a stronger
>>> or weaker pull, or ...  If so we might later need to add some dts
>>> properties like "i2c-scl-rising-time-ns" and make the math more
>>> dynamic here, unless your hardware somehow automatically adjusts for
>>> this type of thing...
>>> These values are derived by our HW team to comply with the t-high and
>>
>> t-low specs of I2C. We have confirmed on scope that the frequency of SCL is
>> indeed less than/equal to the spec. We have not come across slaves who have
>> needed to tweak these things. We are open to adding these properties in dts
>> if you have any such slaves not conforming due to board-layout of other
>> reasons.
> 
> OK, I'm fine with leaving something like this till later if/when it
> comes up.  Documenting a little bit more about how these timings work
> seems like it would be nice, though, at least mentioning what the
> source clock is...
>

Yes, we will document how t-high and t-low is derived from source.

>>> Wow, that's a cluster of arcane calls to handle a call that probably
>>> will never fail (it just enables clocks and sets pinctrl).  Sigh.
>>> ...but as far as I can tell the whole sequence is right.  You
>>> definitely need a "put" after a failed get and it looks like
>>> pm_runtime_set_suspended() has a special exception where it can be
>>> called if you got a runtime error...
>>
>> We didn't have this in before either. But this condition is somewhat
>> frequent if I2C transactions are called on cusp of exiting system suspend.
>> (e.g. PMIC slave getting a wakeup-IRQ and trying to read from PMIC through
>> I2C to read its status as to what caused that wake-up. At that time,
>> get_sync doesn't really enable resources (kernel 4.9) since the runtime-pm
>> ref-count isn't decremented. We run the risk of unclocked access if these
>> arcane calls aren't present. You can go through runtime-pm documentation
>> chapter 6 for more details.
> 
> Yeah, I certainly agree that the calls are needed if
> pm_runtime_get_sync() and I'm not suggesting removing them.  Hence the
> "as far as I can tell the whole sequence is right".
> 
> ...but I'm actually kinda worried if you're saying that you actually
> ran into this case.  Hopefully that got fixed and code no longer tries
> to read from the PMIC at a bad time anymore?  That code should be
> fixed not to be running so late in suspend.
> 

I have added Harry Y and Abhijeet D (developers for PMIC I2C client
team). They can comment if there is still a usecase of very late
transaction during suspend and/or very early transaction during resume.

> 
>>>>
>>>> +       /* Make sure no transactions are pending */
>>>> +       ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
>>>> +       if (!ret) {
>>>> +               dev_err(gi2c->se.dev, "late I2C transaction request\n");
>>>> +               return -EBUSY;
>>>> +       }
>>>
>>>
>>> Does this happen?  How?
>>>
>>> Nothing about this code looks special for your hardware.  If this is
>>> really needed, why is it not part of the i2c core since there's
>>> nothing specific about your driver here?
>>>
>> There have been some clients that don't implement sys-suspend/resume
>> callbacks (so i2c adapter has no clue they are done with their transactions)
>> and this allows us to be flexible when they call I2C transactions extremely
>> late.
> 
> Still feels like this belongs in the i2c core, not your driver.  Maybe
> you should send a patch for the core and remove it from here?
> 
> ...and also, it seems like any i2c clients that don't implement the
> suspend/resume callbacks and try to do i2c transactions late in the
> game need to be fixed.  It should be documented that this isn't a
> valid thing for a driver to do and if we end up in this error case
> then it's not an i2c issue but it's a bad driver somewhere.
> 
You are right: this check is special for our HW due to usecases
mentioned above.
This check can go in i2c-core, but then it will not be necessary if
all adapters and clients that we work with are upstreamed (and all
implement system suspend/resume).
We will remove this in next version of geni i2c-adapter driver.

Thanks
Sagar

-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-03-08 22:32             ` Stephen Boyd
@ 2018-03-09  4:57               ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-09  4:57 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/8/2018 3:32 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-07 22:06:29)
>>
>>
>> On 3/6/2018 2:45 PM, Stephen Boyd wrote:
>>> Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
>>>> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
>>>
>>> Ok. I've seen similar designs in some mmc drivers. For example, you can
>>> look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
>>> clk_ops and then just start using that clk directly from the driver.
>>> There are also some helper functions for dividers that would hopefully
>>> make the job of calculating the best divider easier.
>> Thanks for the pointers. I will take a look at it. In the meanwhile I
>> had discussions with our clock team. They pointed out that the register
>> to write the divider value here is outside the scope of clock controller
>> which makes it trickier to implement your suggestion. They are already
>> in the mailing list and we will discuss further and get back to you in
>> this regard.
> 
> Ok. Let me know if I can help answer any questions.
Ok.
> 
>>>>>
>>>>> Why are these noirq variants? Please add a comment.
>>>> The intention is to enable the console UART port usage as late as
>>>> possible in the suspend cycle. Hence noirq variants. I will add this as
>>>> a comment. Please let me know if the usage does not match the intention.
>>>
>>> Hm.. Does no_console_suspend not work? Or not work well enough?
>> It works. When console suspend is disabled, the suspend operation does
>> not get triggered and the resume operation checks if the console suspend
>> is disabled and performs the needed thing.
> 
> Ok so then do we need the noirq variants? Or console suspend is special
> enough for this to not matter?
I am a little confused as to whether you prefer the console to not 
suspend at all or you prefer the console suspend at an earlier stage 
than no_irq stage.

If it is former, then with the console_suspend_enabled flag set by 
default this seems the right thing to do. Atleast my understanding is 
that console is expecting the serial port to suspend as well.

If it is latter, then I will check the stage at which suspend_console() 
is initiated and can suspend the serial port after that.
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-09  4:57               ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-09  4:57 UTC (permalink / raw)
  To: Stephen Boyd, andy.gross, corbet, david.brown, gregkh,
	mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/8/2018 3:32 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-07 22:06:29)
>>
>>
>> On 3/6/2018 2:45 PM, Stephen Boyd wrote:
>>> Quoting Karthik Ramasubramanian (2018-03-05 16:51:23)
>>>> On 3/2/2018 3:11 PM, Stephen Boyd wrote:
>>>
>>> Ok. I've seen similar designs in some mmc drivers. For example, you can
>>> look at drivers/mmc/host/meson-gx-mmc.c and see that they register a
>>> clk_ops and then just start using that clk directly from the driver.
>>> There are also some helper functions for dividers that would hopefully
>>> make the job of calculating the best divider easier.
>> Thanks for the pointers. I will take a look at it. In the meanwhile I
>> had discussions with our clock team. They pointed out that the register
>> to write the divider value here is outside the scope of clock controller
>> which makes it trickier to implement your suggestion. They are already
>> in the mailing list and we will discuss further and get back to you in
>> this regard.
> 
> Ok. Let me know if I can help answer any questions.
Ok.
> 
>>>>>
>>>>> Why are these noirq variants? Please add a comment.
>>>> The intention is to enable the console UART port usage as late as
>>>> possible in the suspend cycle. Hence noirq variants. I will add this as
>>>> a comment. Please let me know if the usage does not match the intention.
>>>
>>> Hm.. Does no_console_suspend not work? Or not work well enough?
>> It works. When console suspend is disabled, the suspend operation does
>> not get triggered and the resume operation checks if the console suspend
>> is disabled and performs the needed thing.
> 
> Ok so then do we need the noirq variants? Or console suspend is special
> enough for this to not matter?
I am a little confused as to whether you prefer the console to not 
suspend at all or you prefer the console suspend at an earlier stage 
than no_irq stage.

If it is former, then with the console_suspend_enabled flag set by 
default this seems the right thing to do. Atleast my understanding is 
that console is expecting the serial port to suspend as well.

If it is latter, then I will check the stage at which suspend_console() 
is initiated and can suspend the serial port after that.
> 
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-03-09  1:06             ` Sagar Dharia
@ 2018-03-09  5:02               ` Doug Anderson
  -1 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-09  5:02 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi,

On Thu, Mar 8, 2018 at 5:06 PM, Sagar Dharia <sdharia@codeaurora.org> wrote:
> Hi Doug
>
>
> On 3/8/2018 2:12 PM, Doug Anderson wrote:
>>
>> Hi,
>>
>> On Wed, Mar 7, 2018 at 9:19 PM, Doug Anderson <dianders@chromium.org>
>> wrote:
>>>>>
>>>>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>>>>> a lot by transferring i2c commands over DMA compared to a FIFO?
>>>>> Enough to justify the code complexity and the set of bugs that will
>>>>> show up?  I'm sure it will be a controversial assertion given that the
>>>>> code's already written, but personally I'd be supportive of ripping
>>>>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>>>>> agrees.  To me it seems like premature optimization.
>>>>
>>>>
>>>>
>>>> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data
>>>> transfers
>>>> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size
>>>> is
>>>> 32, so we can definitely avoid at least 1 interrupt when DMA mode is
>>>> used
>>>> with data size > 32.
>>>
>>>
>>> Does that 1-2 interrupts make any real difference, though?  In theory
>>> it really shouldn't affect the transfer rate.  We should be able to
>>> service the interrupt plenty fast and if we were concerned we would
>>> tweak the watermark code a little bit.  So I guess we're worried about
>>> the extra CPU cycles (and power cost) to service those extra couple
>>> interrupts?
>>>
>>> In theory when touch data is coming in or NFC data is coming in then
>>> we're probably not in a super low power state to begin with.  If it's
>>> touch data we likely want to have the CPU boosted a bunch to respond
>>> to the user quickly.  If we've got 8 cores available all of which can
>>> run at 1GHz or more a few interrupts won't kill us.  NFC data is
>>> probably not common enough that we need to optimize power/CPU
>>> utilizatoin for that.
>>>
>>>
>>> So while i can believe that you do save an interrupt or two, I still
>>> am not convinced that those interrupts are worth a bunch of complex
>>> code (and a whole second code path) to save.
>>>
>>>
>>> ...also note that above you said that coming out of runtime suspend
>>> can take several msec.  That seems like it dwarfs any slight
>>> difference in timing between a FIFO-based operation and DMA.
>>
>>
>> One last note here (sorry for not thinking of this last night) is that
>> I would also be interested in considering _only_ supporting the DMA
>> path.  Is it somehow intrinsically bad to use the DMA flow for a
>> 1-byte transfer?  Is there a bunch of extra overhead or power draw?
>>
>> Specifically my main point is that maintaining two separate flows (the
>> FIFO flow vs the DMA flow) adds complexity, code size, and bugs.  If
>> there's a really good reason to maintain both flows then fine, but we
>> should really consider if this is something that's really giving us
>> value before we agree to it.
>>
>
> FIFO mode gives us 2 advantages:
> 1. small transfers don't have to go through 'dma-map/unmap penalties.
> Some small buffers come from the stack of client caller and the
> dma-map/unmap may fail.
> 2. bring-ups are 'less eventful' (with temp. change to just not have DMA
> mode at all during bring-ups) since SMMU translation/DMA path from QUP
> (master) to memory slave may not always available when critical I2C
> peripherals need to be brought up (e.g. PMIC). CPU to QUP (slave) path
> is usually available.
>
> On the other side, DMA mode has other advantages:
> 1. Multiple android clients are still heavily using I2C in spite of
> faster peripheral buses being available in industry.
> As an example, some multi-finger Touch screens use I2C and the data to
> be transferred per transaction over the bus grows well beyond 70-100
> bytes based on number of fingers. These transactions are very frequent
> when touch is being used, and in an environment where other heavy system
> users are also running (MM/graphics).
> Another example is, NFC uses I2C (as of now) to transfer as much as 700+
> bytes. This can save us 20+ interrupts per transfer.
>
> These transfers are mostly in burst. So the RPMh penalty to resume the
> shared resources is only experienced for very first transfer. Remaining
> transfers in the burst benefit from DMA if they are too big.
>
> Goal here is to have common driver for upstream targets and android and
> android has seen proven advantages with both modes.
> If we end up keeping DMA only for downstream (or FIFO only for
> downstream), then we lose the advantage of having code in upstream since
> we have to maintain downstream patch with other mode.

OK, fair enough.  Having DMA mode alone would be a pain at bringup if
nothing else.  You're right.

I would still argue that perhaps those extra interrupts for FIFO mode
aren't quite as bit of a deal as you're making it out to be.  I've
been on systems that get massive number of interrupts almost
constantly and really it wasn't noticeable.

In any case, I didn't think I'd really convince anyone with this one,
so unless someone out there who matters actually feels the same way as
me then feel free to just ignore this and keep supporting both DMA and
FIFO mode.


-Doug

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

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-09  5:02               ` Doug Anderson
  0 siblings, 0 replies; 65+ messages in thread
From: Doug Anderson @ 2018-03-09  5:02 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: Jonathan Corbet, Andy Gross, David Brown, Rob Herring,
	Mark Rutland, Wolfram Sang, Greg Kroah-Hartman,
	Karthikeyan Ramasubramanian, linux-doc, linux-arm-msm,
	devicetree, linux-i2c, linux-serial, Jiri Slaby, evgreen,
	acourbot, Girish Mahadevan, swboyd

Hi,

On Thu, Mar 8, 2018 at 5:06 PM, Sagar Dharia <sdharia@codeaurora.org> wrote:
> Hi Doug
>
>
> On 3/8/2018 2:12 PM, Doug Anderson wrote:
>>
>> Hi,
>>
>> On Wed, Mar 7, 2018 at 9:19 PM, Doug Anderson <dianders@chromium.org>
>> wrote:
>>>>>
>>>>> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
>>>>> a lot by transferring i2c commands over DMA compared to a FIFO?
>>>>> Enough to justify the code complexity and the set of bugs that will
>>>>> show up?  I'm sure it will be a controversial assertion given that the
>>>>> code's already written, but personally I'd be supportive of ripping
>>>>> DMA mode out to simplify the driver.  I'd be curious if anyone else
>>>>> agrees.  To me it seems like premature optimization.
>>>>
>>>>
>>>>
>>>> Yes, we have multiple clients (e.g. touch, NFC) using I2C for data
>>>> transfers
>>>> bigger than 32 bytes (some transfers are 100s of bytes). The fifo size
>>>> is
>>>> 32, so we can definitely avoid at least 1 interrupt when DMA mode is
>>>> used
>>>> with data size > 32.
>>>
>>>
>>> Does that 1-2 interrupts make any real difference, though?  In theory
>>> it really shouldn't affect the transfer rate.  We should be able to
>>> service the interrupt plenty fast and if we were concerned we would
>>> tweak the watermark code a little bit.  So I guess we're worried about
>>> the extra CPU cycles (and power cost) to service those extra couple
>>> interrupts?
>>>
>>> In theory when touch data is coming in or NFC data is coming in then
>>> we're probably not in a super low power state to begin with.  If it's
>>> touch data we likely want to have the CPU boosted a bunch to respond
>>> to the user quickly.  If we've got 8 cores available all of which can
>>> run at 1GHz or more a few interrupts won't kill us.  NFC data is
>>> probably not common enough that we need to optimize power/CPU
>>> utilizatoin for that.
>>>
>>>
>>> So while i can believe that you do save an interrupt or two, I still
>>> am not convinced that those interrupts are worth a bunch of complex
>>> code (and a whole second code path) to save.
>>>
>>>
>>> ...also note that above you said that coming out of runtime suspend
>>> can take several msec.  That seems like it dwarfs any slight
>>> difference in timing between a FIFO-based operation and DMA.
>>
>>
>> One last note here (sorry for not thinking of this last night) is that
>> I would also be interested in considering _only_ supporting the DMA
>> path.  Is it somehow intrinsically bad to use the DMA flow for a
>> 1-byte transfer?  Is there a bunch of extra overhead or power draw?
>>
>> Specifically my main point is that maintaining two separate flows (the
>> FIFO flow vs the DMA flow) adds complexity, code size, and bugs.  If
>> there's a really good reason to maintain both flows then fine, but we
>> should really consider if this is something that's really giving us
>> value before we agree to it.
>>
>
> FIFO mode gives us 2 advantages:
> 1. small transfers don't have to go through 'dma-map/unmap penalties.
> Some small buffers come from the stack of client caller and the
> dma-map/unmap may fail.
> 2. bring-ups are 'less eventful' (with temp. change to just not have DMA
> mode at all during bring-ups) since SMMU translation/DMA path from QUP
> (master) to memory slave may not always available when critical I2C
> peripherals need to be brought up (e.g. PMIC). CPU to QUP (slave) path
> is usually available.
>
> On the other side, DMA mode has other advantages:
> 1. Multiple android clients are still heavily using I2C in spite of
> faster peripheral buses being available in industry.
> As an example, some multi-finger Touch screens use I2C and the data to
> be transferred per transaction over the bus grows well beyond 70-100
> bytes based on number of fingers. These transactions are very frequent
> when touch is being used, and in an environment where other heavy system
> users are also running (MM/graphics).
> Another example is, NFC uses I2C (as of now) to transfer as much as 700+
> bytes. This can save us 20+ interrupts per transfer.
>
> These transfers are mostly in burst. So the RPMh penalty to resume the
> shared resources is only experienced for very first transfer. Remaining
> transfers in the burst benefit from DMA if they are too big.
>
> Goal here is to have common driver for upstream targets and android and
> android has seen proven advantages with both modes.
> If we end up keeping DMA only for downstream (or FIFO only for
> downstream), then we lose the advantage of having code in upstream since
> we have to maintain downstream patch with other mode.

OK, fair enough.  Having DMA mode alone would be a pain at bringup if
nothing else.  You're right.

I would still argue that perhaps those extra interrupts for FIFO mode
aren't quite as bit of a deal as you're making it out to be.  I've
been on systems that get massive number of interrupts almost
constantly and really it wasn't noticeable.

In any case, I didn't think I'd really convince anyone with this one,
so unless someone out there who matters actually feels the same way as
me then feel free to just ignore this and keep supporting both DMA and
FIFO mode.


-Doug
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
  2018-03-07 21:16     ` Doug Anderson
@ 2018-03-09  6:43       ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-09  6:43 UTC (permalink / raw)
  To: Doug Anderson, Jonathan Corbet, Andy Gross, David Brown,
	Rob Herring, Mark Rutland, Wolfram Sang, Greg Kroah-Hartman
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	Jiri Slaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan,
	swboyd



On 3/7/2018 2:16 PM, Doug Anderson wrote:
> Hi,
> 
> On Tue, Feb 27, 2018 at 5:38 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         |  11 +
>>   drivers/i2c/busses/Makefile        |   1 +
>>   drivers/i2c/busses/i2c-qcom-geni.c | 626 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 638 insertions(+)
> 
> I'm not an expert on geni (and, to be honest, I haven't read the main
> geni patch yet).  ...but I figured I could at least add my $0.02 since
> I've stared at i2c bus drivers a lot in the past.  Feel free to tell
> me if I'm full or crap...
> 
> 
>>   create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>>
>> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
>> index e2954fb..1ddf5cd 100644
>> --- a/drivers/i2c/busses/Kconfig
>> +++ b/drivers/i2c/busses/Kconfig
>> @@ -848,6 +848,17 @@ 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
>> +       depends on QCOM_GENI_SE
>> +       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.
> 
> Kind of a generic description and this driver is only for new SoCs,
> right?  Maybe make it a little more specific?
Ok.
> 
> 
>> +
>> +         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..e1e4268
>> --- /dev/null
>> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
>> @@ -0,0 +1,626 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> +
>> +#include <linux/clk.h>
>> +#include <linux/dma-mapping.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/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_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           BIT(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
>> +/* I2C SCL COUNTER fields */
>> +#define HIGH_COUNTER_MSK       GENMASK(29, 20)
>> +#define HIGH_COUNTER_SHFT      20
>> +#define LOW_COUNTER_MSK                GENMASK(19, 10)
>> +#define LOW_COUNTER_SHFT       10
>> +#define CYCLE_COUNTER_MSK      GENMASK(9, 0)
>> +
>> +#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
> 
> Above should be an enum; then use the enum type as the parameter to
> geni_i2c_err() so it's obvious that "err" is not a normal linux error
> code.
> 
> 
>> +#define I2C_NACK               GP_IRQ1
>> +#define I2C_BUS_PROTO          GP_IRQ3
>> +#define I2C_ARB_LOST           GP_IRQ4
> 
> Get rid of definition of GP_IRQ1, 3, and 4 and just define I2C_NACK,
> I2C_BUS_PROTO, and I2C_ARB_LOST directly.
> 
> 
>> +#define DM_I2C_CB_ERR          ((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
>> +                                                                       << 5)
> 
> Should these really be using "GP_IRQ1", "GP_IRQ3", and "GP_IRQ4".
> Does this use of those numbers have anything to do with the other use
> of them?  Seems like this should just be BIT(1) | BIT(3) | BIT(4).
> 
> Said another way: does bit 1 in this field coorespond to NACK, bit 3
> correspond to BUS_PROTO, and bit 4 correspond to ARB_LOST?  If not
> then I see no reason to try to tie them together.  If they do
> correspond then use BIT(I2C_NACK), etc...
> 
The programming manual identifies the bits of the IRQ_STATUS register as 
GP_IRQ* and when the concerned serial engines are used as I2C 
controllers, those bit fields mean NACK, BUS_PROTO, ARB_LOST, etc. That 
is why it was mentioned that way. I will update as you point out.
> 
>> +
>> +#define I2C_AUTO_SUSPEND_DELAY 250
> 
> Why 250 ms?  That seems like an eternity.  Is it really that expensive
> to turn resources off and on?  I would sorta just expect clocks and
> stuff to get turned off right after a transaction finished unless
> another one was pending right behind it...
> 
> 
>> +#define KHz(freq)              (1000 * freq)
> 
> I probably wouldn't define KHz macro and just used numbers like 100000
> like all the other i2c drivers, but I guess it's OK.  Should be all
> caps, though?
I will change to all caps.
> 
> 
>> +#define PACKING_BYTES_PW       4
>> +
>> +struct geni_i2c_dev {
>> +       struct geni_se se;
>> +       u32 tx_wm;
>> +       int irq;
>> +       int err;
>> +       struct i2c_adapter adap;
>> +       struct completion done;
>> +       struct i2c_msg *cur;
>> +       int cur_wr;
>> +       int cur_rd;
>> +       u32 clk_freq_out;
>> +       const struct geni_i2c_clk_fld *clk_fld;
>> +};
>> +
>> +struct geni_i2c_err_log {
>> +       int err;
>> +       const char *msg;
>> +};
>> +
>> +static struct geni_i2c_err_log gi2c_log[] = {
> 
> static const?
Ok.
> 
> 
>> +       [GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
>> +       [I2C_NACK] = {-ENOTCONN, "NACK: slv unresponsive, check its power/reset-ln"},
> 
> Longer than 80 characters; don't split the string, but you could still
> wrap better.
In the v2 patch, there was a comment to break the 80 character limit to 
improve the 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 const 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},
> 
> So I guess this is all relying on an input serial clock of 19.2MHz?
> Maybe document that?
> 
> Assuming I'm understanding the math here, is it really OK for your
> 100kHz and 1MHz mode to be running slightly fast?
> 
> 19200. / 2 / 24
>>>> 400.0
> 
> 19200. / 7 / 26
>>>> 105.49450549450549
> 
> 19200. / 1 / 18
>>>> 1066.6666666666667
> 
> It seems like you'd want the fastest clock that you can make that's
> _less than_ the spec.
> 
> 
> It would also be interesting to know if it's expected that boards
> might need to tweak the t_high / t_low depending on their electrical
> characteristics.  In the past I've had lots of requests from board
> makers to tweak things because they've got a long trace, or a stronger
> or weaker pull, or ...  If so we might later need to add some dts
> properties like "i2c-scl-rising-time-ns" and make the math more
> dynamic here, unless your hardware somehow automatically adjusts for
> this type of thing...
> 
> 
>> +};
>> +
>> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
>> +{
>> +       int i;
>> +       const 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) {
>> +                       gi2c->clk_fld = geni_i2c_clk_map + i;
> 
> Isn't "geni_i2c_clk_map + i" just "itr"?
Yes right.
> 
> 
>> +                       return 0;
>> +               }
>> +       }
>> +       return -EINVAL;
>> +}
>> +
>> +static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
>> +{
>> +       const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
>> +       u32 val;
>> +
>> +       writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
>> +
>> +       val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
>> +       writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
>> +
>> +       val = itr->t_high << HIGH_COUNTER_SHFT;
>> +       val |= itr->t_low << LOW_COUNTER_SHFT;
>> +       val |= itr->t_cycle;
>> +       writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
>> +       /*
>> +        * Ensure later writes/reads to serial engine register block is
>> +        * not reordered before this point.
>> +        */
>> +       mb();
> 
> This mb() is to make sure that later writes to "gi2c->se.base" are not
> reordered to be above the ones in this function?  You don't need a
> mb().  writel_relaxed() already enforces this.
Let me check if there are no readl_relaxed after this. If not, I will 
remove the barrier.
> 
> 
>> +}
>> +
>> +static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
>> +{
>> +       u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0);
>> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
>> +       u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
>> +       u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS);
>> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
>> +       u32 rx_st, tx_st;
>> +
>> +       if (dma) {
>> +               rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
>> +               tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
>> +       } else {
>> +               rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
>> +               tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS);
>> +       }
>> +       dev_dbg(gi2c->se.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->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
>> +               m_cmd, geni_s, geni_ios);
>> +}
>> +
>> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
>> +{
>> +       gi2c->err = gi2c_log[err].err;
> 
> You should only set gi2c->err if it was 0 to start with.  You want
> "err" to contain the first error, not the last one.  This is
> especially important due to the comment elsewhere in this patch "if
> this is err with done-bit not set, handle that through timeout".  You
> don't want the timeout to clobber the true error.
True.
> 
> 
> On a separate note: I wonder if it makes sense to couch the rest of
> this function in something that will compile to a no-op if DEBUG and
> DYNAMIC_DEBUG aren't defined?  Then you can avoid including code for
> all these readl calls.
Given that these are not common scenarios, it may be a premature 
optimization.
> 
>> +       if (gi2c->cur)
>> +               dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
>> +                       gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
>> +       dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
>> +
>> +       if (err != I2C_NACK && err != GENI_ABORT_DONE)
>> +               geni_i2c_err_misc(gi2c);
>> +}
>> +
>> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
>> +{
>> +       struct geni_i2c_dev *gi2c = dev;
>> +       int j;
>> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
>> +       u32 rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
>> +       u32 dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
>> +       u32 dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
>> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
>> +       struct i2c_msg *cur = gi2c->cur;
>> +
>> +       if (!cur ||
>> +           m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
>> +           dm_rx_st & (DM_I2C_CB_ERR)) {
>> +               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);
>> +
>> +               /* Disable the TX Watermark interrupt to stop TX */
>> +               if (!dma)
>> +                       writel_relaxed(0, gi2c->se.base +
>> +                                          SE_GENI_TX_WATERMARK_REG);
>> +               goto irqret;
>> +       }
>> +
>> +       if (dma) {
>> +               dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n",
>> +                       dm_tx_st, dm_rx_st);
>> +               goto irqret;
>> +       }
>> +
>> +       if (cur->flags & I2C_M_RD &&
>> +           m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) {
>> +               u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
>> +
>> +               for (j = 0; j < rxcnt; j++) {
>> +                       u32 val;
>> +                       int p = 0;
>> +
>> +                       val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn);
>> +                       while (gi2c->cur_rd < cur->len && p < sizeof(val)) {
>> +                               cur->buf[gi2c->cur_rd++] = val & 0xff;
>> +                               val >>= 8;
>> +                               p++;
>> +                       }
>> +                       if (gi2c->cur_rd == cur->len)
>> +                               break;
>> +               }
>> +       } else if (!(cur->flags & I2C_M_RD) &&
>> +                  m_stat & M_TX_FIFO_WATERMARK_EN) {
>> +               for (j = 0; j < gi2c->tx_wm; j++) {
>> +                       u32 temp;
>> +                       u32 val = 0;
>> +                       int p = 0;
>> +
>> +                       while (gi2c->cur_wr < cur->len && p < sizeof(val)) {
>> +                               temp = (u32)cur->buf[gi2c->cur_wr++];
> 
> What is the (u32) cast doing here?
The intention is to cast a char to u32. I will revisit its purpose and 
also check if any warnings were observed.
> 
> 
>> +                               val |= (temp << (p * 8));
> 
> Get rid of extra parenthesis.
Ok.
> 
> 
>> +                               p++;
>> +                       }
>> +                       writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn);
>> +                       /* TX Complete, Disable the TX Watermark interrupt */
>> +                       if (gi2c->cur_wr == cur->len) {
>> +                               writel_relaxed(0, gi2c->se.base +
>> +                                               SE_GENI_TX_WATERMARK_REG);
>> +                               break;
>> +                       }
>> +               }
>> +       }
>> +irqret:
>> +       if (m_stat)
>> +               writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR);
>> +
>> +       if (dma) {
>> +               if (dm_tx_st)
>> +                       writel_relaxed(dm_tx_st, gi2c->se.base +
>> +                                               SE_DMA_TX_IRQ_CLR);
>> +               if (dm_rx_st)
>> +                       writel_relaxed(dm_rx_st, gi2c->se.base +
>> +                                               SE_DMA_RX_IRQ_CLR);
>> +       }
>> +       /* if this is err with done-bit not set, handle that through timeout. */
>> +       if (m_stat & M_CMD_DONE_EN)
>> +               complete(&gi2c->done);
>> +       else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
>> +               complete(&gi2c->done);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
>> +{
>> +       u32 val;
>> +       unsigned long timeout = HZ;
> 
> Rename to time_left?  ...and maybe use a #define for the init value?
Ok.
> 
> 
>> +
>> +       geni_i2c_err(gi2c, GENI_TIMEOUT);
>> +       gi2c->cur = NULL;
> 
> Don't you need a spinlock or something?  In most of the other cases
> you could get away with no locking because the irq isn't happening at
> the same time as other code that's mucking with stuff, but in the
> timeout case we may be mucking with stuff at the same time as the irq.
Except for aborting the current command, there is no yanking away of 
buffer used by IRQ. But I will check the programming manual regarding 
what will happen to the contents of the RX FIFO & access to the TX FIFO 
when the command gets aborted.
> 
> 
>> +       geni_se_abort_m_cmd(&gi2c->se);
>> +       do {
>> +               timeout = wait_for_completion_timeout(&gi2c->done, timeout);
>> +               val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
>> +       } while (!(val & M_CMD_ABORT_EN) && timeout);
> 
> Print an error if there was a timeout aborting?
Ok.
> 
> 
>> +}
>> +
>> +static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
>> +                               u32 m_param)
>> +{
>> +       dma_addr_t rx_dma;
>> +       enum geni_se_xfer_mode mode;
>> +       unsigned long timeout;
>> +
>> +       gi2c->cur = msg;
>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
> 
> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
> a lot by transferring i2c commands over DMA compared to a FIFO?
> Enough to justify the code complexity and the set of bugs that will
> show up?  I'm sure it will be a controversial assertion given that the
> code's already written, but personally I'd be supportive of ripping
> DMA mode out to simplify the driver.  I'd be curious if anyone else
> agrees.  To me it seems like premature optimization.
> 
> 
>> +       geni_se_select_mode(&gi2c->se, mode);
>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
>> +       if (mode == GENI_SE_DMA) {
>> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len);
> 
> Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
> check this flag before using msg->buf for DMA?  ...or use
> i2c_get_dma_safe_msg_buf()?
> 
> ...btw: the relative lack of people doing this in the kernel is
> further evidence of DMA not really being worth it for i2c busses.
> 
> 
>> +               if (!rx_dma) {
>> +                       mode = GENI_SE_FIFO;
>> +                       geni_se_select_mode(&gi2c->se, mode);
>> +               }
>> +       }
>> +
>> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);
> 
> Perhaps make a #define for the timeout instead of just hardcoding HZ (1 second).
> 
> 
>> +       if (!timeout)
> 
> Can you rename "timeout" to "time_left"?  Otherwise this read like "if
> there wasn't a timeout then abort".
> 
> 
>> +               geni_i2c_abort_xfer(gi2c);
>> +
>> +       gi2c->cur_rd = 0;
>> +       if (mode == GENI_SE_DMA) {
>> +               if (gi2c->err) {
>> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
>> +                       wait_for_completion_timeout(&gi2c->done, HZ);
> 
> Worth printing an error if this one times out?  Seems like we'd be in
> bad shape...
Ok.
> 
> ...also: to be paranoid do you need a re_init_completion before you
> reset things?  In theory one could conceive of the concept that the
> earlier completion timed out and then the DMA interrupt came right
> after.  Now there will be a completion already on the books so your
> wait will return instantly even though the reset hasn't been done.
> 
reinit_completion can help only if the prior DMA interrupt came before 
reinit_completion. If it comes after reinit_completion, then it will end 
up signaling the wait prematurely.

Rather the better idea is to wait and check if indeed the reset is done. 
If it is done, then all is fine. Else go back to wait again. Same logic 
like in the case of abort.
> 
>> +               }
>> +               geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
>> +       }
>> +       if (gi2c->err)
>> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
> 
> OK, so I'm a bit baffled.  You've got all these tables in this driver
> that give you nice/informative error messages.  Then those nice error
> messages are just calling dev_dbg() and here you print out an arcane
> linux error?
Agree, I will try to log a human-readable error while avoiding the log spew.
> 
> Also: seems like you wouldn't want to print errors for NACKs, right?
> Otherwise i2cdetect is going to be spewing isn't it?
> 
> 
>> +       return gi2c->err;
>> +}
>> +
>> +static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
>> +                               u32 m_param)
>> +{
>> +       dma_addr_t tx_dma;
>> +       enum geni_se_xfer_mode mode;
>> +       unsigned long timeout;
>> +
>> +       gi2c->cur = msg;
>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
>> +       geni_se_select_mode(&gi2c->se, mode);
>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
>> +       if (mode == GENI_SE_DMA) {
>> +               tx_dma = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len);
>> +               if (!tx_dma) {
>> +                       mode = GENI_SE_FIFO;
>> +                       geni_se_select_mode(&gi2c->se, mode);
>> +               }
>> +       }
>> +
>> +       if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
>> +               writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG);
>> +
>> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);
>> +       if (!timeout)
>> +               geni_i2c_abort_xfer(gi2c);
>> +
>> +       gi2c->cur_wr = 0;
>> +       if (mode == GENI_SE_DMA) {
>> +               if (gi2c->err) {
>> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
>> +                       wait_for_completion_timeout(&gi2c->done, HZ);
>> +               }
>> +               geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
>> +       }
>> +       if (gi2c->err)
>> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
>> +       return gi2c->err;
>> +}
>> +
>> +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;
>> +
>> +       gi2c->err = 0;
>> +       reinit_completion(&gi2c->done);
>> +       ret = pm_runtime_get_sync(gi2c->se.dev);
>> +       if (ret < 0) {
>> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
>> +               pm_runtime_put_noidle(gi2c->se.dev);
>> +               /* Set device in suspended since resume failed */
>> +               pm_runtime_set_suspended(gi2c->se.dev);
>> +               return ret;
> 
> Wow, that's a cluster of arcane calls to handle a call that probably
> will never fail (it just enables clocks and sets pinctrl).  Sigh.
> ...but as far as I can tell the whole sequence is right.  You
> definitely need a "put" after a failed get and it looks like
> pm_runtime_set_suspended() has a special exception where it can be
> called if you got a runtime error...
> 
> 
>> +       }
>> +
>> +       qcom_geni_i2c_conf(gi2c);
>> +       for (i = 0; i < num; i++) {
>> +               u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
>> +
>> +               m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
>> +
>> +               if (msgs[i].flags & I2C_M_RD)
>> +                       ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
>> +               else
>> +                       ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
>> +
>> +               if (ret) {
>> +                       dev_err(gi2c->se.dev, "i2c error %d @ %d\n", ret, i);
>> +                       break;
>> +               }
>> +       }
>> +       if (ret == 0)
>> +               ret = num;
>> +
>> +       pm_runtime_mark_last_busy(gi2c->se.dev);
>> +       pm_runtime_put_autosuspend(gi2c->se.dev);
>> +       gi2c->cur = NULL;
>> +       gi2c->err = 0;
>> +       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;
>> +       u32 proto, tx_depth;
>> +       int ret;
>> +
>> +       gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
>> +       if (!gi2c)
>> +               return -ENOMEM;
>> +
>> +       gi2c->se.dev = &pdev->dev;
>> +       gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(gi2c->se.base)) {
>> +               ret = PTR_ERR(gi2c->se.base);
>> +               dev_err(&pdev->dev, "Err IO Mapping register block %d\n", ret);
> 
> No need for error message with devm_ioremap_resource().
Ok.
> 
> 
>> +               return ret;
>> +       }
>> +
>> +       gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
>> +       if (IS_ERR(gi2c->se.clk)) {
>> +               ret = PTR_ERR(gi2c->se.clk);
>> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +               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);
>> +       }
> 
> I feel like it should default to 100KHz.  i2c_parse_fw_timings()
> defaults to this and to me the wording "New drivers almost always
> should use the defaults" makes me feel this should be the defaults.
> 
>> +
>> +       gi2c->irq = platform_get_irq(pdev, 0);
>> +       if (gi2c->irq < 0) {
>> +               dev_err(&pdev->dev, "IRQ error for i2c-geni\n");
>> +               return gi2c->irq;
>> +       }
>> +
>> +       ret = geni_i2c_clk_map_idx(gi2c);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Invalid clk frequency %d KHz: %d\n",
>> +                       gi2c->clk_freq_out, ret);
> 
> Need a divide by 1000 since your printout includes "KHz".  Also note
> that the proper Si units is kHz not KHz, isn't it?
I will remove the KHz and just log as Hz.
> 
> 
>> +               return ret;
>> +       }
>> +
>> +       gi2c->adap.algo = &geni_i2c_algo;
>> +       init_completion(&gi2c->done);
>> +       platform_set_drvdata(pdev, gi2c);
>> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
>> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
>> +                       gi2c->irq, ret);
>> +               return ret;
>> +       }
>> +       disable_irq(gi2c->irq);
> 
> Can you explain the goal of the disable_irq() here.  Is it actually
> needed for something or does it somehow save power?  From drivers I've
> reviewed in the past this doesn't seem like a common thing to do, so
> I'm curious what it's supposed to gain for you.  I'd be inclined to
> just delete the whole disable/enable of the irq from this driver.
> 
> 
>> +       i2c_set_adapdata(&gi2c->adap, gi2c);
>> +       gi2c->adap.dev.parent = &pdev->dev;
>> +       gi2c->adap.dev.of_node = pdev->dev.of_node;
>> +       strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
>> +
>> +       ret = geni_se_resources_on(&gi2c->se);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
>> +               return ret;
>> +       }
>> +       proto = geni_se_read_proto(&gi2c->se);
>> +       tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
>> +       if (unlikely(proto != GENI_SE_I2C)) {
> 
> Avoid compiler hints like unlikely() unless you're really truly
> optimizing a tight inner loop.  Otherwise let the compiler do its job.
Ok.
> 
> 
>> +               dev_err(&pdev->dev, "Invalid proto %d\n", proto);
>> +               geni_se_resources_off(&gi2c->se);
>> +               return -ENXIO;
>> +       }
>> +       gi2c->tx_wm = tx_depth - 1;
>> +       geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
>> +       geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
>> +                                                       true, true, true);
>> +       geni_se_resources_off(&gi2c->se);
>> +       dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
>> +
>> +       pm_runtime_set_suspended(gi2c->se.dev);
>> +       pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
>> +       pm_runtime_use_autosuspend(gi2c->se.dev);
>> +       pm_runtime_enable(gi2c->se.dev);
>> +       i2c_add_adapter(&gi2c->adap);
>> +
>> +       dev_dbg(&pdev->dev, "I2C probed\n");
> 
> Is this really a useful dev_dbg()?  Just turn on initcall debugging...
Can be removed.
> 
> 
>> +       return 0;
>> +}
>> +
>> +static int geni_i2c_remove(struct platform_device *pdev)
>> +{
>> +       struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
>> +
>> +       pm_runtime_disable(gi2c->se.dev);
> 
> Is this right?  You don't want to disable PM Runtime transitions but
> you actually want to force the adapter into suspended state, don't
> you?  I don't see anything that does that...
> 
> 
>> +       i2c_del_adapter(&gi2c->adap);
>> +       return 0;
>> +}
>> +
>> +static int geni_i2c_resume_noirq(struct device *device)
>> +{
>> +       return 0;
>> +}
> 
> No need for a dummy function; just stick NULL in the structure, no?
> 
>> +
>> +#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->se);
>> +       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->se);
>> +       if (ret)
>> +               return ret;
>> +
>> +       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->se.dev, "late I2C transaction request\n");
>> +               return -EBUSY;
>> +       }
> 
> Does this happen?  How?
> 
> Nothing about this code looks special for your hardware.  If this is
> really needed, why is it not part of the i2c core since there's
> nothing specific about your driver here?
> 
> 
>> +       if (!pm_runtime_status_suspended(device)) {
>> +               geni_i2c_runtime_suspend(device);
>> +               pm_runtime_disable(device);
>> +               pm_runtime_set_suspended(device);
>> +               pm_runtime_enable(device);
>> +       }
> 
> Similar question.  Why do you need this special case code?  Are there
> cases where we're all the way at suspend_noirq and yet we still
> haven't runtime suspended?  Can you please document how we get into
> this state?
> 
> 
>> +       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,
> 
> Please use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS and SET_RUNTIME_PM_OPS.  Then
> you can get rid of all the dummy functions.  AKA something like:
> 
> static const struct dev_pm_ops geni_i2c_pm_ops = {
>    SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL)
>    SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, NULL)
> };
> 
Ok.
> 
>> +};
>> +
>> +static const struct of_device_id geni_i2c_dt_match[] = {
>> +       { .compatible = "qcom,geni-i2c" },
>> +       {}
>> +};
>> +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 = "geni_i2c",
>> +               .pm = &geni_i2c_pm_ops,
>> +               .of_match_table = geni_i2c_dt_match,
>> +       },
>> +};
>> +
>> +module_platform_driver(geni_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores");
>> +MODULE_LICENSE("GPL v2");

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

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

* Re: [v3, 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
@ 2018-03-09  6:43       ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-09  6:43 UTC (permalink / raw)
  To: Doug Anderson, Jonathan Corbet, Andy Gross, David Brown,
	Rob Herring, Mark Rutland, Wolfram Sang, Greg Kroah-Hartman
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	Jiri Slaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan,
	swboyd



On 3/7/2018 2:16 PM, Doug Anderson wrote:
> Hi,
> 
> On Tue, Feb 27, 2018 at 5:38 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         |  11 +
>>   drivers/i2c/busses/Makefile        |   1 +
>>   drivers/i2c/busses/i2c-qcom-geni.c | 626 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 638 insertions(+)
> 
> I'm not an expert on geni (and, to be honest, I haven't read the main
> geni patch yet).  ...but I figured I could at least add my $0.02 since
> I've stared at i2c bus drivers a lot in the past.  Feel free to tell
> me if I'm full or crap...
> 
> 
>>   create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>>
>> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
>> index e2954fb..1ddf5cd 100644
>> --- a/drivers/i2c/busses/Kconfig
>> +++ b/drivers/i2c/busses/Kconfig
>> @@ -848,6 +848,17 @@ 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
>> +       depends on QCOM_GENI_SE
>> +       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.
> 
> Kind of a generic description and this driver is only for new SoCs,
> right?  Maybe make it a little more specific?
Ok.
> 
> 
>> +
>> +         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..e1e4268
>> --- /dev/null
>> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
>> @@ -0,0 +1,626 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> +
>> +#include <linux/clk.h>
>> +#include <linux/dma-mapping.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/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_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           BIT(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
>> +/* I2C SCL COUNTER fields */
>> +#define HIGH_COUNTER_MSK       GENMASK(29, 20)
>> +#define HIGH_COUNTER_SHFT      20
>> +#define LOW_COUNTER_MSK                GENMASK(19, 10)
>> +#define LOW_COUNTER_SHFT       10
>> +#define CYCLE_COUNTER_MSK      GENMASK(9, 0)
>> +
>> +#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
> 
> Above should be an enum; then use the enum type as the parameter to
> geni_i2c_err() so it's obvious that "err" is not a normal linux error
> code.
> 
> 
>> +#define I2C_NACK               GP_IRQ1
>> +#define I2C_BUS_PROTO          GP_IRQ3
>> +#define I2C_ARB_LOST           GP_IRQ4
> 
> Get rid of definition of GP_IRQ1, 3, and 4 and just define I2C_NACK,
> I2C_BUS_PROTO, and I2C_ARB_LOST directly.
> 
> 
>> +#define DM_I2C_CB_ERR          ((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
>> +                                                                       << 5)
> 
> Should these really be using "GP_IRQ1", "GP_IRQ3", and "GP_IRQ4".
> Does this use of those numbers have anything to do with the other use
> of them?  Seems like this should just be BIT(1) | BIT(3) | BIT(4).
> 
> Said another way: does bit 1 in this field coorespond to NACK, bit 3
> correspond to BUS_PROTO, and bit 4 correspond to ARB_LOST?  If not
> then I see no reason to try to tie them together.  If they do
> correspond then use BIT(I2C_NACK), etc...
> 
The programming manual identifies the bits of the IRQ_STATUS register as 
GP_IRQ* and when the concerned serial engines are used as I2C 
controllers, those bit fields mean NACK, BUS_PROTO, ARB_LOST, etc. That 
is why it was mentioned that way. I will update as you point out.
> 
>> +
>> +#define I2C_AUTO_SUSPEND_DELAY 250
> 
> Why 250 ms?  That seems like an eternity.  Is it really that expensive
> to turn resources off and on?  I would sorta just expect clocks and
> stuff to get turned off right after a transaction finished unless
> another one was pending right behind it...
> 
> 
>> +#define KHz(freq)              (1000 * freq)
> 
> I probably wouldn't define KHz macro and just used numbers like 100000
> like all the other i2c drivers, but I guess it's OK.  Should be all
> caps, though?
I will change to all caps.
> 
> 
>> +#define PACKING_BYTES_PW       4
>> +
>> +struct geni_i2c_dev {
>> +       struct geni_se se;
>> +       u32 tx_wm;
>> +       int irq;
>> +       int err;
>> +       struct i2c_adapter adap;
>> +       struct completion done;
>> +       struct i2c_msg *cur;
>> +       int cur_wr;
>> +       int cur_rd;
>> +       u32 clk_freq_out;
>> +       const struct geni_i2c_clk_fld *clk_fld;
>> +};
>> +
>> +struct geni_i2c_err_log {
>> +       int err;
>> +       const char *msg;
>> +};
>> +
>> +static struct geni_i2c_err_log gi2c_log[] = {
> 
> static const?
Ok.
> 
> 
>> +       [GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
>> +       [I2C_NACK] = {-ENOTCONN, "NACK: slv unresponsive, check its power/reset-ln"},
> 
> Longer than 80 characters; don't split the string, but you could still
> wrap better.
In the v2 patch, there was a comment to break the 80 character limit to 
improve the 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 const 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},
> 
> So I guess this is all relying on an input serial clock of 19.2MHz?
> Maybe document that?
> 
> Assuming I'm understanding the math here, is it really OK for your
> 100kHz and 1MHz mode to be running slightly fast?
> 
> 19200. / 2 / 24
>>>> 400.0
> 
> 19200. / 7 / 26
>>>> 105.49450549450549
> 
> 19200. / 1 / 18
>>>> 1066.6666666666667
> 
> It seems like you'd want the fastest clock that you can make that's
> _less than_ the spec.
> 
> 
> It would also be interesting to know if it's expected that boards
> might need to tweak the t_high / t_low depending on their electrical
> characteristics.  In the past I've had lots of requests from board
> makers to tweak things because they've got a long trace, or a stronger
> or weaker pull, or ...  If so we might later need to add some dts
> properties like "i2c-scl-rising-time-ns" and make the math more
> dynamic here, unless your hardware somehow automatically adjusts for
> this type of thing...
> 
> 
>> +};
>> +
>> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
>> +{
>> +       int i;
>> +       const 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) {
>> +                       gi2c->clk_fld = geni_i2c_clk_map + i;
> 
> Isn't "geni_i2c_clk_map + i" just "itr"?
Yes right.
> 
> 
>> +                       return 0;
>> +               }
>> +       }
>> +       return -EINVAL;
>> +}
>> +
>> +static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
>> +{
>> +       const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
>> +       u32 val;
>> +
>> +       writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
>> +
>> +       val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
>> +       writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
>> +
>> +       val = itr->t_high << HIGH_COUNTER_SHFT;
>> +       val |= itr->t_low << LOW_COUNTER_SHFT;
>> +       val |= itr->t_cycle;
>> +       writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
>> +       /*
>> +        * Ensure later writes/reads to serial engine register block is
>> +        * not reordered before this point.
>> +        */
>> +       mb();
> 
> This mb() is to make sure that later writes to "gi2c->se.base" are not
> reordered to be above the ones in this function?  You don't need a
> mb().  writel_relaxed() already enforces this.
Let me check if there are no readl_relaxed after this. If not, I will 
remove the barrier.
> 
> 
>> +}
>> +
>> +static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
>> +{
>> +       u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0);
>> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
>> +       u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
>> +       u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS);
>> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
>> +       u32 rx_st, tx_st;
>> +
>> +       if (dma) {
>> +               rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
>> +               tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
>> +       } else {
>> +               rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
>> +               tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS);
>> +       }
>> +       dev_dbg(gi2c->se.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->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
>> +               m_cmd, geni_s, geni_ios);
>> +}
>> +
>> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
>> +{
>> +       gi2c->err = gi2c_log[err].err;
> 
> You should only set gi2c->err if it was 0 to start with.  You want
> "err" to contain the first error, not the last one.  This is
> especially important due to the comment elsewhere in this patch "if
> this is err with done-bit not set, handle that through timeout".  You
> don't want the timeout to clobber the true error.
True.
> 
> 
> On a separate note: I wonder if it makes sense to couch the rest of
> this function in something that will compile to a no-op if DEBUG and
> DYNAMIC_DEBUG aren't defined?  Then you can avoid including code for
> all these readl calls.
Given that these are not common scenarios, it may be a premature 
optimization.
> 
>> +       if (gi2c->cur)
>> +               dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
>> +                       gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
>> +       dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
>> +
>> +       if (err != I2C_NACK && err != GENI_ABORT_DONE)
>> +               geni_i2c_err_misc(gi2c);
>> +}
>> +
>> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
>> +{
>> +       struct geni_i2c_dev *gi2c = dev;
>> +       int j;
>> +       u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
>> +       u32 rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
>> +       u32 dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
>> +       u32 dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
>> +       u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
>> +       struct i2c_msg *cur = gi2c->cur;
>> +
>> +       if (!cur ||
>> +           m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
>> +           dm_rx_st & (DM_I2C_CB_ERR)) {
>> +               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);
>> +
>> +               /* Disable the TX Watermark interrupt to stop TX */
>> +               if (!dma)
>> +                       writel_relaxed(0, gi2c->se.base +
>> +                                          SE_GENI_TX_WATERMARK_REG);
>> +               goto irqret;
>> +       }
>> +
>> +       if (dma) {
>> +               dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n",
>> +                       dm_tx_st, dm_rx_st);
>> +               goto irqret;
>> +       }
>> +
>> +       if (cur->flags & I2C_M_RD &&
>> +           m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) {
>> +               u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
>> +
>> +               for (j = 0; j < rxcnt; j++) {
>> +                       u32 val;
>> +                       int p = 0;
>> +
>> +                       val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn);
>> +                       while (gi2c->cur_rd < cur->len && p < sizeof(val)) {
>> +                               cur->buf[gi2c->cur_rd++] = val & 0xff;
>> +                               val >>= 8;
>> +                               p++;
>> +                       }
>> +                       if (gi2c->cur_rd == cur->len)
>> +                               break;
>> +               }
>> +       } else if (!(cur->flags & I2C_M_RD) &&
>> +                  m_stat & M_TX_FIFO_WATERMARK_EN) {
>> +               for (j = 0; j < gi2c->tx_wm; j++) {
>> +                       u32 temp;
>> +                       u32 val = 0;
>> +                       int p = 0;
>> +
>> +                       while (gi2c->cur_wr < cur->len && p < sizeof(val)) {
>> +                               temp = (u32)cur->buf[gi2c->cur_wr++];
> 
> What is the (u32) cast doing here?
The intention is to cast a char to u32. I will revisit its purpose and 
also check if any warnings were observed.
> 
> 
>> +                               val |= (temp << (p * 8));
> 
> Get rid of extra parenthesis.
Ok.
> 
> 
>> +                               p++;
>> +                       }
>> +                       writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn);
>> +                       /* TX Complete, Disable the TX Watermark interrupt */
>> +                       if (gi2c->cur_wr == cur->len) {
>> +                               writel_relaxed(0, gi2c->se.base +
>> +                                               SE_GENI_TX_WATERMARK_REG);
>> +                               break;
>> +                       }
>> +               }
>> +       }
>> +irqret:
>> +       if (m_stat)
>> +               writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR);
>> +
>> +       if (dma) {
>> +               if (dm_tx_st)
>> +                       writel_relaxed(dm_tx_st, gi2c->se.base +
>> +                                               SE_DMA_TX_IRQ_CLR);
>> +               if (dm_rx_st)
>> +                       writel_relaxed(dm_rx_st, gi2c->se.base +
>> +                                               SE_DMA_RX_IRQ_CLR);
>> +       }
>> +       /* if this is err with done-bit not set, handle that through timeout. */
>> +       if (m_stat & M_CMD_DONE_EN)
>> +               complete(&gi2c->done);
>> +       else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
>> +               complete(&gi2c->done);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
>> +{
>> +       u32 val;
>> +       unsigned long timeout = HZ;
> 
> Rename to time_left?  ...and maybe use a #define for the init value?
Ok.
> 
> 
>> +
>> +       geni_i2c_err(gi2c, GENI_TIMEOUT);
>> +       gi2c->cur = NULL;
> 
> Don't you need a spinlock or something?  In most of the other cases
> you could get away with no locking because the irq isn't happening at
> the same time as other code that's mucking with stuff, but in the
> timeout case we may be mucking with stuff at the same time as the irq.
Except for aborting the current command, there is no yanking away of 
buffer used by IRQ. But I will check the programming manual regarding 
what will happen to the contents of the RX FIFO & access to the TX FIFO 
when the command gets aborted.
> 
> 
>> +       geni_se_abort_m_cmd(&gi2c->se);
>> +       do {
>> +               timeout = wait_for_completion_timeout(&gi2c->done, timeout);
>> +               val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
>> +       } while (!(val & M_CMD_ABORT_EN) && timeout);
> 
> Print an error if there was a timeout aborting?
Ok.
> 
> 
>> +}
>> +
>> +static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
>> +                               u32 m_param)
>> +{
>> +       dma_addr_t rx_dma;
>> +       enum geni_se_xfer_mode mode;
>> +       unsigned long timeout;
>> +
>> +       gi2c->cur = msg;
>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
> 
> DMA is hard and i2c transfers > 32 bytes are rare.  Do we really gain
> a lot by transferring i2c commands over DMA compared to a FIFO?
> Enough to justify the code complexity and the set of bugs that will
> show up?  I'm sure it will be a controversial assertion given that the
> code's already written, but personally I'd be supportive of ripping
> DMA mode out to simplify the driver.  I'd be curious if anyone else
> agrees.  To me it seems like premature optimization.
> 
> 
>> +       geni_se_select_mode(&gi2c->se, mode);
>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
>> +       if (mode == GENI_SE_DMA) {
>> +               rx_dma = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len);
> 
> Randomly I noticed a flag called "I2C_M_DMA_SAFE".  Do we need to
> check this flag before using msg->buf for DMA?  ...or use
> i2c_get_dma_safe_msg_buf()?
> 
> ...btw: the relative lack of people doing this in the kernel is
> further evidence of DMA not really being worth it for i2c busses.
> 
> 
>> +               if (!rx_dma) {
>> +                       mode = GENI_SE_FIFO;
>> +                       geni_se_select_mode(&gi2c->se, mode);
>> +               }
>> +       }
>> +
>> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);
> 
> Perhaps make a #define for the timeout instead of just hardcoding HZ (1 second).
> 
> 
>> +       if (!timeout)
> 
> Can you rename "timeout" to "time_left"?  Otherwise this read like "if
> there wasn't a timeout then abort".
> 
> 
>> +               geni_i2c_abort_xfer(gi2c);
>> +
>> +       gi2c->cur_rd = 0;
>> +       if (mode == GENI_SE_DMA) {
>> +               if (gi2c->err) {
>> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
>> +                       wait_for_completion_timeout(&gi2c->done, HZ);
> 
> Worth printing an error if this one times out?  Seems like we'd be in
> bad shape...
Ok.
> 
> ...also: to be paranoid do you need a re_init_completion before you
> reset things?  In theory one could conceive of the concept that the
> earlier completion timed out and then the DMA interrupt came right
> after.  Now there will be a completion already on the books so your
> wait will return instantly even though the reset hasn't been done.
> 
reinit_completion can help only if the prior DMA interrupt came before 
reinit_completion. If it comes after reinit_completion, then it will end 
up signaling the wait prematurely.

Rather the better idea is to wait and check if indeed the reset is done. 
If it is done, then all is fine. Else go back to wait again. Same logic 
like in the case of abort.
> 
>> +               }
>> +               geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
>> +       }
>> +       if (gi2c->err)
>> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
> 
> OK, so I'm a bit baffled.  You've got all these tables in this driver
> that give you nice/informative error messages.  Then those nice error
> messages are just calling dev_dbg() and here you print out an arcane
> linux error?
Agree, I will try to log a human-readable error while avoiding the log spew.
> 
> Also: seems like you wouldn't want to print errors for NACKs, right?
> Otherwise i2cdetect is going to be spewing isn't it?
> 
> 
>> +       return gi2c->err;
>> +}
>> +
>> +static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
>> +                               u32 m_param)
>> +{
>> +       dma_addr_t tx_dma;
>> +       enum geni_se_xfer_mode mode;
>> +       unsigned long timeout;
>> +
>> +       gi2c->cur = msg;
>> +       mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
>> +       geni_se_select_mode(&gi2c->se, mode);
>> +       writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
>> +       geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
>> +       if (mode == GENI_SE_DMA) {
>> +               tx_dma = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len);
>> +               if (!tx_dma) {
>> +                       mode = GENI_SE_FIFO;
>> +                       geni_se_select_mode(&gi2c->se, mode);
>> +               }
>> +       }
>> +
>> +       if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
>> +               writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG);
>> +
>> +       timeout = wait_for_completion_timeout(&gi2c->done, HZ);
>> +       if (!timeout)
>> +               geni_i2c_abort_xfer(gi2c);
>> +
>> +       gi2c->cur_wr = 0;
>> +       if (mode == GENI_SE_DMA) {
>> +               if (gi2c->err) {
>> +                       writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
>> +                       wait_for_completion_timeout(&gi2c->done, HZ);
>> +               }
>> +               geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
>> +       }
>> +       if (gi2c->err)
>> +               dev_err(gi2c->se.dev, "i2c error :%d\n", gi2c->err);
>> +       return gi2c->err;
>> +}
>> +
>> +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;
>> +
>> +       gi2c->err = 0;
>> +       reinit_completion(&gi2c->done);
>> +       ret = pm_runtime_get_sync(gi2c->se.dev);
>> +       if (ret < 0) {
>> +               dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
>> +               pm_runtime_put_noidle(gi2c->se.dev);
>> +               /* Set device in suspended since resume failed */
>> +               pm_runtime_set_suspended(gi2c->se.dev);
>> +               return ret;
> 
> Wow, that's a cluster of arcane calls to handle a call that probably
> will never fail (it just enables clocks and sets pinctrl).  Sigh.
> ...but as far as I can tell the whole sequence is right.  You
> definitely need a "put" after a failed get and it looks like
> pm_runtime_set_suspended() has a special exception where it can be
> called if you got a runtime error...
> 
> 
>> +       }
>> +
>> +       qcom_geni_i2c_conf(gi2c);
>> +       for (i = 0; i < num; i++) {
>> +               u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
>> +
>> +               m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
>> +
>> +               if (msgs[i].flags & I2C_M_RD)
>> +                       ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
>> +               else
>> +                       ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
>> +
>> +               if (ret) {
>> +                       dev_err(gi2c->se.dev, "i2c error %d @ %d\n", ret, i);
>> +                       break;
>> +               }
>> +       }
>> +       if (ret == 0)
>> +               ret = num;
>> +
>> +       pm_runtime_mark_last_busy(gi2c->se.dev);
>> +       pm_runtime_put_autosuspend(gi2c->se.dev);
>> +       gi2c->cur = NULL;
>> +       gi2c->err = 0;
>> +       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;
>> +       u32 proto, tx_depth;
>> +       int ret;
>> +
>> +       gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
>> +       if (!gi2c)
>> +               return -ENOMEM;
>> +
>> +       gi2c->se.dev = &pdev->dev;
>> +       gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(gi2c->se.base)) {
>> +               ret = PTR_ERR(gi2c->se.base);
>> +               dev_err(&pdev->dev, "Err IO Mapping register block %d\n", ret);
> 
> No need for error message with devm_ioremap_resource().
Ok.
> 
> 
>> +               return ret;
>> +       }
>> +
>> +       gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
>> +       if (IS_ERR(gi2c->se.clk)) {
>> +               ret = PTR_ERR(gi2c->se.clk);
>> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
>> +               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);
>> +       }
> 
> I feel like it should default to 100KHz.  i2c_parse_fw_timings()
> defaults to this and to me the wording "New drivers almost always
> should use the defaults" makes me feel this should be the defaults.
> 
>> +
>> +       gi2c->irq = platform_get_irq(pdev, 0);
>> +       if (gi2c->irq < 0) {
>> +               dev_err(&pdev->dev, "IRQ error for i2c-geni\n");
>> +               return gi2c->irq;
>> +       }
>> +
>> +       ret = geni_i2c_clk_map_idx(gi2c);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Invalid clk frequency %d KHz: %d\n",
>> +                       gi2c->clk_freq_out, ret);
> 
> Need a divide by 1000 since your printout includes "KHz".  Also note
> that the proper Si units is kHz not KHz, isn't it?
I will remove the KHz and just log as Hz.
> 
> 
>> +               return ret;
>> +       }
>> +
>> +       gi2c->adap.algo = &geni_i2c_algo;
>> +       init_completion(&gi2c->done);
>> +       platform_set_drvdata(pdev, gi2c);
>> +       ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
>> +                              IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
>> +                       gi2c->irq, ret);
>> +               return ret;
>> +       }
>> +       disable_irq(gi2c->irq);
> 
> Can you explain the goal of the disable_irq() here.  Is it actually
> needed for something or does it somehow save power?  From drivers I've
> reviewed in the past this doesn't seem like a common thing to do, so
> I'm curious what it's supposed to gain for you.  I'd be inclined to
> just delete the whole disable/enable of the irq from this driver.
> 
> 
>> +       i2c_set_adapdata(&gi2c->adap, gi2c);
>> +       gi2c->adap.dev.parent = &pdev->dev;
>> +       gi2c->adap.dev.of_node = pdev->dev.of_node;
>> +       strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
>> +
>> +       ret = geni_se_resources_on(&gi2c->se);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
>> +               return ret;
>> +       }
>> +       proto = geni_se_read_proto(&gi2c->se);
>> +       tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
>> +       if (unlikely(proto != GENI_SE_I2C)) {
> 
> Avoid compiler hints like unlikely() unless you're really truly
> optimizing a tight inner loop.  Otherwise let the compiler do its job.
Ok.
> 
> 
>> +               dev_err(&pdev->dev, "Invalid proto %d\n", proto);
>> +               geni_se_resources_off(&gi2c->se);
>> +               return -ENXIO;
>> +       }
>> +       gi2c->tx_wm = tx_depth - 1;
>> +       geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
>> +       geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
>> +                                                       true, true, true);
>> +       geni_se_resources_off(&gi2c->se);
>> +       dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
>> +
>> +       pm_runtime_set_suspended(gi2c->se.dev);
>> +       pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
>> +       pm_runtime_use_autosuspend(gi2c->se.dev);
>> +       pm_runtime_enable(gi2c->se.dev);
>> +       i2c_add_adapter(&gi2c->adap);
>> +
>> +       dev_dbg(&pdev->dev, "I2C probed\n");
> 
> Is this really a useful dev_dbg()?  Just turn on initcall debugging...
Can be removed.
> 
> 
>> +       return 0;
>> +}
>> +
>> +static int geni_i2c_remove(struct platform_device *pdev)
>> +{
>> +       struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
>> +
>> +       pm_runtime_disable(gi2c->se.dev);
> 
> Is this right?  You don't want to disable PM Runtime transitions but
> you actually want to force the adapter into suspended state, don't
> you?  I don't see anything that does that...
> 
> 
>> +       i2c_del_adapter(&gi2c->adap);
>> +       return 0;
>> +}
>> +
>> +static int geni_i2c_resume_noirq(struct device *device)
>> +{
>> +       return 0;
>> +}
> 
> No need for a dummy function; just stick NULL in the structure, no?
> 
>> +
>> +#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->se);
>> +       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->se);
>> +       if (ret)
>> +               return ret;
>> +
>> +       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->se.dev, "late I2C transaction request\n");
>> +               return -EBUSY;
>> +       }
> 
> Does this happen?  How?
> 
> Nothing about this code looks special for your hardware.  If this is
> really needed, why is it not part of the i2c core since there's
> nothing specific about your driver here?
> 
> 
>> +       if (!pm_runtime_status_suspended(device)) {
>> +               geni_i2c_runtime_suspend(device);
>> +               pm_runtime_disable(device);
>> +               pm_runtime_set_suspended(device);
>> +               pm_runtime_enable(device);
>> +       }
> 
> Similar question.  Why do you need this special case code?  Are there
> cases where we're all the way at suspend_noirq and yet we still
> haven't runtime suspended?  Can you please document how we get into
> this state?
> 
> 
>> +       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,
> 
> Please use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS and SET_RUNTIME_PM_OPS.  Then
> you can get rid of all the dummy functions.  AKA something like:
> 
> static const struct dev_pm_ops geni_i2c_pm_ops = {
>    SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL)
>    SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, NULL)
> };
> 
Ok.
> 
>> +};
>> +
>> +static const struct of_device_id geni_i2c_dt_match[] = {
>> +       { .compatible = "qcom,geni-i2c" },
>> +       {}
>> +};
>> +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 = "geni_i2c",
>> +               .pm = &geni_i2c_pm_ops,
>> +               .of_match_table = geni_i2c_dt_match,
>> +       },
>> +};
>> +
>> +module_platform_driver(geni_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores");
>> +MODULE_LICENSE("GPL v2");

-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
  2018-03-06 21:56         ` Stephen Boyd
@ 2018-03-09 18:18           ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-09 18:18 UTC (permalink / raw)
  To: Stephen Boyd, Stephen Boyd, andy.gross, corbet, david.brown,
	gregkh, mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan



On 3/6/2018 2:56 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
>>
>>
>> On 3/2/2018 1:41 PM, Stephen Boyd wrote:
>>> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
>>>> +/**
>>>> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
>>>> + * @se:        Pointer to the concerned Serial Engine.
>>>> + *
>>>> + * Return: Protocol value as configured in the serial engine.
>>>> + */
>>>> +u32 geni_se_read_proto(struct geni_se *se)
>>>> +{
>>>> +       u32 val;
>>>> +
>>>> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
>>>> +
>>>> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_read_proto);
>>>
>>> Is this API really needed outside of this file? It would seem like the
>>> drivers that implement the protocol, which are child devices, would only
>>> use this API to confirm that the protocol chosen is for their particular
>>> protocol.
>> No, this API is meant for the protocol drivers to confirm that the
>> serial engine is programmed with the firmware for the concerned protocol
>> before using the serial engine. If the check fails, the protocol drivers
>> stop using the serial engine.
> 
> Ok maybe we don't really need it then?
Without this function the protocol drivers may not be able to verify if 
the serial engine is programmed with the right protocol. Operating on a 
serial engine that is not programmed with the right protocol leads to 
totally undefined behavior.
> >>>> +
>>>> +       ret = geni_se_clks_on(se);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       ret = pinctrl_pm_select_default_state(se->dev);
>>>> +       if (ret)
>>>> +               geni_se_clks_off(se);
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_resources_on);
>>>
>>> IS there a reason why we can't use runtime PM or normal linux PM
>>> infrastructure to power on the wrapper and keep it powered while the
>>> protocol driver is active?
>> Besides turning on the clocks & pinctrl settings, wrapper also has to do
>> the bus scaling votes. The bus scaling votes depend on the individual
>> serial interface bandwidth requirements. The bus scaling votes is not
>> present currently. But once the support comes in, this function enables
>> adding it.
> 
> Ok, but that would basically be some code consolidation around picking a
> bandwidth and enabling/disabling? It sounds like it could go into either
> the serial interface drivers or into the runtime PM path of the wrapper.
Not really. SPI slaves, for example, can operate on different 
frequencies and therefore within a serial engine the bandwidth 
requirements can vary based on the slave. UART & I2C serial interfaces 
have different bandwidth requirements than SPI. So each serial interface 
driver has to specify their bandwidth requirements depending on their 
use-case. This function also allows for aggregation of the votes from 
the wrapper perspective, instead of constant RPMh communication
> 
>>>
>>>> +
>>>> +/**
>>>> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
>>>> + * @se:                Pointer to the concerned Serial Engine.
>>>> + * @tbl:       Table in which the output is returned.
>>>> + *
>>>> + * This function is called by the protocol drivers to determine the different
>>>> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
>>>> +{
>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>> +       unsigned long freq = 0;
>>>> +       int i;
>>>> +       int ret = 0;
>>>> +
>>>> +       mutex_lock(&wrapper->lock);
>>>> +       if (wrapper->clk_perf_tbl) {
>>>> +               *tbl = wrapper->clk_perf_tbl;
>>>> +               ret = wrapper->num_clk_levels;
>>>> +               goto out_unlock;
>>>> +       }
>>>> +
>>>> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
>>>> +                                       sizeof(*wrapper->clk_perf_tbl),
>>>> +                                       GFP_KERNEL);
>>>> +       if (!wrapper->clk_perf_tbl) {
>>>> +               ret = -ENOMEM;
>>>> +               goto out_unlock;
>>>> +       }
>>>> +
>>>> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
>>>> +               freq = clk_round_rate(se->clk, freq + 1);
>>>> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
>>>> +                       break;
>>>> +               wrapper->clk_perf_tbl[i] = freq;
>>>> +       }
>>>> +       wrapper->num_clk_levels = i;
>>>> +       *tbl = wrapper->clk_perf_tbl;
>>>> +       ret = wrapper->num_clk_levels;
>>>> +out_unlock:
>>>> +       mutex_unlock(&wrapper->lock);
>>>
>>> Is this lock actually protecting anything? I mean to say, is any more
>>> than one geni protocol driver calling this function at a time? Or is
>>> the same geni protocol driver calling this from multiple threads at the
>>> same time? The lock looks almost useless.
>> Yes, there is a possibility of multiple I2C instances within the same
>> wrapper trying to get this table simultaneously.
>>
>> As Evan mentioned in the other thread, Bjorn had the comment to move it
>> to the probe and remove the lock. I looked into the possibility of it.
>>   From the hardware perspective, this table belongs to the wrapper and is
>> shared by all the serial engines within the wrapper. But due to software
>> implementation reasons, clk_round_rate can be be performed only on the
>> clocks that are tagged as DFS compatible and only the serial engine
>> clocks are tagged so. At least this was the understanding based on our
>> earlier discussion with the concerned folks. We will revisit it and
>> check if anything has changed recently.
> 
> Hmm sounds like the round rate should happen on the parent of the
> se_clk, and this wrapper DT binding should get the clk for the parent of
> the se->clk to run round_rate() on. Then it could all be done in probe,
> which sounds good.
The parent of the se->clk is also specific to the serial engine itself. 
So putting that into the wrapper's DT binding does not look like a right 
location. For now, I will move the table to the individual serial engine 
themselves. Hence the lock can be removed.
> 
>>>> +
>>>> +/**
>>>> + * struct geni_se - GENI Serial Engine
>>>> + * @base:              Base Address of the Serial Engine's register block.
>>>> + * @dev:               Pointer to the Serial Engine device.
>>>> + * @wrapper:           Pointer to the parent QUP Wrapper core.
>>>> + * @clk:               Handle to the core serial engine clock.
>>>> + */
>>>> +struct geni_se {
>>>> +       void __iomem *base;
>>>> +       struct device *dev;
>>>> +       void *wrapper;
>>>
>>> Can this get the geni_wrapper type? It could be opaque if you like.
>> I am not sure if it is ok to have the children know the details of the
>> parent. That is why it is kept as opaque.
> 
> That's fine, but I mean to have struct geni_wrapper *wrapper, and then
> struct geni_wrapper; in this file. Children won't know details and we
> get slightly more type safety.
Ok.
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver
@ 2018-03-09 18:18           ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-09 18:18 UTC (permalink / raw)
  To: Stephen Boyd, Stephen Boyd, andy.gross, corbet, david.brown,
	gregkh, mark.rutland, robh+dt, wsa
  Cc: linux-doc, linux-arm-msm, devicetree, linux-i2c, linux-serial,
	jslaby, evgreen, acourbot, Sagar Dharia, Girish Mahadevan



On 3/6/2018 2:56 PM, Stephen Boyd wrote:
> Quoting Karthik Ramasubramanian (2018-03-02 16:58:23)
>>
>>
>> On 3/2/2018 1:41 PM, Stephen Boyd wrote:
>>> Quoting Karthikeyan Ramasubramanian (2018-02-27 17:38:07)
>>>> +/**
>>>> + * geni_se_read_proto() - Read the protocol configured for a Serial Engine
>>>> + * @se:        Pointer to the concerned Serial Engine.
>>>> + *
>>>> + * Return: Protocol value as configured in the serial engine.
>>>> + */
>>>> +u32 geni_se_read_proto(struct geni_se *se)
>>>> +{
>>>> +       u32 val;
>>>> +
>>>> +       val = readl_relaxed(se->base + GENI_FW_REVISION_RO);
>>>> +
>>>> +       return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_read_proto);
>>>
>>> Is this API really needed outside of this file? It would seem like the
>>> drivers that implement the protocol, which are child devices, would only
>>> use this API to confirm that the protocol chosen is for their particular
>>> protocol.
>> No, this API is meant for the protocol drivers to confirm that the
>> serial engine is programmed with the firmware for the concerned protocol
>> before using the serial engine. If the check fails, the protocol drivers
>> stop using the serial engine.
> 
> Ok maybe we don't really need it then?
Without this function the protocol drivers may not be able to verify if 
the serial engine is programmed with the right protocol. Operating on a 
serial engine that is not programmed with the right protocol leads to 
totally undefined behavior.
> >>>> +
>>>> +       ret = geni_se_clks_on(se);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       ret = pinctrl_pm_select_default_state(se->dev);
>>>> +       if (ret)
>>>> +               geni_se_clks_off(se);
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +EXPORT_SYMBOL(geni_se_resources_on);
>>>
>>> IS there a reason why we can't use runtime PM or normal linux PM
>>> infrastructure to power on the wrapper and keep it powered while the
>>> protocol driver is active?
>> Besides turning on the clocks & pinctrl settings, wrapper also has to do
>> the bus scaling votes. The bus scaling votes depend on the individual
>> serial interface bandwidth requirements. The bus scaling votes is not
>> present currently. But once the support comes in, this function enables
>> adding it.
> 
> Ok, but that would basically be some code consolidation around picking a
> bandwidth and enabling/disabling? It sounds like it could go into either
> the serial interface drivers or into the runtime PM path of the wrapper.
Not really. SPI slaves, for example, can operate on different 
frequencies and therefore within a serial engine the bandwidth 
requirements can vary based on the slave. UART & I2C serial interfaces 
have different bandwidth requirements than SPI. So each serial interface 
driver has to specify their bandwidth requirements depending on their 
use-case. This function also allows for aggregation of the votes from 
the wrapper perspective, instead of constant RPMh communication
> 
>>>
>>>> +
>>>> +/**
>>>> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
>>>> + * @se:                Pointer to the concerned Serial Engine.
>>>> + * @tbl:       Table in which the output is returned.
>>>> + *
>>>> + * This function is called by the protocol drivers to determine the different
>>>> + * clock frequencies supported by Serial 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 *se, unsigned long **tbl)
>>>> +{
>>>> +       struct geni_wrapper *wrapper = se->wrapper;
>>>> +       unsigned long freq = 0;
>>>> +       int i;
>>>> +       int ret = 0;
>>>> +
>>>> +       mutex_lock(&wrapper->lock);
>>>> +       if (wrapper->clk_perf_tbl) {
>>>> +               *tbl = wrapper->clk_perf_tbl;
>>>> +               ret = wrapper->num_clk_levels;
>>>> +               goto out_unlock;
>>>> +       }
>>>> +
>>>> +       wrapper->clk_perf_tbl = kcalloc(MAX_CLK_PERF_LEVEL,
>>>> +                                       sizeof(*wrapper->clk_perf_tbl),
>>>> +                                       GFP_KERNEL);
>>>> +       if (!wrapper->clk_perf_tbl) {
>>>> +               ret = -ENOMEM;
>>>> +               goto out_unlock;
>>>> +       }
>>>> +
>>>> +       for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
>>>> +               freq = clk_round_rate(se->clk, freq + 1);
>>>> +               if (!freq || freq == wrapper->clk_perf_tbl[i - 1])
>>>> +                       break;
>>>> +               wrapper->clk_perf_tbl[i] = freq;
>>>> +       }
>>>> +       wrapper->num_clk_levels = i;
>>>> +       *tbl = wrapper->clk_perf_tbl;
>>>> +       ret = wrapper->num_clk_levels;
>>>> +out_unlock:
>>>> +       mutex_unlock(&wrapper->lock);
>>>
>>> Is this lock actually protecting anything? I mean to say, is any more
>>> than one geni protocol driver calling this function at a time? Or is
>>> the same geni protocol driver calling this from multiple threads at the
>>> same time? The lock looks almost useless.
>> Yes, there is a possibility of multiple I2C instances within the same
>> wrapper trying to get this table simultaneously.
>>
>> As Evan mentioned in the other thread, Bjorn had the comment to move it
>> to the probe and remove the lock. I looked into the possibility of it.
>>   From the hardware perspective, this table belongs to the wrapper and is
>> shared by all the serial engines within the wrapper. But due to software
>> implementation reasons, clk_round_rate can be be performed only on the
>> clocks that are tagged as DFS compatible and only the serial engine
>> clocks are tagged so. At least this was the understanding based on our
>> earlier discussion with the concerned folks. We will revisit it and
>> check if anything has changed recently.
> 
> Hmm sounds like the round rate should happen on the parent of the
> se_clk, and this wrapper DT binding should get the clk for the parent of
> the se->clk to run round_rate() on. Then it could all be done in probe,
> which sounds good.
The parent of the se->clk is also specific to the serial engine itself. 
So putting that into the wrapper's DT binding does not look like a right 
location. For now, I will move the table to the individual serial engine 
themselves. Hence the lock can be removed.
> 
>>>> +
>>>> +/**
>>>> + * struct geni_se - GENI Serial Engine
>>>> + * @base:              Base Address of the Serial Engine's register block.
>>>> + * @dev:               Pointer to the Serial Engine device.
>>>> + * @wrapper:           Pointer to the parent QUP Wrapper core.
>>>> + * @clk:               Handle to the core serial engine clock.
>>>> + */
>>>> +struct geni_se {
>>>> +       void __iomem *base;
>>>> +       struct device *dev;
>>>> +       void *wrapper;
>>>
>>> Can this get the geni_wrapper type? It could be opaque if you like.
>> I am not sure if it is ok to have the children know the details of the
>> parent. That is why it is kept as opaque.
> 
> That's fine, but I mean to have struct geni_wrapper *wrapper, and then
> struct geni_wrapper; in this file. Children won't know details and we
> get slightly more type safety.
Ok.
> 
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-03-03  0:11     ` Evan Green
@ 2018-03-13 20:16       ` Karthik Ramasubramanian
  -1 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-13 20:16 UTC (permalink / raw)
  To: Evan Green
  Cc: Jonathan Corbet, Andy Gross, David Brown, robh+dt, mark.rutland,
	wsa, gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/2/2018 5:11 PM, Evan Green wrote:
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +#define RX_BYTES_PW 1
>> +#else
>> +#define RX_BYTES_PW 4
>> +#endif
> 
> This seems fishy to me. Does either setting work? If so, why not just
> have one value?
Yes, either one works. In the interrupt driven mode, sometimes due to
increased interrupt latency the RX FIFO may overflow if we use only 1
byte per FIFO word - given there are no flow control lines in the debug
uart. Hence using 4 bytes in the FIFO word will help to prevent the FIFO
overflow - especially in the case where commands are executed through
scripts.

In polling mode, using 1 byte per word helps to use the hardware to
buffer the data instead of software buffering especially when the
framework keeps reading one byte at a time.
> 
>> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +                               int offset, int bit_field, bool set)
>> +{
>> +       u32 reg;
>> +       struct qcom_geni_serial_port *port;
>> +       unsigned int baud;
>> +       unsigned int fifo_bits;
>> +       unsigned long timeout_us = 20000;
>> +
>> +       /* Ensure polling is not re-ordered before the prior writes/reads */
>> +       mb();
>> +
>> +       if (uport->private_data) {
>> +               port = to_dev_port(uport, uport);
>> +               baud = port->cur_baud;
>> +               if (!baud)
>> +                       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.
>> +                */
>> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
> 
> This fluff is a little mysterious, can it be explained at all? Do you
> think the fluff factor is in units of time (as you have it) or bits?
> Time makes sense I guess if we're worried about clock source
> differences.
The fluff is in micro-seconds and can help with unforeseen delays in
emulation platforms.
> 
>> +
>> +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;
>> +       bool locked = true;
>> +       unsigned long flags;
>> +
>> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
>> +
>> +       port = get_port_from_line(co->index);
>> +       if (IS_ERR(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);
> 
> I too am a little lost on the locking here. What exactly is the lock
> protecting? Looks like for the most part it's trying to synchronize
> with the ISR? What specifically in the ISR? I just wanted to go
> through and check to make sure whatever the shared resource is is
> appropriately protected.
The lock protects 2 simultaneous writers from putting the hardware in
the bad state. The output of the command entered in a shell can trigger
a write in the interrupt context while logging activity can trigger a
simultaneous write.
> 
>> +       }
>> +}
>> +
>> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
>> +{
>> +       u32 i = rx_bytes;
>> +       u32 rx_fifo;
>> +       unsigned char *buf;
>> +       struct tty_port *tport;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       tport = &uport->state->port;
>> +       while (i > 0) {
>> +               int c;
>> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;
> 
> Please replace this with a min macro.
Ok.
> 
>> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
>> +{
>> +       int ret = 0;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       struct circ_buf *xmit = &uport->state->xmit;
>> +       size_t avail;
>> +       size_t remaining;
>> +       int i = 0;
>> +       u32 status;
>> +       unsigned int chunk;
>> +       int tail;
>> +
>> +       chunk = uart_circ_chars_pending(xmit);
>> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
>> +       /* Both FIFO and framework buffer are drained */
>> +       if ((chunk == port->xmit_size) && !status) {
>> +               port->xmit_size = 0;
>> +               uart_circ_clear(xmit);
>> +               qcom_geni_serial_stop_tx(uport);
>> +               goto out_write_wakeup;
>> +       }
>> +       chunk -= port->xmit_size;
>> +
>> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
>> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
>> +       if (chunk > (UART_XMIT_SIZE - tail))
>> +               chunk = UART_XMIT_SIZE - tail;
>> +       if (chunk > avail)
>> +               chunk = avail;
>> +
>> +       if (!chunk)
>> +               goto out_write_wakeup;
>> +
>> +       qcom_geni_serial_setup_tx(uport, chunk);
>> +
>> +       remaining = chunk;
>> +       while (i < chunk) {
>> +               unsigned int tx_bytes;
>> +               unsigned int buf = 0;
>> +               int c;
>> +
>> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
>> +               for (c = 0; c < tx_bytes ; c++)
>> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
>> +
>> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
>> +
>> +               i += tx_bytes;
>> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
>> +               uport->icount.tx += tx_bytes;
>> +               remaining -= tx_bytes;
>> +       }
>> +       qcom_geni_serial_poll_tx_done(uport);
>> +       port->xmit_size += chunk;
>> +out_write_wakeup:
>> +       uart_write_wakeup(uport);
>> +       return ret;
>> +}
> 
> This function can't fail, please change the return type to void.
Ok.
> 
>> +
>> +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);
> 
> This is one part of where I'm confused. What are we protecting here
> with the lock? disable_irq waits for any pending ISRs to finish
> according to its comment, so you know you're not racing with the ISR.
In android, console shutdown can be invoked while console write happens.
This lock prevents shutdown from not interfering with the write and
vice-versa.
> 
>> +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)
>> +{
>> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
>> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
>> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
>> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
>> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
>> +}
>> +
> 
> I agree with Stephen's comment, this should be inlined into the single
> place it's called from.
> 
> Thanks Karthik!
> -Evan
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-13 20:16       ` Karthik Ramasubramanian
  0 siblings, 0 replies; 65+ messages in thread
From: Karthik Ramasubramanian @ 2018-03-13 20:16 UTC (permalink / raw)
  To: Evan Green
  Cc: Jonathan Corbet, Andy Gross, David Brown, robh+dt, mark.rutland,
	wsa, gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson



On 3/2/2018 5:11 PM, Evan Green wrote:
>> +
>> +#ifdef CONFIG_CONSOLE_POLL
>> +#define RX_BYTES_PW 1
>> +#else
>> +#define RX_BYTES_PW 4
>> +#endif
> 
> This seems fishy to me. Does either setting work? If so, why not just
> have one value?
Yes, either one works. In the interrupt driven mode, sometimes due to
increased interrupt latency the RX FIFO may overflow if we use only 1
byte per FIFO word - given there are no flow control lines in the debug
uart. Hence using 4 bytes in the FIFO word will help to prevent the FIFO
overflow - especially in the case where commands are executed through
scripts.

In polling mode, using 1 byte per word helps to use the hardware to
buffer the data instead of software buffering especially when the
framework keeps reading one byte at a time.
> 
>> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
>> +                               int offset, int bit_field, bool set)
>> +{
>> +       u32 reg;
>> +       struct qcom_geni_serial_port *port;
>> +       unsigned int baud;
>> +       unsigned int fifo_bits;
>> +       unsigned long timeout_us = 20000;
>> +
>> +       /* Ensure polling is not re-ordered before the prior writes/reads */
>> +       mb();
>> +
>> +       if (uport->private_data) {
>> +               port = to_dev_port(uport, uport);
>> +               baud = port->cur_baud;
>> +               if (!baud)
>> +                       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.
>> +                */
>> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
> 
> This fluff is a little mysterious, can it be explained at all? Do you
> think the fluff factor is in units of time (as you have it) or bits?
> Time makes sense I guess if we're worried about clock source
> differences.
The fluff is in micro-seconds and can help with unforeseen delays in
emulation platforms.
> 
>> +
>> +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;
>> +       bool locked = true;
>> +       unsigned long flags;
>> +
>> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
>> +
>> +       port = get_port_from_line(co->index);
>> +       if (IS_ERR(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);
> 
> I too am a little lost on the locking here. What exactly is the lock
> protecting? Looks like for the most part it's trying to synchronize
> with the ISR? What specifically in the ISR? I just wanted to go
> through and check to make sure whatever the shared resource is is
> appropriately protected.
The lock protects 2 simultaneous writers from putting the hardware in
the bad state. The output of the command entered in a shell can trigger
a write in the interrupt context while logging activity can trigger a
simultaneous write.
> 
>> +       }
>> +}
>> +
>> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes, bool drop)
>> +{
>> +       u32 i = rx_bytes;
>> +       u32 rx_fifo;
>> +       unsigned char *buf;
>> +       struct tty_port *tport;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +
>> +       tport = &uport->state->port;
>> +       while (i > 0) {
>> +               int c;
>> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw : i;
> 
> Please replace this with a min macro.
Ok.
> 
>> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
>> +{
>> +       int ret = 0;
>> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
>> +       struct circ_buf *xmit = &uport->state->xmit;
>> +       size_t avail;
>> +       size_t remaining;
>> +       int i = 0;
>> +       u32 status;
>> +       unsigned int chunk;
>> +       int tail;
>> +
>> +       chunk = uart_circ_chars_pending(xmit);
>> +       status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
>> +       /* Both FIFO and framework buffer are drained */
>> +       if ((chunk == port->xmit_size) && !status) {
>> +               port->xmit_size = 0;
>> +               uart_circ_clear(xmit);
>> +               qcom_geni_serial_stop_tx(uport);
>> +               goto out_write_wakeup;
>> +       }
>> +       chunk -= port->xmit_size;
>> +
>> +       avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
>> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
>> +       if (chunk > (UART_XMIT_SIZE - tail))
>> +               chunk = UART_XMIT_SIZE - tail;
>> +       if (chunk > avail)
>> +               chunk = avail;
>> +
>> +       if (!chunk)
>> +               goto out_write_wakeup;
>> +
>> +       qcom_geni_serial_setup_tx(uport, chunk);
>> +
>> +       remaining = chunk;
>> +       while (i < chunk) {
>> +               unsigned int tx_bytes;
>> +               unsigned int buf = 0;
>> +               int c;
>> +
>> +               tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
>> +               for (c = 0; c < tx_bytes ; c++)
>> +                       buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
>> +
>> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
>> +
>> +               i += tx_bytes;
>> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
>> +               uport->icount.tx += tx_bytes;
>> +               remaining -= tx_bytes;
>> +       }
>> +       qcom_geni_serial_poll_tx_done(uport);
>> +       port->xmit_size += chunk;
>> +out_write_wakeup:
>> +       uart_write_wakeup(uport);
>> +       return ret;
>> +}
> 
> This function can't fail, please change the return type to void.
Ok.
> 
>> +
>> +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);
> 
> This is one part of where I'm confused. What are we protecting here
> with the lock? disable_irq waits for any pending ISRs to finish
> according to its comment, so you know you're not racing with the ISR.
In android, console shutdown can be invoked while console write happens.
This lock prevents shutdown from not interfering with the write and
vice-versa.
> 
>> +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)
>> +{
>> +       writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
>> +       writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
>> +       writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
>> +       writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
>> +       writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
>> +       writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
>> +       writel_relaxed(s_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
>> +}
>> +
> 
> I agree with Stephen's comment, this should be inlined into the single
> place it's called from.
> 
> Thanks Karthik!
> -Evan
> 
Regards,
Karthik.
-- 
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-doc" 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] 65+ messages in thread

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  2018-03-13 20:16       ` Karthik Ramasubramanian
@ 2018-03-16 18:39         ` Evan Green
  -1 siblings, 0 replies; 65+ messages in thread
From: Evan Green @ 2018-03-16 18:39 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: Jonathan Corbet, Andy Gross, David Brown, robh+dt, mark.rutland,
	wsa, gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Hi Karthik,

On Tue, Mar 13, 2018 at 1:16 PM Karthik Ramasubramanian <
kramasub@codeaurora.org> wrote:



> On 3/2/2018 5:11 PM, Evan Green wrote:
> >> +
> >> +#ifdef CONFIG_CONSOLE_POLL
> >> +#define RX_BYTES_PW 1
> >> +#else
> >> +#define RX_BYTES_PW 4
> >> +#endif
> >
> > This seems fishy to me. Does either setting work? If so, why not just
> > have one value?
> Yes, either one works. In the interrupt driven mode, sometimes due to
> increased interrupt latency the RX FIFO may overflow if we use only 1
> byte per FIFO word - given there are no flow control lines in the debug
> uart. Hence using 4 bytes in the FIFO word will help to prevent the FIFO
> overflow - especially in the case where commands are executed through
> scripts.
> In polling mode, using 1 byte per word helps to use the hardware to
> buffer the data instead of software buffering especially when the
> framework keeps reading one byte at a time.

Ok, I think I understand. Let me paraphrase in case I'm wrong: Normally,
you want all 4 bytes per word so that you use the hardware's full FIFO
capability. This works out well since on receive you tell the system how
many bytes you've received, so you're never stuck with leftover bytes.

In polling mode, however, the system asks you for one byte, and the problem
is with 4 bytes per FIFO word you may end up having read three additional
bytes that you don't know what to do with. Configuring the UART to 1 byte
per word allows you to skip coding up a couple of conditionals and an extra
couple u32s in the device struct for saving those extra bytes, but divides
the hardware FIFO size by 4.

It seems a little cheesy to me just to avoid a bit of logic, but I'm not
going to put my foot down about it. I guess it might get complicated when
the console pulls in four bytes, but then only ends up eating one of them,
and then we have to figure out how to get the other three into the normal
ISR-based path.

> >
> >> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> >> +                               int offset, int bit_field, bool set)
> >> +{
> >> +       u32 reg;
> >> +       struct qcom_geni_serial_port *port;
> >> +       unsigned int baud;
> >> +       unsigned int fifo_bits;
> >> +       unsigned long timeout_us = 20000;
> >> +
> >> +       /* Ensure polling is not re-ordered before the prior
writes/reads */
> >> +       mb();
> >> +
> >> +       if (uport->private_data) {
> >> +               port = to_dev_port(uport, uport);
> >> +               baud = port->cur_baud;
> >> +               if (!baud)
> >> +                       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.
> >> +                */
> >> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
> >
> > This fluff is a little mysterious, can it be explained at all? Do you
> > think the fluff factor is in units of time (as you have it) or bits?
> > Time makes sense I guess if we're worried about clock source
> > differences.
> The fluff is in micro-seconds and can help with unforeseen delays in
> emulation platforms.

So emulated platforms go out to lunch, but that generally doesn't depend on
baud rate or how many bits there are. Ok. Might be worth noting what that's
for.

> >
> >> +
> >> +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;
> >> +       bool locked = true;
> >> +       unsigned long flags;
> >> +
> >> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> >> +
> >> +       port = get_port_from_line(co->index);
> >> +       if (IS_ERR(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);
> >
> > I too am a little lost on the locking here. What exactly is the lock
> > protecting? Looks like for the most part it's trying to synchronize
> > with the ISR? What specifically in the ISR? I just wanted to go
> > through and check to make sure whatever the shared resource is is
> > appropriately protected.
> The lock protects 2 simultaneous writers from putting the hardware in
> the bad state. The output of the command entered in a shell can trigger
> a write in the interrupt context while logging activity can trigger a
> simultaneous write.

Can you be any more specific here? What puts the hardware in a bad state?
Maybe I see what you're referring to, where both writers see the FIFO has
space and then end up overrunning it because they both add to it.
Similarly, you wouldn't want both the ISR and the polling code to read the
FIFO status and pull bytes out of the FIFO on rx.

If it's the FIFO that's being protected, then:
1. What about qcom_geni_serial_poll_put_char? It writes to the FIFO but
appears not to be locked when called directly via uart_ops. Up in
serial_core.c it looks like that lock is used for configuration changes,
but not transmit.
2. Same with qcom_geni_serial_get_char.

Based on your comment below, I'm coming to understand that the lock is
protecting the FIFO, whose state is partially in the IRQ_STATUS registers.
Shutdown and the ISR alter those bits, so that's why they're locked. This
finally makes sense to me, but took a lot of work to figure out. The lock
doesn't protect all of the bits in the IRQ registers, just a couple of
bits. Perhaps a comment somewhere indicating exactly what is being
protected (the FIFO, and the FIFO status bits specifically in the IRQ
registers) would be helpful to future folks trying to understand.

> >
> >> +       }
> >> +}
> >> +
> >> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes,
bool drop)
> >> +{
> >> +       u32 i = rx_bytes;
> >> +       u32 rx_fifo;
> >> +       unsigned char *buf;
> >> +       struct tty_port *tport;
> >> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> >> +
> >> +       tport = &uport->state->port;
> >> +       while (i > 0) {
> >> +               int c;
> >> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw
: i;
> >
> > Please replace this with a min macro.
> Ok.
> >
> >> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> >> +{
> >> +       int ret = 0;
> >> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> >> +       struct circ_buf *xmit = &uport->state->xmit;
> >> +       size_t avail;
> >> +       size_t remaining;
> >> +       int i = 0;
> >> +       u32 status;
> >> +       unsigned int chunk;
> >> +       int tail;
> >> +
> >> +       chunk = uart_circ_chars_pending(xmit);
> >> +       status = readl_relaxed(uport->membase +
SE_GENI_TX_FIFO_STATUS);
> >> +       /* Both FIFO and framework buffer are drained */
> >> +       if ((chunk == port->xmit_size) && !status) {
> >> +               port->xmit_size = 0;
> >> +               uart_circ_clear(xmit);
> >> +               qcom_geni_serial_stop_tx(uport);
> >> +               goto out_write_wakeup;
> >> +       }
> >> +       chunk -= port->xmit_size;
> >> +
> >> +       avail = (port->tx_fifo_depth - port->tx_wm) *
port->tx_bytes_pw;
> >> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
> >> +       if (chunk > (UART_XMIT_SIZE - tail))
> >> +               chunk = UART_XMIT_SIZE - tail;
> >> +       if (chunk > avail)
> >> +               chunk = avail;
> >> +
> >> +       if (!chunk)
> >> +               goto out_write_wakeup;
> >> +
> >> +       qcom_geni_serial_setup_tx(uport, chunk);
> >> +
> >> +       remaining = chunk;
> >> +       while (i < chunk) {
> >> +               unsigned int tx_bytes;
> >> +               unsigned int buf = 0;
> >> +               int c;
> >> +
> >> +               tx_bytes = min_t(size_t, remaining,
(size_t)port->tx_bytes_pw);
> >> +               for (c = 0; c < tx_bytes ; c++)
> >> +                       buf |= (xmit->buf[tail + c] << (c *
BITS_PER_BYTE));
> >> +
> >> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
> >> +
> >> +               i += tx_bytes;
> >> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> >> +               uport->icount.tx += tx_bytes;
> >> +               remaining -= tx_bytes;
> >> +       }
> >> +       qcom_geni_serial_poll_tx_done(uport);
> >> +       port->xmit_size += chunk;
> >> +out_write_wakeup:
> >> +       uart_write_wakeup(uport);
> >> +       return ret;
> >> +}
> >
> > This function can't fail, please change the return type to void.
> Ok.
> >
> >> +
> >> +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);
> >
> > This is one part of where I'm confused. What are we protecting here
> > with the lock? disable_irq waits for any pending ISRs to finish
> > according to its comment, so you know you're not racing with the ISR.
> In android, console shutdown can be invoked while console write happens.
> This lock prevents shutdown from not interfering with the write and
> vice-versa.

-Evan

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

* Re: [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
@ 2018-03-16 18:39         ` Evan Green
  0 siblings, 0 replies; 65+ messages in thread
From: Evan Green @ 2018-03-16 18:39 UTC (permalink / raw)
  To: Karthikeyan Ramasubramanian
  Cc: Jonathan Corbet, Andy Gross, David Brown, robh+dt, mark.rutland,
	wsa, gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
	linux-serial, jslaby, acourbot, Girish Mahadevan, Sagar Dharia,
	Doug Anderson

Hi Karthik,

On Tue, Mar 13, 2018 at 1:16 PM Karthik Ramasubramanian <
kramasub@codeaurora.org> wrote:



> On 3/2/2018 5:11 PM, Evan Green wrote:
> >> +
> >> +#ifdef CONFIG_CONSOLE_POLL
> >> +#define RX_BYTES_PW 1
> >> +#else
> >> +#define RX_BYTES_PW 4
> >> +#endif
> >
> > This seems fishy to me. Does either setting work? If so, why not just
> > have one value?
> Yes, either one works. In the interrupt driven mode, sometimes due to
> increased interrupt latency the RX FIFO may overflow if we use only 1
> byte per FIFO word - given there are no flow control lines in the debug
> uart. Hence using 4 bytes in the FIFO word will help to prevent the FIFO
> overflow - especially in the case where commands are executed through
> scripts.
> In polling mode, using 1 byte per word helps to use the hardware to
> buffer the data instead of software buffering especially when the
> framework keeps reading one byte at a time.

Ok, I think I understand. Let me paraphrase in case I'm wrong: Normally,
you want all 4 bytes per word so that you use the hardware's full FIFO
capability. This works out well since on receive you tell the system how
many bytes you've received, so you're never stuck with leftover bytes.

In polling mode, however, the system asks you for one byte, and the problem
is with 4 bytes per FIFO word you may end up having read three additional
bytes that you don't know what to do with. Configuring the UART to 1 byte
per word allows you to skip coding up a couple of conditionals and an extra
couple u32s in the device struct for saving those extra bytes, but divides
the hardware FIFO size by 4.

It seems a little cheesy to me just to avoid a bit of logic, but I'm not
going to put my foot down about it. I guess it might get complicated when
the console pulls in four bytes, but then only ends up eating one of them,
and then we have to figure out how to get the other three into the normal
ISR-based path.

> >
> >> +static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
> >> +                               int offset, int bit_field, bool set)
> >> +{
> >> +       u32 reg;
> >> +       struct qcom_geni_serial_port *port;
> >> +       unsigned int baud;
> >> +       unsigned int fifo_bits;
> >> +       unsigned long timeout_us = 20000;
> >> +
> >> +       /* Ensure polling is not re-ordered before the prior
writes/reads */
> >> +       mb();
> >> +
> >> +       if (uport->private_data) {
> >> +               port = to_dev_port(uport, uport);
> >> +               baud = port->cur_baud;
> >> +               if (!baud)
> >> +                       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.
> >> +                */
> >> +               timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
> >
> > This fluff is a little mysterious, can it be explained at all? Do you
> > think the fluff factor is in units of time (as you have it) or bits?
> > Time makes sense I guess if we're worried about clock source
> > differences.
> The fluff is in micro-seconds and can help with unforeseen delays in
> emulation platforms.

So emulated platforms go out to lunch, but that generally doesn't depend on
baud rate or how many bits there are. Ok. Might be worth noting what that's
for.

> >
> >> +
> >> +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;
> >> +       bool locked = true;
> >> +       unsigned long flags;
> >> +
> >> +       WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
> >> +
> >> +       port = get_port_from_line(co->index);
> >> +       if (IS_ERR(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);
> >
> > I too am a little lost on the locking here. What exactly is the lock
> > protecting? Looks like for the most part it's trying to synchronize
> > with the ISR? What specifically in the ISR? I just wanted to go
> > through and check to make sure whatever the shared resource is is
> > appropriately protected.
> The lock protects 2 simultaneous writers from putting the hardware in
> the bad state. The output of the command entered in a shell can trigger
> a write in the interrupt context while logging activity can trigger a
> simultaneous write.

Can you be any more specific here? What puts the hardware in a bad state?
Maybe I see what you're referring to, where both writers see the FIFO has
space and then end up overrunning it because they both add to it.
Similarly, you wouldn't want both the ISR and the polling code to read the
FIFO status and pull bytes out of the FIFO on rx.

If it's the FIFO that's being protected, then:
1. What about qcom_geni_serial_poll_put_char? It writes to the FIFO but
appears not to be locked when called directly via uart_ops. Up in
serial_core.c it looks like that lock is used for configuration changes,
but not transmit.
2. Same with qcom_geni_serial_get_char.

Based on your comment below, I'm coming to understand that the lock is
protecting the FIFO, whose state is partially in the IRQ_STATUS registers.
Shutdown and the ISR alter those bits, so that's why they're locked. This
finally makes sense to me, but took a lot of work to figure out. The lock
doesn't protect all of the bits in the IRQ registers, just a couple of
bits. Perhaps a comment somewhere indicating exactly what is being
protected (the FIFO, and the FIFO status bits specifically in the IRQ
registers) would be helpful to future folks trying to understand.

> >
> >> +       }
> >> +}
> >> +
> >> +static int handle_rx_console(struct uart_port *uport, u32 rx_bytes,
bool drop)
> >> +{
> >> +       u32 i = rx_bytes;
> >> +       u32 rx_fifo;
> >> +       unsigned char *buf;
> >> +       struct tty_port *tport;
> >> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> >> +
> >> +       tport = &uport->state->port;
> >> +       while (i > 0) {
> >> +               int c;
> >> +               int bytes = i > port->rx_bytes_pw ? port->rx_bytes_pw
: i;
> >
> > Please replace this with a min macro.
> Ok.
> >
> >> +static int qcom_geni_serial_handle_tx(struct uart_port *uport)
> >> +{
> >> +       int ret = 0;
> >> +       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
> >> +       struct circ_buf *xmit = &uport->state->xmit;
> >> +       size_t avail;
> >> +       size_t remaining;
> >> +       int i = 0;
> >> +       u32 status;
> >> +       unsigned int chunk;
> >> +       int tail;
> >> +
> >> +       chunk = uart_circ_chars_pending(xmit);
> >> +       status = readl_relaxed(uport->membase +
SE_GENI_TX_FIFO_STATUS);
> >> +       /* Both FIFO and framework buffer are drained */
> >> +       if ((chunk == port->xmit_size) && !status) {
> >> +               port->xmit_size = 0;
> >> +               uart_circ_clear(xmit);
> >> +               qcom_geni_serial_stop_tx(uport);
> >> +               goto out_write_wakeup;
> >> +       }
> >> +       chunk -= port->xmit_size;
> >> +
> >> +       avail = (port->tx_fifo_depth - port->tx_wm) *
port->tx_bytes_pw;
> >> +       tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
> >> +       if (chunk > (UART_XMIT_SIZE - tail))
> >> +               chunk = UART_XMIT_SIZE - tail;
> >> +       if (chunk > avail)
> >> +               chunk = avail;
> >> +
> >> +       if (!chunk)
> >> +               goto out_write_wakeup;
> >> +
> >> +       qcom_geni_serial_setup_tx(uport, chunk);
> >> +
> >> +       remaining = chunk;
> >> +       while (i < chunk) {
> >> +               unsigned int tx_bytes;
> >> +               unsigned int buf = 0;
> >> +               int c;
> >> +
> >> +               tx_bytes = min_t(size_t, remaining,
(size_t)port->tx_bytes_pw);
> >> +               for (c = 0; c < tx_bytes ; c++)
> >> +                       buf |= (xmit->buf[tail + c] << (c *
BITS_PER_BYTE));
> >> +
> >> +               writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
> >> +
> >> +               i += tx_bytes;
> >> +               tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
> >> +               uport->icount.tx += tx_bytes;
> >> +               remaining -= tx_bytes;
> >> +       }
> >> +       qcom_geni_serial_poll_tx_done(uport);
> >> +       port->xmit_size += chunk;
> >> +out_write_wakeup:
> >> +       uart_write_wakeup(uport);
> >> +       return ret;
> >> +}
> >
> > This function can't fail, please change the return type to void.
> Ok.
> >
> >> +
> >> +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);
> >
> > This is one part of where I'm confused. What are we protecting here
> > with the lock? disable_irq waits for any pending ISRs to finish
> > according to its comment, so you know you're not racing with the ISR.
> In android, console shutdown can be invoked while console write happens.
> This lock prevents shutdown from not interfering with the write and
> vice-versa.

-Evan
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" 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] 65+ messages in thread

end of thread, other threads:[~2018-03-16 18:40 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-28  1:38 [PATCH v3 0/4] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
2018-02-28  1:38 ` [PATCH v3 1/4] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
2018-03-05 23:58   ` Rob Herring
2018-03-05 23:58     ` Rob Herring
2018-03-06  0:55     ` Karthik Ramasubramanian
2018-03-06  0:55       ` Karthik Ramasubramanian
2018-03-06 13:22       ` Rob Herring
2018-03-06 13:22         ` Rob Herring
2018-03-06 17:13         ` Karthik Ramasubramanian
2018-03-06 17:13           ` Karthik Ramasubramanian
2018-02-28  1:38 ` [PATCH v3 2/4] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
2018-03-02 20:41   ` Stephen Boyd
2018-03-02 20:41     ` Stephen Boyd
2018-03-02 20:58     ` Evan Green
2018-03-02 20:58       ` Evan Green
2018-03-03  0:58     ` Karthik Ramasubramanian
2018-03-03  0:58       ` Karthik Ramasubramanian
2018-03-06 21:56       ` Stephen Boyd
2018-03-06 21:56         ` Stephen Boyd
     [not found]         ` <152037339742.218381.11498404122038956963-n1Xw8LXHxjTHt/MElyovVYaSKrA+ACpX0E9HWUfgJXw@public.gmane.org>
2018-03-08  6:46           ` Karthik Ramasubramanian
2018-03-08  6:46             ` Karthik Ramasubramanian
     [not found]             ` <945b6c00-dde6-6ec7-4577-4cc0d034796b-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-03-08 13:24               ` Robin Murphy
2018-03-08 13:24                 ` Robin Murphy
     [not found]                 ` <8567be1b-1431-4f1d-cb41-6a7eaa434438-5wv7dgnIgG8@public.gmane.org>
2018-03-08 14:41                   ` Christoph Hellwig
2018-03-08 14:41                     ` Christoph Hellwig
2018-03-08 18:18                   ` Karthik Ramasubramanian
2018-03-08 18:18                     ` Karthik Ramasubramanian
2018-03-09 18:18         ` Karthik Ramasubramanian
2018-03-09 18:18           ` Karthik Ramasubramanian
2018-02-28  1:38 ` [PATCH v3 3/4] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
2018-03-07 21:16   ` [v3, " Doug Anderson
2018-03-07 21:16     ` Doug Anderson
2018-03-08  2:42     ` Sagar Dharia
2018-03-08  2:42       ` Sagar Dharia
2018-03-08  5:19       ` Doug Anderson
2018-03-08  5:19         ` Doug Anderson
2018-03-08 21:12         ` Doug Anderson
2018-03-08 21:12           ` Doug Anderson
2018-03-09  1:06           ` Sagar Dharia
2018-03-09  1:06             ` Sagar Dharia
2018-03-09  5:02             ` Doug Anderson
2018-03-09  5:02               ` Doug Anderson
2018-03-09  1:27         ` Sagar Dharia
2018-03-09  1:27           ` Sagar Dharia
2018-03-09  6:43     ` Karthik Ramasubramanian
2018-03-09  6:43       ` Karthik Ramasubramanian
2018-02-28  1:38 ` [PATCH v3 4/4] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
2018-03-02 22:11   ` Stephen Boyd
2018-03-02 22:11     ` Stephen Boyd
2018-03-06  0:51     ` Karthik Ramasubramanian
2018-03-06  0:51       ` Karthik Ramasubramanian
2018-03-06 21:45       ` Stephen Boyd
2018-03-06 21:45         ` Stephen Boyd
2018-03-08  6:06         ` Karthik Ramasubramanian
2018-03-08  6:06           ` Karthik Ramasubramanian
2018-03-08 22:32           ` Stephen Boyd
2018-03-08 22:32             ` Stephen Boyd
2018-03-09  4:57             ` Karthik Ramasubramanian
2018-03-09  4:57               ` Karthik Ramasubramanian
2018-03-03  0:11   ` Evan Green
2018-03-03  0:11     ` Evan Green
2018-03-13 20:16     ` Karthik Ramasubramanian
2018-03-13 20:16       ` Karthik Ramasubramanian
2018-03-16 18:39       ` Evan Green
2018-03-16 18:39         ` Evan Green

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.