[PATCH 00/14] QCS404: Add ethernet and I2C drivers

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

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 }, {

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

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

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,

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

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,

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

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

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

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(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN); + + /* Set CDR_EXT_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN); + + /* Clear CK_OUT_EN */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Set DLL_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); + + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN); + + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE); + + /* Wait for CK_OUT_EN clear */ + do { + val = readl(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Wait for CK_OUT_EN set */ + retry = 1000; + do { + val = readl(®s->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(®s->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN); + + clrbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_DLL_CLOCK_DIS); + + clrsetbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10); + + clrsetbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2)); + + setbits_le32(®s->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(®s->io_macro_config2, + RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN); + + /* Select RGMII, write 0 to interface select */ + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_INTF_SEL); + + switch (speed) { + case SPEED_1000: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + setbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57); + setbits_le32(®s->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN); + + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); + break; + + case SPEED_100: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + clrsetbits_le32(®s->io_macro_config, + RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6)); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Write 0x5 to PRG_RCLK_DLY_CODE */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, + (BIT(29) | BIT(27))); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); + + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); + break; + + case SPEED_10: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + clrsetbits_le32(®s->io_macro_config, + RGMII_CONFIG_MAX_SPD_PRG_9, + BIT(12) | GENMASK(9, 8)); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Write 0x5 to PRG_RCLK_DLY_CODE */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, + (BIT(29) | BIT(27))); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); + + setbits_le32(®s->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, ®s->io_macro_config); + writel(emac_v2_3_0_por.sdcc_hc_dll_config, ®s->sdcc_hc_dll_config); + writel(emac_v2_3_0_por.sdcc_hc_ddr_config, ®s->sdcc_hc_ddr_config); + writel(emac_v2_3_0_por.sdcc_hc_dll_config2, ®s->sdcc_hc_dll_config2); + writel(emac_v2_3_0_por.sdcc_usr_ctl, ®s->sdcc_usr_ctl); + writel(emac_v2_3_0_por.io_macro_config2, ®s->io_macro_config2); + + ethqos_set_func_clk_en(regs); + + /* Initialize the DLL first */ + + /* Set DLL_RST */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); + + /* Set PDN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); + + /* Clear DLL_RST */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); + + /* Clear PDN */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); + + if (speed == SPEED_1000) { + /* Set DLL_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); + + /* Set CK_OUT_EN */ + setbits_le32(®s->sdcc_hc_dll_config, + SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Set USR_CTL bit 26 with mask of 3 bits */ + clrsetbits_le32(®s->sdcc_usr_ctl, GENMASK(26, 24), BIT(26)); + + /* wait for DLL LOCK */ + do { + mdelay(1); + if (readl(®s->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(®s->io_macro_config)); + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n", + readl(®s->sdcc_hc_dll_config)); + dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n", + readl(®s->sdcc_hc_ddr_config)); + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n", + readl(®s->sdcc_hc_dll_config2)); + dev_dbg(dev, "SDC4_STATUS: %08x\n", + readl(®s->sdc4_status)); + dev_dbg(dev, "SDCC_USR_CTL: %08x\n", + readl(®s->sdcc_usr_ctl)); + dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n", + readl(®s->io_macro_config2)); + dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n", + readl(®s->io_macro_debug1)); + dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n", + readl(®s->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 +};

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(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN);
/* Set CDR_EXT_EN */
setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN);
/* Clear CK_OUT_EN */
clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
/* Set DLL_EN */
setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN);
clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE);
/* Wait for CK_OUT_EN clear */
do {
val = readl(®s->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(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN);
/* Wait for CK_OUT_EN set */
retry = 1000;
do {
val = readl(®s->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(®s->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN);
clrbits_le32(®s->sdcc_hc_dll_config2,
SDCC_DLL_CONFIG2_DLL_CLOCK_DIS);
clrsetbits_le32(®s->sdcc_hc_dll_config2,
SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10);
clrsetbits_le32(®s->sdcc_hc_dll_config2,
SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2));
setbits_le32(®s->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(®s->io_macro_config2,
RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN);
/* Select RGMII, write 0 to interface select */
clrbits_le32(®s->io_macro_config, RGMII_CONFIG_INTF_SEL);
switch (speed) {
case SPEED_1000:
setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE);
clrbits_le32(®s->io_macro_config,
RGMII_CONFIG_BYPASS_TX_ID_EN);
setbits_le32(®s->io_macro_config,
RGMII_CONFIG_POS_NEG_DATA_SEL);
setbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP);
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
setbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_RSVD_CONFIG15);
setbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_RX_PROG_SWAP);
/* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */
clrsetbits_le32(®s->sdcc_hc_ddr_config,
SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57);
setbits_le32(®s->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN);
setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
break;
case SPEED_100:
setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE);
setbits_le32(®s->io_macro_config,
RGMII_CONFIG_BYPASS_TX_ID_EN);
clrbits_le32(®s->io_macro_config,
RGMII_CONFIG_POS_NEG_DATA_SEL);
clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP);
clrsetbits_le32(®s->io_macro_config,
RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6));
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
setbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_RSVD_CONFIG15);
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_RX_PROG_SWAP);
/* Write 0x5 to PRG_RCLK_DLY_CODE */
clrsetbits_le32(®s->sdcc_hc_ddr_config,
SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
(BIT(29) | BIT(27)));
setbits_le32(®s->sdcc_hc_ddr_config,
SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
setbits_le32(®s->sdcc_hc_ddr_config,
SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN);
break;
case SPEED_10:
setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE);
setbits_le32(®s->io_macro_config,
RGMII_CONFIG_BYPASS_TX_ID_EN);
clrbits_le32(®s->io_macro_config,
RGMII_CONFIG_POS_NEG_DATA_SEL);
clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP);
clrsetbits_le32(®s->io_macro_config,
RGMII_CONFIG_MAX_SPD_PRG_9,
BIT(12) | GENMASK(9, 8));
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL);
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN);
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_RSVD_CONFIG15);
clrbits_le32(®s->io_macro_config2,
RGMII_CONFIG2_RX_PROG_SWAP);
/* Write 0x5 to PRG_RCLK_DLY_CODE */
clrsetbits_le32(®s->sdcc_hc_ddr_config,
SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
(BIT(29) | BIT(27)));
setbits_le32(®s->sdcc_hc_ddr_config,
SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY);
setbits_le32(®s->sdcc_hc_ddr_config,
SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN);
setbits_le32(®s->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, ®s->io_macro_config);
writel(emac_v2_3_0_por.sdcc_hc_dll_config, ®s->sdcc_hc_dll_config);
writel(emac_v2_3_0_por.sdcc_hc_ddr_config, ®s->sdcc_hc_ddr_config);
writel(emac_v2_3_0_por.sdcc_hc_dll_config2, ®s->sdcc_hc_dll_config2);
writel(emac_v2_3_0_por.sdcc_usr_ctl, ®s->sdcc_usr_ctl);
writel(emac_v2_3_0_por.io_macro_config2, ®s->io_macro_config2);
ethqos_set_func_clk_en(regs);
/* Initialize the DLL first */
/* Set DLL_RST */
setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
/* Set PDN */
setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
/* Clear DLL_RST */
clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST);
/* Clear PDN */
clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN);
if (speed == SPEED_1000) {
/* Set DLL_EN */
setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN);
/* Set CK_OUT_EN */
setbits_le32(®s->sdcc_hc_dll_config,
SDCC_DLL_CONFIG_CK_OUT_EN);
/* Set USR_CTL bit 26 with mask of 3 bits */
clrsetbits_le32(®s->sdcc_usr_ctl, GENMASK(26, 24), BIT(26));
/* wait for DLL LOCK */
do {
mdelay(1);
if (readl(®s->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(®s->io_macro_config));
dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n",
readl(®s->sdcc_hc_dll_config));
dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n",
readl(®s->sdcc_hc_ddr_config));
dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n",
readl(®s->sdcc_hc_dll_config2));
dev_dbg(dev, "SDC4_STATUS: %08x\n",
readl(®s->sdc4_status));
dev_dbg(dev, "SDCC_USR_CTL: %08x\n",
readl(®s->sdcc_usr_ctl));
dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n",
readl(®s->io_macro_config2));
dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n",
readl(®s->io_macro_debug1));
dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n",
readl(®s->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

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 = <ðernet_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

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

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)

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,

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

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

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....
-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

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-refe...

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

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

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
participants (4)
-
Heiko Schocher
-
Ramon Fried
-
Stephan Gerhold
-
Sumit Garg