diff mbox series

[v1,12/17] drivers: phy: add combo phy driver for agilex5

Message ID 20230621031610.28401-13-jit.loon.lim@intel.com
State Needs Review / ACK, archived
Delegated to: Marek Vasut
Headers show
Series Agilex5 Platform Enablement | expand

Commit Message

Jit Loon Lim June 21, 2023, 3:16 a.m. UTC
This is for new platform enablement for agilex5.
Add combo phy driver for new platform.

Signed-off-by: Jit Loon Lim <jit.loon.lim@intel.com>
---
 drivers/phy/cadence/Kconfig                |   9 +
 drivers/phy/cadence/Makefile               |   1 +
 drivers/phy/cadence/phy-cadence-combophy.c | 855 +++++++++++++++++++++
 3 files changed, 865 insertions(+)
 create mode 100644 drivers/phy/cadence/phy-cadence-combophy.c
diff mbox series

Patch

diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
index 549ddbf504..61d1b36be2 100644
--- a/drivers/phy/cadence/Kconfig
+++ b/drivers/phy/cadence/Kconfig
@@ -1,3 +1,12 @@ 
+config PHY_CADENCE_COMBOPHY
+	tristate "Cadence ComboPhy PHY Driver"
+	depends on MMC_SDHCI_CADENCE
+	help
+	  Enable this to support the Cadence ComboPhy PHY driver
+	  A single module that encapsulates all functionality required to interface
+	  to external NAND Flash, eMMC devices, and SD cards for command and data
+	  transfer.
+
 config PHY_CADENCE_SIERRA
 	tristate "Cadence Sierra PHY Driver"
 	depends on DM_RESET
diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
index af63b32d9f..3092b9f3b8 100644
--- a/drivers/phy/cadence/Makefile
+++ b/drivers/phy/cadence/Makefile
@@ -1,2 +1,3 @@ 
 obj-$(CONFIG_$(SPL_)PHY_CADENCE_SIERRA)	+= phy-cadence-sierra.o
 obj-$(CONFIG_$(SPL_)PHY_CADENCE_TORRENT) += phy-cadence-torrent.o
+obj-$(CONFIG_PHY_CADENCE_COMBOPHY) += phy-cadence-combophy.o
diff --git a/drivers/phy/cadence/phy-cadence-combophy.c b/drivers/phy/cadence/phy-cadence-combophy.c
new file mode 100644
index 0000000000..c49c45e5a0
--- /dev/null
+++ b/drivers/phy/cadence/phy-cadence-combophy.c
@@ -0,0 +1,855 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence COMBOPHY PHY Driver
+ *
+ * Based on the linux driver provided by Cadence Sierra
+ *
+ * Copyright (c) 2022 - 2023 Intel Corporation
+ * Author: Lee, Kah Jing <kah.jing.lee@intel.com>
+ *
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <generic-phy.h>
+#include <reset.h>
+#include <reset-uclass.h>
+#include <wait_bit.h>
+#include <asm/arch/clock_manager.h>
+#include <asm/arch/firewall.h>
+#include <asm/arch/reset_manager.h>
+#include <asm/arch/system_manager.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/devres.h>
+#include <dm/lists.h>
+#include <dm/read.h>
+#include <dm/uclass.h>
+#include <dt-bindings/phy/phy.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+
+/* PHY register offsets */
+/* General define */
+#define BIT_ZERO	0
+#define BIT_ONE		1
+#define SD_HOST_CLK 200000000
+
+/* SDIO Card UHS-I modes */
+#define SDIO_MODE_SDR12		0
+#define SDIO_MODE_SDR25		1
+#define SDIO_MODE_SDR50		2
+#define SDIO_MODE_SDR104	3
+#define SDIO_MODE_DDR50		4
+#define SD_MODE_HS			5
+
+/* eMMC modes */
+#define eMMC_MODE_SDR		0
+#define eMMC_MODE_HS		1
+#define eMMC_MODE_HS200		2
+#define eMMC_MODE_HS400		3
+/* SW_RESET_REG */
+#define SDHCI_CDNS_HRS00		0x00
+#define SDHCI_CDNS_HRS00_SWR		BIT(0)
+/* PHY access port */
+#define SDHCI_CDNS_HRS04		0x10
+#define SDHCI_CDNS_HRS04_ADDR		GENMASK(5, 0)
+/* PHY data access port */
+#define SDHCI_CDNS_HRS05		0x14
+/* eMMC control registers */
+#define SDHCI_CDNS_HRS06		0x18
+
+#define READ_OP				0
+#define WRITE_OP			1
+#define READ_WRITE_OP			2
+#define POLLING_OP			3
+#define READ_PHY_OP			4
+#define READ_WRITE_PHY_OP		5
+
+#define PHY_TYPE_NAND		0
+#define PHY_TYPE_SDMMC		1
+#define PHY_REG_LEN		15
+/* IO_DELAY_INFO_REG */
+#define SDHCI_CDNS_HRS07			0x1c
+#define SDHCI_CDNS_HRS07_RW_COMPENSATE		GENMASK(20, 16)
+#define SDHCI_CDNS_HRS07_IDELAY_VAL		GENMASK(4, 0)
+/* TODO: check DV dfi_init val=9 */
+#define SDHCI_CDNS_HRS07_RW_COMPENSATE_DATA		0x9
+/* TODO: check DV dfi_init val=8; DDR Mode */
+#define SDHCI_CDNS_HRS07_RW_COMPENSATE_DATA_DDR	0x8
+#define SDHCI_CDNS_HRS07_IDELAY_VAL_DATA	0x0
+/* PHY reset port */
+#define SDHCI_CDNS_HRS09			0x24
+#define SDHCI_CDNS_HRS09_PHY_SW_RESET		BIT(0)
+#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE	BIT(1)
+#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE	BIT(2)
+#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE	BIT(3)
+#define SDHCI_CDNS_HRS09_RDCMD_EN		BIT(15)
+#define SDHCI_CDNS_HRS09_RDDATA_EN		BIT(16)
+
+/* PHY reset port */
+#define SDHCI_CDNS_HRS10			0x28
+#define SDHCI_CDNS_HRS10_HCSDCLKADJ		GENMASK(19, 16)
+#define SDHCI_CDNS_HRS10_HCSDCLKADJ_DATA	0x0
+/* HCSDCLKADJ DATA; DDR Mode */
+#define SDHCI_CDNS_HRS10_HCSDCLKADJ_DATA_DDR	0x2
+
+/* CMD_DATA_OUTPUT */
+#define SDHCI_CDNS_HRS16			0x40
+#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY	GENMASK(31, 28)
+#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY	GENMASK(27, 24)
+#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY	GENMASK(23, 20)
+#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY	GENMASK(19, 16)
+#define SDHCI_CDNS_HRS16_WRDATA1_DLY		GENMASK(15, 12)
+#define SDHCI_CDNS_HRS16_WRDATA0_DLY		GENMASK(11, 8)
+#define SDHCI_CDNS_HRS16_WRCMD1_DLY			GENMASK(7, 4)
+#define SDHCI_CDNS_HRS16_WRCMD0_DLY			GENMASK(3, 0)
+#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY_DATA		0x0
+#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY_DATA_DDR	0x1
+#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY_DATA		0x0
+#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY_DATA_DDR	0x1
+#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY_DATA		0x0
+#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY_DATA		0x0
+#define SDHCI_CDNS_HRS16_WRDATA1_DLY_DATA			0x0
+/* TODO: check DV dfi_init val=1 */
+#define SDHCI_CDNS_HRS16_WRDATA0_DLY_DATA			0x1
+#define SDHCI_CDNS_HRS16_WRCMD1_DLY_DATA			0x0
+/* TODO: check DV dfi_init val=1 */
+#define SDHCI_CDNS_HRS16_WRCMD0_DLY_DATA			0x1
+
+/* SRS - Slot Register Set (SDHCI-compatible) */
+#define SDHCI_CDNS_SRS_BASE					0x200
+#define SDHCI_CDNS_SRS00					0x200
+#define SDHCI_CDNS_SRS01					0x204
+#define SDHCI_CDNS_SRS02					0x208
+#define SDHCI_CDNS_SRS03					0x20c
+#define SDHCI_CDNS_SRS04					0x210
+#define SDHCI_CDNS_SRS08					0x220
+#define SDHCI_CDNS_SRS09					0x224
+#define SDHCI_CDNS_SRS09_CI					BIT(16)
+#define SDHCI_CDNS_SRS10					0x228
+#define SDHCI_CDNS_SRS10_VOLT_ON FIELD_PREP(GENMASK(11, 9), BIT_ONE)\
+	| FIELD_PREP(BIT(8), BIT_ONE) | FIELD_PREP(BIT(0), BIT_ZERO)
+#define SDHCI_CDNS_SRS10_VOLT_OFF FIELD_PREP(GENMASK(11, 9), BIT_ONE)\
+	| FIELD_PREP(BIT(8), BIT_ZERO)
+
+#define SDHCI_CDNS_SRS11					0x22c
+#define SDHCI_CDNS_SRS11_RST					0x0
+#define SDHCI_CDNS_SRS12					0x230
+#define SDHCI_CDNS_SRS13					0x234
+#define SDHCI_CDNS_SRS13_DATA					0xffffffff
+#define SDHCI_CDNS_SRS14					0x238
+#define SDHCI_CDNS_SRS15					0x23c
+
+/* PHY register values */
+#define PHY_DQ_TIMING_REG					0x2000
+#define PHY_DQS_TIMING_REG					0x2004
+#define PHY_GATE_LPBK_CTRL_REG					0x2008
+#define PHY_DLL_MASTER_CTRL_REG					0x200C
+#define PHY_DLL_SLAVE_CTRL_REG					0x2010
+#define PHY_CTRL_REG						0x2080
+#define USE_EXT_LPBK_DQS					BIT(22)
+#define USE_LPBK_DQS						BIT(21)
+#define USE_PHONY_DQS						BIT(20)
+#define USE_PHONY_DQS_CMD					BIT(19)
+#define SYNC_METHOD						BIT(31)
+#define SW_HALF_CYCLE_SHIFT					BIT(28)
+#define RD_DEL_SEL						GENMASK(24, 19)
+#define RD_DEL_SEL_DATA						0x34
+#define GATE_CFG_ALWAYS_ON					BIT(6)
+#define UNDERRUN_SUPPRESS					BIT(18)
+#define PARAM_DLL_BYPASS_MODE				BIT(23)
+#define PARAM_PHASE_DETECT_SEL				GENMASK(22, 20)
+#define PARAM_DLL_START_POINT				GENMASK(7, 0)
+#define PARAM_PHASE_DETECT_SEL_DATA			0x2
+#define PARAM_DLL_START_POINT_DATA			0x4
+#define PARAM_DLL_START_POINT_DATA_SDR50		254
+
+#define READ_DQS_CMD_DELAY		GENMASK(31, 24)
+#define CLK_WRDQS_DELAY			GENMASK(23, 16)
+#define CLK_WR_DELAY			GENMASK(15, 8)
+#define READ_DQS_DELAY			GENMASK(7, 0)
+#define READ_DQS_CMD_DELAY_DATA		0x0
+#define CLK_WRDQS_DELAY_DATA		0xca
+#define CLK_WR_DELAY_DATA		0xca
+#define READ_DQS_DELAY_DATA		0x0
+
+#define PHONY_DQS_TIMING		GENMASK(9, 4)
+#define PHONY_DQS_TIMING_DATA		0x0
+
+#define IO_MASK_ALWAYS_ON		BIT(31)
+#define IO_MASK_END			GENMASK(29, 27)
+#define IO_MASK_START			GENMASK(26, 24)
+#define DATA_SELECT_OE_END		GENMASK(2, 0)
+/* TODO: check DV dfi_init val=0 */
+#define IO_MASK_END_DATA		0x5
+/* TODO: check DV dfi_init val=2; DDR Mode */
+#define IO_MASK_END_DATA_DDR		0x2
+#define IO_MASK_START_DATA		0x0
+#define DATA_SELECT_OE_END_DATA		0x1
+
+/* rstmgr settings */
+#define COMBOPHY_RST				BIT(6)
+#define SDMMC_RST				BIT(7)
+
+struct cdns_combophy_reg_pairs {
+	u32 reg;
+	u32 val;
+	u32 op;
+};
+
+struct sdhci_cdns_phy_param {
+	u32 addr;
+	u32 data;
+	u32 offset;
+};
+
+struct sdhci_cdns_phy_cfg {
+	const char *property;
+	u32 addr;
+	u32 offset;
+};
+
+struct cdns_combophy_plat {
+	u32 phy_type;
+	void __iomem *hrs_addr;
+	u32 nr_phy_params;
+	struct sdhci_cdns_phy_param *phy_params;
+	struct reset_ctl softreset_ctl;
+};
+
+struct cdns_combophy_phy {
+	struct udevice *dev;
+	void *base;
+};
+
+struct cdns_combophy_data {
+	u32 speed_mode;
+	struct cdns_combophy_reg_pairs *cdns_phy_regs;
+};
+
+static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
+	{ "cdns,phy-use-ext-lpbk-dqs", PHY_DQS_TIMING_REG, 22,},
+	{ "cdns,phy-use-lpbk-dqs", PHY_DQS_TIMING_REG, 21,},
+	{ "cdns,phy-use-phony-dqs", PHY_DQS_TIMING_REG, 20,},
+	{ "cdns,phy-use-phony-dqs-cmd", PHY_DQS_TIMING_REG, 19,},
+	{ "cdns,phy-io-mask-always-on", PHY_DQ_TIMING_REG, 31,},
+	{ "cdns,phy-io-mask-end", PHY_DQ_TIMING_REG, 27,},
+	{ "cdns,phy-io-mask-start", PHY_DQ_TIMING_REG, 24,},
+	{ "cdns,phy-data-select-oe-end", PHY_DQ_TIMING_REG, 0,},
+	{ "cdns,phy-sync-method", PHY_GATE_LPBK_CTRL_REG, 31,},
+	{ "cdns,phy-sw-half-cycle-shift", PHY_GATE_LPBK_CTRL_REG, 28,},
+	{ "cdns,phy-rd-del-sel", PHY_GATE_LPBK_CTRL_REG, 19,},
+	{ "cdns,phy-underrun-suppress", PHY_GATE_LPBK_CTRL_REG, 18,},
+	{ "cdns,phy-gate-cfg-always-on", PHY_GATE_LPBK_CTRL_REG, 6,},
+	{ "cdns,phy-param-dll-bypass-mode", PHY_DLL_MASTER_CTRL_REG, 23,},
+	{ "cdns,phy-param-phase-detect-sel", PHY_DLL_MASTER_CTRL_REG, 20,},
+	{ "cdns,phy-param-dll-start-point", PHY_DLL_MASTER_CTRL_REG, 0,},
+	{ "cdns,phy-read-dqs-cmd-delay", PHY_DLL_SLAVE_CTRL_REG, 24,},
+	{ "cdns,phy-clk-wrdqs-delay", PHY_DLL_SLAVE_CTRL_REG, 16,},
+	{ "cdns,phy-clk-wr-delay", PHY_DLL_SLAVE_CTRL_REG, 8,},
+	{ "cdns,phy-read-dqs-delay", PHY_DLL_SLAVE_CTRL_REG, 0,},
+	{ "cdns,phy-phony-dqs-timing", PHY_CTRL_REG, 4,},
+	{ "cdns,hrs09-rddata-en", SDHCI_CDNS_HRS09, 16,},
+	{ "cdns,hrs09-rdcmd-en", SDHCI_CDNS_HRS09, 15,},
+	{ "cdns,hrs09-extended-wr-mode", SDHCI_CDNS_HRS09, 3,},
+	{ "cdns,hrs09-extended-rd-mode", SDHCI_CDNS_HRS09, 2,},
+	{ "cdns,hrs10-hcsdclkadj", SDHCI_CDNS_HRS10, 16,},
+	{ "cdns,hrs16-wrdata1-sdclk-dly", SDHCI_CDNS_HRS16, 28,},
+	{ "cdns,hrs16-wrdata0-sdclk-dly", SDHCI_CDNS_HRS16, 24,},
+	{ "cdns,hrs16-wrcmd1-sdclk-dly", SDHCI_CDNS_HRS16, 20,},
+	{ "cdns,hrs16-wrcmd0-sdclk-dly", SDHCI_CDNS_HRS16, 16,},
+	{ "cdns,hrs16-wrdata1-dly", SDHCI_CDNS_HRS16, 12,},
+	{ "cdns,hrs16-wrdata0-dly", SDHCI_CDNS_HRS16, 8,},
+	{ "cdns,hrs16-wrcmd1-dly", SDHCI_CDNS_HRS16, 4,},
+	{ "cdns,hrs16-wrcmd0-dly", SDHCI_CDNS_HRS16, 0,},
+	{ "cdns,hrs07-rw-compensate", SDHCI_CDNS_HRS07, 16,},
+	{ "cdns,hrs07-idelay-val", SDHCI_CDNS_HRS07, 0,}
+};
+
+static u32 sdhci_cdns_phy_param_count(struct cdns_combophy_plat *plat,
+				      const void *fdt, int nodeoffset)
+{
+	const fdt32_t *prop;
+	int i;
+	u32 count = 0;
+
+	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
+		prop = fdt_getprop(fdt, nodeoffset,
+				   sdhci_cdns_phy_cfgs[i].property, NULL);
+		if (!prop)
+			continue;
+
+		count++;
+	}
+
+	debug("%s: count:%d\n", __func__, count);
+	return count;
+}
+
+static void sdhci_cdns_phy_param_parse(struct cdns_combophy_plat *plat,
+				       const void *fdt, int nodeoffset)
+{
+	struct sdhci_cdns_phy_param *p = plat->phy_params;
+	const fdt32_t *prop;
+	int i;
+
+	for (i = 0; i < plat->nr_phy_params; i++) {
+		prop = fdt_getprop(fdt, nodeoffset,
+				   sdhci_cdns_phy_cfgs[i].property, NULL);
+		if (!prop)
+			continue;
+
+		p->addr = sdhci_cdns_phy_cfgs[i].addr;
+		p->data = fdt32_to_cpu(*prop);
+		p->offset = sdhci_cdns_phy_cfgs[i].offset;
+		debug("%s: p->addr: %02x, p->data: %02x, p->offset: %02x\n",
+		      __func__, p->addr, p->data, p->offset);
+		p++;
+	}
+}
+
+static u32 sdhci_cdns_dfi_phy_val(struct cdns_combophy_plat *plat, u32 reg)
+{
+	int i;
+	u32 tmp = 0;
+
+	for (i = 0; i < plat->nr_phy_params; i++) {
+		if (plat->phy_params[i].addr == reg) {
+			tmp |= plat->phy_params[i].data << plat->phy_params[i].offset;
+			debug("%s: p->addr: %02x, p->data: %02x, p->offset: %02x\n",
+			      __func__, plat->phy_params[i].addr, plat->phy_params[i].data,
+			      plat->phy_params[i].offset);
+		}
+	}
+
+	debug("%s: reg value:%08x\n", __func__, tmp);
+	return tmp;
+}
+
+static int sdhci_cdns_write_phy_reg_mask(u32 addr, u32 data, u32 mask,
+					 void __iomem *hrs_addr)
+{
+	void __iomem *reg = hrs_addr + SDHCI_CDNS_HRS04;
+	u32 tmp;
+
+	tmp = addr;
+
+	/* get PHY address */
+	writel(tmp, reg);
+	debug("%s: register = 0x%08x\n", __func__, readl(hrs_addr +
+	SDHCI_CDNS_HRS04));
+
+	/* read current PHY register value, before write */
+	reg = hrs_addr + SDHCI_CDNS_HRS05;
+	tmp = readl(reg);
+	debug("%s: register = 0x%08x\n", __func__, readl(hrs_addr +
+	SDHCI_CDNS_HRS05));
+	tmp &= ~mask;
+	tmp |= data;
+
+	/* write operation */
+	writel(tmp, reg);
+	debug("%s: register = 0x%08x\n", __func__, readl(hrs_addr +
+	SDHCI_CDNS_HRS05));
+
+	return 0;
+}
+
+static u32 sdhci_cdns_read_phy_reg(u32 addr, void __iomem *hrs_addr)
+{
+	void __iomem *reg = hrs_addr + SDHCI_CDNS_HRS04;
+	u32 tmp;
+
+	tmp = addr;
+
+	/* get PHY address */
+	writel(tmp, reg);
+	debug("%s: register = 0x%08x\n", __func__, readl(reg));
+
+	/* read current PHY register value, before write */
+	reg = hrs_addr + SDHCI_CDNS_HRS05;
+	tmp = readl(reg);
+	debug("%s: register = 0x%08x\n", __func__, readl(reg));
+
+	return tmp;
+}
+
+/* initialize clock */
+static void sdhci_set_clk(u32 freq_khz, void __iomem *hrs_addr)
+{
+	void __iomem *reg = hrs_addr + SDHCI_CDNS_SRS11;
+
+	/* calculate the value to set */
+	u32 sdclkfs = (SD_HOST_CLK / 2000) / freq_khz;
+	u32 dtcvval = 0xe;
+
+	/* disable SDCE */
+	writel(0, reg);
+	debug("Disable SDCE\n");
+
+	/* Enable ICE (internal clock enable) */
+	writel(dtcvval << 16 | sdclkfs << 8 | 1 << 0, reg);
+	debug("Enable ICE: SRS11: %08x\n", readl(reg));
+
+	reg = hrs_addr + SDHCI_CDNS_SRS11;
+	/* Wait for ICS (internal clock stable) */
+	/* polling for ICS = 1 */
+	wait_for_bit_le32(reg, BIT(1), true, 10, false);
+
+	reg = hrs_addr + SDHCI_CDNS_HRS09;
+	/* Enable DLL reset */
+	writel(readl(reg) & ~0x00000001, reg);
+	debug("Enable DLL reset\n");
+
+	/* Set extended_wr_mode */
+	writel((readl(reg) & 0xFFFFFFF7) |
+		((sdclkfs > 0 ? 1 << 3 : 0 << 3)), reg);
+	debug("Set extended_wr_mode\n");
+
+	/* Release DLL reset */
+	writel(readl(reg) | BIT(0), reg);
+	writel(readl(reg) | 3 << 15, reg);
+	debug("Release DLL reset\n");
+
+	/* polling for phy_init = 1 */
+	wait_for_bit_le32(reg, BIT(1), true, 10, false);
+
+	debug("PHY init complete\n");
+
+	reg = hrs_addr + SDHCI_CDNS_SRS11;
+	writel(dtcvval << 16 | sdclkfs << 8 | 1 << 0 | 1 << 2, reg);
+	debug("SD clock enabled\n");
+}
+
+static int sdhci_cdns_detect_card(void __iomem *hrs_addr)
+{
+	u32 tmp;
+	void __iomem *reg = hrs_addr + SDHCI_CDNS_SRS09;
+
+	/* step 1, polling for sdcard */
+	/* polling for SRS9.CI = 1 */
+	wait_for_bit_le32(reg, SDHCI_CDNS_SRS09_CI, true, 10, false);
+
+	debug("SDcard detected\n");
+
+	debug("ON voltage\n");
+	tmp = SDHCI_CDNS_SRS10_VOLT_ON;
+	reg = hrs_addr + SDHCI_CDNS_SRS10;
+	writel(tmp, reg);
+
+	debug("OFF Voltage\n");
+	tmp = SDHCI_CDNS_SRS10_VOLT_OFF;
+	writel(tmp, reg);
+
+	debug("ON voltage\n");
+	tmp = SDHCI_CDNS_SRS10_VOLT_ON;
+	reg = hrs_addr + SDHCI_CDNS_SRS10;
+	writel(tmp, reg);
+
+	/* sdhci_set_clk - 100000 */
+	debug("SD clock\n");
+	sdhci_set_clk(100000, hrs_addr);
+
+	/* set SRS13 */
+	tmp = SDHCI_CDNS_SRS13_DATA;
+	reg = hrs_addr + SDHCI_CDNS_SRS13;
+	writel(tmp, reg);
+	debug("SRS13: %08x\n", readl(reg));
+
+	return 0;
+}
+
+static int sdhci_cdns_combophy_phy_prog(void __iomem *hrs_addr)
+{
+	u32 ret, tmp;
+	u32 mask = 0x0;
+
+	/* step 1, switch on SW_RESET */
+	tmp = SDHCI_CDNS_HRS00_SWR;
+	writel(tmp, hrs_addr);
+	/* polling for HRS0.0 (SWR) = 0 */
+	wait_for_bit_le32(hrs_addr, SDHCI_CDNS_HRS00_SWR, false, 10, false);
+	debug("SW reset\n");
+
+	/* switch on DLL_RESET */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS09);
+	tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ZERO);
+
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS09);
+	debug("PHY_SW_RESET: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS09));
+
+	/* program PHY_DQS_TIMING_REG */
+	tmp = FIELD_PREP(USE_EXT_LPBK_DQS, BIT_ONE) |
+			FIELD_PREP(USE_LPBK_DQS, BIT_ONE) |
+			FIELD_PREP(USE_PHONY_DQS, BIT_ONE) |
+			FIELD_PREP(USE_PHONY_DQS_CMD, BIT_ONE);
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DQS_TIMING_REG, tmp, tmp, hrs_addr);
+
+	/* program PHY_GATE_LPBK_CTRL_REG */
+	tmp = FIELD_PREP(SYNC_METHOD, BIT_ONE) |
+			FIELD_PREP(SW_HALF_CYCLE_SHIFT, BIT_ZERO) |
+			FIELD_PREP(RD_DEL_SEL, RD_DEL_SEL_DATA) |
+			FIELD_PREP(UNDERRUN_SUPPRESS, BIT_ONE) |
+			FIELD_PREP(GATE_CFG_ALWAYS_ON, BIT_ONE);
+	mask = SYNC_METHOD | SW_HALF_CYCLE_SHIFT | RD_DEL_SEL | UNDERRUN_SUPPRESS |
+			GATE_CFG_ALWAYS_ON;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_GATE_LPBK_CTRL_REG, tmp, mask,
+					    hrs_addr);
+
+	/* program PHY_DLL_MASTER_CTRL_REG */
+	tmp = FIELD_PREP(PARAM_DLL_BYPASS_MODE, BIT_ONE) |
+			FIELD_PREP(PARAM_PHASE_DETECT_SEL, PARAM_PHASE_DETECT_SEL_DATA) |
+			FIELD_PREP(PARAM_DLL_START_POINT, PARAM_DLL_START_POINT_DATA);
+	mask = PARAM_DLL_BYPASS_MODE | PARAM_PHASE_DETECT_SEL |
+			PARAM_DLL_START_POINT;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_MASTER_CTRL_REG, tmp, mask,
+					    hrs_addr);
+
+	/* program PHY_DLL_SLAVE_CTRL_REG */
+	tmp = FIELD_PREP(READ_DQS_CMD_DELAY, READ_DQS_CMD_DELAY_DATA) |
+			FIELD_PREP(CLK_WRDQS_DELAY, CLK_WRDQS_DELAY_DATA) |
+			FIELD_PREP(CLK_WR_DELAY, CLK_WR_DELAY_DATA) |
+			FIELD_PREP(READ_DQS_DELAY, READ_DQS_DELAY_DATA);
+	mask = READ_DQS_CMD_DELAY | CLK_WRDQS_DELAY | CLK_WR_DELAY | READ_DQS_DELAY;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_SLAVE_CTRL_REG, tmp, mask,
+					    hrs_addr);
+
+	/* program PHY_CTRL_REG */
+	tmp = FIELD_PREP(PHONY_DQS_TIMING, PHONY_DQS_TIMING_DATA);
+	mask = PHONY_DQS_TIMING;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_CTRL_REG, tmp, mask, hrs_addr);
+
+	/* switch off DLL_RESET */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS09);
+	tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ONE);
+
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS09);
+
+	/* polling for PHY_INIT_COMPLETE bit */
+	wait_for_bit_le32(hrs_addr + SDHCI_CDNS_HRS09,
+			  SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE, true, 10, false);
+
+	/* program PHY_DQ_TIMING_REG */
+	tmp = sdhci_cdns_read_phy_reg(PHY_DQ_TIMING_REG, hrs_addr) & 0x07FFFF8;
+	tmp |= FIELD_PREP(IO_MASK_ALWAYS_ON, BIT_ZERO) |
+			FIELD_PREP(IO_MASK_END, IO_MASK_END_DATA) |
+			FIELD_PREP(IO_MASK_START, IO_MASK_START_DATA) |
+			FIELD_PREP(DATA_SELECT_OE_END, DATA_SELECT_OE_END_DATA);
+	mask = IO_MASK_ALWAYS_ON | IO_MASK_END | IO_MASK_START | DATA_SELECT_OE_END;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DQ_TIMING_REG, tmp, mask, hrs_addr);
+
+	/* program HRS09, register 42 */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS09) & 0xFFFE7FF3;
+	tmp &= ~(SDHCI_CDNS_HRS09_RDDATA_EN |
+			SDHCI_CDNS_HRS09_RDCMD_EN |
+			SDHCI_CDNS_HRS09_EXTENDED_WR_MODE |
+			SDHCI_CDNS_HRS09_EXTENDED_RD_MODE);
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_RDDATA_EN, BIT_ONE) |
+			FIELD_PREP(SDHCI_CDNS_HRS09_RDCMD_EN, BIT_ONE) |
+			FIELD_PREP(SDHCI_CDNS_HRS09_EXTENDED_WR_MODE, BIT_ONE) |
+			FIELD_PREP(SDHCI_CDNS_HRS09_EXTENDED_RD_MODE, BIT_ONE);
+
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS09);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+			SDHCI_CDNS_HRS09));
+
+	/* program HRS10, register 43 */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS10) & 0xFFF0FFFF;
+	tmp &= ~SDHCI_CDNS_HRS10_HCSDCLKADJ;
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS10_HCSDCLKADJ,
+			SDHCI_CDNS_HRS10_HCSDCLKADJ_DATA);
+
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS10);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+			SDHCI_CDNS_HRS10));
+
+	/* program HRS16, register 48 */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS16);
+	tmp &= ~(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY |
+			SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY |
+			SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY |
+			SDHCI_CDNS_HRS16_WRDATA1_DLY |
+			SDHCI_CDNS_HRS16_WRDATA0_DLY |
+			SDHCI_CDNS_HRS16_WRCMD1_DLY |
+			SDHCI_CDNS_HRS16_WRCMD0_DLY);
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY,
+				SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY,
+				   SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY,
+				   SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY,
+				   SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_DLY,
+				   SDHCI_CDNS_HRS16_WRDATA1_DLY_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_DLY,
+				   SDHCI_CDNS_HRS16_WRDATA0_DLY_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_DLY,
+				   SDHCI_CDNS_HRS16_WRCMD1_DLY_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_DLY,
+				   SDHCI_CDNS_HRS16_WRCMD0_DLY_DATA);
+
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS16);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+		SDHCI_CDNS_HRS16));
+
+	/* program HRS07, register 40 */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS07);
+	tmp &= ~(SDHCI_CDNS_HRS07_RW_COMPENSATE |
+			SDHCI_CDNS_HRS07_IDELAY_VAL);
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS07_RW_COMPENSATE,
+				   SDHCI_CDNS_HRS07_RW_COMPENSATE_DATA) |
+			FIELD_PREP(SDHCI_CDNS_HRS07_IDELAY_VAL,
+				   SDHCI_CDNS_HRS07_IDELAY_VAL_DATA);
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS07);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+			SDHCI_CDNS_HRS07));
+	/* end of combophy init */
+
+	/* initialize sdcard/eMMC & set clock*/
+	ret = sdhci_cdns_detect_card(hrs_addr);
+	debug("eMMC Mode: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS06));
+
+	return 0;
+}
+
+static int cdns_combophy_phy_init(struct phy *gphy)
+{
+	u32 tmp;
+	int ret;
+	struct cdns_combophy_plat *plat = dev_get_plat(gphy->dev);
+
+#if (IS_ENABLED(CONFIG_SPL_BUILD))
+		/* assert & deassert softreset */
+		ret = reset_assert(&plat->softreset_ctl);
+		if (ret < 0) {
+			pr_err("COMBOPHY soft reset deassert failed: %d", ret);
+			return ret;
+		}
+
+		ret = reset_deassert(&plat->softreset_ctl);
+		if (ret < 0) {
+			pr_err("COMBOPHY soft reset deassert failed: %d", ret);
+			return ret;
+		}
+#endif
+
+	if (plat->phy_type == PHY_TYPE_SDMMC) {
+		tmp = SYSMGR_SOC64_COMBOPHY_DFISEL_SDMMC;
+
+#if (IS_ENABLED(CONFIG_SPL_BUILD))
+#if (IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5_EMU))
+		/* OCRAM FW - allow non secure sdmmc */
+		writel(NON_SECURE_ACCESS, OCRAM_SECURE_REGION1_REG);
+#endif
+		/* configure DFI_SEL for SDMMC */
+		writel(tmp, socfpga_get_sysmgr_addr() + SYSMGR_SOC64_COMBOPHY_DFISEL);
+		debug("DFISEL: %08x\nSDMMC_USEFPGA: %08x\n",
+		      readl(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_COMBOPHY_DFISEL),
+		      readl(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_SDMMC_USEFPGA));
+		debug("clkmgr.mainpllgrp.en: 0x%08x\nclkmgr.mainpllgrp.ens: 0x%08x\n",
+		      readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_EN),
+		      readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_ENS));
+		debug("clkmgr.perpllgrp.en: 0x%08x\nclkmgr.perpllgrp.ens: 0x%08x\n",
+		      readl(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_EN),
+		      readl(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_ENS));
+
+		/* configure default base clkmgr clock - 200MHz */
+		writel((readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_NOCDIV)
+			& 0xfffcffff) |
+			(CLKMGR_NOCDIV_SOFTPHY_DIV_ONE << CLKMGR_NOCDIV_SOFTPHY_OFFSET),
+			socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_NOCDIV);
+
+		debug("clkmgr.nocdiv: 0x%08x\n",
+		      readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_NOCDIV));
+
+		/* enable DDR secure zone access for SDMMC */
+		writel(SECURE_TRANS_SET, SECURE_TRANS_REG);
+#endif
+
+		/* TODO: add speed-mode checking in device tree */
+		ret = sdhci_cdns_combophy_phy_prog(plat->hrs_addr);
+
+	} else if (plat->phy_type == PHY_TYPE_NAND) {
+		/* configure DFI_SEL for NAND */
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdns_combophy_phy_on(struct phy *gphy)
+{
+	return 0;
+}
+
+static int cdns_combophy_phy_configure(struct phy *gphy, void *params)
+{
+	u32 ret, tmp;
+	u32 mask = 0x0;
+	struct cdns_combophy_plat *plat = dev_get_plat(gphy->dev);
+	void __iomem *hrs_addr = plat->hrs_addr;
+
+	/* switch on DLL_RESET */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS09);
+	tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ZERO);
+
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS09);
+	debug("PHY_SW_RESET: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS09));
+
+	/* program PHY_DQS_TIMING_REG */
+	tmp = sdhci_cdns_dfi_phy_val(plat, PHY_DQS_TIMING_REG);
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DQS_TIMING_REG, tmp, tmp, hrs_addr);
+
+	/* program PHY_GATE_LPBK_CTRL_REG */
+	tmp = sdhci_cdns_dfi_phy_val(plat, PHY_GATE_LPBK_CTRL_REG);
+	mask = SYNC_METHOD | SW_HALF_CYCLE_SHIFT | RD_DEL_SEL | UNDERRUN_SUPPRESS |
+			GATE_CFG_ALWAYS_ON;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_GATE_LPBK_CTRL_REG, tmp, mask,
+					    hrs_addr);
+
+	/* program PHY_DLL_MASTER_CTRL_REG */
+	tmp = sdhci_cdns_dfi_phy_val(plat, PHY_DLL_MASTER_CTRL_REG);
+	mask = PARAM_DLL_BYPASS_MODE | PARAM_PHASE_DETECT_SEL |
+			PARAM_DLL_START_POINT;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_MASTER_CTRL_REG, tmp, mask,
+					    hrs_addr);
+
+	/* program PHY_DLL_SLAVE_CTRL_REG */
+	tmp = sdhci_cdns_dfi_phy_val(plat, PHY_DLL_SLAVE_CTRL_REG);
+	mask = READ_DQS_CMD_DELAY | CLK_WRDQS_DELAY | CLK_WR_DELAY | READ_DQS_DELAY;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_SLAVE_CTRL_REG, tmp, mask,
+					    hrs_addr);
+
+	/* program PHY_CTRL_REG */
+	debug("%s: Skip PHY_CTRL_REG - PHONY_DQS_TIMING field\n", __func__);
+
+	/* switch off DLL_RESET */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS09);
+	tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
+	tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ONE);
+
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS09);
+
+	/* polling for PHY_INIT_COMPLETE bit */
+	wait_for_bit_le32(hrs_addr + SDHCI_CDNS_HRS09,
+			  SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE, true, 10, false);
+
+	/* program PHY_DQ_TIMING_REG */
+	tmp = sdhci_cdns_read_phy_reg(PHY_DQ_TIMING_REG, hrs_addr) & 0x07FFFF8;
+	tmp |= sdhci_cdns_dfi_phy_val(plat, PHY_DQ_TIMING_REG);
+	mask = IO_MASK_ALWAYS_ON | IO_MASK_END | IO_MASK_START | DATA_SELECT_OE_END;
+	ret = sdhci_cdns_write_phy_reg_mask(PHY_DQ_TIMING_REG, tmp, mask, hrs_addr);
+
+	/* program HRS09, register 42 */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS09) & 0xFFFE7FF3;
+	tmp |= sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS09);
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS09);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+			SDHCI_CDNS_HRS09));
+
+	/* program HRS10, register 43 */
+	tmp = readl(hrs_addr + SDHCI_CDNS_HRS10) & 0xFFF0FFFF;
+	tmp |= sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS10);
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS10);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+			SDHCI_CDNS_HRS10));
+
+	/* program HRS16, register 48 */
+	tmp = sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS16);
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS16);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+		SDHCI_CDNS_HRS16));
+
+	/* program HRS07, register 40 */
+	tmp = sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS07);
+	writel(tmp, hrs_addr + SDHCI_CDNS_HRS07);
+	debug("%s: register = 0x%x\n", __func__, readl(hrs_addr +
+			SDHCI_CDNS_HRS07));
+	/* end of combophy init */
+
+	/* initialize sdcard/eMMC & set clock*/
+	ret = sdhci_cdns_detect_card(hrs_addr);
+	debug("eMMC Mode: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS06));
+
+	return 0;
+}
+
+static int cdns_combophy_phy_off(struct phy *gphy)
+{
+	return 0;
+}
+
+static int cdns_combophy_phy_reset(struct phy *gphy)
+{
+	return 0;
+};
+
+static const struct phy_ops ops = {
+	.configure	= cdns_combophy_phy_configure,
+	.init		= cdns_combophy_phy_init,
+	.power_on	= cdns_combophy_phy_on,
+	.power_off	= cdns_combophy_phy_off,
+	.reset		= cdns_combophy_phy_reset,
+};
+
+static int cdns_combophy_phy_probe(struct udevice *dev)
+{
+	struct cdns_combophy_plat *plat = dev_get_plat(dev);
+	u32 nr_phy_params;
+	int ret = 0;
+
+	plat->phy_type = dev_read_u32_default(dev, "phy-type", 0);
+	plat->hrs_addr = dev_remap_addr_index(dev, 0);
+
+	/* get softreset reset */
+	ret = reset_get_by_name(dev, "reset", &plat->softreset_ctl);
+	if (ret)
+		pr_err("can't get soft reset for %s (%d)", dev->name, ret);
+
+	nr_phy_params = sdhci_cdns_phy_param_count(plat, gd->fdt_blob,
+						   dev_of_offset(dev));
+	if (!nr_phy_params)
+		return ret;
+
+	plat->phy_params = kcalloc(nr_phy_params, sizeof(*plat->phy_params),
+				   GFP_KERNEL);
+	if (!plat->phy_params)
+		return -ENOMEM;
+
+	plat->nr_phy_params = nr_phy_params;
+	sdhci_cdns_phy_param_parse(plat, gd->fdt_blob, dev_of_offset(dev));
+
+	debug("%s: ComboPHY probed\n", __func__);
+
+	return ret;
+}
+
+static int cdns_combophy_phy_remove(struct udevice *dev)
+{
+	return 0;
+}
+
+static const struct udevice_id cdns_combophy_id_table[] = {
+	{ .compatible = "cdns,combophy" },
+	{}
+};
+
+U_BOOT_DRIVER(combophy_phy_provider) = {
+	.name		= "combophy",
+	.id		= UCLASS_PHY,
+	.of_match	= cdns_combophy_id_table,
+	.probe		= cdns_combophy_phy_probe,
+	.remove		= cdns_combophy_phy_remove,
+	.ops		= &ops,
+	.priv_auto	= sizeof(struct cdns_combophy_phy),
+	.plat_auto  = sizeof(struct cdns_combophy_plat),
+};