[PATCH v1 12/17] drivers: phy: add combo phy driver for agilex5
Jit Loon Lim
jit.loon.lim at intel.com
Wed Jun 21 05:16:05 CEST 2023
This is for new platform enablement for agilex5.
Add combo phy driver for new platform.
Signed-off-by: Jit Loon Lim <jit.loon.lim at 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 --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 at 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),
+};
--
2.26.2
More information about the U-Boot
mailing list