All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/14] QCS404: Add ethernet and I2C drivers
@ 2023-01-20  7:17 Sumit Garg
  2023-01-20  7:17 ` [PATCH 01/14] qcs404: sysmap: Don't map reserved memory ranges Sumit Garg
                   ` (14 more replies)
  0 siblings, 15 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Patch#1 is a fix for QCS404 system memory map to not map reserved memory
regions as an occasional system hang is observed.

Rest of the patches add support for Qualcomm ethernet and I2C drivers
specifically tested on QCS404 SoC.

Sumit Garg (14):
  qcs404: sysmap: Don't map reserved memory ranges
  qcs404-evb: Enable msm_gpio driver support
  clocks: qcs404: Add support for ethernet clocks
  pinctrl: qcs404: Enable ethernet pinmux options
  pinctrl-snapdragon: Get rid of custom drive-strength values
  net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional
  net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz
  net: dwc_eth_qos: Add Qcom ethernet driver glue layer
  dts: qcs404-evb: Add ethernet controller node
  clock-snapdragon: Add clk_rcg_set_rate() with mnd_width=0
  clocks: qcs404: Add support for I2C clocks
  pinctrl: qcs404: Enable I2C pinmux options
  i2c: Add support for Qualcomm I2C driver
  dts: qcs404-evb: Add I2C controller nodes

 arch/arm/dts/dragonboard410c.dts              |   3 +-
 arch/arm/dts/dragonboard820c.dts              |   3 +-
 arch/arm/dts/qcom-ipq4019.dtsi                |   1 -
 arch/arm/dts/qcs404-evb.dts                   | 200 +++++-
 arch/arm/mach-snapdragon/clock-qcs404.c       | 118 ++++
 arch/arm/mach-snapdragon/clock-snapdragon.c   |  24 +
 arch/arm/mach-snapdragon/clock-snapdragon.h   |   2 +
 .../include/mach/sysmap-qcs404.h              |  31 +
 arch/arm/mach-snapdragon/pinctrl-qcs404.c     |  13 +
 arch/arm/mach-snapdragon/pinctrl-snapdragon.c |   8 +-
 arch/arm/mach-snapdragon/sysmap-qcs404.c      |  14 +-
 configs/qcs404evb_defconfig                   |   1 +
 drivers/i2c/Kconfig                           |   6 +
 drivers/i2c/Makefile                          |   1 +
 drivers/i2c/qup_i2c.c                         | 592 +++++++++++++++++
 drivers/net/Kconfig                           |   7 +
 drivers/net/Makefile                          |   1 +
 drivers/net/dwc_eth_qos.c                     |  35 +-
 drivers/net/dwc_eth_qos.h                     |   4 +
 drivers/net/dwc_eth_qos_qcom.c                | 612 ++++++++++++++++++
 .../dt-bindings/pinctrl/pinctrl-snapdragon.h  |  22 -
 21 files changed, 1658 insertions(+), 40 deletions(-)
 create mode 100644 drivers/i2c/qup_i2c.c
 create mode 100644 drivers/net/dwc_eth_qos_qcom.c
 delete mode 100644 include/dt-bindings/pinctrl/pinctrl-snapdragon.h

-- 
2.34.1


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

* [PATCH 01/14] qcs404: sysmap: Don't map reserved memory ranges
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 02/14] qcs404-evb: Enable msm_gpio driver support Sumit Garg
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Currently u-boot maps whole of 1G RAM but there reserved memory ranges on
QCS404 which are reserved for TrustZone, various firmware components etc.
Any access to these reserved memory ranges causes a bus hang issue. So
disable mapping for reserved memory ranges in u-boot.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/mach-snapdragon/sysmap-qcs404.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-snapdragon/sysmap-qcs404.c b/arch/arm/mach-snapdragon/sysmap-qcs404.c
index b7409031a0..64ca4adf1b 100644
--- a/arch/arm/mach-snapdragon/sysmap-qcs404.c
+++ b/arch/arm/mach-snapdragon/sysmap-qcs404.c
@@ -19,7 +19,19 @@ static struct mm_region qcs404_mem_map[] = {
 	}, {
 		.virt = 0x80000000UL, /* DDR */
 		.phys = 0x80000000UL, /* DDR */
-		.size = 0x40000000UL,
+		.size = 0x05900000UL,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+			 PTE_BLOCK_INNER_SHARE
+	}, {
+		.virt = 0x89600000UL, /* DDR */
+		.phys = 0x89600000UL, /* DDR */
+		.size = 0x162000000UL,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+			 PTE_BLOCK_INNER_SHARE
+	}, {
+		.virt = 0xa0000000UL, /* DDR */
+		.phys = 0xa0000000UL, /* DDR */
+		.size = 0x20000000UL,
 		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
 			 PTE_BLOCK_INNER_SHARE
 	}, {
-- 
2.34.1


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

* [PATCH 02/14] qcs404-evb: Enable msm_gpio driver support
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
  2023-01-20  7:17 ` [PATCH 01/14] qcs404: sysmap: Don't map reserved memory ranges Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 03/14] clocks: qcs404: Add support for ethernet clocks Sumit Garg
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/dts/qcs404-evb.dts | 4 ++++
 configs/qcs404evb_defconfig | 1 +
 2 files changed, 5 insertions(+)

diff --git a/arch/arm/dts/qcs404-evb.dts b/arch/arm/dts/qcs404-evb.dts
index 0639af8fe3..c8bcf9f71d 100644
--- a/arch/arm/dts/qcs404-evb.dts
+++ b/arch/arm/dts/qcs404-evb.dts
@@ -40,6 +40,10 @@
 		pinctrl_north@1300000 {
 			compatible = "qcom,qcs404-pinctrl";
 			reg = <0x1300000 0x200000>;
+			gpio-controller;
+			gpio-count = <120>;
+			gpio-bank-name="soc";
+			#gpio-cells = <2>;
 
 			blsp1_uart2: uart {
 				pins = "GPIO_17", "GPIO_18";
diff --git a/configs/qcs404evb_defconfig b/configs/qcs404evb_defconfig
index dae1551411..d64cd74269 100644
--- a/configs/qcs404evb_defconfig
+++ b/configs/qcs404evb_defconfig
@@ -44,6 +44,7 @@ CONFIG_DM_PMIC=y
 CONFIG_PMIC_QCOM=y
 CONFIG_DM_RESET=y
 CONFIG_MSM_SERIAL=y
+CONFIG_MSM_GPIO=y
 CONFIG_SPMI_MSM=y
 CONFIG_USB=y
 CONFIG_USB_XHCI_HCD=y
-- 
2.34.1


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

* [PATCH 03/14] clocks: qcs404: Add support for ethernet clocks
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
  2023-01-20  7:17 ` [PATCH 01/14] qcs404: sysmap: Don't map reserved memory ranges Sumit Garg
  2023-01-20  7:17 ` [PATCH 02/14] qcs404-evb: Enable msm_gpio driver support Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 04/14] pinctrl: qcs404: Enable ethernet pinmux options Sumit Garg
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/mach-snapdragon/clock-qcs404.c       | 60 +++++++++++++++++++
 .../include/mach/sysmap-qcs404.h              | 14 +++++
 2 files changed, 74 insertions(+)

diff --git a/arch/arm/mach-snapdragon/clock-qcs404.c b/arch/arm/mach-snapdragon/clock-qcs404.c
index 6fe92afe8d..b8f5691aae 100644
--- a/arch/arm/mach-snapdragon/clock-qcs404.c
+++ b/arch/arm/mach-snapdragon/clock-qcs404.c
@@ -18,6 +18,9 @@
 /* GPLL0 clock control registers */
 #define GPLL0_STATUS_ACTIVE BIT(31)
 
+#define CFG_CLK_SRC_GPLL1	BIT(8)
+#define GPLL1_STATUS_ACTIVE	BIT(31)
+
 static struct vote_clk gcc_blsp1_ahb_clk = {
 	.cbcr_reg = BLSP1_AHB_CBCR,
 	.ena_vote = APCS_CLOCK_BRANCH_ENA_VOTE,
@@ -47,6 +50,13 @@ static struct pll_vote_clk gpll0_vote_clk = {
 	.vote_bit = BIT(0),
 };
 
+static struct pll_vote_clk gpll1_vote_clk = {
+	.status = GPLL1_STATUS,
+	.status_bit = GPLL1_STATUS_ACTIVE,
+	.ena_vote = APCS_GPLL_ENA_VOTE,
+	.vote_bit = BIT(1),
+};
+
 static const struct bcr_regs usb30_master_regs = {
 	.cfg_rcgr = USB30_MASTER_CFG_RCGR,
 	.cmd_rcgr = USB30_MASTER_CMD_RCGR,
@@ -55,6 +65,22 @@ static const struct bcr_regs usb30_master_regs = {
 	.D = USB30_MASTER_D,
 };
 
+static const struct bcr_regs emac_regs = {
+	.cfg_rcgr = EMAC_CFG_RCGR,
+	.cmd_rcgr = EMAC_CMD_RCGR,
+	.M = EMAC_M,
+	.N = EMAC_N,
+	.D = EMAC_D,
+};
+
+static const struct bcr_regs emac_ptp_regs = {
+	.cfg_rcgr = EMAC_PTP_CFG_RCGR,
+	.cmd_rcgr = EMAC_PTP_CMD_RCGR,
+	.M = EMAC_M,
+	.N = EMAC_N,
+	.D = EMAC_D,
+};
+
 ulong msm_set_rate(struct clk *clk, ulong rate)
 {
 	struct msm_clk_priv *priv = dev_get_priv(clk->dev);
@@ -79,6 +105,20 @@ ulong msm_set_rate(struct clk *clk, ulong rate)
 	case GCC_SDCC1_AHB_CLK:
 		clk_enable_cbc(priv->base + SDCC_AHB_CBCR(1));
 		break;
+	case GCC_ETH_RGMII_CLK:
+		if (rate == 250000000)
+			clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 0, 0,
+					     CFG_CLK_SRC_GPLL1);
+		else if (rate == 125000000)
+			clk_rcg_set_rate_mnd(priv->base, &emac_regs, 4, 0, 0,
+					     CFG_CLK_SRC_GPLL1);
+		else if (rate == 50000000)
+			clk_rcg_set_rate_mnd(priv->base, &emac_regs, 10, 0, 0,
+					     CFG_CLK_SRC_GPLL1);
+		else if (rate == 5000000)
+			clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 1, 50,
+					     CFG_CLK_SRC_GPLL1);
+		break;
 	default:
 		return 0;
 	}
@@ -111,6 +151,26 @@ int msm_enable(struct clk *clk)
 	case GCC_USB2A_PHY_SLEEP_CLK:
 		clk_enable_cbc(priv->base + USB_HS_PHY_CFG_AHB_CBCR);
 		break;
+	case GCC_ETH_PTP_CLK:
+		/* SPEED_1000: freq -> 250MHz */
+		clk_enable_cbc(priv->base + ETH_PTP_CBCR);
+		clk_enable_gpll0(priv->base, &gpll1_vote_clk);
+		clk_rcg_set_rate_mnd(priv->base, &emac_ptp_regs, 2, 0, 0,
+				     CFG_CLK_SRC_GPLL1);
+		break;
+	case GCC_ETH_RGMII_CLK:
+		/* SPEED_1000: freq -> 250MHz */
+		clk_enable_cbc(priv->base + ETH_RGMII_CBCR);
+		clk_enable_gpll0(priv->base, &gpll1_vote_clk);
+		clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 0, 0,
+				     CFG_CLK_SRC_GPLL1);
+		break;
+	case GCC_ETH_SLAVE_AHB_CLK:
+		clk_enable_cbc(priv->base + ETH_SLAVE_AHB_CBCR);
+		break;
+	case GCC_ETH_AXI_CLK:
+		clk_enable_cbc(priv->base + ETH_AXI_CBCR);
+		break;
 	default:
 		return 0;
 	}
diff --git a/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h b/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h
index e448faad2d..8920c4ee8f 100644
--- a/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h
+++ b/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h
@@ -12,6 +12,7 @@
 
 /* Clocks: (from CLK_CTL_BASE)  */
 #define GPLL0_STATUS			(0x21000)
+#define GPLL1_STATUS			(0x20000)
 #define APCS_GPLL_ENA_VOTE		(0x45000)
 #define APCS_CLOCK_BRANCH_ENA_VOTE	(0x45004)
 
@@ -54,4 +55,17 @@
 #define USB2A_PHY_SLEEP_CBCR		(0x4102C)
 #define USB_HS_PHY_CFG_AHB_CBCR		(0x41030)
 
+/* ETH controller clock control registers */
+#define ETH_PTP_CBCR			(0x4e004)
+#define ETH_RGMII_CBCR			(0x4e008)
+#define ETH_SLAVE_AHB_CBCR		(0x4e00c)
+#define ETH_AXI_CBCR			(0x4e010)
+#define EMAC_PTP_CMD_RCGR		(0x4e014)
+#define EMAC_PTP_CFG_RCGR		(0x4e018)
+#define EMAC_CMD_RCGR			(0x4e01c)
+#define EMAC_CFG_RCGR			(0x4e020)
+#define EMAC_M				(0x4e024)
+#define EMAC_N				(0x4e028)
+#define EMAC_D				(0x4e02c)
+
 #endif
-- 
2.34.1


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

* [PATCH 04/14] pinctrl: qcs404: Enable ethernet pinmux options
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (2 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 03/14] clocks: qcs404: Add support for ethernet clocks Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 05/14] pinctrl-snapdragon: Get rid of custom drive-strength values Sumit Garg
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/mach-snapdragon/pinctrl-qcs404.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/mach-snapdragon/pinctrl-qcs404.c b/arch/arm/mach-snapdragon/pinctrl-qcs404.c
index 889ead0f57..5a7fbfd441 100644
--- a/arch/arm/mach-snapdragon/pinctrl-qcs404.c
+++ b/arch/arm/mach-snapdragon/pinctrl-qcs404.c
@@ -22,6 +22,13 @@ static const char * const msm_pinctrl_pins[] = {
 
 static const struct pinctrl_function msm_pinctrl_functions[] = {
 	{"blsp_uart2", 1},
+	{"rgmii_int", 1},
+	{"rgmii_ck", 1},
+	{"rgmii_tx", 1},
+	{"rgmii_ctl", 1},
+	{"rgmii_rx", 1},
+	{"rgmii_mdio", 1},
+	{"rgmii_mdc", 1},
 };
 
 static const char *qcs404_get_function_name(struct udevice *dev,
-- 
2.34.1


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

* [PATCH 05/14] pinctrl-snapdragon: Get rid of custom drive-strength values
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (3 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 04/14] pinctrl: qcs404: Enable ethernet pinmux options Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 06/14] net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional Sumit Garg
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Use standard pinconf drive-strength values from Linux DT bindings rather
than ones based on custom u-boot header. These changes are in direction
to make u-boot DTs for Qcom SoCs to be compatible with standard Linux
DT bindings.

Also, add support for pinconf bias-pull-up.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/dts/dragonboard410c.dts              |  3 +--
 arch/arm/dts/dragonboard820c.dts              |  3 +--
 arch/arm/dts/qcom-ipq4019.dtsi                |  1 -
 arch/arm/dts/qcs404-evb.dts                   |  1 -
 arch/arm/mach-snapdragon/pinctrl-snapdragon.c |  8 ++++++-
 .../dt-bindings/pinctrl/pinctrl-snapdragon.h  | 22 -------------------
 6 files changed, 9 insertions(+), 29 deletions(-)
 delete mode 100644 include/dt-bindings/pinctrl/pinctrl-snapdragon.h

diff --git a/arch/arm/dts/dragonboard410c.dts b/arch/arm/dts/dragonboard410c.dts
index 59cf45eb17..9230dd3fd9 100644
--- a/arch/arm/dts/dragonboard410c.dts
+++ b/arch/arm/dts/dragonboard410c.dts
@@ -9,7 +9,6 @@
 
 #include "skeleton64.dtsi"
 #include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/pinctrl/pinctrl-snapdragon.h>
 
 / {
 	model = "Qualcomm Technologies, Inc. Dragonboard 410c";
@@ -71,7 +70,7 @@
 			blsp1_uart: uart {
 				function = "blsp1_uart";
 				pins = "GPIO_4", "GPIO_5";
-				drive-strength = <DRIVE_STRENGTH_8MA>;
+				drive-strength = <8>;
 				bias-disable;
 			};
 		};
diff --git a/arch/arm/dts/dragonboard820c.dts b/arch/arm/dts/dragonboard820c.dts
index aaca681d2e..ad201d4874 100644
--- a/arch/arm/dts/dragonboard820c.dts
+++ b/arch/arm/dts/dragonboard820c.dts
@@ -8,7 +8,6 @@
 /dts-v1/;
 
 #include "skeleton64.dtsi"
-#include <dt-bindings/pinctrl/pinctrl-snapdragon.h>
 
 / {
 	model = "Qualcomm Technologies, Inc. DB820c";
@@ -71,7 +70,7 @@
 			blsp8_uart: uart {
 				function = "blsp_uart8";
 				pins = "GPIO_4", "GPIO_5";
-				drive-strength = <DRIVE_STRENGTH_8MA>;
+				drive-strength = <8>;
 				bias-disable;
 			};
 		};
diff --git a/arch/arm/dts/qcom-ipq4019.dtsi b/arch/arm/dts/qcom-ipq4019.dtsi
index 181732d262..6edc69da67 100644
--- a/arch/arm/dts/qcom-ipq4019.dtsi
+++ b/arch/arm/dts/qcom-ipq4019.dtsi
@@ -9,7 +9,6 @@
 
 #include "skeleton.dtsi"
 #include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/pinctrl/pinctrl-snapdragon.h>
 #include <dt-bindings/clock/qcom,ipq4019-gcc.h>
 #include <dt-bindings/reset/qcom,ipq4019-reset.h>
 
diff --git a/arch/arm/dts/qcs404-evb.dts b/arch/arm/dts/qcs404-evb.dts
index c8bcf9f71d..cc70afa4c8 100644
--- a/arch/arm/dts/qcs404-evb.dts
+++ b/arch/arm/dts/qcs404-evb.dts
@@ -9,7 +9,6 @@
 
 #include "skeleton64.dtsi"
 #include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/pinctrl/pinctrl-snapdragon.h>
 #include <dt-bindings/clock/qcom,gcc-qcs404.h>
 
 / {
diff --git a/arch/arm/mach-snapdragon/pinctrl-snapdragon.c b/arch/arm/mach-snapdragon/pinctrl-snapdragon.c
index ab884ab6bf..826dc51486 100644
--- a/arch/arm/mach-snapdragon/pinctrl-snapdragon.c
+++ b/arch/arm/mach-snapdragon/pinctrl-snapdragon.c
@@ -28,8 +28,9 @@ struct msm_pinctrl_priv {
 #define TLMM_GPIO_DISABLE BIT(9)
 
 static const struct pinconf_param msm_conf_params[] = {
-	{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 3 },
+	{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 2 },
 	{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+	{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 3 },
 };
 
 static int msm_get_functions_count(struct udevice *dev)
@@ -89,6 +90,7 @@ static int msm_pinconf_set(struct udevice *dev, unsigned int pin_selector,
 
 	switch (param) {
 	case PIN_CONFIG_DRIVE_STRENGTH:
+		argument = (argument / 2) - 1;
 		clrsetbits_le32(priv->base + GPIO_CONFIG_OFFSET(pin_selector),
 				TLMM_DRV_STRENGTH_MASK, argument << 6);
 		break;
@@ -96,6 +98,10 @@ static int msm_pinconf_set(struct udevice *dev, unsigned int pin_selector,
 		clrbits_le32(priv->base + GPIO_CONFIG_OFFSET(pin_selector),
 			     TLMM_GPIO_PULL_MASK);
 		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		clrsetbits_le32(priv->base + GPIO_CONFIG_OFFSET(pin_selector),
+				TLMM_GPIO_PULL_MASK, argument);
+		break;
 	default:
 		return 0;
 	}
diff --git a/include/dt-bindings/pinctrl/pinctrl-snapdragon.h b/include/dt-bindings/pinctrl/pinctrl-snapdragon.h
deleted file mode 100644
index 615affb6f2..0000000000
--- a/include/dt-bindings/pinctrl/pinctrl-snapdragon.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * This header provides constants for Qualcomm Snapdragon pinctrl bindings.
- *
- * (C) Copyright 2018 Ramon Fried <ramon.fried@gmail.com>
- *
- */
-
-#ifndef _DT_BINDINGS_PINCTRL_SNAPDRAGON_H
-#define _DT_BINDINGS_PINCTRL_SNAPDRAGON_H
-
-/* GPIO Drive Strength */
-#define DRIVE_STRENGTH_2MA        0
-#define DRIVE_STRENGTH_4MA        1
-#define DRIVE_STRENGTH_6MA        2
-#define DRIVE_STRENGTH_8MA        3
-#define DRIVE_STRENGTH_10MA       4
-#define DRIVE_STRENGTH_12MA       5
-#define DRIVE_STRENGTH_14MA       6
-#define DRIVE_STRENGTH_16MA       7
-
-#endif
-- 
2.34.1


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

* [PATCH 06/14] net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (4 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 05/14] pinctrl-snapdragon: Get rid of custom drive-strength values Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-02-04  0:32   ` Ramon Fried
  2023-01-20  7:17 ` [PATCH 07/14] net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz Sumit Garg
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 drivers/net/dwc_eth_qos.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index afc47b56ff..753a912607 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -774,10 +774,13 @@ static int eqos_start(struct udevice *dev)
 		pr_err("eqos_calibrate_pads() failed: %d", ret);
 		goto err_stop_resets;
 	}
-	rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
 
-	val = (rate / 1000000) - 1;
-	writel(val, &eqos->mac_regs->us_tic_counter);
+	if (eqos->config->ops->eqos_get_tick_clk_rate) {
+		rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
+
+		val = (rate / 1000000) - 1;
+		writel(val, &eqos->mac_regs->us_tic_counter);
+	}
 
 	/*
 	 * if PHY was already connected and configured,
-- 
2.34.1


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

* [PATCH 07/14] net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (5 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 06/14] net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-02-04  0:32   ` Ramon Fried
  2023-01-20  7:17 ` [PATCH 08/14] net: dwc_eth_qos: Add Qcom ethernet driver glue layer Sumit Garg
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

The GMAC controller on QCS404 SoC (support added by upcoming patch) fails
to work with maximum tx/rx_fifo_sz supported by the hardware (16K). So
allow platforms to override FIFO size using corresponding DT node
properties.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 drivers/net/dwc_eth_qos.c | 19 +++++++++++++------
 drivers/net/dwc_eth_qos.h |  1 +
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 753a912607..65b8556be2 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -852,12 +852,19 @@ static int eqos_start(struct udevice *dev)
 	rx_fifo_sz = (val >> EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT) &
 		EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK;
 
-	/*
-	 * r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting.
-	 * r/tqs is encoded as (n / 256) - 1.
-	 */
-	tqs = (128 << tx_fifo_sz) / 256 - 1;
-	rqs = (128 << rx_fifo_sz) / 256 - 1;
+	/* r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting */
+	tx_fifo_sz = 128 << tx_fifo_sz;
+	rx_fifo_sz = 128 << rx_fifo_sz;
+
+	/* Allow platform to override TX/RX fifo size */
+	if (eqos->tx_fifo_sz)
+		tx_fifo_sz = eqos->tx_fifo_sz;
+	if (eqos->rx_fifo_sz)
+		rx_fifo_sz = eqos->rx_fifo_sz;
+
+	/* r/tqs is encoded as (n / 256) - 1 */
+	tqs = tx_fifo_sz / 256 - 1;
+	rqs = rx_fifo_sz / 256 - 1;
 
 	clrsetbits_le32(&eqos->mtl_regs->txq0_operation_mode,
 			EQOS_MTL_TXQ0_OPERATION_MODE_TQS_MASK <<
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index 8fccd6f057..466a792de7 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -276,6 +276,7 @@ struct eqos_priv {
 	bool started;
 	bool reg_access_ok;
 	bool clk_ck_enabled;
+	unsigned int tx_fifo_sz, rx_fifo_sz;
 };
 
 void eqos_inval_desc_generic(void *desc);
-- 
2.34.1


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

* [PATCH 08/14] net: dwc_eth_qos: Add Qcom ethernet driver glue layer
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (6 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 07/14] net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-02-04  0:33   ` Ramon Fried
  2023-01-20  7:17 ` [PATCH 09/14] dts: qcs404-evb: Add ethernet controller node Sumit Garg
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

The Qualcom ETHQOS hardware supports an RGMII macro which needs to be
configured according to following link speeds:
- SPEED_1000
- SPEED_100
- SPEED_10

So add a corresponding glue driver to configure RGMII macro.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 drivers/net/Kconfig            |   7 +
 drivers/net/Makefile           |   1 +
 drivers/net/dwc_eth_qos.c      |   7 +
 drivers/net/dwc_eth_qos.h      |   3 +
 drivers/net/dwc_eth_qos_qcom.c | 612 +++++++++++++++++++++++++++++++++
 5 files changed, 630 insertions(+)
 create mode 100644 drivers/net/dwc_eth_qos_qcom.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 7873538cc2..815e1f9248 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -242,6 +242,13 @@ config DWC_ETH_QOS_TEGRA186
 	  The Synopsys Designware Ethernet QOS IP block with specific
 	  configuration used in NVIDIA's Tegra186 chip.
 
+config DWC_ETH_QOS_QCOM
+	bool "Synopsys DWC Ethernet QOS device support for Qcom SoCs"
+	depends on DWC_ETH_QOS
+	help
+	  The Synopsys Designware Ethernet QOS IP block with specific
+	  configuration used in Qcom QCS404 SoC.
+
 config E1000
 	bool "Intel PRO/1000 Gigabit Ethernet support"
 	depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 5b4e60eea3..b009b10aca 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
 obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o
 obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
 obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
+obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o
 obj-$(CONFIG_E1000) += e1000.o
 obj-$(CONFIG_E1000_SPI) += e1000_spi.o
 obj-$(CONFIG_EEPRO100) += eepro100.o
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 65b8556be2..112deb546d 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -1712,6 +1712,13 @@ static const struct udevice_id eqos_ids[] = {
 	},
 #endif
 
+#if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM)
+	{
+		.compatible = "qcom,qcs404-ethqos",
+		.data = (ulong)&eqos_qcom_config
+	},
+#endif
+
 	{ }
 };
 
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index 466a792de7..fddbe9336c 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -253,6 +253,7 @@ struct eqos_priv {
 	struct eqos_mtl_regs *mtl_regs;
 	struct eqos_dma_regs *dma_regs;
 	struct eqos_tegra186_regs *tegra186_regs;
+	void *eqos_qcom_rgmii_regs;
 	struct reset_ctl reset_ctl;
 	struct gpio_desc phy_reset_gpio;
 	struct clk clk_master_bus;
@@ -277,6 +278,7 @@ struct eqos_priv {
 	bool reg_access_ok;
 	bool clk_ck_enabled;
 	unsigned int tx_fifo_sz, rx_fifo_sz;
+	u32 reset_delays[3];
 };
 
 void eqos_inval_desc_generic(void *desc);
@@ -286,3 +288,4 @@ void eqos_flush_buffer_generic(void *buf, size_t size);
 int eqos_null_ops(struct udevice *dev);
 
 extern struct eqos_config eqos_imx_config;
+extern struct eqos_config eqos_qcom_config;
diff --git a/drivers/net/dwc_eth_qos_qcom.c b/drivers/net/dwc_eth_qos_qcom.c
new file mode 100644
index 0000000000..df83f1c5f9
--- /dev/null
+++ b/drivers/net/dwc_eth_qos_qcom.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2022-2023 Sumit Garg <sumit.garg@linaro.org>
+ *
+ * Qcom DWMAC specific glue layer
+ */
+
+#include <common.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <phy.h>
+#include <reset.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#include "dwc_eth_qos.h"
+
+/* RGMII_IO_MACRO_CONFIG fields */
+#define RGMII_CONFIG_FUNC_CLK_EN		BIT(30)
+#define RGMII_CONFIG_POS_NEG_DATA_SEL		BIT(23)
+#define RGMII_CONFIG_GPIO_CFG_RX_INT		GENMASK(21, 20)
+#define RGMII_CONFIG_GPIO_CFG_TX_INT		GENMASK(19, 17)
+#define RGMII_CONFIG_MAX_SPD_PRG_9		GENMASK(16, 8)
+#define RGMII_CONFIG_MAX_SPD_PRG_2		GENMASK(7, 6)
+#define RGMII_CONFIG_INTF_SEL			GENMASK(5, 4)
+#define RGMII_CONFIG_BYPASS_TX_ID_EN		BIT(3)
+#define RGMII_CONFIG_LOOPBACK_EN		BIT(2)
+#define RGMII_CONFIG_PROG_SWAP			BIT(1)
+#define RGMII_CONFIG_DDR_MODE			BIT(0)
+
+/* SDCC_HC_REG_DLL_CONFIG fields */
+#define SDCC_DLL_CONFIG_DLL_RST			BIT(30)
+#define SDCC_DLL_CONFIG_PDN			BIT(29)
+#define SDCC_DLL_CONFIG_MCLK_FREQ		GENMASK(26, 24)
+#define SDCC_DLL_CONFIG_CDR_SELEXT		GENMASK(23, 20)
+#define SDCC_DLL_CONFIG_CDR_EXT_EN		BIT(19)
+#define SDCC_DLL_CONFIG_CK_OUT_EN		BIT(18)
+#define SDCC_DLL_CONFIG_CDR_EN			BIT(17)
+#define SDCC_DLL_CONFIG_DLL_EN			BIT(16)
+#define SDCC_DLL_MCLK_GATING_EN			BIT(5)
+#define SDCC_DLL_CDR_FINE_PHASE			GENMASK(3, 2)
+
+/* SDCC_HC_REG_DDR_CONFIG fields */
+#define SDCC_DDR_CONFIG_PRG_DLY_EN		BIT(31)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY	GENMASK(26, 21)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE	GENMASK(29, 27)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN	BIT(30)
+#define SDCC_DDR_CONFIG_PRG_RCLK_DLY		GENMASK(8, 0)
+
+/* SDCC_HC_REG_DLL_CONFIG2 fields */
+#define SDCC_DLL_CONFIG2_DLL_CLOCK_DIS		BIT(21)
+#define SDCC_DLL_CONFIG2_MCLK_FREQ_CALC		GENMASK(17, 10)
+#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL	GENMASK(3, 2)
+#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW	BIT(1)
+#define SDCC_DLL_CONFIG2_DDR_CAL_EN		BIT(0)
+
+/* SDC4_STATUS bits */
+#define SDC4_STATUS_DLL_LOCK			BIT(7)
+
+/* RGMII_IO_MACRO_CONFIG2 fields */
+#define RGMII_CONFIG2_RSVD_CONFIG15		GENMASK(31, 17)
+#define RGMII_CONFIG2_RGMII_CLK_SEL_CFG		BIT(16)
+#define RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN	BIT(13)
+#define RGMII_CONFIG2_CLK_DIVIDE_SEL		BIT(12)
+#define RGMII_CONFIG2_RX_PROG_SWAP		BIT(7)
+#define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL	BIT(6)
+#define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN	BIT(5)
+
+struct dwmac_rgmii_regs {
+	u32 io_macro_config;		/* 0x00 */
+	u32 sdcc_hc_dll_config;		/* 0x04 */
+	u32 reserved_1;			/* 0x08 */
+	u32 sdcc_hc_ddr_config;		/* 0x0c */
+	u32 sdcc_hc_dll_config2;	/* 0x10 */
+	u32 sdc4_status;		/* 0x14 */
+	u32 sdcc_usr_ctl;		/* 0x18 */
+	u32 io_macro_config2;		/* 0x1c */
+	u32 io_macro_debug1;		/* 0x20 */
+	u32 reserved_2;			/* 0x24 */
+	u32 emac_sys_low_power_dbg;	/* 0x28 */
+	u32 reserved_3[53];		/* upto 0x100 */
+};
+
+static struct dwmac_rgmii_regs emac_v2_3_0_por = {
+	.io_macro_config = 0x00C01343,
+	.sdcc_hc_dll_config = 0x2004642C,
+	.sdcc_hc_ddr_config = 0x00000000,
+	.sdcc_hc_dll_config2 = 0x00200000,
+	.sdcc_usr_ctl = 0x00010800,
+	.io_macro_config2 = 0x00002060
+};
+
+static void ethqos_set_func_clk_en(struct dwmac_rgmii_regs *regs)
+{
+	setbits_le32(&regs->io_macro_config, RGMII_CONFIG_FUNC_CLK_EN);
+}
+
+static int ethqos_dll_configure(struct udevice *dev,
+				struct dwmac_rgmii_regs *regs)
+{
+	unsigned int val;
+	int retry = 1000;
+
+	/* Set CDR_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN);
+
+	/* Set CDR_EXT_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN);
+
+	/* Clear CK_OUT_EN */
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
+
+	/* Set DLL_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
+
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN);
+
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE);
+
+	/* Wait for CK_OUT_EN clear */
+	do {
+		val = readl(&regs->sdcc_hc_dll_config);
+		val &= SDCC_DLL_CONFIG_CK_OUT_EN;
+		if (!val)
+			break;
+		mdelay(1);
+		retry--;
+	} while (retry > 0);
+	if (!retry)
+		dev_err(dev, "Clear CK_OUT_EN timedout\n");
+
+	/* Set CK_OUT_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
+
+	/* Wait for CK_OUT_EN set */
+	retry = 1000;
+	do {
+		val = readl(&regs->sdcc_hc_dll_config);
+		val &= SDCC_DLL_CONFIG_CK_OUT_EN;
+		if (val)
+			break;
+		mdelay(1);
+		retry--;
+	} while (retry > 0);
+	if (!retry)
+		dev_err(dev, "Set CK_OUT_EN timedout\n");
+
+	/* Set DDR_CAL_EN */
+	setbits_le32(&regs->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN);
+
+	clrbits_le32(&regs->sdcc_hc_dll_config2,
+		     SDCC_DLL_CONFIG2_DLL_CLOCK_DIS);
+
+	clrsetbits_le32(&regs->sdcc_hc_dll_config2,
+			SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10);
+
+	clrsetbits_le32(&regs->sdcc_hc_dll_config2,
+			SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2));
+
+	setbits_le32(&regs->sdcc_hc_dll_config2,
+		     SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW);
+
+	return 0;
+}
+
+static int ethqos_rgmii_macro_init(struct udevice *dev,
+				   struct dwmac_rgmii_regs *regs,
+				   unsigned long speed)
+{
+	/* Disable loopback mode */
+	clrbits_le32(&regs->io_macro_config2,
+		     RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN);
+
+	/* Select RGMII, write 0 to interface select */
+	clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_INTF_SEL);
+
+	switch (speed) {
+	case SPEED_1000:
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
+		clrbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_BYPASS_TX_ID_EN);
+		setbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_POS_NEG_DATA_SEL);
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
+
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
+		setbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RSVD_CONFIG15);
+		setbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RX_PROG_SWAP);
+
+		/* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */
+		clrsetbits_le32(&regs->sdcc_hc_ddr_config,
+				SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57);
+		setbits_le32(&regs->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN);
+
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
+		break;
+
+	case SPEED_100:
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
+		setbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_BYPASS_TX_ID_EN);
+		clrbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_POS_NEG_DATA_SEL);
+		clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
+		clrsetbits_le32(&regs->io_macro_config,
+				RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6));
+
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
+		setbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RSVD_CONFIG15);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RX_PROG_SWAP);
+
+		/* Write 0x5 to PRG_RCLK_DLY_CODE */
+		clrsetbits_le32(&regs->sdcc_hc_ddr_config,
+				SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
+				(BIT(29) | BIT(27)));
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
+
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
+		break;
+
+	case SPEED_10:
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
+		setbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_BYPASS_TX_ID_EN);
+		clrbits_le32(&regs->io_macro_config,
+			     RGMII_CONFIG_POS_NEG_DATA_SEL);
+		clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
+		clrsetbits_le32(&regs->io_macro_config,
+				RGMII_CONFIG_MAX_SPD_PRG_9,
+				BIT(12) | GENMASK(9, 8));
+
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RSVD_CONFIG15);
+		clrbits_le32(&regs->io_macro_config2,
+			     RGMII_CONFIG2_RX_PROG_SWAP);
+
+		/* Write 0x5 to PRG_RCLK_DLY_CODE */
+		clrsetbits_le32(&regs->sdcc_hc_ddr_config,
+				SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
+				(BIT(29) | BIT(27)));
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
+		setbits_le32(&regs->sdcc_hc_ddr_config,
+			     SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
+
+		setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
+		break;
+
+	default:
+		dev_err(dev, "Invalid speed %ld\n", speed);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ethqos_configure(struct udevice *dev,
+			    struct dwmac_rgmii_regs *regs,
+			    unsigned long speed)
+{
+	unsigned int retry = 1000;
+
+	/* Reset to POR values and enable clk */
+	writel(emac_v2_3_0_por.io_macro_config, &regs->io_macro_config);
+	writel(emac_v2_3_0_por.sdcc_hc_dll_config, &regs->sdcc_hc_dll_config);
+	writel(emac_v2_3_0_por.sdcc_hc_ddr_config, &regs->sdcc_hc_ddr_config);
+	writel(emac_v2_3_0_por.sdcc_hc_dll_config2, &regs->sdcc_hc_dll_config2);
+	writel(emac_v2_3_0_por.sdcc_usr_ctl, &regs->sdcc_usr_ctl);
+	writel(emac_v2_3_0_por.io_macro_config2, &regs->io_macro_config2);
+
+	ethqos_set_func_clk_en(regs);
+
+	/* Initialize the DLL first */
+
+	/* Set DLL_RST */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
+
+	/* Set PDN */
+	setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
+
+	/* Clear DLL_RST */
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
+
+	/* Clear PDN */
+	clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
+
+	if (speed == SPEED_1000) {
+		/* Set DLL_EN */
+		setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
+
+		/* Set CK_OUT_EN */
+		setbits_le32(&regs->sdcc_hc_dll_config,
+			     SDCC_DLL_CONFIG_CK_OUT_EN);
+
+		/* Set USR_CTL bit 26 with mask of 3 bits */
+		clrsetbits_le32(&regs->sdcc_usr_ctl, GENMASK(26, 24), BIT(26));
+
+		/* wait for DLL LOCK */
+		do {
+			mdelay(1);
+			if (readl(&regs->sdc4_status) & SDC4_STATUS_DLL_LOCK)
+				break;
+			retry--;
+		} while (retry > 0);
+		if (!retry)
+			dev_err(dev, "Timeout while waiting for DLL lock\n");
+
+		ethqos_dll_configure(dev, regs);
+	}
+
+	ethqos_rgmii_macro_init(dev, regs, speed);
+
+	return 0;
+}
+
+static void ethqos_rgmii_dump(struct udevice *dev,
+			      struct dwmac_rgmii_regs *regs)
+{
+	dev_dbg(dev, "Rgmii register dump\n");
+	dev_dbg(dev, "RGMII_IO_MACRO_CONFIG: %08x\n",
+		readl(&regs->io_macro_config));
+	dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n",
+		readl(&regs->sdcc_hc_dll_config));
+	dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n",
+		readl(&regs->sdcc_hc_ddr_config));
+	dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n",
+		readl(&regs->sdcc_hc_dll_config2));
+	dev_dbg(dev, "SDC4_STATUS: %08x\n",
+		readl(&regs->sdc4_status));
+	dev_dbg(dev, "SDCC_USR_CTL: %08x\n",
+		readl(&regs->sdcc_usr_ctl));
+	dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n",
+		readl(&regs->io_macro_config2));
+	dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n",
+		readl(&regs->io_macro_debug1));
+	dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n",
+		readl(&regs->emac_sys_low_power_dbg));
+}
+
+static int qcom_eqos_rgmii_set_speed(struct udevice *dev,
+				     void *rgmii_regs,
+				     unsigned long speed)
+{
+	int ret;
+
+	ethqos_rgmii_dump(dev, rgmii_regs);
+
+	ret = ethqos_configure(dev, rgmii_regs, speed);
+	if (ret)
+		return ret;
+
+	ethqos_rgmii_dump(dev, rgmii_regs);
+
+	return 0;
+}
+
+static int qcom_eqos_rgmii_reset(struct udevice *dev, void *rgmii_regs)
+{
+	ethqos_set_func_clk_en(rgmii_regs);
+
+	return 0;
+}
+
+static int eqos_start_clks_qcom(struct udevice *dev)
+{
+	if (IS_ENABLED(CONFIG_CLK)) {
+		struct clk_bulk clocks;
+		int ret;
+
+		ret = clk_get_bulk(dev, &clocks);
+		if (ret)
+			return ret;
+
+		ret = clk_enable_bulk(&clocks);
+		if (ret)
+			return ret;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static int eqos_stop_clks_qcom(struct udevice *dev)
+{
+	if (IS_ENABLED(CONFIG_CLK)) {
+		struct clk_bulk clocks;
+		int ret;
+
+		ret = clk_get_bulk(dev, &clocks);
+		if (ret)
+			return ret;
+
+		ret = clk_disable_bulk(&clocks);
+		if (ret)
+			return ret;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static int eqos_start_resets_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	int ret;
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	if (!eqos->phy) {
+		ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
+		if (ret < 0) {
+			pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d", ret);
+			return ret;
+		}
+
+		udelay(eqos->reset_delays[0]);
+
+		ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1);
+		if (ret < 0) {
+			pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
+			return ret;
+		}
+
+		udelay(eqos->reset_delays[1]);
+
+		ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
+		if (ret < 0) {
+			pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
+			return ret;
+		}
+
+		udelay(eqos->reset_delays[2]);
+	}
+
+	ret = reset_deassert(&eqos->reset_ctl);
+	if (ret < 0) {
+		pr_err("reset_deassert() failed: %d", ret);
+		return ret;
+	}
+
+	ret = qcom_eqos_rgmii_reset(dev, eqos->eqos_qcom_rgmii_regs);
+	if (ret < 0) {
+		pr_err("qcom rgmii_reset failed: %d", ret);
+		return ret;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+/* Clock rates */
+#define RGMII_1000_NOM_CLK_FREQ			(250 * 1000 * 1000UL)
+#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ	 (50 * 1000 * 1000UL)
+#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ	  (5 * 1000 * 1000UL)
+
+static int eqos_set_tx_clk_speed_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	ulong rate;
+	int ret;
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	switch (eqos->phy->speed) {
+	case SPEED_1000:
+		rate = RGMII_1000_NOM_CLK_FREQ;
+		break;
+	case SPEED_100:
+		rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ;
+		break;
+	case SPEED_10:
+		rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ;
+		break;
+	default:
+		pr_err("invalid speed %d", eqos->phy->speed);
+		return -EINVAL;
+	}
+
+	ret = clk_set_rate(&eqos->clk_tx, rate);
+	if (ret < 0) {
+		pr_err("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret);
+		return ret;
+	}
+
+	ret = qcom_eqos_rgmii_set_speed(dev, eqos->eqos_qcom_rgmii_regs,
+					eqos->phy->speed);
+	if (ret < 0) {
+		pr_err("qcom set_speed: %d, failed: %d", eqos->phy->speed, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int eqos_probe_resources_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	phy_interface_t interface;
+	int reset_flags = GPIOD_IS_OUT;
+	int ret;
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	interface = eqos->config->interface(dev);
+
+	if (interface == PHY_INTERFACE_MODE_NA) {
+		pr_err("Invalid PHY interface\n");
+		return -EINVAL;
+	}
+
+	eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0);
+
+	eqos->tx_fifo_sz = dev_read_u32_default(dev, "tx-fifo-depth", 0);
+	eqos->rx_fifo_sz = dev_read_u32_default(dev, "rx-fifo-depth", 0);
+
+	ret = reset_get_by_name(dev, "emac", &eqos->reset_ctl);
+	if (ret) {
+		pr_err("reset_get_by_name(rst) failed: %d", ret);
+		return ret;
+	}
+
+	if (dev_read_bool(dev, "snps,reset-active-low"))
+		reset_flags |= GPIOD_ACTIVE_LOW;
+
+	ret = gpio_request_by_name(dev, "snps,reset-gpio", 0,
+				   &eqos->phy_reset_gpio, reset_flags);
+	if (ret == 0) {
+		ret = dev_read_u32_array(dev, "snps,reset-delays-us",
+					 eqos->reset_delays, 3);
+	} else if (ret == -ENOENT) {
+		ret = 0;
+	}
+
+	eqos->eqos_qcom_rgmii_regs = (void *)dev_read_addr_name(dev, "rgmii");
+	if ((fdt_addr_t)eqos->eqos_qcom_rgmii_regs == FDT_ADDR_T_NONE) {
+		pr_err("Invalid RGMII address\n");
+		return -EINVAL;
+	}
+
+	ret = clk_get_by_name(dev, "rgmii", &eqos->clk_tx);
+	if (ret) {
+		pr_err("clk_get_by_name(tx) failed: %d", ret);
+		return -EINVAL;
+	}
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static int eqos_remove_resources_qcom(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+
+	debug("%s(dev=%p):\n", __func__, dev);
+
+	clk_free(&eqos->clk_tx);
+	dm_gpio_free(dev, &eqos->phy_reset_gpio);
+	reset_free(&eqos->reset_ctl);
+
+	debug("%s: OK\n", __func__);
+	return 0;
+}
+
+static struct eqos_ops eqos_qcom_ops = {
+	.eqos_inval_desc = eqos_inval_desc_generic,
+	.eqos_flush_desc = eqos_flush_desc_generic,
+	.eqos_inval_buffer = eqos_inval_buffer_generic,
+	.eqos_flush_buffer = eqos_flush_buffer_generic,
+	.eqos_probe_resources = eqos_probe_resources_qcom,
+	.eqos_remove_resources = eqos_remove_resources_qcom,
+	.eqos_stop_resets = eqos_null_ops,
+	.eqos_start_resets = eqos_start_resets_qcom,
+	.eqos_stop_clks = eqos_stop_clks_qcom,
+	.eqos_start_clks = eqos_start_clks_qcom,
+	.eqos_calibrate_pads = eqos_null_ops,
+	.eqos_disable_calibration = eqos_null_ops,
+	.eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_qcom,
+	.eqos_get_enetaddr = eqos_null_ops,
+};
+
+struct eqos_config __maybe_unused eqos_qcom_config = {
+	.reg_access_always_ok = false,
+	.mdio_wait = 10,
+	.swr_wait = 50,
+	.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+	.config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+	.axi_bus_width = EQOS_AXI_WIDTH_64,
+	.interface = dev_read_phy_mode,
+	.ops = &eqos_qcom_ops
+};
-- 
2.34.1


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

* [PATCH 09/14] dts: qcs404-evb: Add ethernet controller node
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (7 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 08/14] net: dwc_eth_qos: Add Qcom ethernet driver glue layer Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 10/14] clock-snapdragon: Add clk_rcg_set_rate() with mnd_width=0 Sumit Garg
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/dts/qcs404-evb.dts | 98 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 97 insertions(+), 1 deletion(-)

diff --git a/arch/arm/dts/qcs404-evb.dts b/arch/arm/dts/qcs404-evb.dts
index cc70afa4c8..2de0e7537b 100644
--- a/arch/arm/dts/qcs404-evb.dts
+++ b/arch/arm/dts/qcs404-evb.dts
@@ -36,7 +36,7 @@
 		ranges = <0x0 0x0 0x0 0xffffffff>;
 		compatible = "simple-bus";
 
-		pinctrl_north@1300000 {
+		soc_gpios: pinctrl_north@1300000 {
 			compatible = "qcom,qcs404-pinctrl";
 			reg = <0x1300000 0x200000>;
 			gpio-controller;
@@ -48,6 +48,61 @@
 				pins = "GPIO_17", "GPIO_18";
 				function = "blsp_uart2";
 			};
+
+			ethernet_defaults: ethernet-defaults {
+				int {
+					pins = "GPIO_61";
+					function = "rgmii_int";
+					bias-disable;
+					drive-strength = <2>;
+				};
+				mdc {
+					pins = "GPIO_76";
+					function = "rgmii_mdc";
+					bias-pull-up;
+				};
+				mdio {
+					pins = "GPIO_75";
+					function = "rgmii_mdio";
+					bias-pull-up;
+				};
+				tx {
+					pins = "GPIO_67", "GPIO_66", "GPIO_65", "GPIO_64";
+					function = "rgmii_tx";
+					bias-pull-up;
+					drive-strength = <16>;
+				};
+				rx {
+					pins = "GPIO_73", "GPIO_72", "GPIO_71", "GPIO_70";
+					function = "rgmii_rx";
+					bias-disable;
+					drive-strength = <2>;
+				};
+				tx-ctl {
+					pins = "GPIO_68";
+					function = "rgmii_ctl";
+					bias-pull-up;
+					drive-strength = <16>;
+				};
+				rx-ctl {
+					pins = "GPIO_74";
+					function = "rgmii_ctl";
+					bias-disable;
+					drive-strength = <2>;
+				};
+				tx-ck {
+					pins = "GPIO_63";
+					function = "rgmii_ck";
+					bias-pull-up;
+					drive-strength = <16>;
+				};
+				rx-ck {
+					pins = "GPIO_69";
+					function = "rgmii_ck";
+					bias-disable;
+					drive-strength = <2>;
+				};
+			};
 		};
 
 		gcc: clock-controller@1800000 {
@@ -172,6 +227,47 @@
 			};
 		};
 
+		ethernet: ethernet@7a80000 {
+			compatible = "qcom,qcs404-ethqos";
+			reg = <0x07a80000 0x10000>,
+				<0x07a96000 0x100>;
+			reg-names = "stmmaceth", "rgmii";
+			clock-names = "stmmaceth", "pclk", "ptp_ref", "rgmii";
+			clocks = <&gcc GCC_ETH_AXI_CLK>,
+				 <&gcc GCC_ETH_SLAVE_AHB_CLK>,
+				 <&gcc GCC_ETH_PTP_CLK>,
+				 <&gcc GCC_ETH_RGMII_CLK>;
+
+			resets = <&reset GCC_EMAC_BCR>;
+			reset-names = "emac";
+
+			snps,tso;
+			rx-fifo-depth = <4096>;
+			tx-fifo-depth = <4096>;
+
+			snps,reset-gpio = <&soc_gpios 60 GPIO_ACTIVE_LOW>;
+			snps,reset-active-low;
+			snps,reset-delays-us = <0 10000 10000>;
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&ethernet_defaults>;
+
+			phy-handle = <&phy1>;
+			phy-mode = "rgmii";
+			max-speed = <1000>;
+
+			mdio {
+				#address-cells = <0x1>;
+				#size-cells = <0x0>;
+				compatible = "snps,dwmac-mdio";
+				phy1: phy@3 {
+					compatible = "ethernet-phy-ieee802.3-c22";
+					device_type = "ethernet-phy";
+					reg = <0x3>;
+				};
+			};
+		};
+
 		spmi@200f000 {
 			compatible = "qcom,spmi-pmic-arb";
 			reg = <0x200f000 0x1000
-- 
2.34.1


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

* [PATCH 10/14] clock-snapdragon: Add clk_rcg_set_rate() with mnd_width=0
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (8 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 09/14] dts: qcs404-evb: Add ethernet controller node Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 11/14] clocks: qcs404: Add support for I2C clocks Sumit Garg
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Add clk_rcg_set_rate() which allows to configure clocks without programming
MND values. This is required for configuring I2C clocks on QCS404.

Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/mach-snapdragon/clock-snapdragon.c | 24 +++++++++++++++++++++
 arch/arm/mach-snapdragon/clock-snapdragon.h |  2 ++
 2 files changed, 26 insertions(+)

diff --git a/arch/arm/mach-snapdragon/clock-snapdragon.c b/arch/arm/mach-snapdragon/clock-snapdragon.c
index fda7098274..0ac45dce9a 100644
--- a/arch/arm/mach-snapdragon/clock-snapdragon.c
+++ b/arch/arm/mach-snapdragon/clock-snapdragon.c
@@ -111,6 +111,30 @@ void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
 	clk_bcr_update(base + regs->cmd_rcgr);
 }
 
+/* root set rate for clocks with half integer and mnd_width=0 */
+void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
+		      int source)
+{
+	u32 cfg;
+
+	/* setup src select and divider */
+	cfg  = readl(base + regs->cfg_rcgr);
+	cfg &= ~CFG_MASK;
+	cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
+
+	/*
+	 * Set the divider; HW permits fraction dividers (+0.5), but
+	 * for simplicity, we will support integers only
+	 */
+	if (div)
+		cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
+
+	writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
+
+	/* Inform h/w to start using the new config. */
+	clk_bcr_update(base + regs->cmd_rcgr);
+}
+
 static int msm_clk_probe(struct udevice *dev)
 {
 	struct msm_clk_priv *priv = dev_get_priv(dev);
diff --git a/arch/arm/mach-snapdragon/clock-snapdragon.h b/arch/arm/mach-snapdragon/clock-snapdragon.h
index 2ac53b538d..c90bbefa58 100644
--- a/arch/arm/mach-snapdragon/clock-snapdragon.h
+++ b/arch/arm/mach-snapdragon/clock-snapdragon.h
@@ -42,5 +42,7 @@ void clk_enable_cbc(phys_addr_t cbcr);
 void clk_enable_vote_clk(phys_addr_t base, const struct vote_clk *vclk);
 void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
 			  int div, int m, int n, int source);
+void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
+		      int source);
 
 #endif
-- 
2.34.1


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

* [PATCH 11/14] clocks: qcs404: Add support for I2C clocks
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (9 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 10/14] clock-snapdragon: Add clk_rcg_set_rate() with mnd_width=0 Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 12/14] pinctrl: qcs404: Enable I2C pinmux options Sumit Garg
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/mach-snapdragon/clock-qcs404.c       | 58 +++++++++++++++++++
 .../include/mach/sysmap-qcs404.h              | 17 ++++++
 2 files changed, 75 insertions(+)

diff --git a/arch/arm/mach-snapdragon/clock-qcs404.c b/arch/arm/mach-snapdragon/clock-qcs404.c
index b8f5691aae..3357b54c30 100644
--- a/arch/arm/mach-snapdragon/clock-qcs404.c
+++ b/arch/arm/mach-snapdragon/clock-qcs404.c
@@ -81,6 +81,36 @@ static const struct bcr_regs emac_ptp_regs = {
 	.D = EMAC_D,
 };
 
+static const struct bcr_regs blsp1_qup0_i2c_apps_regs = {
+	.cmd_rcgr = BLSP1_QUP0_I2C_APPS_CMD_RCGR,
+	.cfg_rcgr = BLSP1_QUP0_I2C_APPS_CFG_RCGR,
+	/* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup1_i2c_apps_regs = {
+	.cmd_rcgr = BLSP1_QUP1_I2C_APPS_CMD_RCGR,
+	.cfg_rcgr = BLSP1_QUP1_I2C_APPS_CFG_RCGR,
+	/* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup2_i2c_apps_regs = {
+	.cmd_rcgr = BLSP1_QUP2_I2C_APPS_CMD_RCGR,
+	.cfg_rcgr = BLSP1_QUP2_I2C_APPS_CFG_RCGR,
+	/* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup3_i2c_apps_regs = {
+	.cmd_rcgr = BLSP1_QUP3_I2C_APPS_CMD_RCGR,
+	.cfg_rcgr = BLSP1_QUP3_I2C_APPS_CFG_RCGR,
+	/* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup4_i2c_apps_regs = {
+	.cmd_rcgr = BLSP1_QUP4_I2C_APPS_CMD_RCGR,
+	.cfg_rcgr = BLSP1_QUP4_I2C_APPS_CFG_RCGR,
+	/* mnd_width = 0 */
+};
+
 ulong msm_set_rate(struct clk *clk, ulong rate)
 {
 	struct msm_clk_priv *priv = dev_get_priv(clk->dev);
@@ -171,6 +201,34 @@ int msm_enable(struct clk *clk)
 	case GCC_ETH_AXI_CLK:
 		clk_enable_cbc(priv->base + ETH_AXI_CBCR);
 		break;
+	case GCC_BLSP1_AHB_CLK:
+		clk_enable_vote_clk(priv->base, &gcc_blsp1_ahb_clk);
+		break;
+	case GCC_BLSP1_QUP0_I2C_APPS_CLK:
+		clk_enable_cbc(priv->base + BLSP1_QUP0_I2C_APPS_CBCR);
+		clk_rcg_set_rate(priv->base, &blsp1_qup0_i2c_apps_regs, 0,
+				 CFG_CLK_SRC_CXO);
+		break;
+	case GCC_BLSP1_QUP1_I2C_APPS_CLK:
+		clk_enable_cbc(priv->base + BLSP1_QUP1_I2C_APPS_CBCR);
+		clk_rcg_set_rate(priv->base, &blsp1_qup1_i2c_apps_regs, 0,
+				 CFG_CLK_SRC_CXO);
+		break;
+	case GCC_BLSP1_QUP2_I2C_APPS_CLK:
+		clk_enable_cbc(priv->base + BLSP1_QUP2_I2C_APPS_CBCR);
+		clk_rcg_set_rate(priv->base, &blsp1_qup2_i2c_apps_regs, 0,
+				 CFG_CLK_SRC_CXO);
+		break;
+	case GCC_BLSP1_QUP3_I2C_APPS_CLK:
+		clk_enable_cbc(priv->base + BLSP1_QUP3_I2C_APPS_CBCR);
+		clk_rcg_set_rate(priv->base, &blsp1_qup3_i2c_apps_regs, 0,
+				 CFG_CLK_SRC_CXO);
+		break;
+	case GCC_BLSP1_QUP4_I2C_APPS_CLK:
+		clk_enable_cbc(priv->base + BLSP1_QUP4_I2C_APPS_CBCR);
+		clk_rcg_set_rate(priv->base, &blsp1_qup4_i2c_apps_regs, 0,
+				 CFG_CLK_SRC_CXO);
+		break;
 	default:
 		return 0;
 	}
diff --git a/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h b/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h
index 8920c4ee8f..5768fb1377 100644
--- a/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h
+++ b/arch/arm/mach-snapdragon/include/mach/sysmap-qcs404.h
@@ -28,6 +28,23 @@
 #define BLSP1_UART2_APPS_N		(0x3040)
 #define BLSP1_UART2_APPS_D		(0x3044)
 
+/* I2C controller clock control registerss */
+#define BLSP1_QUP0_I2C_APPS_CBCR	(0x6028)
+#define BLSP1_QUP0_I2C_APPS_CMD_RCGR	(0x602C)
+#define BLSP1_QUP0_I2C_APPS_CFG_RCGR	(0x6030)
+#define BLSP1_QUP1_I2C_APPS_CBCR	(0x2008)
+#define BLSP1_QUP1_I2C_APPS_CMD_RCGR	(0x200C)
+#define BLSP1_QUP1_I2C_APPS_CFG_RCGR	(0x2010)
+#define BLSP1_QUP2_I2C_APPS_CBCR	(0x3010)
+#define BLSP1_QUP2_I2C_APPS_CMD_RCGR	(0x3000)
+#define BLSP1_QUP2_I2C_APPS_CFG_RCGR	(0x3004)
+#define BLSP1_QUP3_I2C_APPS_CBCR	(0x4020)
+#define BLSP1_QUP3_I2C_APPS_CMD_RCGR	(0x4000)
+#define BLSP1_QUP3_I2C_APPS_CFG_RCGR	(0x4004)
+#define BLSP1_QUP4_I2C_APPS_CBCR	(0x5020)
+#define BLSP1_QUP4_I2C_APPS_CMD_RCGR	(0x5000)
+#define BLSP1_QUP4_I2C_APPS_CFG_RCGR	(0x5004)
+
 /* SD controller clock control registers */
 #define SDCC_BCR(n)			(((n) * 0x1000) + 0x41000)
 #define SDCC_CMD_RCGR(n)		(((n) * 0x1000) + 0x41004)
-- 
2.34.1


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

* [PATCH 12/14] pinctrl: qcs404: Enable I2C pinmux options
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (10 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 11/14] clocks: qcs404: Add support for I2C clocks Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  7:17 ` [PATCH 13/14] i2c: Add support for Qualcomm I2C driver Sumit Garg
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/mach-snapdragon/pinctrl-qcs404.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/mach-snapdragon/pinctrl-qcs404.c b/arch/arm/mach-snapdragon/pinctrl-qcs404.c
index 5a7fbfd441..a6e53c4412 100644
--- a/arch/arm/mach-snapdragon/pinctrl-qcs404.c
+++ b/arch/arm/mach-snapdragon/pinctrl-qcs404.c
@@ -29,6 +29,12 @@ static const struct pinctrl_function msm_pinctrl_functions[] = {
 	{"rgmii_rx", 1},
 	{"rgmii_mdio", 1},
 	{"rgmii_mdc", 1},
+	{"blsp_i2c0", 3},
+	{"blsp_i2c1", 2},
+	{"blsp_i2c_sda_a2", 3},
+	{"blsp_i2c_scl_a2", 3},
+	{"blsp_i2c3", 2},
+	{"blsp_i2c4", 1},
 };
 
 static const char *qcs404_get_function_name(struct udevice *dev,
-- 
2.34.1


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

* [PATCH 13/14] i2c: Add support for Qualcomm I2C driver
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (11 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 12/14] pinctrl: qcs404: Enable I2C pinmux options Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-20  8:46   ` Heiko Schocher
  2023-01-20  7:17 ` [PATCH 14/14] dts: qcs404-evb: Add I2C controller nodes Sumit Garg
  2023-01-30 16:27 ` [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
  14 siblings, 1 reply; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Add support for Qualcomm I2C QUP driver which is inspired from
corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c.

Currently this driver only support FIFO polling mode which is sufficient
to support devices like eeprom, rtc etc.

Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 drivers/i2c/Kconfig   |   6 +
 drivers/i2c/Makefile  |   1 +
 drivers/i2c/qup_i2c.c | 592 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 599 insertions(+)
 create mode 100644 drivers/i2c/qup_i2c.c

diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 76e19918aa..2a1d36f91a 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -635,6 +635,12 @@ config SYS_I2C_TEGRA
 	help
 	  Support for NVIDIA I2C controller available in Tegra SoCs.
 
+config SYS_I2C_QUP
+	bool "Qualcomm I2C controller"
+	depends on ARCH_SNAPDRAGON
+	help
+	  Support for Qualcomm I2C controller.
+
 config SYS_I2C_UNIPHIER
 	bool "UniPhier I2C driver"
 	depends on ARCH_UNIPHIER && DM_I2C
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 920aafb91c..f6aa39dd7a 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o
 obj-$(CONFIG_SYS_I2C_SUN8I_RSB) += sun8i_rsb.o
 obj-$(CONFIG_SYS_I2C_SYNQUACER) += synquacer_i2c.o
 obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
+obj-$(CONFIG_SYS_I2C_QUP) += qup_i2c.o
 obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o
 obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o
 obj-$(CONFIG_SYS_I2C_VERSATILE) += i2c-versatile.o
diff --git a/drivers/i2c/qup_i2c.c b/drivers/i2c/qup_i2c.c
new file mode 100644
index 0000000000..6787fc75fc
--- /dev/null
+++ b/drivers/i2c/qup_i2c.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2022-2023, Sumit Garg <sumit.garg@linaro.org>
+ *
+ * Inspired by corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c
+ */
+
+#include <init.h>
+#include <env.h>
+#include <common.h>
+#include <log.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/compat.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <i2c.h>
+#include <watchdog.h>
+#include <fdtdec.h>
+#include <clk.h>
+#include <reset.h>
+#include <asm/arch/gpio.h>
+#include <cpu_func.h>
+#include <asm/system.h>
+#include <asm/gpio.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+
+/* QUP Registers */
+#define QUP_CONFIG				0x000
+#define QUP_STATE				0x004
+#define QUP_IO_MODE				0x008
+#define QUP_SW_RESET				0x00c
+#define QUP_OPERATIONAL				0x018
+#define QUP_ERROR_FLAGS				0x01c
+#define QUP_ERROR_FLAGS_EN			0x020
+#define QUP_TEST_CTRL				0x024 /* NOT USED */
+#define QUP_OPERATIONAL_MASK			0x028
+#define QUP_HW_VERSION				0x030
+#define QUP_MX_OUTPUT_CNT			0x100
+#define QUP_OUT_DEBUG				0x108 /* NOT USED */
+#define QUP_OUT_FIFO_CNT			0x10C /* NOT USED */
+#define QUP_OUT_FIFO_BASE			0x110
+#define QUP_MX_WRITE_CNT			0x150
+#define QUP_MX_INPUT_CNT			0x200
+#define QUP_MX_READ_CNT				0x208
+#define QUP_IN_READ_CUR				0x20C /* NOT USED */
+#define QUP_IN_DEBUG				0x210 /* NOT USED */
+#define QUP_IN_FIFO_CNT				0x214 /* NOT USED */
+#define QUP_IN_FIFO_BASE			0x218
+#define QUP_I2C_CLK_CTL				0x400
+#define QUP_I2C_STATUS				0x404
+#define QUP_I2C_MASTER_GEN			0x408
+#define QUP_I2C_MASTER_BUS_CLR			0x40C /* NOT USED */
+
+/* QUP States and reset values */
+#define QUP_RESET_STATE				0
+#define QUP_RUN_STATE				1
+#define QUP_PAUSE_STATE				3
+#define QUP_STATE_MASK				3
+
+#define QUP_STATE_VALID				BIT(2)
+#define QUP_I2C_MAST_GEN			BIT(4)
+#define QUP_I2C_FLUSH				BIT(6)
+
+#define QUP_OPERATIONAL_RESET			0x000ff0
+#define QUP_I2C_STATUS_RESET			0xfffffc
+
+/* QUP OPERATIONAL FLAGS */
+#define QUP_I2C_NACK_FLAG			BIT(3)
+#define QUP_OUT_NOT_EMPTY			BIT(4)
+#define QUP_IN_NOT_EMPTY			BIT(5)
+#define QUP_OUT_FULL				BIT(6)
+#define QUP_OUT_SVC_FLAG			BIT(8)
+#define QUP_IN_SVC_FLAG				BIT(9)
+#define QUP_MX_OUTPUT_DONE			BIT(10)
+#define QUP_MX_INPUT_DONE			BIT(11)
+#define OUT_BLOCK_WRITE_REQ			BIT(12)
+#define IN_BLOCK_READ_REQ			BIT(13)
+
+/* I2C mini core related values */
+#define QUP_NO_OUTPUT				BIT(6)
+#define QUP_NO_INPUT				BIT(7)
+#define QUP_CLOCK_AUTO_GATE			BIT(13)
+#define I2C_MINI_CORE				(2 << 8)
+#define I2C_N_VAL				15
+#define I2C_N_VAL_V2				7
+
+/* Most significant word offset in FIFO port */
+#define QUP_MSW_SHIFT				(I2C_N_VAL + 1)
+
+/* Packing/Unpacking words in FIFOs, and IO modes */
+#define QUP_OUTPUT_BLK_MODE			BIT(10)
+#define QUP_OUTPUT_BAM_MODE			(BIT(10) | BIT(11))
+#define QUP_INPUT_BLK_MODE			BIT(12)
+#define QUP_INPUT_BAM_MODE			(BIT(12) | BIT(13))
+#define QUP_BAM_MODE				(QUP_OUTPUT_BAM_MODE | QUP_INPUT_BAM_MODE)
+#define QUP_BLK_MODE				(QUP_OUTPUT_BLK_MODE | QUP_INPUT_BLK_MODE)
+#define QUP_UNPACK_EN				BIT(14)
+#define QUP_PACK_EN				BIT(15)
+
+#define QUP_REPACK_EN				(QUP_UNPACK_EN | QUP_PACK_EN)
+#define QUP_V2_TAGS_EN				1
+
+#define QUP_OUTPUT_BLOCK_SIZE(x)		(((x) >> 0) & 0x03)
+#define QUP_OUTPUT_FIFO_SIZE(x)			(((x) >> 2) & 0x07)
+#define QUP_INPUT_BLOCK_SIZE(x)			(((x) >> 5) & 0x03)
+#define QUP_INPUT_FIFO_SIZE(x)			(((x) >> 7) & 0x07)
+
+/* QUP v2 tags */
+#define QUP_TAG_V2_START			0x81
+#define QUP_TAG_V2_DATAWR			0x82
+#define QUP_TAG_V2_DATAWR_STOP			0x83
+#define QUP_TAG_V2_DATARD			0x85
+#define QUP_TAG_V2_DATARD_NACK			0x86
+#define QUP_TAG_V2_DATARD_STOP			0x87
+
+/* Status, Error flags */
+#define I2C_STATUS_WR_BUFFER_FULL		BIT(0)
+#define I2C_STATUS_BUS_ERROR			BIT(2)
+#define I2C_STATUS_PACKET_NACKED		BIT(3)
+#define I2C_STATUS_ARB_LOST			BIT(4)
+#define I2C_STATUS_INVALID_WRITE		BIT(5)
+#define I2C_STATUS_FAILED			BIT(6)
+#define I2C_STATUS_BUS_ACTIVE			BIT(8)
+#define I2C_STATUS_BUS_MASTER			BIT(9)
+#define I2C_STATUS_INVALID_TAG			BIT(23)
+#define I2C_STATUS_INVALID_READ_ADDR		BIT(24)
+#define I2C_STATUS_INVALID_READ_SEQ		BIT(25)
+#define I2C_STATUS_I2C_SDA			BIT(26)
+#define I2C_STATUS_I2C_SCL			BIT(27)
+#define I2C_STATUS_ERROR_MASK			0x38000fc
+#define QUP_STATUS_ERROR_FLAGS			0x7c
+
+#define QUP_I2C_MX_CONFIG_DURING_RUN		BIT(31)
+
+/* Minimum transfer timeout for i2c transfers in micro seconds */
+#define TOUT_CNT				2 * 1000 * 1000
+
+/* Default values. Use these if FW query fails */
+#define DEFAULT_CLK_FREQ			I2C_SPEED_STANDARD_RATE
+#define DEFAULT_SRC_CLK				19200000
+
+/*
+ * Max tags length (start, stop and maximum 2 bytes address) for each QUP
+ * data transfer
+ */
+#define QUP_MAX_TAGS_LEN			4
+/* Max data length for each DATARD tags */
+#define RECV_MAX_DATA_LEN			254
+/* TAG length for DATA READ in RX FIFO */
+#define READ_RX_TAGS_LEN			2
+
+struct qup_i2c_priv {
+	phys_addr_t base;
+	struct clk core;
+	struct clk iface;
+	u32 in_fifo_sz;
+	u32 out_fifo_sz;
+	u32 clk_ctl;
+	u32 config_run;
+};
+
+static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
+{
+	return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
+}
+
+static int qup_i2c_poll_state_mask(struct qup_i2c_priv *qup,
+				   u32 req_state, u32 req_mask)
+{
+	int retries = 1;
+	u32 state;
+
+	/*
+	 * State transition takes 3 AHB clocks cycles + 3 I2C master clock
+	 * cycles. So retry once after a 1uS delay.
+	 */
+	do {
+		state = readl(qup->base + QUP_STATE);
+
+		if (state & QUP_STATE_VALID &&
+		    (state & req_mask) == req_state)
+			return 0;
+
+		udelay(1);
+	} while (retries--);
+
+	return -ETIMEDOUT;
+}
+
+static int qup_i2c_poll_state(struct qup_i2c_priv *qup, u32 req_state)
+{
+	return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK);
+}
+
+static int qup_i2c_poll_state_valid(struct qup_i2c_priv *qup)
+{
+	return qup_i2c_poll_state_mask(qup, 0, 0);
+}
+
+static int qup_i2c_poll_state_i2c_master(struct qup_i2c_priv *qup)
+{
+	return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN);
+}
+
+static int qup_i2c_change_state(struct qup_i2c_priv *qup, u32 state)
+{
+	if (qup_i2c_poll_state_valid(qup) != 0)
+		return -EIO;
+
+	writel(state, qup->base + QUP_STATE);
+
+	if (qup_i2c_poll_state(qup, state) != 0)
+		return -EIO;
+	return 0;
+}
+
+/*
+ * Function to check wheather Input or Output FIFO
+ * has data to be serviced
+ */
+static int qup_i2c_check_fifo_status(struct qup_i2c_priv *qup, u32 reg_addr,
+				     u32 flags)
+{
+	unsigned long count = TOUT_CNT;
+	u32 val, status_flag;
+	int ret = 0;
+
+	do {
+		val = readl(qup->base + reg_addr);
+		status_flag = val & flags;
+
+		if (!count) {
+			printf("%s, timeout\n", __func__);
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		count--;
+		udelay(1);
+	} while (!status_flag);
+
+	return ret;
+}
+
+/*
+ * Function to configure Input and Output enable/disable
+ */
+static void qup_i2c_enable_io_config(struct qup_i2c_priv *qup, u32 write_cnt,
+				     u32 read_cnt)
+{
+	u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
+
+	writel(qup->config_run | write_cnt, qup->base + QUP_MX_WRITE_CNT);
+
+	if (read_cnt)
+		writel(qup->config_run | read_cnt, qup->base + QUP_MX_READ_CNT);
+	else
+		qup_config |= QUP_NO_INPUT;
+
+	writel(qup_config, qup->base + QUP_CONFIG);
+}
+
+static unsigned int qup_i2c_read_word(struct qup_i2c_priv *qup)
+{
+	return readl(qup->base + QUP_IN_FIFO_BASE);
+}
+
+static void qup_i2c_write_word(struct qup_i2c_priv *qup, u32 word)
+{
+	writel(word, qup->base + QUP_OUT_FIFO_BASE);
+}
+
+static int qup_i2c_blsp_read(struct qup_i2c_priv *qup, unsigned int addr,
+			     bool last, u8 *buffer, unsigned int bytes)
+{
+	unsigned int i, j, word;
+	int ret = 0;
+
+	/* FIFO mode size limitation, for larger size implement block mode */
+	if (bytes > (qup->in_fifo_sz - READ_RX_TAGS_LEN))
+		return -EINVAL;
+
+	qup_i2c_enable_io_config(qup, QUP_MAX_TAGS_LEN,
+				 bytes + READ_RX_TAGS_LEN);
+
+	if (last)
+		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
+					QUP_TAG_V2_DATARD_STOP << 16 |
+					bytes << 24);
+	else
+		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
+					QUP_TAG_V2_DATARD << 16 | bytes << 24);
+
+	ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+	if (ret)
+		return ret;
+
+	ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG);
+	if (ret)
+		return ret;
+	writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+	ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_IN_SVC_FLAG);
+	if (ret)
+		return ret;
+	writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+	word = qup_i2c_read_word(qup);
+	*(buffer++) = (word >> (8 * READ_RX_TAGS_LEN)) & 0xff;
+	if (bytes > 1)
+		*(buffer++) = (word >> (8 * (READ_RX_TAGS_LEN + 1))) & 0xff;
+
+	for (i = 2; i < bytes; i += 4) {
+		word = qup_i2c_read_word(qup);
+
+		for (j = 0; j < 4; j++) {
+			if ((i + j) == bytes)
+				break;
+			*buffer = (word >> (j * 8)) & 0xff;
+			buffer++;
+		}
+	}
+
+	ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+	return ret;
+}
+
+static int qup_i2c_blsp_write(struct qup_i2c_priv *qup, unsigned int addr,
+			      bool first, bool last, const u8 *buffer,
+			      unsigned int bytes)
+{
+	unsigned int i;
+	u32 word = 0;
+	int ret = 0;
+
+	/* FIFO mode size limitation, for larger size implement block mode */
+	if (bytes > (qup->out_fifo_sz - QUP_MAX_TAGS_LEN))
+		return -EINVAL;
+
+	qup_i2c_enable_io_config(qup, bytes + QUP_MAX_TAGS_LEN, 0);
+
+	if (first) {
+		ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+		if (ret)
+			return ret;
+
+		writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+
+		ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+		if (ret)
+			return ret;
+	}
+
+	if (last)
+		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
+					QUP_TAG_V2_DATAWR_STOP << 16 |
+					bytes << 24);
+	else
+		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
+					QUP_TAG_V2_DATAWR << 16 | bytes << 24);
+
+	for (i = 0; i < bytes; i++) {
+		/* Write the byte of data */
+		word |= *buffer << ((i % 4) * 8);
+		if ((i % 4) == 3) {
+			qup_i2c_write_word(qup, word);
+			word = 0;
+		}
+		buffer++;
+	}
+
+	if ((i % 4) != 0)
+		qup_i2c_write_word(qup, word);
+
+	ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+	if (ret)
+		return ret;
+
+	ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG);
+	if (ret)
+		return ret;
+	writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+	ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+	return ret;
+}
+
+static void qup_i2c_conf_mode_v2(struct qup_i2c_priv *qup)
+{
+	u32 io_mode = QUP_REPACK_EN;
+
+	writel(0, qup->base + QUP_MX_OUTPUT_CNT);
+	writel(0, qup->base + QUP_MX_INPUT_CNT);
+
+	writel(io_mode, qup->base + QUP_IO_MODE);
+}
+
+static int qup_i2c_xfer_v2(struct udevice *bus, struct i2c_msg msgs[], int num)
+{
+	struct qup_i2c_priv *qup = dev_get_priv(bus);
+	int ret, idx = 0;
+	u32 i2c_addr;
+
+	writel(1, qup->base + QUP_SW_RESET);
+	ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
+	if (ret)
+		goto out;
+
+	/* Configure QUP as I2C mini core */
+	writel(I2C_MINI_CORE | I2C_N_VAL_V2 | QUP_NO_INPUT,
+	       qup->base + QUP_CONFIG);
+	writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);
+
+	if (qup_i2c_poll_state_i2c_master(qup)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	qup_i2c_conf_mode_v2(qup);
+
+	for (idx = 0; idx < num; idx++) {
+		struct i2c_msg *m = &msgs[idx];
+
+		qup->config_run = !idx ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
+		i2c_addr = i2c_8bit_addr_from_msg(m);
+
+		if (m->flags & I2C_M_RD)
+			ret = qup_i2c_blsp_read(qup, i2c_addr, idx == (num - 1),
+						m->buf, m->len);
+		else
+			ret = qup_i2c_blsp_write(qup, i2c_addr, idx == 0,
+						 idx == (num - 1), m->buf,
+						 m->len);
+		if (ret)
+			break;
+	}
+out:
+	qup_i2c_change_state(qup, QUP_RESET_STATE);
+	return ret;
+}
+
+static int qup_i2c_enable_clocks(struct udevice *dev, struct qup_i2c_priv *qup)
+{
+	int ret;
+
+	ret = clk_enable(&qup->core);
+	if (ret) {
+		dev_err(dev, "clk_enable failed %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_enable(&qup->iface);
+	if (ret) {
+		dev_err(dev, "clk_enable failed %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qup_i2c_probe(struct udevice *dev)
+{
+	static const int blk_sizes[] = {4, 16, 32};
+	struct qup_i2c_priv *qup = dev_get_priv(dev);
+	u32 io_mode, hw_ver, size, size_idx;
+	int ret;
+
+	qup->base = (phys_addr_t)dev_read_addr_ptr(dev);
+	if (!qup->base)
+		return -EINVAL;
+
+	ret = clk_get_by_name(dev, "core", &qup->core);
+	if (ret) {
+		pr_err("clk_get_by_name(core) failed: %d\n", ret);
+		return ret;
+	}
+	ret = clk_get_by_name(dev, "iface", &qup->iface);
+	if (ret) {
+		pr_err("clk_get_by_name(iface) failed: %d\n", ret);
+		return ret;
+	}
+	qup_i2c_enable_clocks(dev, qup);
+
+	writel(1, qup->base + QUP_SW_RESET);
+	ret = qup_i2c_poll_state_valid(qup);
+	if (ret)
+		return ret;
+
+	hw_ver = readl(qup->base + QUP_HW_VERSION);
+	dev_dbg(dev, "Revision %x\n", hw_ver);
+
+	io_mode = readl(qup->base + QUP_IO_MODE);
+
+	/*
+	 * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
+	 * associated with each byte written/received
+	 */
+	size_idx = QUP_OUTPUT_BLOCK_SIZE(io_mode);
+	if (size_idx >= ARRAY_SIZE(blk_sizes)) {
+		ret = -EIO;
+		return ret;
+	}
+	size = QUP_OUTPUT_FIFO_SIZE(io_mode);
+	qup->out_fifo_sz = blk_sizes[size_idx] * (2 << size);
+
+	size_idx = QUP_INPUT_BLOCK_SIZE(io_mode);
+	if (size_idx >= ARRAY_SIZE(blk_sizes)) {
+		ret = -EIO;
+		return ret;
+	}
+	size = QUP_INPUT_FIFO_SIZE(io_mode);
+	qup->in_fifo_sz = blk_sizes[size_idx] * (2 << size);
+
+	dev_dbg(dev, "IN:fifo:%d, OUT:fifo:%d\n", qup->in_fifo_sz,
+		qup->out_fifo_sz);
+
+	return 0;
+}
+
+static int qup_i2c_set_bus_speed(struct udevice *dev, unsigned int clk_freq)
+{
+	struct qup_i2c_priv *qup = dev_get_priv(dev);
+	unsigned int src_clk_freq;
+	int fs_div, hs_div;
+
+	/* We support frequencies up to FAST Mode Plus (1MHz) */
+	if (!clk_freq || clk_freq > I2C_SPEED_FAST_PLUS_RATE) {
+		dev_err(dev, "clock frequency not supported %d\n", clk_freq);
+		return -EINVAL;
+	}
+
+	src_clk_freq = clk_get_rate(&qup->iface);
+	if ((int)src_clk_freq < 0) {
+		src_clk_freq = DEFAULT_SRC_CLK;
+		dev_dbg(dev, "using default core freq %d\n", src_clk_freq);
+	}
+
+	dev_dbg(dev, "src_clk_freq %u\n", src_clk_freq);
+	dev_dbg(dev, "clk_freq     %u\n", clk_freq);
+
+	hs_div = 3;
+	if (clk_freq <= I2C_SPEED_STANDARD_RATE) {
+		fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
+		qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
+	} else {
+		/* 33%/66% duty cycle */
+		fs_div = ((src_clk_freq / clk_freq) - 6) * 2 / 3;
+		qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff);
+	}
+
+	dev_dbg(dev, "clk_ctl      %u\n", qup->clk_ctl);
+
+	return 0;
+}
+
+/* Probe to see if a chip is present. */
+static int qup_i2c_probe_chip(struct udevice *dev, uint chip_addr,
+			      uint chip_flags)
+{
+	struct qup_i2c_priv *qup = dev_get_priv(dev);
+	u32 hw_ver = readl(qup->base + QUP_HW_VERSION);
+
+	return hw_ver ? 0 : -1;
+}
+
+static const struct dm_i2c_ops qup_i2c_ops = {
+	.xfer		= qup_i2c_xfer_v2,
+	.probe_chip	= qup_i2c_probe_chip,
+	.set_bus_speed	= qup_i2c_set_bus_speed,
+};
+
+static const struct udevice_id qup_i2c_ids[] = {
+	{ .compatible = "qcom,i2c-qup-v2.1.1" },
+	{ .compatible = "qcom,i2c-qup-v2.2.1" },
+	{ .compatible = "qcom,i2c-qup" },
+	{}
+};
+
+U_BOOT_DRIVER(i2c_qup) = {
+	.name	= "i2c_qup",
+	.id	= UCLASS_I2C,
+	.of_match = qup_i2c_ids,
+	.probe	= qup_i2c_probe,
+	.priv_auto = sizeof(struct qup_i2c_priv),
+	.ops	= &qup_i2c_ops,
+};
-- 
2.34.1


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

* [PATCH 14/14] dts: qcs404-evb: Add I2C controller nodes
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (12 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 13/14] i2c: Add support for Qualcomm I2C driver Sumit Garg
@ 2023-01-20  7:17 ` Sumit Garg
  2023-01-30 16:27 ` [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-20  7:17 UTC (permalink / raw)
  To: u-boot
  Cc: rfried.dev, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson,
	Sumit Garg

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 arch/arm/dts/qcs404-evb.dts | 97 +++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/arch/arm/dts/qcs404-evb.dts b/arch/arm/dts/qcs404-evb.dts
index 2de0e7537b..8d7893c116 100644
--- a/arch/arm/dts/qcs404-evb.dts
+++ b/arch/arm/dts/qcs404-evb.dts
@@ -23,6 +23,11 @@
 
 	aliases {
 		serial0 = &debug_uart;
+		i2c0 = &blsp1_i2c0;
+		i2c1 = &blsp1_i2c1;
+		i2c2 = &blsp1_i2c2;
+		i2c3 = &blsp1_i2c3;
+		i2c4 = &blsp1_i2c4;
 	};
 
 	memory {
@@ -49,6 +54,38 @@
 				function = "blsp_uart2";
 			};
 
+			blsp1_i2c0_default: blsp1-i2c0-default {
+				pins = "GPIO_32", "GPIO_33";
+				function = "blsp_i2c0";
+			};
+
+			blsp1_i2c1_default: blsp1-i2c1-default {
+				pins = "GPIO_24", "GPIO_25";
+				function = "blsp_i2c1";
+			};
+
+			blsp1_i2c2_default: blsp1-i2c2-default {
+				sda {
+					pins = "GPIO_19";
+					function = "blsp_i2c_sda_a2";
+				};
+
+				scl {
+					pins = "GPIO_20";
+					function = "blsp_i2c_scl_a2";
+				};
+			};
+
+			blsp1_i2c3_default: blsp1-i2c3-default {
+				pins = "GPIO_84", "GPIO_85";
+				function = "blsp_i2c3";
+			};
+
+			blsp1_i2c4_default: blsp1-i2c4-default {
+				pins = "GPIO_117", "GPIO_118";
+				function = "blsp_i2c4";
+			};
+
 			ethernet_defaults: ethernet-defaults {
 				int {
 					pins = "GPIO_61";
@@ -105,6 +142,66 @@
 			};
 		};
 
+		blsp1_i2c0: i2c@78b5000 {
+			compatible = "qcom,i2c-qup-v2.2.1";
+			reg = <0x078b5000 0x600>;
+			clocks = <&gcc GCC_BLSP1_AHB_CLK>,
+				 <&gcc GCC_BLSP1_QUP0_I2C_APPS_CLK>;
+			clock-names = "iface", "core";
+			pinctrl-names = "default";
+			pinctrl-0 = <&blsp1_i2c0_default>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		blsp1_i2c1: i2c@78b6000 {
+			compatible = "qcom,i2c-qup-v2.2.1";
+			reg = <0x078b6000 0x600>;
+			clocks = <&gcc GCC_BLSP1_AHB_CLK>,
+				 <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
+			clock-names = "iface", "core";
+			pinctrl-names = "default";
+			pinctrl-0 = <&blsp1_i2c1_default>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		blsp1_i2c2: i2c@78b7000 {
+			compatible = "qcom,i2c-qup-v2.2.1";
+			reg = <0x078b7000 0x600>;
+			clocks = <&gcc GCC_BLSP1_AHB_CLK>,
+				 <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
+			clock-names = "iface", "core";
+			pinctrl-names = "default";
+			pinctrl-0 = <&blsp1_i2c2_default>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		blsp1_i2c3: i2c@78b8000 {
+			compatible = "qcom,i2c-qup-v2.2.1";
+			reg = <0x078b8000 0x600>;
+			clocks = <&gcc GCC_BLSP1_AHB_CLK>,
+				 <&gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>;
+			clock-names = "iface", "core";
+			pinctrl-names = "default";
+			pinctrl-0 = <&blsp1_i2c3_default>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		blsp1_i2c4: i2c@78b9000 {
+			compatible = "qcom,i2c-qup-v2.2.1";
+			reg = <0x078b9000 0x600>;
+			clocks = <&gcc GCC_BLSP1_AHB_CLK>,
+				 <&gcc GCC_BLSP1_QUP4_I2C_APPS_CLK>;
+			clock-names = "iface", "core";
+			pinctrl-names = "default";
+			pinctrl-0 = <&blsp1_i2c4_default>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
 		gcc: clock-controller@1800000 {
 			compatible = "qcom,gcc-qcs404";
 			reg = <0x1800000 0x80000>;
-- 
2.34.1


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

* Re: [PATCH 13/14] i2c: Add support for Qualcomm I2C driver
  2023-01-20  7:17 ` [PATCH 13/14] i2c: Add support for Qualcomm I2C driver Sumit Garg
@ 2023-01-20  8:46   ` Heiko Schocher
  2023-01-20 12:25     ` Sumit Garg
  0 siblings, 1 reply; 23+ messages in thread
From: Heiko Schocher @ 2023-01-20  8:46 UTC (permalink / raw)
  To: Sumit Garg, u-boot
  Cc: rfried.dev, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson

Helo Sumit Garg,

On 20.01.23 08:17, Sumit Garg wrote:
> Add support for Qualcomm I2C QUP driver which is inspired from
> corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c.
> 
> Currently this driver only support FIFO polling mode which is sufficient
> to support devices like eeprom, rtc etc.
> 
> Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
> Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> ---
>  drivers/i2c/Kconfig   |   6 +
>  drivers/i2c/Makefile  |   1 +
>  drivers/i2c/qup_i2c.c | 592 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 599 insertions(+)
>  create mode 100644 drivers/i2c/qup_i2c.c
> 
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 76e19918aa..2a1d36f91a 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -635,6 +635,12 @@ config SYS_I2C_TEGRA
>  	help
>  	  Support for NVIDIA I2C controller available in Tegra SoCs.
>  
> +config SYS_I2C_QUP
> +	bool "Qualcomm I2C controller"
> +	depends on ARCH_SNAPDRAGON
> +	help
> +	  Support for Qualcomm I2C controller.

Please sort alphabetical, and may you can ad here some more infos
like link to datasheet?

> +
>  config SYS_I2C_UNIPHIER
>  	bool "UniPhier I2C driver"
>  	depends on ARCH_UNIPHIER && DM_I2C
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index 920aafb91c..f6aa39dd7a 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -50,6 +50,7 @@ obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o
>  obj-$(CONFIG_SYS_I2C_SUN8I_RSB) += sun8i_rsb.o
>  obj-$(CONFIG_SYS_I2C_SYNQUACER) += synquacer_i2c.o
>  obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
> +obj-$(CONFIG_SYS_I2C_QUP) += qup_i2c.o

Please sort alphabetical

>  obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o
>  obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o
>  obj-$(CONFIG_SYS_I2C_VERSATILE) += i2c-versatile.o
> diff --git a/drivers/i2c/qup_i2c.c b/drivers/i2c/qup_i2c.c
> new file mode 100644
> index 0000000000..6787fc75fc
> --- /dev/null
> +++ b/drivers/i2c/qup_i2c.c
> @@ -0,0 +1,592 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2014, Sony Mobile Communications AB.
> + * Copyright (c) 2022-2023, Sumit Garg <sumit.garg@linaro.org>
> + *
> + * Inspired by corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c
> + */
> +
> +#include <init.h>
> +#include <env.h>
> +#include <common.h>
> +#include <log.h>
> +#include <dm/device_compat.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/err.h>
> +#include <linux/compat.h>
> +#include <linux/bitops.h>
> +#include <asm/io.h>
> +#include <i2c.h>
> +#include <watchdog.h>
> +#include <fdtdec.h>
> +#include <clk.h>
> +#include <reset.h>
> +#include <asm/arch/gpio.h>
> +#include <cpu_func.h>
> +#include <asm/system.h>
> +#include <asm/gpio.h>
> +#include <dm.h>
> +#include <dm/pinctrl.h>
> +
> +/* QUP Registers */
> +#define QUP_CONFIG				0x000
> +#define QUP_STATE				0x004
> +#define QUP_IO_MODE				0x008
> +#define QUP_SW_RESET				0x00c
> +#define QUP_OPERATIONAL				0x018
> +#define QUP_ERROR_FLAGS				0x01c
> +#define QUP_ERROR_FLAGS_EN			0x020
> +#define QUP_TEST_CTRL				0x024 /* NOT USED */
> +#define QUP_OPERATIONAL_MASK			0x028
> +#define QUP_HW_VERSION				0x030
> +#define QUP_MX_OUTPUT_CNT			0x100
> +#define QUP_OUT_DEBUG				0x108 /* NOT USED */
> +#define QUP_OUT_FIFO_CNT			0x10C /* NOT USED */
> +#define QUP_OUT_FIFO_BASE			0x110
> +#define QUP_MX_WRITE_CNT			0x150
> +#define QUP_MX_INPUT_CNT			0x200
> +#define QUP_MX_READ_CNT				0x208
> +#define QUP_IN_READ_CUR				0x20C /* NOT USED */
> +#define QUP_IN_DEBUG				0x210 /* NOT USED */
> +#define QUP_IN_FIFO_CNT				0x214 /* NOT USED */
> +#define QUP_IN_FIFO_BASE			0x218
> +#define QUP_I2C_CLK_CTL				0x400
> +#define QUP_I2C_STATUS				0x404
> +#define QUP_I2C_MASTER_GEN			0x408
> +#define QUP_I2C_MASTER_BUS_CLR			0x40C /* NOT USED */
> +
> +/* QUP States and reset values */
> +#define QUP_RESET_STATE				0
> +#define QUP_RUN_STATE				1
> +#define QUP_PAUSE_STATE				3
> +#define QUP_STATE_MASK				3
> +
> +#define QUP_STATE_VALID				BIT(2)
> +#define QUP_I2C_MAST_GEN			BIT(4)
> +#define QUP_I2C_FLUSH				BIT(6)
> +
> +#define QUP_OPERATIONAL_RESET			0x000ff0
> +#define QUP_I2C_STATUS_RESET			0xfffffc
> +
> +/* QUP OPERATIONAL FLAGS */
> +#define QUP_I2C_NACK_FLAG			BIT(3)
> +#define QUP_OUT_NOT_EMPTY			BIT(4)
> +#define QUP_IN_NOT_EMPTY			BIT(5)
> +#define QUP_OUT_FULL				BIT(6)
> +#define QUP_OUT_SVC_FLAG			BIT(8)
> +#define QUP_IN_SVC_FLAG				BIT(9)
> +#define QUP_MX_OUTPUT_DONE			BIT(10)
> +#define QUP_MX_INPUT_DONE			BIT(11)
> +#define OUT_BLOCK_WRITE_REQ			BIT(12)
> +#define IN_BLOCK_READ_REQ			BIT(13)
> +
> +/* I2C mini core related values */

What means "mini core" ? May worth to add this info also in Kconfig help ?

> +#define QUP_NO_OUTPUT				BIT(6)
> +#define QUP_NO_INPUT				BIT(7)
> +#define QUP_CLOCK_AUTO_GATE			BIT(13)
> +#define I2C_MINI_CORE				(2 << 8)
> +#define I2C_N_VAL				15
> +#define I2C_N_VAL_V2				7

May you want to move this to a sperate headerfile?

Also I2C_... names, sounds like this are common i2c names, which
we should prevent, as this defines are used only in your driver ... or?

> +
> +/* Most significant word offset in FIFO port */
> +#define QUP_MSW_SHIFT				(I2C_N_VAL + 1)
> +
> +/* Packing/Unpacking words in FIFOs, and IO modes */
> +#define QUP_OUTPUT_BLK_MODE			BIT(10)
> +#define QUP_OUTPUT_BAM_MODE			(BIT(10) | BIT(11))
> +#define QUP_INPUT_BLK_MODE			BIT(12)
> +#define QUP_INPUT_BAM_MODE			(BIT(12) | BIT(13))
> +#define QUP_BAM_MODE				(QUP_OUTPUT_BAM_MODE | QUP_INPUT_BAM_MODE)
> +#define QUP_BLK_MODE				(QUP_OUTPUT_BLK_MODE | QUP_INPUT_BLK_MODE)
> +#define QUP_UNPACK_EN				BIT(14)
> +#define QUP_PACK_EN				BIT(15)
> +
> +#define QUP_REPACK_EN				(QUP_UNPACK_EN | QUP_PACK_EN)
> +#define QUP_V2_TAGS_EN				1
> +
> +#define QUP_OUTPUT_BLOCK_SIZE(x)		(((x) >> 0) & 0x03)
> +#define QUP_OUTPUT_FIFO_SIZE(x)			(((x) >> 2) & 0x07)
> +#define QUP_INPUT_BLOCK_SIZE(x)			(((x) >> 5) & 0x03)
> +#define QUP_INPUT_FIFO_SIZE(x)			(((x) >> 7) & 0x07)
> +
> +/* QUP v2 tags */
> +#define QUP_TAG_V2_START			0x81
> +#define QUP_TAG_V2_DATAWR			0x82
> +#define QUP_TAG_V2_DATAWR_STOP			0x83
> +#define QUP_TAG_V2_DATARD			0x85
> +#define QUP_TAG_V2_DATARD_NACK			0x86
> +#define QUP_TAG_V2_DATARD_STOP			0x87
> +
> +/* Status, Error flags */
> +#define I2C_STATUS_WR_BUFFER_FULL		BIT(0)
> +#define I2C_STATUS_BUS_ERROR			BIT(2)
> +#define I2C_STATUS_PACKET_NACKED		BIT(3)
> +#define I2C_STATUS_ARB_LOST			BIT(4)
> +#define I2C_STATUS_INVALID_WRITE		BIT(5)
> +#define I2C_STATUS_FAILED			BIT(6)
> +#define I2C_STATUS_BUS_ACTIVE			BIT(8)
> +#define I2C_STATUS_BUS_MASTER			BIT(9)
> +#define I2C_STATUS_INVALID_TAG			BIT(23)
> +#define I2C_STATUS_INVALID_READ_ADDR		BIT(24)
> +#define I2C_STATUS_INVALID_READ_SEQ		BIT(25)
> +#define I2C_STATUS_I2C_SDA			BIT(26)
> +#define I2C_STATUS_I2C_SCL			BIT(27)
> +#define I2C_STATUS_ERROR_MASK			0x38000fc

None of this I2C_STATUS_X defines are used in your driver, please reomve.

> +#define QUP_STATUS_ERROR_FLAGS			0x7c
> +
> +#define QUP_I2C_MX_CONFIG_DURING_RUN		BIT(31)
> +
> +/* Minimum transfer timeout for i2c transfers in micro seconds */
> +#define TOUT_CNT				2 * 1000 * 1000
> +
> +/* Default values. Use these if FW query fails */
> +#define DEFAULT_CLK_FREQ			I2C_SPEED_STANDARD_RATE
> +#define DEFAULT_SRC_CLK				19200000
> +
> +/*
> + * Max tags length (start, stop and maximum 2 bytes address) for each QUP
> + * data transfer
> + */
> +#define QUP_MAX_TAGS_LEN			4
> +/* Max data length for each DATARD tags */
> +#define RECV_MAX_DATA_LEN			254
> +/* TAG length for DATA READ in RX FIFO */
> +#define READ_RX_TAGS_LEN			2
> +
> +struct qup_i2c_priv {
> +	phys_addr_t base;
> +	struct clk core;
> +	struct clk iface;
> +	u32 in_fifo_sz;
> +	u32 out_fifo_sz;
> +	u32 clk_ctl;
> +	u32 config_run;
> +};
> +
> +static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
> +{
> +	return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
> +}
> +
> +static int qup_i2c_poll_state_mask(struct qup_i2c_priv *qup,
> +				   u32 req_state, u32 req_mask)
> +{
> +	int retries = 1;
> +	u32 state;
> +
> +	/*
> +	 * State transition takes 3 AHB clocks cycles + 3 I2C master clock
> +	 * cycles. So retry once after a 1uS delay.
> +	 */
> +	do {
> +		state = readl(qup->base + QUP_STATE);
> +
> +		if (state & QUP_STATE_VALID &&
> +		    (state & req_mask) == req_state)
> +			return 0;
> +
> +		udelay(1);
> +	} while (retries--);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int qup_i2c_poll_state(struct qup_i2c_priv *qup, u32 req_state)
> +{
> +	return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK);
> +}
> +
> +static int qup_i2c_poll_state_valid(struct qup_i2c_priv *qup)
> +{
> +	return qup_i2c_poll_state_mask(qup, 0, 0);
> +}
> +
> +static int qup_i2c_poll_state_i2c_master(struct qup_i2c_priv *qup)
> +{
> +	return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN);
> +}
> +
> +static int qup_i2c_change_state(struct qup_i2c_priv *qup, u32 state)
> +{
> +	if (qup_i2c_poll_state_valid(qup) != 0)
> +		return -EIO;
> +
> +	writel(state, qup->base + QUP_STATE);
> +
> +	if (qup_i2c_poll_state(qup, state) != 0)
> +		return -EIO;
> +	return 0;
> +}
> +
> +/*
> + * Function to check wheather Input or Output FIFO
> + * has data to be serviced
> + */
> +static int qup_i2c_check_fifo_status(struct qup_i2c_priv *qup, u32 reg_addr,
> +				     u32 flags)
> +{
> +	unsigned long count = TOUT_CNT;
> +	u32 val, status_flag;
> +	int ret = 0;
> +
> +	do {
> +		val = readl(qup->base + reg_addr);
> +		status_flag = val & flags;
> +
> +		if (!count) {
> +			printf("%s, timeout\n", __func__);
> +			ret = -ETIMEDOUT;
> +			break;
> +		}
> +
> +		count--;
> +		udelay(1);
> +	} while (!status_flag);
> +
> +	return ret;
> +}
> +
> +/*
> + * Function to configure Input and Output enable/disable
> + */
> +static void qup_i2c_enable_io_config(struct qup_i2c_priv *qup, u32 write_cnt,
> +				     u32 read_cnt)
> +{
> +	u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
> +
> +	writel(qup->config_run | write_cnt, qup->base + QUP_MX_WRITE_CNT);
> +
> +	if (read_cnt)
> +		writel(qup->config_run | read_cnt, qup->base + QUP_MX_READ_CNT);
> +	else
> +		qup_config |= QUP_NO_INPUT;
> +
> +	writel(qup_config, qup->base + QUP_CONFIG);
> +}
> +
> +static unsigned int qup_i2c_read_word(struct qup_i2c_priv *qup)
> +{
> +	return readl(qup->base + QUP_IN_FIFO_BASE);
> +}
> +
> +static void qup_i2c_write_word(struct qup_i2c_priv *qup, u32 word)
> +{
> +	writel(word, qup->base + QUP_OUT_FIFO_BASE);
> +}
> +
> +static int qup_i2c_blsp_read(struct qup_i2c_priv *qup, unsigned int addr,
> +			     bool last, u8 *buffer, unsigned int bytes)
> +{
> +	unsigned int i, j, word;
> +	int ret = 0;
> +
> +	/* FIFO mode size limitation, for larger size implement block mode */
> +	if (bytes > (qup->in_fifo_sz - READ_RX_TAGS_LEN))
> +		return -EINVAL;
> +
> +	qup_i2c_enable_io_config(qup, QUP_MAX_TAGS_LEN,
> +				 bytes + READ_RX_TAGS_LEN);
> +
> +	if (last)
> +		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> +					QUP_TAG_V2_DATARD_STOP << 16 |
> +					bytes << 24);
> +	else
> +		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> +					QUP_TAG_V2_DATARD << 16 | bytes << 24);
> +
> +	ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> +	if (ret)
> +		return ret;
> +
> +	ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG);
> +	if (ret)
> +		return ret;
> +	writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> +
> +	ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_IN_SVC_FLAG);
> +	if (ret)
> +		return ret;
> +	writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> +
> +	word = qup_i2c_read_word(qup);
> +	*(buffer++) = (word >> (8 * READ_RX_TAGS_LEN)) & 0xff;
> +	if (bytes > 1)
> +		*(buffer++) = (word >> (8 * (READ_RX_TAGS_LEN + 1))) & 0xff;
> +
> +	for (i = 2; i < bytes; i += 4) {
> +		word = qup_i2c_read_word(qup);
> +
> +		for (j = 0; j < 4; j++) {
> +			if ((i + j) == bytes)
> +				break;
> +			*buffer = (word >> (j * 8)) & 0xff;
> +			buffer++;
> +		}
> +	}
> +
> +	ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> +	return ret;
> +}
> +
> +static int qup_i2c_blsp_write(struct qup_i2c_priv *qup, unsigned int addr,
> +			      bool first, bool last, const u8 *buffer,
> +			      unsigned int bytes)
> +{
> +	unsigned int i;
> +	u32 word = 0;
> +	int ret = 0;
> +
> +	/* FIFO mode size limitation, for larger size implement block mode */
> +	if (bytes > (qup->out_fifo_sz - QUP_MAX_TAGS_LEN))
> +		return -EINVAL;
> +
> +	qup_i2c_enable_io_config(qup, bytes + QUP_MAX_TAGS_LEN, 0);
> +
> +	if (first) {
> +		ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> +		if (ret)
> +			return ret;
> +
> +		writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> +
> +		ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (last)
> +		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> +					QUP_TAG_V2_DATAWR_STOP << 16 |
> +					bytes << 24);
> +	else
> +		qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> +					QUP_TAG_V2_DATAWR << 16 | bytes << 24);
> +
> +	for (i = 0; i < bytes; i++) {
> +		/* Write the byte of data */
> +		word |= *buffer << ((i % 4) * 8);
> +		if ((i % 4) == 3) {
> +			qup_i2c_write_word(qup, word);
> +			word = 0;
> +		}
> +		buffer++;
> +	}
> +
> +	if ((i % 4) != 0)
> +		qup_i2c_write_word(qup, word);
> +
> +	ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> +	if (ret)
> +		return ret;
> +
> +	ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG);
> +	if (ret)
> +		return ret;
> +	writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> +
> +	ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> +	return ret;
> +}
> +
> +static void qup_i2c_conf_mode_v2(struct qup_i2c_priv *qup)
> +{
> +	u32 io_mode = QUP_REPACK_EN;
> +
> +	writel(0, qup->base + QUP_MX_OUTPUT_CNT);
> +	writel(0, qup->base + QUP_MX_INPUT_CNT);
> +
> +	writel(io_mode, qup->base + QUP_IO_MODE);
> +}
> +
> +static int qup_i2c_xfer_v2(struct udevice *bus, struct i2c_msg msgs[], int num)

If there is an _v2 ... but not finding a _v1 or so ... please remove the "_v2"
in the name as not necessary? Applies for other function names too... or at
least add a comment what V2 means?

> +{
> +	struct qup_i2c_priv *qup = dev_get_priv(bus);
> +	int ret, idx = 0;
> +	u32 i2c_addr;
> +
> +	writel(1, qup->base + QUP_SW_RESET);
> +	ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
> +	if (ret)
> +		goto out;
> +
> +	/* Configure QUP as I2C mini core */
> +	writel(I2C_MINI_CORE | I2C_N_VAL_V2 | QUP_NO_INPUT,
> +	       qup->base + QUP_CONFIG);
> +	writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);
> +
> +	if (qup_i2c_poll_state_i2c_master(qup)) {
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	qup_i2c_conf_mode_v2(qup);
> +
> +	for (idx = 0; idx < num; idx++) {
> +		struct i2c_msg *m = &msgs[idx];
> +
> +		qup->config_run = !idx ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
> +		i2c_addr = i2c_8bit_addr_from_msg(m);
> +
> +		if (m->flags & I2C_M_RD)
> +			ret = qup_i2c_blsp_read(qup, i2c_addr, idx == (num - 1),
> +						m->buf, m->len);
> +		else
> +			ret = qup_i2c_blsp_write(qup, i2c_addr, idx == 0,
> +						 idx == (num - 1), m->buf,
> +						 m->len);
> +		if (ret)
> +			break;
> +	}
> +out:
> +	qup_i2c_change_state(qup, QUP_RESET_STATE);
> +	return ret;
> +}
> +
> +static int qup_i2c_enable_clocks(struct udevice *dev, struct qup_i2c_priv *qup)
> +{
> +	int ret;
> +
> +	ret = clk_enable(&qup->core);
> +	if (ret) {
> +		dev_err(dev, "clk_enable failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_enable(&qup->iface);
> +	if (ret) {
> +		dev_err(dev, "clk_enable failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int qup_i2c_probe(struct udevice *dev)
> +{
> +	static const int blk_sizes[] = {4, 16, 32};
> +	struct qup_i2c_priv *qup = dev_get_priv(dev);
> +	u32 io_mode, hw_ver, size, size_idx;
> +	int ret;
> +
> +	qup->base = (phys_addr_t)dev_read_addr_ptr(dev);
> +	if (!qup->base)
> +		return -EINVAL;
> +
> +	ret = clk_get_by_name(dev, "core", &qup->core);
> +	if (ret) {
> +		pr_err("clk_get_by_name(core) failed: %d\n", ret);
> +		return ret;
> +	}
> +	ret = clk_get_by_name(dev, "iface", &qup->iface);
> +	if (ret) {
> +		pr_err("clk_get_by_name(iface) failed: %d\n", ret);
> +		return ret;
> +	}
> +	qup_i2c_enable_clocks(dev, qup);
> +
> +	writel(1, qup->base + QUP_SW_RESET);
> +	ret = qup_i2c_poll_state_valid(qup);
> +	if (ret)
> +		return ret;
> +
> +	hw_ver = readl(qup->base + QUP_HW_VERSION);
> +	dev_dbg(dev, "Revision %x\n", hw_ver);
> +
> +	io_mode = readl(qup->base + QUP_IO_MODE);
> +
> +	/*
> +	 * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
> +	 * associated with each byte written/received
> +	 */
> +	size_idx = QUP_OUTPUT_BLOCK_SIZE(io_mode);
> +	if (size_idx >= ARRAY_SIZE(blk_sizes)) {
> +		ret = -EIO;
> +		return ret;
> +	}
> +	size = QUP_OUTPUT_FIFO_SIZE(io_mode);
> +	qup->out_fifo_sz = blk_sizes[size_idx] * (2 << size);
> +
> +	size_idx = QUP_INPUT_BLOCK_SIZE(io_mode);
> +	if (size_idx >= ARRAY_SIZE(blk_sizes)) {
> +		ret = -EIO;
> +		return ret;
> +	}
> +	size = QUP_INPUT_FIFO_SIZE(io_mode);
> +	qup->in_fifo_sz = blk_sizes[size_idx] * (2 << size);
> +
> +	dev_dbg(dev, "IN:fifo:%d, OUT:fifo:%d\n", qup->in_fifo_sz,
> +		qup->out_fifo_sz);
> +
> +	return 0;
> +}
> +
> +static int qup_i2c_set_bus_speed(struct udevice *dev, unsigned int clk_freq)
> +{
> +	struct qup_i2c_priv *qup = dev_get_priv(dev);
> +	unsigned int src_clk_freq;
> +	int fs_div, hs_div;
> +
> +	/* We support frequencies up to FAST Mode Plus (1MHz) */
> +	if (!clk_freq || clk_freq > I2C_SPEED_FAST_PLUS_RATE) {
> +		dev_err(dev, "clock frequency not supported %d\n", clk_freq);
> +		return -EINVAL;
> +	}
> +
> +	src_clk_freq = clk_get_rate(&qup->iface);
> +	if ((int)src_clk_freq < 0) {
> +		src_clk_freq = DEFAULT_SRC_CLK;
> +		dev_dbg(dev, "using default core freq %d\n", src_clk_freq);
> +	}
> +
> +	dev_dbg(dev, "src_clk_freq %u\n", src_clk_freq);
> +	dev_dbg(dev, "clk_freq     %u\n", clk_freq);
> +
> +	hs_div = 3;
> +	if (clk_freq <= I2C_SPEED_STANDARD_RATE) {
> +		fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
> +		qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
> +	} else {
> +		/* 33%/66% duty cycle */
> +		fs_div = ((src_clk_freq / clk_freq) - 6) * 2 / 3;
> +		qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff);
> +	}
> +
> +	dev_dbg(dev, "clk_ctl      %u\n", qup->clk_ctl);
> +
> +	return 0;
> +}
> +
> +/* Probe to see if a chip is present. */
> +static int qup_i2c_probe_chip(struct udevice *dev, uint chip_addr,
> +			      uint chip_flags)
> +{
> +	struct qup_i2c_priv *qup = dev_get_priv(dev);
> +	u32 hw_ver = readl(qup->base + QUP_HW_VERSION);
> +
> +	return hw_ver ? 0 : -1;
> +}
> +
> +static const struct dm_i2c_ops qup_i2c_ops = {
> +	.xfer		= qup_i2c_xfer_v2,
> +	.probe_chip	= qup_i2c_probe_chip,
> +	.set_bus_speed	= qup_i2c_set_bus_speed,
> +};
> +
> +static const struct udevice_id qup_i2c_ids[] = {
> +	{ .compatible = "qcom,i2c-qup-v2.1.1" },
> +	{ .compatible = "qcom,i2c-qup-v2.2.1" },
> +	{ .compatible = "qcom,i2c-qup" },
> +	{}
> +};
> +
> +U_BOOT_DRIVER(i2c_qup) = {
> +	.name	= "i2c_qup",
> +	.id	= UCLASS_I2C,
> +	.of_match = qup_i2c_ids,
> +	.probe	= qup_i2c_probe,
> +	.priv_auto = sizeof(struct qup_i2c_priv),
> +	.ops	= &qup_i2c_ops,
> +};
> 

Thanks!

bye,
Heiko
-- 
DENX Software Engineering GmbH,      Managing Director: Erika Unter
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-52   Fax: +49-8142-66989-80   Email: hs@denx.de

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

* Re: [PATCH 13/14] i2c: Add support for Qualcomm I2C driver
  2023-01-20  8:46   ` Heiko Schocher
@ 2023-01-20 12:25     ` Sumit Garg
  2023-01-20 13:37       ` Stephan Gerhold
  0 siblings, 1 reply; 23+ messages in thread
From: Sumit Garg @ 2023-01-20 12:25 UTC (permalink / raw)
  To: hs
  Cc: u-boot, rfried.dev, joe.hershberger, stephan, mworsfold,
	lgillham, jbrennan, nicolas.dechesne, vinod.koul,
	daniel.thompson

Hi Heiko,

Thanks for your review.

On Fri, 20 Jan 2023 at 14:16, Heiko Schocher <hs@denx.de> wrote:
>
> Helo Sumit Garg,
>
> On 20.01.23 08:17, Sumit Garg wrote:
> > Add support for Qualcomm I2C QUP driver which is inspired from
> > corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c.
> >
> > Currently this driver only support FIFO polling mode which is sufficient
> > to support devices like eeprom, rtc etc.
> >
> > Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
> > Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
> > Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> > ---
> >  drivers/i2c/Kconfig   |   6 +
> >  drivers/i2c/Makefile  |   1 +
> >  drivers/i2c/qup_i2c.c | 592 ++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 599 insertions(+)
> >  create mode 100644 drivers/i2c/qup_i2c.c
> >
> > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> > index 76e19918aa..2a1d36f91a 100644
> > --- a/drivers/i2c/Kconfig
> > +++ b/drivers/i2c/Kconfig
> > @@ -635,6 +635,12 @@ config SYS_I2C_TEGRA
> >       help
> >         Support for NVIDIA I2C controller available in Tegra SoCs.
> >
> > +config SYS_I2C_QUP
> > +     bool "Qualcomm I2C controller"
> > +     depends on ARCH_SNAPDRAGON
> > +     help
> > +       Support for Qualcomm I2C controller.
>
> Please sort alphabetical,

Sure.

> and may you can ad here some more infos
> like link to datasheet?
>

I will try to put more info here. However, I don't think the datasheet
is available publically. This driver is inspired from its Linux
counterpart only.

> > +
> >  config SYS_I2C_UNIPHIER
> >       bool "UniPhier I2C driver"
> >       depends on ARCH_UNIPHIER && DM_I2C
> > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> > index 920aafb91c..f6aa39dd7a 100644
> > --- a/drivers/i2c/Makefile
> > +++ b/drivers/i2c/Makefile
> > @@ -50,6 +50,7 @@ obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o
> >  obj-$(CONFIG_SYS_I2C_SUN8I_RSB) += sun8i_rsb.o
> >  obj-$(CONFIG_SYS_I2C_SYNQUACER) += synquacer_i2c.o
> >  obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
> > +obj-$(CONFIG_SYS_I2C_QUP) += qup_i2c.o
>
> Please sort alphabetical
>

Ack.

> >  obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o
> >  obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o
> >  obj-$(CONFIG_SYS_I2C_VERSATILE) += i2c-versatile.o
> > diff --git a/drivers/i2c/qup_i2c.c b/drivers/i2c/qup_i2c.c
> > new file mode 100644
> > index 0000000000..6787fc75fc
> > --- /dev/null
> > +++ b/drivers/i2c/qup_i2c.c
> > @@ -0,0 +1,592 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved.
> > + * Copyright (c) 2014, Sony Mobile Communications AB.
> > + * Copyright (c) 2022-2023, Sumit Garg <sumit.garg@linaro.org>
> > + *
> > + * Inspired by corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c
> > + */
> > +
> > +#include <init.h>
> > +#include <env.h>
> > +#include <common.h>
> > +#include <log.h>
> > +#include <dm/device_compat.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/err.h>
> > +#include <linux/compat.h>
> > +#include <linux/bitops.h>
> > +#include <asm/io.h>
> > +#include <i2c.h>
> > +#include <watchdog.h>
> > +#include <fdtdec.h>
> > +#include <clk.h>
> > +#include <reset.h>
> > +#include <asm/arch/gpio.h>
> > +#include <cpu_func.h>
> > +#include <asm/system.h>
> > +#include <asm/gpio.h>
> > +#include <dm.h>
> > +#include <dm/pinctrl.h>
> > +
> > +/* QUP Registers */
> > +#define QUP_CONFIG                           0x000
> > +#define QUP_STATE                            0x004
> > +#define QUP_IO_MODE                          0x008
> > +#define QUP_SW_RESET                         0x00c
> > +#define QUP_OPERATIONAL                              0x018
> > +#define QUP_ERROR_FLAGS                              0x01c
> > +#define QUP_ERROR_FLAGS_EN                   0x020
> > +#define QUP_TEST_CTRL                                0x024 /* NOT USED */
> > +#define QUP_OPERATIONAL_MASK                 0x028
> > +#define QUP_HW_VERSION                               0x030
> > +#define QUP_MX_OUTPUT_CNT                    0x100
> > +#define QUP_OUT_DEBUG                                0x108 /* NOT USED */
> > +#define QUP_OUT_FIFO_CNT                     0x10C /* NOT USED */
> > +#define QUP_OUT_FIFO_BASE                    0x110
> > +#define QUP_MX_WRITE_CNT                     0x150
> > +#define QUP_MX_INPUT_CNT                     0x200
> > +#define QUP_MX_READ_CNT                              0x208
> > +#define QUP_IN_READ_CUR                              0x20C /* NOT USED */
> > +#define QUP_IN_DEBUG                         0x210 /* NOT USED */
> > +#define QUP_IN_FIFO_CNT                              0x214 /* NOT USED */
> > +#define QUP_IN_FIFO_BASE                     0x218
> > +#define QUP_I2C_CLK_CTL                              0x400
> > +#define QUP_I2C_STATUS                               0x404
> > +#define QUP_I2C_MASTER_GEN                   0x408
> > +#define QUP_I2C_MASTER_BUS_CLR                       0x40C /* NOT USED */
> > +
> > +/* QUP States and reset values */
> > +#define QUP_RESET_STATE                              0
> > +#define QUP_RUN_STATE                                1
> > +#define QUP_PAUSE_STATE                              3
> > +#define QUP_STATE_MASK                               3
> > +
> > +#define QUP_STATE_VALID                              BIT(2)
> > +#define QUP_I2C_MAST_GEN                     BIT(4)
> > +#define QUP_I2C_FLUSH                                BIT(6)
> > +
> > +#define QUP_OPERATIONAL_RESET                        0x000ff0
> > +#define QUP_I2C_STATUS_RESET                 0xfffffc
> > +
> > +/* QUP OPERATIONAL FLAGS */
> > +#define QUP_I2C_NACK_FLAG                    BIT(3)
> > +#define QUP_OUT_NOT_EMPTY                    BIT(4)
> > +#define QUP_IN_NOT_EMPTY                     BIT(5)
> > +#define QUP_OUT_FULL                         BIT(6)
> > +#define QUP_OUT_SVC_FLAG                     BIT(8)
> > +#define QUP_IN_SVC_FLAG                              BIT(9)
> > +#define QUP_MX_OUTPUT_DONE                   BIT(10)
> > +#define QUP_MX_INPUT_DONE                    BIT(11)
> > +#define OUT_BLOCK_WRITE_REQ                  BIT(12)
> > +#define IN_BLOCK_READ_REQ                    BIT(13)
> > +
> > +/* I2C mini core related values */
>
> What means "mini core" ? May worth to add this info also in Kconfig help ?

Okay I will extend this comment as well as extend Kconfig help section too.

>
> > +#define QUP_NO_OUTPUT                                BIT(6)
> > +#define QUP_NO_INPUT                         BIT(7)
> > +#define QUP_CLOCK_AUTO_GATE                  BIT(13)
> > +#define I2C_MINI_CORE                                (2 << 8)
> > +#define I2C_N_VAL                            15
> > +#define I2C_N_VAL_V2                         7
>
> May you want to move this to a sperate headerfile?
>

At this point I don't think there will be any other driver including
that headerfile. So I would like to keep them here only.

> Also I2C_... names, sounds like this are common i2c names, which
> we should prevent, as this defines are used only in your driver ... or?
>

Makes sense, I will get rid of I2C_... names.

> > +
> > +/* Most significant word offset in FIFO port */
> > +#define QUP_MSW_SHIFT                                (I2C_N_VAL + 1)
> > +
> > +/* Packing/Unpacking words in FIFOs, and IO modes */
> > +#define QUP_OUTPUT_BLK_MODE                  BIT(10)
> > +#define QUP_OUTPUT_BAM_MODE                  (BIT(10) | BIT(11))
> > +#define QUP_INPUT_BLK_MODE                   BIT(12)
> > +#define QUP_INPUT_BAM_MODE                   (BIT(12) | BIT(13))
> > +#define QUP_BAM_MODE                         (QUP_OUTPUT_BAM_MODE | QUP_INPUT_BAM_MODE)
> > +#define QUP_BLK_MODE                         (QUP_OUTPUT_BLK_MODE | QUP_INPUT_BLK_MODE)
> > +#define QUP_UNPACK_EN                                BIT(14)
> > +#define QUP_PACK_EN                          BIT(15)
> > +
> > +#define QUP_REPACK_EN                                (QUP_UNPACK_EN | QUP_PACK_EN)
> > +#define QUP_V2_TAGS_EN                               1
> > +
> > +#define QUP_OUTPUT_BLOCK_SIZE(x)             (((x) >> 0) & 0x03)
> > +#define QUP_OUTPUT_FIFO_SIZE(x)                      (((x) >> 2) & 0x07)
> > +#define QUP_INPUT_BLOCK_SIZE(x)                      (((x) >> 5) & 0x03)
> > +#define QUP_INPUT_FIFO_SIZE(x)                       (((x) >> 7) & 0x07)
> > +
> > +/* QUP v2 tags */
> > +#define QUP_TAG_V2_START                     0x81
> > +#define QUP_TAG_V2_DATAWR                    0x82
> > +#define QUP_TAG_V2_DATAWR_STOP                       0x83
> > +#define QUP_TAG_V2_DATARD                    0x85
> > +#define QUP_TAG_V2_DATARD_NACK                       0x86
> > +#define QUP_TAG_V2_DATARD_STOP                       0x87
> > +
> > +/* Status, Error flags */
> > +#define I2C_STATUS_WR_BUFFER_FULL            BIT(0)
> > +#define I2C_STATUS_BUS_ERROR                 BIT(2)
> > +#define I2C_STATUS_PACKET_NACKED             BIT(3)
> > +#define I2C_STATUS_ARB_LOST                  BIT(4)
> > +#define I2C_STATUS_INVALID_WRITE             BIT(5)
> > +#define I2C_STATUS_FAILED                    BIT(6)
> > +#define I2C_STATUS_BUS_ACTIVE                        BIT(8)
> > +#define I2C_STATUS_BUS_MASTER                        BIT(9)
> > +#define I2C_STATUS_INVALID_TAG                       BIT(23)
> > +#define I2C_STATUS_INVALID_READ_ADDR         BIT(24)
> > +#define I2C_STATUS_INVALID_READ_SEQ          BIT(25)
> > +#define I2C_STATUS_I2C_SDA                   BIT(26)
> > +#define I2C_STATUS_I2C_SCL                   BIT(27)
> > +#define I2C_STATUS_ERROR_MASK                        0x38000fc
>
> None of this I2C_STATUS_X defines are used in your driver, please reomve.
>

Ack.

> > +#define QUP_STATUS_ERROR_FLAGS                       0x7c
> > +
> > +#define QUP_I2C_MX_CONFIG_DURING_RUN         BIT(31)
> > +
> > +/* Minimum transfer timeout for i2c transfers in micro seconds */
> > +#define TOUT_CNT                             2 * 1000 * 1000
> > +
> > +/* Default values. Use these if FW query fails */
> > +#define DEFAULT_CLK_FREQ                     I2C_SPEED_STANDARD_RATE
> > +#define DEFAULT_SRC_CLK                              19200000
> > +
> > +/*
> > + * Max tags length (start, stop and maximum 2 bytes address) for each QUP
> > + * data transfer
> > + */
> > +#define QUP_MAX_TAGS_LEN                     4
> > +/* Max data length for each DATARD tags */
> > +#define RECV_MAX_DATA_LEN                    254
> > +/* TAG length for DATA READ in RX FIFO */
> > +#define READ_RX_TAGS_LEN                     2
> > +
> > +struct qup_i2c_priv {
> > +     phys_addr_t base;
> > +     struct clk core;
> > +     struct clk iface;
> > +     u32 in_fifo_sz;
> > +     u32 out_fifo_sz;
> > +     u32 clk_ctl;
> > +     u32 config_run;
> > +};
> > +
> > +static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
> > +{
> > +     return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
> > +}
> > +
> > +static int qup_i2c_poll_state_mask(struct qup_i2c_priv *qup,
> > +                                u32 req_state, u32 req_mask)
> > +{
> > +     int retries = 1;
> > +     u32 state;
> > +
> > +     /*
> > +      * State transition takes 3 AHB clocks cycles + 3 I2C master clock
> > +      * cycles. So retry once after a 1uS delay.
> > +      */
> > +     do {
> > +             state = readl(qup->base + QUP_STATE);
> > +
> > +             if (state & QUP_STATE_VALID &&
> > +                 (state & req_mask) == req_state)
> > +                     return 0;
> > +
> > +             udelay(1);
> > +     } while (retries--);
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int qup_i2c_poll_state(struct qup_i2c_priv *qup, u32 req_state)
> > +{
> > +     return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK);
> > +}
> > +
> > +static int qup_i2c_poll_state_valid(struct qup_i2c_priv *qup)
> > +{
> > +     return qup_i2c_poll_state_mask(qup, 0, 0);
> > +}
> > +
> > +static int qup_i2c_poll_state_i2c_master(struct qup_i2c_priv *qup)
> > +{
> > +     return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN);
> > +}
> > +
> > +static int qup_i2c_change_state(struct qup_i2c_priv *qup, u32 state)
> > +{
> > +     if (qup_i2c_poll_state_valid(qup) != 0)
> > +             return -EIO;
> > +
> > +     writel(state, qup->base + QUP_STATE);
> > +
> > +     if (qup_i2c_poll_state(qup, state) != 0)
> > +             return -EIO;
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Function to check wheather Input or Output FIFO
> > + * has data to be serviced
> > + */
> > +static int qup_i2c_check_fifo_status(struct qup_i2c_priv *qup, u32 reg_addr,
> > +                                  u32 flags)
> > +{
> > +     unsigned long count = TOUT_CNT;
> > +     u32 val, status_flag;
> > +     int ret = 0;
> > +
> > +     do {
> > +             val = readl(qup->base + reg_addr);
> > +             status_flag = val & flags;
> > +
> > +             if (!count) {
> > +                     printf("%s, timeout\n", __func__);
> > +                     ret = -ETIMEDOUT;
> > +                     break;
> > +             }
> > +
> > +             count--;
> > +             udelay(1);
> > +     } while (!status_flag);
> > +
> > +     return ret;
> > +}
> > +
> > +/*
> > + * Function to configure Input and Output enable/disable
> > + */
> > +static void qup_i2c_enable_io_config(struct qup_i2c_priv *qup, u32 write_cnt,
> > +                                  u32 read_cnt)
> > +{
> > +     u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
> > +
> > +     writel(qup->config_run | write_cnt, qup->base + QUP_MX_WRITE_CNT);
> > +
> > +     if (read_cnt)
> > +             writel(qup->config_run | read_cnt, qup->base + QUP_MX_READ_CNT);
> > +     else
> > +             qup_config |= QUP_NO_INPUT;
> > +
> > +     writel(qup_config, qup->base + QUP_CONFIG);
> > +}
> > +
> > +static unsigned int qup_i2c_read_word(struct qup_i2c_priv *qup)
> > +{
> > +     return readl(qup->base + QUP_IN_FIFO_BASE);
> > +}
> > +
> > +static void qup_i2c_write_word(struct qup_i2c_priv *qup, u32 word)
> > +{
> > +     writel(word, qup->base + QUP_OUT_FIFO_BASE);
> > +}
> > +
> > +static int qup_i2c_blsp_read(struct qup_i2c_priv *qup, unsigned int addr,
> > +                          bool last, u8 *buffer, unsigned int bytes)
> > +{
> > +     unsigned int i, j, word;
> > +     int ret = 0;
> > +
> > +     /* FIFO mode size limitation, for larger size implement block mode */
> > +     if (bytes > (qup->in_fifo_sz - READ_RX_TAGS_LEN))
> > +             return -EINVAL;
> > +
> > +     qup_i2c_enable_io_config(qup, QUP_MAX_TAGS_LEN,
> > +                              bytes + READ_RX_TAGS_LEN);
> > +
> > +     if (last)
> > +             qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> > +                                     QUP_TAG_V2_DATARD_STOP << 16 |
> > +                                     bytes << 24);
> > +     else
> > +             qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> > +                                     QUP_TAG_V2_DATARD << 16 | bytes << 24);
> > +
> > +     ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG);
> > +     if (ret)
> > +             return ret;
> > +     writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> > +
> > +     ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_IN_SVC_FLAG);
> > +     if (ret)
> > +             return ret;
> > +     writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> > +
> > +     word = qup_i2c_read_word(qup);
> > +     *(buffer++) = (word >> (8 * READ_RX_TAGS_LEN)) & 0xff;
> > +     if (bytes > 1)
> > +             *(buffer++) = (word >> (8 * (READ_RX_TAGS_LEN + 1))) & 0xff;
> > +
> > +     for (i = 2; i < bytes; i += 4) {
> > +             word = qup_i2c_read_word(qup);
> > +
> > +             for (j = 0; j < 4; j++) {
> > +                     if ((i + j) == bytes)
> > +                             break;
> > +                     *buffer = (word >> (j * 8)) & 0xff;
> > +                     buffer++;
> > +             }
> > +     }
> > +
> > +     ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> > +     return ret;
> > +}
> > +
> > +static int qup_i2c_blsp_write(struct qup_i2c_priv *qup, unsigned int addr,
> > +                           bool first, bool last, const u8 *buffer,
> > +                           unsigned int bytes)
> > +{
> > +     unsigned int i;
> > +     u32 word = 0;
> > +     int ret = 0;
> > +
> > +     /* FIFO mode size limitation, for larger size implement block mode */
> > +     if (bytes > (qup->out_fifo_sz - QUP_MAX_TAGS_LEN))
> > +             return -EINVAL;
> > +
> > +     qup_i2c_enable_io_config(qup, bytes + QUP_MAX_TAGS_LEN, 0);
> > +
> > +     if (first) {
> > +             ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> > +
> > +             ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     if (last)
> > +             qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> > +                                     QUP_TAG_V2_DATAWR_STOP << 16 |
> > +                                     bytes << 24);
> > +     else
> > +             qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 |
> > +                                     QUP_TAG_V2_DATAWR << 16 | bytes << 24);
> > +
> > +     for (i = 0; i < bytes; i++) {
> > +             /* Write the byte of data */
> > +             word |= *buffer << ((i % 4) * 8);
> > +             if ((i % 4) == 3) {
> > +                     qup_i2c_write_word(qup, word);
> > +                     word = 0;
> > +             }
> > +             buffer++;
> > +     }
> > +
> > +     if ((i % 4) != 0)
> > +             qup_i2c_write_word(qup, word);
> > +
> > +     ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG);
> > +     if (ret)
> > +             return ret;
> > +     writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> > +
> > +     ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> > +     return ret;
> > +}
> > +
> > +static void qup_i2c_conf_mode_v2(struct qup_i2c_priv *qup)
> > +{
> > +     u32 io_mode = QUP_REPACK_EN;
> > +
> > +     writel(0, qup->base + QUP_MX_OUTPUT_CNT);
> > +     writel(0, qup->base + QUP_MX_INPUT_CNT);
> > +
> > +     writel(io_mode, qup->base + QUP_IO_MODE);
> > +}
> > +
> > +static int qup_i2c_xfer_v2(struct udevice *bus, struct i2c_msg msgs[], int num)
>
> If there is an _v2 ... but not finding a _v1 or so ... please remove the "_v2"
> in the name as not necessary? Applies for other function names too... or at
> least add a comment what V2 means?
>

Actually, v1 is there in the upstream Linux driver [1] but it can't be
tested until a corresponding SoC support is added in u-boot. So I
specified it as v2 for clarity and will add a comment regarding this
in the next version.

[1] https://elixir.bootlin.com/linux/v6.2-rc4/source/drivers/i2c/busses/i2c-qup.c#L1688

-Sumit

> > +{
> > +     struct qup_i2c_priv *qup = dev_get_priv(bus);
> > +     int ret, idx = 0;
> > +     u32 i2c_addr;
> > +
> > +     writel(1, qup->base + QUP_SW_RESET);
> > +     ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
> > +     if (ret)
> > +             goto out;
> > +
> > +     /* Configure QUP as I2C mini core */
> > +     writel(I2C_MINI_CORE | I2C_N_VAL_V2 | QUP_NO_INPUT,
> > +            qup->base + QUP_CONFIG);
> > +     writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);
> > +
> > +     if (qup_i2c_poll_state_i2c_master(qup)) {
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     qup_i2c_conf_mode_v2(qup);
> > +
> > +     for (idx = 0; idx < num; idx++) {
> > +             struct i2c_msg *m = &msgs[idx];
> > +
> > +             qup->config_run = !idx ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
> > +             i2c_addr = i2c_8bit_addr_from_msg(m);
> > +
> > +             if (m->flags & I2C_M_RD)
> > +                     ret = qup_i2c_blsp_read(qup, i2c_addr, idx == (num - 1),
> > +                                             m->buf, m->len);
> > +             else
> > +                     ret = qup_i2c_blsp_write(qup, i2c_addr, idx == 0,
> > +                                              idx == (num - 1), m->buf,
> > +                                              m->len);
> > +             if (ret)
> > +                     break;
> > +     }
> > +out:
> > +     qup_i2c_change_state(qup, QUP_RESET_STATE);
> > +     return ret;
> > +}
> > +
> > +static int qup_i2c_enable_clocks(struct udevice *dev, struct qup_i2c_priv *qup)
> > +{
> > +     int ret;
> > +
> > +     ret = clk_enable(&qup->core);
> > +     if (ret) {
> > +             dev_err(dev, "clk_enable failed %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = clk_enable(&qup->iface);
> > +     if (ret) {
> > +             dev_err(dev, "clk_enable failed %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int qup_i2c_probe(struct udevice *dev)
> > +{
> > +     static const int blk_sizes[] = {4, 16, 32};
> > +     struct qup_i2c_priv *qup = dev_get_priv(dev);
> > +     u32 io_mode, hw_ver, size, size_idx;
> > +     int ret;
> > +
> > +     qup->base = (phys_addr_t)dev_read_addr_ptr(dev);
> > +     if (!qup->base)
> > +             return -EINVAL;
> > +
> > +     ret = clk_get_by_name(dev, "core", &qup->core);
> > +     if (ret) {
> > +             pr_err("clk_get_by_name(core) failed: %d\n", ret);
> > +             return ret;
> > +     }
> > +     ret = clk_get_by_name(dev, "iface", &qup->iface);
> > +     if (ret) {
> > +             pr_err("clk_get_by_name(iface) failed: %d\n", ret);
> > +             return ret;
> > +     }
> > +     qup_i2c_enable_clocks(dev, qup);
> > +
> > +     writel(1, qup->base + QUP_SW_RESET);
> > +     ret = qup_i2c_poll_state_valid(qup);
> > +     if (ret)
> > +             return ret;
> > +
> > +     hw_ver = readl(qup->base + QUP_HW_VERSION);
> > +     dev_dbg(dev, "Revision %x\n", hw_ver);
> > +
> > +     io_mode = readl(qup->base + QUP_IO_MODE);
> > +
> > +     /*
> > +      * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
> > +      * associated with each byte written/received
> > +      */
> > +     size_idx = QUP_OUTPUT_BLOCK_SIZE(io_mode);
> > +     if (size_idx >= ARRAY_SIZE(blk_sizes)) {
> > +             ret = -EIO;
> > +             return ret;
> > +     }
> > +     size = QUP_OUTPUT_FIFO_SIZE(io_mode);
> > +     qup->out_fifo_sz = blk_sizes[size_idx] * (2 << size);
> > +
> > +     size_idx = QUP_INPUT_BLOCK_SIZE(io_mode);
> > +     if (size_idx >= ARRAY_SIZE(blk_sizes)) {
> > +             ret = -EIO;
> > +             return ret;
> > +     }
> > +     size = QUP_INPUT_FIFO_SIZE(io_mode);
> > +     qup->in_fifo_sz = blk_sizes[size_idx] * (2 << size);
> > +
> > +     dev_dbg(dev, "IN:fifo:%d, OUT:fifo:%d\n", qup->in_fifo_sz,
> > +             qup->out_fifo_sz);
> > +
> > +     return 0;
> > +}
> > +
> > +static int qup_i2c_set_bus_speed(struct udevice *dev, unsigned int clk_freq)
> > +{
> > +     struct qup_i2c_priv *qup = dev_get_priv(dev);
> > +     unsigned int src_clk_freq;
> > +     int fs_div, hs_div;
> > +
> > +     /* We support frequencies up to FAST Mode Plus (1MHz) */
> > +     if (!clk_freq || clk_freq > I2C_SPEED_FAST_PLUS_RATE) {
> > +             dev_err(dev, "clock frequency not supported %d\n", clk_freq);
> > +             return -EINVAL;
> > +     }
> > +
> > +     src_clk_freq = clk_get_rate(&qup->iface);
> > +     if ((int)src_clk_freq < 0) {
> > +             src_clk_freq = DEFAULT_SRC_CLK;
> > +             dev_dbg(dev, "using default core freq %d\n", src_clk_freq);
> > +     }
> > +
> > +     dev_dbg(dev, "src_clk_freq %u\n", src_clk_freq);
> > +     dev_dbg(dev, "clk_freq     %u\n", clk_freq);
> > +
> > +     hs_div = 3;
> > +     if (clk_freq <= I2C_SPEED_STANDARD_RATE) {
> > +             fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
> > +             qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
> > +     } else {
> > +             /* 33%/66% duty cycle */
> > +             fs_div = ((src_clk_freq / clk_freq) - 6) * 2 / 3;
> > +             qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff);
> > +     }
> > +
> > +     dev_dbg(dev, "clk_ctl      %u\n", qup->clk_ctl);
> > +
> > +     return 0;
> > +}
> > +
> > +/* Probe to see if a chip is present. */
> > +static int qup_i2c_probe_chip(struct udevice *dev, uint chip_addr,
> > +                           uint chip_flags)
> > +{
> > +     struct qup_i2c_priv *qup = dev_get_priv(dev);
> > +     u32 hw_ver = readl(qup->base + QUP_HW_VERSION);
> > +
> > +     return hw_ver ? 0 : -1;
> > +}
> > +
> > +static const struct dm_i2c_ops qup_i2c_ops = {
> > +     .xfer           = qup_i2c_xfer_v2,
> > +     .probe_chip     = qup_i2c_probe_chip,
> > +     .set_bus_speed  = qup_i2c_set_bus_speed,
> > +};
> > +
> > +static const struct udevice_id qup_i2c_ids[] = {
> > +     { .compatible = "qcom,i2c-qup-v2.1.1" },
> > +     { .compatible = "qcom,i2c-qup-v2.2.1" },
> > +     { .compatible = "qcom,i2c-qup" },
> > +     {}
> > +};
> > +
> > +U_BOOT_DRIVER(i2c_qup) = {
> > +     .name   = "i2c_qup",
> > +     .id     = UCLASS_I2C,
> > +     .of_match = qup_i2c_ids,
> > +     .probe  = qup_i2c_probe,
> > +     .priv_auto = sizeof(struct qup_i2c_priv),
> > +     .ops    = &qup_i2c_ops,
> > +};
> >
>
> Thanks!
>
> bye,
> Heiko
> --
> DENX Software Engineering GmbH,      Managing Director: Erika Unter
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: +49-8142-66989-52   Fax: +49-8142-66989-80   Email: hs@denx.de

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

* Re: [PATCH 13/14] i2c: Add support for Qualcomm I2C driver
  2023-01-20 12:25     ` Sumit Garg
@ 2023-01-20 13:37       ` Stephan Gerhold
  2023-01-23  6:27         ` Sumit Garg
  0 siblings, 1 reply; 23+ messages in thread
From: Stephan Gerhold @ 2023-01-20 13:37 UTC (permalink / raw)
  To: Sumit Garg
  Cc: hs, u-boot, rfried.dev, joe.hershberger, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson

On Fri, Jan 20, 2023 at 05:55:35PM +0530, Sumit Garg wrote:
> On Fri, 20 Jan 2023 at 14:16, Heiko Schocher <hs@denx.de> wrote:
> > On 20.01.23 08:17, Sumit Garg wrote:
> > > Add support for Qualcomm I2C QUP driver which is inspired from
> > > corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c.
> > >
> > > Currently this driver only support FIFO polling mode which is sufficient
> > > to support devices like eeprom, rtc etc.
> > >
> > > Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
> > > Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
> > > Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> > > ---
> > >  drivers/i2c/Kconfig   |   6 +
> > >  drivers/i2c/Makefile  |   1 +
> > >  drivers/i2c/qup_i2c.c | 592 ++++++++++++++++++++++++++++++++++++++++++
> > >  3 files changed, 599 insertions(+)
> > >  create mode 100644 drivers/i2c/qup_i2c.c
> > >
> > > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> > > index 76e19918aa..2a1d36f91a 100644
> > > --- a/drivers/i2c/Kconfig
> > > +++ b/drivers/i2c/Kconfig
> > > @@ -635,6 +635,12 @@ config SYS_I2C_TEGRA
> > >       help
> > >         Support for NVIDIA I2C controller available in Tegra SoCs.
> > >
> > > +config SYS_I2C_QUP
> > > +     bool "Qualcomm I2C controller"
> > > +     depends on ARCH_SNAPDRAGON
> > > +     help
> > > +       Support for Qualcomm I2C controller.
> [...]
> > and may you can ad here some more infos
> > like link to datasheet?
> >
> 
> I will try to put more info here. However, I don't think the datasheet
> is available publically. This driver is inspired from its Linux
> counterpart only.
> 

FWIW: The I2C/SPI QUP controller is publicly documented in the
Snapdragon 410E (APQ8016E) Technical Reference Manual [1],
chapter "6.1 Qualcomm Universal Peripherals Engine (QUP)".

Stephan

[1]: https://developer.qualcomm.com/download/sd410/snapdragon-410e-technical-reference-manual.pdf

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

* Re: [PATCH 13/14] i2c: Add support for Qualcomm I2C driver
  2023-01-20 13:37       ` Stephan Gerhold
@ 2023-01-23  6:27         ` Sumit Garg
  0 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-23  6:27 UTC (permalink / raw)
  To: Stephan Gerhold
  Cc: hs, u-boot, rfried.dev, joe.hershberger, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson

On Fri, 20 Jan 2023 at 19:08, Stephan Gerhold <stephan@gerhold.net> wrote:
>
> On Fri, Jan 20, 2023 at 05:55:35PM +0530, Sumit Garg wrote:
> > On Fri, 20 Jan 2023 at 14:16, Heiko Schocher <hs@denx.de> wrote:
> > > On 20.01.23 08:17, Sumit Garg wrote:
> > > > Add support for Qualcomm I2C QUP driver which is inspired from
> > > > corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c.
> > > >
> > > > Currently this driver only support FIFO polling mode which is sufficient
> > > > to support devices like eeprom, rtc etc.
> > > >
> > > > Co-developed-by: Mike Worsfold <mworsfold@impinj.com>
> > > > Signed-off-by: Mike Worsfold <mworsfold@impinj.com>
> > > > Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> > > > ---
> > > >  drivers/i2c/Kconfig   |   6 +
> > > >  drivers/i2c/Makefile  |   1 +
> > > >  drivers/i2c/qup_i2c.c | 592 ++++++++++++++++++++++++++++++++++++++++++
> > > >  3 files changed, 599 insertions(+)
> > > >  create mode 100644 drivers/i2c/qup_i2c.c
> > > >
> > > > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> > > > index 76e19918aa..2a1d36f91a 100644
> > > > --- a/drivers/i2c/Kconfig
> > > > +++ b/drivers/i2c/Kconfig
> > > > @@ -635,6 +635,12 @@ config SYS_I2C_TEGRA
> > > >       help
> > > >         Support for NVIDIA I2C controller available in Tegra SoCs.
> > > >
> > > > +config SYS_I2C_QUP
> > > > +     bool "Qualcomm I2C controller"
> > > > +     depends on ARCH_SNAPDRAGON
> > > > +     help
> > > > +       Support for Qualcomm I2C controller.
> > [...]
> > > and may you can ad here some more infos
> > > like link to datasheet?
> > >
> >
> > I will try to put more info here. However, I don't think the datasheet
> > is available publically. This driver is inspired from its Linux
> > counterpart only.
> >
>
> FWIW: The I2C/SPI QUP controller is publicly documented in the
> Snapdragon 410E (APQ8016E) Technical Reference Manual [1],
> chapter "6.1 Qualcomm Universal Peripherals Engine (QUP)".
>

Thanks for the reference, I wasn't aware that APQ8016E has the same
QUP controller. I will include this reference instead.

-Sumit

> Stephan
>
> [1]: https://developer.qualcomm.com/download/sd410/snapdragon-410e-technical-reference-manual.pdf

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

* Re: [PATCH 00/14] QCS404: Add ethernet and I2C drivers
  2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
                   ` (13 preceding siblings ...)
  2023-01-20  7:17 ` [PATCH 14/14] dts: qcs404-evb: Add I2C controller nodes Sumit Garg
@ 2023-01-30 16:27 ` Sumit Garg
  14 siblings, 0 replies; 23+ messages in thread
From: Sumit Garg @ 2023-01-30 16:27 UTC (permalink / raw)
  To: joe.hershberger, rfried.dev
  Cc: u-boot, hs, stephan, mworsfold, lgillham, jbrennan,
	nicolas.dechesne, vinod.koul, daniel.thompson

On Fri, 20 Jan 2023 at 12:47, Sumit Garg <sumit.garg@linaro.org> wrote:
>
> Patch#1 is a fix for QCS404 system memory map to not map reserved memory
> regions as an occasional system hang is observed.
>
> Rest of the patches add support for Qualcomm ethernet

Gentle ping for any comments/feedback regarding the ethernet patches.

-Sumit

> and I2C drivers
> specifically tested on QCS404 SoC.
>
> Sumit Garg (14):
>   qcs404: sysmap: Don't map reserved memory ranges
>   qcs404-evb: Enable msm_gpio driver support
>   clocks: qcs404: Add support for ethernet clocks
>   pinctrl: qcs404: Enable ethernet pinmux options
>   pinctrl-snapdragon: Get rid of custom drive-strength values
>   net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional
>   net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz
>   net: dwc_eth_qos: Add Qcom ethernet driver glue layer
>   dts: qcs404-evb: Add ethernet controller node
>   clock-snapdragon: Add clk_rcg_set_rate() with mnd_width=0
>   clocks: qcs404: Add support for I2C clocks
>   pinctrl: qcs404: Enable I2C pinmux options
>   i2c: Add support for Qualcomm I2C driver
>   dts: qcs404-evb: Add I2C controller nodes
>
>  arch/arm/dts/dragonboard410c.dts              |   3 +-
>  arch/arm/dts/dragonboard820c.dts              |   3 +-
>  arch/arm/dts/qcom-ipq4019.dtsi                |   1 -
>  arch/arm/dts/qcs404-evb.dts                   | 200 +++++-
>  arch/arm/mach-snapdragon/clock-qcs404.c       | 118 ++++
>  arch/arm/mach-snapdragon/clock-snapdragon.c   |  24 +
>  arch/arm/mach-snapdragon/clock-snapdragon.h   |   2 +
>  .../include/mach/sysmap-qcs404.h              |  31 +
>  arch/arm/mach-snapdragon/pinctrl-qcs404.c     |  13 +
>  arch/arm/mach-snapdragon/pinctrl-snapdragon.c |   8 +-
>  arch/arm/mach-snapdragon/sysmap-qcs404.c      |  14 +-
>  configs/qcs404evb_defconfig                   |   1 +
>  drivers/i2c/Kconfig                           |   6 +
>  drivers/i2c/Makefile                          |   1 +
>  drivers/i2c/qup_i2c.c                         | 592 +++++++++++++++++
>  drivers/net/Kconfig                           |   7 +
>  drivers/net/Makefile                          |   1 +
>  drivers/net/dwc_eth_qos.c                     |  35 +-
>  drivers/net/dwc_eth_qos.h                     |   4 +
>  drivers/net/dwc_eth_qos_qcom.c                | 612 ++++++++++++++++++
>  .../dt-bindings/pinctrl/pinctrl-snapdragon.h  |  22 -
>  21 files changed, 1658 insertions(+), 40 deletions(-)
>  create mode 100644 drivers/i2c/qup_i2c.c
>  create mode 100644 drivers/net/dwc_eth_qos_qcom.c
>  delete mode 100644 include/dt-bindings/pinctrl/pinctrl-snapdragon.h
>
> --
> 2.34.1
>

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

* Re: [PATCH 06/14] net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional
  2023-01-20  7:17 ` [PATCH 06/14] net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional Sumit Garg
@ 2023-02-04  0:32   ` Ramon Fried
  0 siblings, 0 replies; 23+ messages in thread
From: Ramon Fried @ 2023-02-04  0:32 UTC (permalink / raw)
  To: Sumit Garg
  Cc: u-boot, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson

On Fri, Jan 20, 2023 at 9:18 AM Sumit Garg <sumit.garg@linaro.org> wrote:
>
> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> ---
>  drivers/net/dwc_eth_qos.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
> index afc47b56ff..753a912607 100644
> --- a/drivers/net/dwc_eth_qos.c
> +++ b/drivers/net/dwc_eth_qos.c
> @@ -774,10 +774,13 @@ static int eqos_start(struct udevice *dev)
>                 pr_err("eqos_calibrate_pads() failed: %d", ret);
>                 goto err_stop_resets;
>         }
> -       rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
>
> -       val = (rate / 1000000) - 1;
> -       writel(val, &eqos->mac_regs->us_tic_counter);
> +       if (eqos->config->ops->eqos_get_tick_clk_rate) {
> +               rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
> +
> +               val = (rate / 1000000) - 1;
> +               writel(val, &eqos->mac_regs->us_tic_counter);
> +       }
>
>         /*
>          * if PHY was already connected and configured,
> --
> 2.34.1
>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>

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

* Re: [PATCH 07/14] net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz
  2023-01-20  7:17 ` [PATCH 07/14] net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz Sumit Garg
@ 2023-02-04  0:32   ` Ramon Fried
  0 siblings, 0 replies; 23+ messages in thread
From: Ramon Fried @ 2023-02-04  0:32 UTC (permalink / raw)
  To: Sumit Garg
  Cc: u-boot, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson

On Fri, Jan 20, 2023 at 9:18 AM Sumit Garg <sumit.garg@linaro.org> wrote:
>
> The GMAC controller on QCS404 SoC (support added by upcoming patch) fails
> to work with maximum tx/rx_fifo_sz supported by the hardware (16K). So
> allow platforms to override FIFO size using corresponding DT node
> properties.
>
> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> ---
>  drivers/net/dwc_eth_qos.c | 19 +++++++++++++------
>  drivers/net/dwc_eth_qos.h |  1 +
>  2 files changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
> index 753a912607..65b8556be2 100644
> --- a/drivers/net/dwc_eth_qos.c
> +++ b/drivers/net/dwc_eth_qos.c
> @@ -852,12 +852,19 @@ static int eqos_start(struct udevice *dev)
>         rx_fifo_sz = (val >> EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT) &
>                 EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK;
>
> -       /*
> -        * r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting.
> -        * r/tqs is encoded as (n / 256) - 1.
> -        */
> -       tqs = (128 << tx_fifo_sz) / 256 - 1;
> -       rqs = (128 << rx_fifo_sz) / 256 - 1;
> +       /* r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting */
> +       tx_fifo_sz = 128 << tx_fifo_sz;
> +       rx_fifo_sz = 128 << rx_fifo_sz;
> +
> +       /* Allow platform to override TX/RX fifo size */
> +       if (eqos->tx_fifo_sz)
> +               tx_fifo_sz = eqos->tx_fifo_sz;
> +       if (eqos->rx_fifo_sz)
> +               rx_fifo_sz = eqos->rx_fifo_sz;
> +
> +       /* r/tqs is encoded as (n / 256) - 1 */
> +       tqs = tx_fifo_sz / 256 - 1;
> +       rqs = rx_fifo_sz / 256 - 1;
>
>         clrsetbits_le32(&eqos->mtl_regs->txq0_operation_mode,
>                         EQOS_MTL_TXQ0_OPERATION_MODE_TQS_MASK <<
> diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
> index 8fccd6f057..466a792de7 100644
> --- a/drivers/net/dwc_eth_qos.h
> +++ b/drivers/net/dwc_eth_qos.h
> @@ -276,6 +276,7 @@ struct eqos_priv {
>         bool started;
>         bool reg_access_ok;
>         bool clk_ck_enabled;
> +       unsigned int tx_fifo_sz, rx_fifo_sz;
>  };
>
>  void eqos_inval_desc_generic(void *desc);
> --
> 2.34.1
>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>

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

* Re: [PATCH 08/14] net: dwc_eth_qos: Add Qcom ethernet driver glue layer
  2023-01-20  7:17 ` [PATCH 08/14] net: dwc_eth_qos: Add Qcom ethernet driver glue layer Sumit Garg
@ 2023-02-04  0:33   ` Ramon Fried
  0 siblings, 0 replies; 23+ messages in thread
From: Ramon Fried @ 2023-02-04  0:33 UTC (permalink / raw)
  To: Sumit Garg
  Cc: u-boot, hs, joe.hershberger, stephan, mworsfold, lgillham,
	jbrennan, nicolas.dechesne, vinod.koul, daniel.thompson

On Fri, Jan 20, 2023 at 9:18 AM Sumit Garg <sumit.garg@linaro.org> wrote:
>
> The Qualcom ETHQOS hardware supports an RGMII macro which needs to be
> configured according to following link speeds:
> - SPEED_1000
> - SPEED_100
> - SPEED_10
>
> So add a corresponding glue driver to configure RGMII macro.
>
> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> ---
>  drivers/net/Kconfig            |   7 +
>  drivers/net/Makefile           |   1 +
>  drivers/net/dwc_eth_qos.c      |   7 +
>  drivers/net/dwc_eth_qos.h      |   3 +
>  drivers/net/dwc_eth_qos_qcom.c | 612 +++++++++++++++++++++++++++++++++
>  5 files changed, 630 insertions(+)
>  create mode 100644 drivers/net/dwc_eth_qos_qcom.c
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 7873538cc2..815e1f9248 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -242,6 +242,13 @@ config DWC_ETH_QOS_TEGRA186
>           The Synopsys Designware Ethernet QOS IP block with specific
>           configuration used in NVIDIA's Tegra186 chip.
>
> +config DWC_ETH_QOS_QCOM
> +       bool "Synopsys DWC Ethernet QOS device support for Qcom SoCs"
> +       depends on DWC_ETH_QOS
> +       help
> +         The Synopsys Designware Ethernet QOS IP block with specific
> +         configuration used in Qcom QCS404 SoC.
> +
>  config E1000
>         bool "Intel PRO/1000 Gigabit Ethernet support"
>         depends on PCI
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 5b4e60eea3..b009b10aca 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
>  obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o
>  obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
>  obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
> +obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o
>  obj-$(CONFIG_E1000) += e1000.o
>  obj-$(CONFIG_E1000_SPI) += e1000_spi.o
>  obj-$(CONFIG_EEPRO100) += eepro100.o
> diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
> index 65b8556be2..112deb546d 100644
> --- a/drivers/net/dwc_eth_qos.c
> +++ b/drivers/net/dwc_eth_qos.c
> @@ -1712,6 +1712,13 @@ static const struct udevice_id eqos_ids[] = {
>         },
>  #endif
>
> +#if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM)
> +       {
> +               .compatible = "qcom,qcs404-ethqos",
> +               .data = (ulong)&eqos_qcom_config
> +       },
> +#endif
> +
>         { }
>  };
>
> diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
> index 466a792de7..fddbe9336c 100644
> --- a/drivers/net/dwc_eth_qos.h
> +++ b/drivers/net/dwc_eth_qos.h
> @@ -253,6 +253,7 @@ struct eqos_priv {
>         struct eqos_mtl_regs *mtl_regs;
>         struct eqos_dma_regs *dma_regs;
>         struct eqos_tegra186_regs *tegra186_regs;
> +       void *eqos_qcom_rgmii_regs;
>         struct reset_ctl reset_ctl;
>         struct gpio_desc phy_reset_gpio;
>         struct clk clk_master_bus;
> @@ -277,6 +278,7 @@ struct eqos_priv {
>         bool reg_access_ok;
>         bool clk_ck_enabled;
>         unsigned int tx_fifo_sz, rx_fifo_sz;
> +       u32 reset_delays[3];
>  };
>
>  void eqos_inval_desc_generic(void *desc);
> @@ -286,3 +288,4 @@ void eqos_flush_buffer_generic(void *buf, size_t size);
>  int eqos_null_ops(struct udevice *dev);
>
>  extern struct eqos_config eqos_imx_config;
> +extern struct eqos_config eqos_qcom_config;
> diff --git a/drivers/net/dwc_eth_qos_qcom.c b/drivers/net/dwc_eth_qos_qcom.c
> new file mode 100644
> index 0000000000..df83f1c5f9
> --- /dev/null
> +++ b/drivers/net/dwc_eth_qos_qcom.c
> @@ -0,0 +1,612 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2022-2023 Sumit Garg <sumit.garg@linaro.org>
> + *
> + * Qcom DWMAC specific glue layer
> + */
> +
> +#include <common.h>
> +#include <asm/global_data.h>
> +#include <asm/gpio.h>
> +#include <asm/io.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <dm/device_compat.h>
> +#include <phy.h>
> +#include <reset.h>
> +#include <syscon.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +
> +#include "dwc_eth_qos.h"
> +
> +/* RGMII_IO_MACRO_CONFIG fields */
> +#define RGMII_CONFIG_FUNC_CLK_EN               BIT(30)
> +#define RGMII_CONFIG_POS_NEG_DATA_SEL          BIT(23)
> +#define RGMII_CONFIG_GPIO_CFG_RX_INT           GENMASK(21, 20)
> +#define RGMII_CONFIG_GPIO_CFG_TX_INT           GENMASK(19, 17)
> +#define RGMII_CONFIG_MAX_SPD_PRG_9             GENMASK(16, 8)
> +#define RGMII_CONFIG_MAX_SPD_PRG_2             GENMASK(7, 6)
> +#define RGMII_CONFIG_INTF_SEL                  GENMASK(5, 4)
> +#define RGMII_CONFIG_BYPASS_TX_ID_EN           BIT(3)
> +#define RGMII_CONFIG_LOOPBACK_EN               BIT(2)
> +#define RGMII_CONFIG_PROG_SWAP                 BIT(1)
> +#define RGMII_CONFIG_DDR_MODE                  BIT(0)
> +
> +/* SDCC_HC_REG_DLL_CONFIG fields */
> +#define SDCC_DLL_CONFIG_DLL_RST                        BIT(30)
> +#define SDCC_DLL_CONFIG_PDN                    BIT(29)
> +#define SDCC_DLL_CONFIG_MCLK_FREQ              GENMASK(26, 24)
> +#define SDCC_DLL_CONFIG_CDR_SELEXT             GENMASK(23, 20)
> +#define SDCC_DLL_CONFIG_CDR_EXT_EN             BIT(19)
> +#define SDCC_DLL_CONFIG_CK_OUT_EN              BIT(18)
> +#define SDCC_DLL_CONFIG_CDR_EN                 BIT(17)
> +#define SDCC_DLL_CONFIG_DLL_EN                 BIT(16)
> +#define SDCC_DLL_MCLK_GATING_EN                        BIT(5)
> +#define SDCC_DLL_CDR_FINE_PHASE                        GENMASK(3, 2)
> +
> +/* SDCC_HC_REG_DDR_CONFIG fields */
> +#define SDCC_DDR_CONFIG_PRG_DLY_EN             BIT(31)
> +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY       GENMASK(26, 21)
> +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE  GENMASK(29, 27)
> +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN    BIT(30)
> +#define SDCC_DDR_CONFIG_PRG_RCLK_DLY           GENMASK(8, 0)
> +
> +/* SDCC_HC_REG_DLL_CONFIG2 fields */
> +#define SDCC_DLL_CONFIG2_DLL_CLOCK_DIS         BIT(21)
> +#define SDCC_DLL_CONFIG2_MCLK_FREQ_CALC                GENMASK(17, 10)
> +#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL  GENMASK(3, 2)
> +#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW   BIT(1)
> +#define SDCC_DLL_CONFIG2_DDR_CAL_EN            BIT(0)
> +
> +/* SDC4_STATUS bits */
> +#define SDC4_STATUS_DLL_LOCK                   BIT(7)
> +
> +/* RGMII_IO_MACRO_CONFIG2 fields */
> +#define RGMII_CONFIG2_RSVD_CONFIG15            GENMASK(31, 17)
> +#define RGMII_CONFIG2_RGMII_CLK_SEL_CFG                BIT(16)
> +#define RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN     BIT(13)
> +#define RGMII_CONFIG2_CLK_DIVIDE_SEL           BIT(12)
> +#define RGMII_CONFIG2_RX_PROG_SWAP             BIT(7)
> +#define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL      BIT(6)
> +#define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN    BIT(5)
> +
> +struct dwmac_rgmii_regs {
> +       u32 io_macro_config;            /* 0x00 */
> +       u32 sdcc_hc_dll_config;         /* 0x04 */
> +       u32 reserved_1;                 /* 0x08 */
> +       u32 sdcc_hc_ddr_config;         /* 0x0c */
> +       u32 sdcc_hc_dll_config2;        /* 0x10 */
> +       u32 sdc4_status;                /* 0x14 */
> +       u32 sdcc_usr_ctl;               /* 0x18 */
> +       u32 io_macro_config2;           /* 0x1c */
> +       u32 io_macro_debug1;            /* 0x20 */
> +       u32 reserved_2;                 /* 0x24 */
> +       u32 emac_sys_low_power_dbg;     /* 0x28 */
> +       u32 reserved_3[53];             /* upto 0x100 */
> +};
> +
> +static struct dwmac_rgmii_regs emac_v2_3_0_por = {
> +       .io_macro_config = 0x00C01343,
> +       .sdcc_hc_dll_config = 0x2004642C,
> +       .sdcc_hc_ddr_config = 0x00000000,
> +       .sdcc_hc_dll_config2 = 0x00200000,
> +       .sdcc_usr_ctl = 0x00010800,
> +       .io_macro_config2 = 0x00002060
> +};
> +
> +static void ethqos_set_func_clk_en(struct dwmac_rgmii_regs *regs)
> +{
> +       setbits_le32(&regs->io_macro_config, RGMII_CONFIG_FUNC_CLK_EN);
> +}
> +
> +static int ethqos_dll_configure(struct udevice *dev,
> +                               struct dwmac_rgmii_regs *regs)
> +{
> +       unsigned int val;
> +       int retry = 1000;
> +
> +       /* Set CDR_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN);
> +
> +       /* Set CDR_EXT_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN);
> +
> +       /* Clear CK_OUT_EN */
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
> +
> +       /* Set DLL_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
> +
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN);
> +
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE);
> +
> +       /* Wait for CK_OUT_EN clear */
> +       do {
> +               val = readl(&regs->sdcc_hc_dll_config);
> +               val &= SDCC_DLL_CONFIG_CK_OUT_EN;
> +               if (!val)
> +                       break;
> +               mdelay(1);
> +               retry--;
> +       } while (retry > 0);
> +       if (!retry)
> +               dev_err(dev, "Clear CK_OUT_EN timedout\n");
> +
> +       /* Set CK_OUT_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
> +
> +       /* Wait for CK_OUT_EN set */
> +       retry = 1000;
> +       do {
> +               val = readl(&regs->sdcc_hc_dll_config);
> +               val &= SDCC_DLL_CONFIG_CK_OUT_EN;
> +               if (val)
> +                       break;
> +               mdelay(1);
> +               retry--;
> +       } while (retry > 0);
> +       if (!retry)
> +               dev_err(dev, "Set CK_OUT_EN timedout\n");
> +
> +       /* Set DDR_CAL_EN */
> +       setbits_le32(&regs->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN);
> +
> +       clrbits_le32(&regs->sdcc_hc_dll_config2,
> +                    SDCC_DLL_CONFIG2_DLL_CLOCK_DIS);
> +
> +       clrsetbits_le32(&regs->sdcc_hc_dll_config2,
> +                       SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10);
> +
> +       clrsetbits_le32(&regs->sdcc_hc_dll_config2,
> +                       SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2));
> +
> +       setbits_le32(&regs->sdcc_hc_dll_config2,
> +                    SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW);
> +
> +       return 0;
> +}
> +
> +static int ethqos_rgmii_macro_init(struct udevice *dev,
> +                                  struct dwmac_rgmii_regs *regs,
> +                                  unsigned long speed)
> +{
> +       /* Disable loopback mode */
> +       clrbits_le32(&regs->io_macro_config2,
> +                    RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN);
> +
> +       /* Select RGMII, write 0 to interface select */
> +       clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_INTF_SEL);
> +
> +       switch (speed) {
> +       case SPEED_1000:
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
> +               clrbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_BYPASS_TX_ID_EN);
> +               setbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_POS_NEG_DATA_SEL);
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
> +
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
> +               setbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RSVD_CONFIG15);
> +               setbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RX_PROG_SWAP);
> +
> +               /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */
> +               clrsetbits_le32(&regs->sdcc_hc_ddr_config,
> +                               SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57);
> +               setbits_le32(&regs->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN);
> +
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
> +               break;
> +
> +       case SPEED_100:
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
> +               setbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_BYPASS_TX_ID_EN);
> +               clrbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_POS_NEG_DATA_SEL);
> +               clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
> +               clrsetbits_le32(&regs->io_macro_config,
> +                               RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6));
> +
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
> +               setbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RSVD_CONFIG15);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RX_PROG_SWAP);
> +
> +               /* Write 0x5 to PRG_RCLK_DLY_CODE */
> +               clrsetbits_le32(&regs->sdcc_hc_ddr_config,
> +                               SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
> +                               (BIT(29) | BIT(27)));
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
> +
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
> +               break;
> +
> +       case SPEED_10:
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_DDR_MODE);
> +               setbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_BYPASS_TX_ID_EN);
> +               clrbits_le32(&regs->io_macro_config,
> +                            RGMII_CONFIG_POS_NEG_DATA_SEL);
> +               clrbits_le32(&regs->io_macro_config, RGMII_CONFIG_PROG_SWAP);
> +               clrsetbits_le32(&regs->io_macro_config,
> +                               RGMII_CONFIG_MAX_SPD_PRG_9,
> +                               BIT(12) | GENMASK(9, 8));
> +
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RSVD_CONFIG15);
> +               clrbits_le32(&regs->io_macro_config2,
> +                            RGMII_CONFIG2_RX_PROG_SWAP);
> +
> +               /* Write 0x5 to PRG_RCLK_DLY_CODE */
> +               clrsetbits_le32(&regs->sdcc_hc_ddr_config,
> +                               SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
> +                               (BIT(29) | BIT(27)));
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
> +               setbits_le32(&regs->sdcc_hc_ddr_config,
> +                            SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
> +
> +               setbits_le32(&regs->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
> +               break;
> +
> +       default:
> +               dev_err(dev, "Invalid speed %ld\n", speed);
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int ethqos_configure(struct udevice *dev,
> +                           struct dwmac_rgmii_regs *regs,
> +                           unsigned long speed)
> +{
> +       unsigned int retry = 1000;
> +
> +       /* Reset to POR values and enable clk */
> +       writel(emac_v2_3_0_por.io_macro_config, &regs->io_macro_config);
> +       writel(emac_v2_3_0_por.sdcc_hc_dll_config, &regs->sdcc_hc_dll_config);
> +       writel(emac_v2_3_0_por.sdcc_hc_ddr_config, &regs->sdcc_hc_ddr_config);
> +       writel(emac_v2_3_0_por.sdcc_hc_dll_config2, &regs->sdcc_hc_dll_config2);
> +       writel(emac_v2_3_0_por.sdcc_usr_ctl, &regs->sdcc_usr_ctl);
> +       writel(emac_v2_3_0_por.io_macro_config2, &regs->io_macro_config2);
> +
> +       ethqos_set_func_clk_en(regs);
> +
> +       /* Initialize the DLL first */
> +
> +       /* Set DLL_RST */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
> +
> +       /* Set PDN */
> +       setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
> +
> +       /* Clear DLL_RST */
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
> +
> +       /* Clear PDN */
> +       clrbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
> +
> +       if (speed == SPEED_1000) {
> +               /* Set DLL_EN */
> +               setbits_le32(&regs->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
> +
> +               /* Set CK_OUT_EN */
> +               setbits_le32(&regs->sdcc_hc_dll_config,
> +                            SDCC_DLL_CONFIG_CK_OUT_EN);
> +
> +               /* Set USR_CTL bit 26 with mask of 3 bits */
> +               clrsetbits_le32(&regs->sdcc_usr_ctl, GENMASK(26, 24), BIT(26));
> +
> +               /* wait for DLL LOCK */
> +               do {
> +                       mdelay(1);
> +                       if (readl(&regs->sdc4_status) & SDC4_STATUS_DLL_LOCK)
> +                               break;
> +                       retry--;
> +               } while (retry > 0);
> +               if (!retry)
> +                       dev_err(dev, "Timeout while waiting for DLL lock\n");
> +
> +               ethqos_dll_configure(dev, regs);
> +       }
> +
> +       ethqos_rgmii_macro_init(dev, regs, speed);
> +
> +       return 0;
> +}
> +
> +static void ethqos_rgmii_dump(struct udevice *dev,
> +                             struct dwmac_rgmii_regs *regs)
> +{
> +       dev_dbg(dev, "Rgmii register dump\n");
> +       dev_dbg(dev, "RGMII_IO_MACRO_CONFIG: %08x\n",
> +               readl(&regs->io_macro_config));
> +       dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n",
> +               readl(&regs->sdcc_hc_dll_config));
> +       dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n",
> +               readl(&regs->sdcc_hc_ddr_config));
> +       dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n",
> +               readl(&regs->sdcc_hc_dll_config2));
> +       dev_dbg(dev, "SDC4_STATUS: %08x\n",
> +               readl(&regs->sdc4_status));
> +       dev_dbg(dev, "SDCC_USR_CTL: %08x\n",
> +               readl(&regs->sdcc_usr_ctl));
> +       dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n",
> +               readl(&regs->io_macro_config2));
> +       dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n",
> +               readl(&regs->io_macro_debug1));
> +       dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n",
> +               readl(&regs->emac_sys_low_power_dbg));
> +}
> +
> +static int qcom_eqos_rgmii_set_speed(struct udevice *dev,
> +                                    void *rgmii_regs,
> +                                    unsigned long speed)
> +{
> +       int ret;
> +
> +       ethqos_rgmii_dump(dev, rgmii_regs);
> +
> +       ret = ethqos_configure(dev, rgmii_regs, speed);
> +       if (ret)
> +               return ret;
> +
> +       ethqos_rgmii_dump(dev, rgmii_regs);
> +
> +       return 0;
> +}
> +
> +static int qcom_eqos_rgmii_reset(struct udevice *dev, void *rgmii_regs)
> +{
> +       ethqos_set_func_clk_en(rgmii_regs);
> +
> +       return 0;
> +}
> +
> +static int eqos_start_clks_qcom(struct udevice *dev)
> +{
> +       if (IS_ENABLED(CONFIG_CLK)) {
> +               struct clk_bulk clocks;
> +               int ret;
> +
> +               ret = clk_get_bulk(dev, &clocks);
> +               if (ret)
> +                       return ret;
> +
> +               ret = clk_enable_bulk(&clocks);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static int eqos_stop_clks_qcom(struct udevice *dev)
> +{
> +       if (IS_ENABLED(CONFIG_CLK)) {
> +               struct clk_bulk clocks;
> +               int ret;
> +
> +               ret = clk_get_bulk(dev, &clocks);
> +               if (ret)
> +                       return ret;
> +
> +               ret = clk_disable_bulk(&clocks);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static int eqos_start_resets_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +       int ret;
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       if (!eqos->phy) {
> +               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
> +               if (ret < 0) {
> +                       pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d", ret);
> +                       return ret;
> +               }
> +
> +               udelay(eqos->reset_delays[0]);
> +
> +               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1);
> +               if (ret < 0) {
> +                       pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
> +                       return ret;
> +               }
> +
> +               udelay(eqos->reset_delays[1]);
> +
> +               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
> +               if (ret < 0) {
> +                       pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret);
> +                       return ret;
> +               }
> +
> +               udelay(eqos->reset_delays[2]);
> +       }
> +
> +       ret = reset_deassert(&eqos->reset_ctl);
> +       if (ret < 0) {
> +               pr_err("reset_deassert() failed: %d", ret);
> +               return ret;
> +       }
> +
> +       ret = qcom_eqos_rgmii_reset(dev, eqos->eqos_qcom_rgmii_regs);
> +       if (ret < 0) {
> +               pr_err("qcom rgmii_reset failed: %d", ret);
> +               return ret;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +/* Clock rates */
> +#define RGMII_1000_NOM_CLK_FREQ                        (250 * 1000 * 1000UL)
> +#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ      (50 * 1000 * 1000UL)
> +#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ        (5 * 1000 * 1000UL)
> +
> +static int eqos_set_tx_clk_speed_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +       ulong rate;
> +       int ret;
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       switch (eqos->phy->speed) {
> +       case SPEED_1000:
> +               rate = RGMII_1000_NOM_CLK_FREQ;
> +               break;
> +       case SPEED_100:
> +               rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ;
> +               break;
> +       case SPEED_10:
> +               rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ;
> +               break;
> +       default:
> +               pr_err("invalid speed %d", eqos->phy->speed);
> +               return -EINVAL;
> +       }
> +
> +       ret = clk_set_rate(&eqos->clk_tx, rate);
> +       if (ret < 0) {
> +               pr_err("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret);
> +               return ret;
> +       }
> +
> +       ret = qcom_eqos_rgmii_set_speed(dev, eqos->eqos_qcom_rgmii_regs,
> +                                       eqos->phy->speed);
> +       if (ret < 0) {
> +               pr_err("qcom set_speed: %d, failed: %d", eqos->phy->speed, ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int eqos_probe_resources_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +       phy_interface_t interface;
> +       int reset_flags = GPIOD_IS_OUT;
> +       int ret;
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       interface = eqos->config->interface(dev);
> +
> +       if (interface == PHY_INTERFACE_MODE_NA) {
> +               pr_err("Invalid PHY interface\n");
> +               return -EINVAL;
> +       }
> +
> +       eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0);
> +
> +       eqos->tx_fifo_sz = dev_read_u32_default(dev, "tx-fifo-depth", 0);
> +       eqos->rx_fifo_sz = dev_read_u32_default(dev, "rx-fifo-depth", 0);
> +
> +       ret = reset_get_by_name(dev, "emac", &eqos->reset_ctl);
> +       if (ret) {
> +               pr_err("reset_get_by_name(rst) failed: %d", ret);
> +               return ret;
> +       }
> +
> +       if (dev_read_bool(dev, "snps,reset-active-low"))
> +               reset_flags |= GPIOD_ACTIVE_LOW;
> +
> +       ret = gpio_request_by_name(dev, "snps,reset-gpio", 0,
> +                                  &eqos->phy_reset_gpio, reset_flags);
> +       if (ret == 0) {
> +               ret = dev_read_u32_array(dev, "snps,reset-delays-us",
> +                                        eqos->reset_delays, 3);
> +       } else if (ret == -ENOENT) {
> +               ret = 0;
> +       }
> +
> +       eqos->eqos_qcom_rgmii_regs = (void *)dev_read_addr_name(dev, "rgmii");
> +       if ((fdt_addr_t)eqos->eqos_qcom_rgmii_regs == FDT_ADDR_T_NONE) {
> +               pr_err("Invalid RGMII address\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = clk_get_by_name(dev, "rgmii", &eqos->clk_tx);
> +       if (ret) {
> +               pr_err("clk_get_by_name(tx) failed: %d", ret);
> +               return -EINVAL;
> +       }
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static int eqos_remove_resources_qcom(struct udevice *dev)
> +{
> +       struct eqos_priv *eqos = dev_get_priv(dev);
> +
> +       debug("%s(dev=%p):\n", __func__, dev);
> +
> +       clk_free(&eqos->clk_tx);
> +       dm_gpio_free(dev, &eqos->phy_reset_gpio);
> +       reset_free(&eqos->reset_ctl);
> +
> +       debug("%s: OK\n", __func__);
> +       return 0;
> +}
> +
> +static struct eqos_ops eqos_qcom_ops = {
> +       .eqos_inval_desc = eqos_inval_desc_generic,
> +       .eqos_flush_desc = eqos_flush_desc_generic,
> +       .eqos_inval_buffer = eqos_inval_buffer_generic,
> +       .eqos_flush_buffer = eqos_flush_buffer_generic,
> +       .eqos_probe_resources = eqos_probe_resources_qcom,
> +       .eqos_remove_resources = eqos_remove_resources_qcom,
> +       .eqos_stop_resets = eqos_null_ops,
> +       .eqos_start_resets = eqos_start_resets_qcom,
> +       .eqos_stop_clks = eqos_stop_clks_qcom,
> +       .eqos_start_clks = eqos_start_clks_qcom,
> +       .eqos_calibrate_pads = eqos_null_ops,
> +       .eqos_disable_calibration = eqos_null_ops,
> +       .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_qcom,
> +       .eqos_get_enetaddr = eqos_null_ops,
> +};
> +
> +struct eqos_config __maybe_unused eqos_qcom_config = {
> +       .reg_access_always_ok = false,
> +       .mdio_wait = 10,
> +       .swr_wait = 50,
> +       .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
> +       .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
> +       .axi_bus_width = EQOS_AXI_WIDTH_64,
> +       .interface = dev_read_phy_mode,
> +       .ops = &eqos_qcom_ops
> +};
> --
> 2.34.1
>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>

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

end of thread, other threads:[~2023-02-04  0:38 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-20  7:17 [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg
2023-01-20  7:17 ` [PATCH 01/14] qcs404: sysmap: Don't map reserved memory ranges Sumit Garg
2023-01-20  7:17 ` [PATCH 02/14] qcs404-evb: Enable msm_gpio driver support Sumit Garg
2023-01-20  7:17 ` [PATCH 03/14] clocks: qcs404: Add support for ethernet clocks Sumit Garg
2023-01-20  7:17 ` [PATCH 04/14] pinctrl: qcs404: Enable ethernet pinmux options Sumit Garg
2023-01-20  7:17 ` [PATCH 05/14] pinctrl-snapdragon: Get rid of custom drive-strength values Sumit Garg
2023-01-20  7:17 ` [PATCH 06/14] net: dwc_eth_qos: Make eqos_get_tick_clk_rate callback optional Sumit Garg
2023-02-04  0:32   ` Ramon Fried
2023-01-20  7:17 ` [PATCH 07/14] net: dwc_eth_qos: Allow platform to override tx/rx_fifo_sz Sumit Garg
2023-02-04  0:32   ` Ramon Fried
2023-01-20  7:17 ` [PATCH 08/14] net: dwc_eth_qos: Add Qcom ethernet driver glue layer Sumit Garg
2023-02-04  0:33   ` Ramon Fried
2023-01-20  7:17 ` [PATCH 09/14] dts: qcs404-evb: Add ethernet controller node Sumit Garg
2023-01-20  7:17 ` [PATCH 10/14] clock-snapdragon: Add clk_rcg_set_rate() with mnd_width=0 Sumit Garg
2023-01-20  7:17 ` [PATCH 11/14] clocks: qcs404: Add support for I2C clocks Sumit Garg
2023-01-20  7:17 ` [PATCH 12/14] pinctrl: qcs404: Enable I2C pinmux options Sumit Garg
2023-01-20  7:17 ` [PATCH 13/14] i2c: Add support for Qualcomm I2C driver Sumit Garg
2023-01-20  8:46   ` Heiko Schocher
2023-01-20 12:25     ` Sumit Garg
2023-01-20 13:37       ` Stephan Gerhold
2023-01-23  6:27         ` Sumit Garg
2023-01-20  7:17 ` [PATCH 14/14] dts: qcs404-evb: Add I2C controller nodes Sumit Garg
2023-01-30 16:27 ` [PATCH 00/14] QCS404: Add ethernet and I2C drivers Sumit Garg

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.