[U-Boot] [PATCH 0/7] Add support of SD3.0 UHS modes for ZynqMP

This patch series is meant to add SD3.0 support for ZynqMP platform. The first five patches in the series mostly setting up things in sdhci layer to support SD3.0 , the sixth patch is to add SD3.0 support for ZynqMP platform and the last patch is to enable this support for ZynqMP zcu102 rev1.0 eval board.
Repo: u-boot-mmc.git Branch: hs200-working
Siva Durga Prasad Paladugu (7): mmc: sdhci: Add support for disabling clock mmc: sdhci: Handle execute tuning command in sdhci_send_command sdhci: Add new sdhci ops for platform specific tuning and delays mmc: sdhci: Invoke platform specific tuning and delay routines mmc : sdhci: Read capabilities register1 and update host caps mmc: zynq_sdhci: Add support for SD3.0 zynqmp: zcu102: Enable UHS support for ZCU102 Rev1.0 board
board/xilinx/zynqmp/Makefile | 2 + board/xilinx/zynqmp/tap_delays.c | 230 ++++++++++++++++++++++++++ configs/xilinx_zynqmp_zcu102_rev1_0_defconfig | 2 + drivers/mmc/sdhci.c | 67 +++++++- drivers/mmc/zynq_sdhci.c | 230 +++++++++++++++++++++++++- include/sdhci.h | 9 + 6 files changed, 530 insertions(+), 10 deletions(-) create mode 100644 board/xilinx/zynqmp/tap_delays.c

This patch adds support to disable clock if clk_disable was set and then enable or set clock if the clock was changed or clock was disabled when clock needs to be enabled.
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com --- drivers/mmc/sdhci.c | 7 ++++++- include/sdhci.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index e2ddf5d..2bd721f 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -437,8 +437,13 @@ static int sdhci_set_ios(struct mmc *mmc) if (host->ops && host->ops->set_control_reg) host->ops->set_control_reg(host);
- if (mmc->clock != host->clock) + if ((mmc->clock != host->clock || host->clk_disabled) && + !mmc->clk_disable) sdhci_set_clock(mmc, mmc->clock); + if (mmc->clk_disable) { + sdhci_set_clock(mmc, 0); + host->clk_disabled = true; + }
/* Set bus width */ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); diff --git a/include/sdhci.h b/include/sdhci.h index 7e84012..4fc4140 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -259,6 +259,7 @@ struct sdhci_host { uint voltages;
struct mmc_config cfg; + bool clk_disabled; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS

This patch upadted sdhci_send_command to handle execute tuning command.
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com --- drivers/mmc/sdhci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 2bd721f..8239079 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -161,7 +161,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
/* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ - if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + if ((cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) || + (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK)) mask &= ~SDHCI_DATA_INHIBIT;
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { @@ -183,6 +184,9 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
mask = SDHCI_INT_RESPONSE; + if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK) + mask = SDHCI_INT_DATA_AVAIL; + if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; else if (cmd->resp_type & MMC_RSP_136) @@ -198,7 +202,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, flags |= SDHCI_CMD_CRC; if (cmd->resp_type & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; - if (data) + if (data || (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK)) flags |= SDHCI_CMD_DATA;
/* Set Transfer mode regarding to data flag */

This patch adds new hooks for any platform specific tuning and tap delays programing. These are needed for supporting SD3.0.Y
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com --- include/sdhci.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/sdhci.h b/include/sdhci.h index 4fc4140..3269992 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -237,6 +237,8 @@ struct sdhci_ops { void (*set_control_reg)(struct sdhci_host *host); void (*set_ios_post)(struct sdhci_host *host); void (*set_clock)(struct sdhci_host *host, u32 div); + int (*platform_execute_tuning)(struct mmc *host, u8 opcode); + void (*set_delay)(struct sdhci_host *host); };
struct sdhci_host {

This patch adds support to invoke any platform specific tuning and delay routines if available.
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com --- drivers/mmc/sdhci.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 8239079..2c3dcd4 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -306,6 +306,24 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, return -ECOMM; }
+#if defined(CONFIG_DM_MMC) && defined(MMC_SUPPORTS_TUNING) +static int sdhci_execute_tuning(struct udevice *dev, uint opcode) +{ + int err; + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct sdhci_host *host = mmc->priv; + + debug("%s\n", __func__); + + if (host->ops->platform_execute_tuning) { + err = host->ops->platform_execute_tuning(mmc, opcode); + if (err) + return err; + return 0; + } + return 0; +} +#endif static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) { struct sdhci_host *host = mmc->priv; @@ -330,6 +348,9 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (clock == 0) return 0;
+ if (host->ops->set_delay) + host->ops->set_delay(host); + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { /* * Check if the Host Controller supports Programmable Clock @@ -523,6 +544,9 @@ int sdhci_probe(struct udevice *dev) const struct dm_mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, +#ifdef MMC_SUPPORTS_TUNING + .execute_tuning = sdhci_execute_tuning, +#endif }; #else static const struct mmc_ops sdhci_ops = {

This patch reads the capabilities register1 and update the host caps accordingly for mmc layer usage. This patch mainly reads for UHS capabilities inorder to support SD3.0.
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com --- drivers/mmc/sdhci.c | 28 +++++++++++++++++++++++++++- include/sdhci.h | 6 ++++++ 2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 2c3dcd4..2b7bec0 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -559,7 +559,7 @@ static const struct mmc_ops sdhci_ops = { int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, u32 f_max, u32 f_min) { - u32 caps, caps_1; + u32 caps, caps_1 = 0;
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
@@ -635,6 +635,32 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, cfg->host_caps &= ~MMC_MODE_8BIT; }
+ if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) + caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + + if (!(cfg->voltages & MMC_VDD_165_195) || + (host->quirks & SDHCI_QUIRK_NO_1_8_V)) + caps_1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | + SDHCI_SUPPORT_DDR50); + + if (caps_1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | + SDHCI_SUPPORT_DDR50)) + cfg->host_caps |= MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25); + + if (caps_1 & SDHCI_SUPPORT_SDR104) { + cfg->host_caps |= MMC_CAP(UHS_SDR104) | MMC_CAP(UHS_SDR50); + /* + * SD3.0: SDR104 is supported so (for eMMC) the caps2 + * field can be promoted to support HS200. + */ + cfg->host_caps |= MMC_CAP(MMC_HS_200); + } else if (caps_1 & SDHCI_SUPPORT_SDR50) { + cfg->host_caps |= MMC_CAP(UHS_SDR50); + } + + if (caps_1 & SDHCI_SUPPORT_DDR50) + cfg->host_caps |= MMC_CAP(UHS_DDR50); + if (host->host_caps) cfg->host_caps |= host->host_caps;
diff --git a/include/sdhci.h b/include/sdhci.h index 3269992..289042f 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -167,6 +167,11 @@ #define SDHCI_CAN_64BIT BIT(28)
#define SDHCI_CAPABILITIES_1 0x44 +#define SDHCI_SUPPORT_SDR50 0x00000001 +#define SDHCI_SUPPORT_SDR104 0x00000002 +#define SDHCI_SUPPORT_DDR50 0x00000004 +#define SDHCI_USE_SDR50_TUNING 0x00002000 + #define SDHCI_CLOCK_MUL_MASK 0x00FF0000 #define SDHCI_CLOCK_MUL_SHIFT 16
@@ -215,6 +220,7 @@ #define SDHCI_QUIRK_BROKEN_VOLTAGE (1 << 4) #define SDHCI_QUIRK_WAIT_SEND_CMD (1 << 6) #define SDHCI_QUIRK_USE_WIDE8 (1 << 8) +#define SDHCI_QUIRK_NO_1_8_V (1 << 9)
/* to make gcc happy */ struct sdhci_host;

This patch adds support of SD3.0 for ZynqMP.
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com --- board/xilinx/zynqmp/Makefile | 2 + board/xilinx/zynqmp/tap_delays.c | 230 +++++++++++++++++++++++++++++++++++++++ drivers/mmc/zynq_sdhci.c | 230 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 456 insertions(+), 6 deletions(-) create mode 100644 board/xilinx/zynqmp/tap_delays.c
diff --git a/board/xilinx/zynqmp/Makefile b/board/xilinx/zynqmp/Makefile index 75aab92..f2e4d26 100644 --- a/board/xilinx/zynqmp/Makefile +++ b/board/xilinx/zynqmp/Makefile @@ -26,6 +26,8 @@ ifneq ($(call ifdef_any_of, CONFIG_ZYNQMP_PSU_INIT_ENABLED CONFIG_SPL_BUILD),) obj-y += $(init-objs) endif
+obj-$(CONFIG_MMC_SDHCI_ZYNQ) += tap_delays.o + # Suppress "warning: function declaration isn't a prototype" CFLAGS_REMOVE_psu_init_gpl.o := -Wstrict-prototypes
diff --git a/board/xilinx/zynqmp/tap_delays.c b/board/xilinx/zynqmp/tap_delays.c new file mode 100644 index 0000000..9c93291 --- /dev/null +++ b/board/xilinx/zynqmp/tap_delays.c @@ -0,0 +1,230 @@ +/* + * Xilinx ZynqMP SoC Tap Delay Programming + * + * Copyright (C) 2016 Xilinx, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/sys_proto.h> + +#define SD_DLL_CTRL 0xFF180358 +#define SD_ITAP_DLY 0xFF180314 +#define SD_OTAP_DLY 0xFF180318 +#define SD0_DLL_RST_MASK 0x00000004 +#define SD0_DLL_RST 0x00000004 +#define SD1_DLL_RST_MASK 0x00040000 +#define SD1_DLL_RST 0x00040000 +#define SD0_ITAPCHGWIN_MASK 0x00000200 +#define SD0_ITAPCHGWIN 0x00000200 +#define SD1_ITAPCHGWIN_MASK 0x02000000 +#define SD1_ITAPCHGWIN 0x02000000 +#define SD0_ITAPDLYENA_MASK 0x00000100 +#define SD0_ITAPDLYENA 0x00000100 +#define SD1_ITAPDLYENA_MASK 0x01000000 +#define SD1_ITAPDLYENA 0x01000000 +#define SD0_ITAPDLYSEL_MASK 0x000000FF +#define SD0_ITAPDLYSEL_HSD 0x00000015 +#define SD0_ITAPDLYSEL_SD_DDR50 0x0000003D +#define SD0_ITAPDLYSEL_MMC_DDR50 0x00000012 + +#define SD1_ITAPDLYSEL_MASK 0x00FF0000 +#define SD1_ITAPDLYSEL_HSD 0x00150000 +#define SD1_ITAPDLYSEL_SD_DDR50 0x003D0000 +#define SD1_ITAPDLYSEL_MMC_DDR50 0x00120000 + +#define SD0_OTAPDLYSEL_MASK 0x0000003F +#define SD0_OTAPDLYSEL_MMC_HSD 0x00000006 +#define SD0_OTAPDLYSEL_SD_HSD 0x00000005 +#define SD0_OTAPDLYSEL_SDR50 0x00000003 +#define SD0_OTAPDLYSEL_SDR104_B0 0x00000003 +#define SD0_OTAPDLYSEL_SDR104_B2 0x00000002 +#define SD0_OTAPDLYSEL_SD_DDR50 0x00000004 +#define SD0_OTAPDLYSEL_MMC_DDR50 0x00000006 + +#define SD1_OTAPDLYSEL_MASK 0x003F0000 +#define SD1_OTAPDLYSEL_MMC_HSD 0x00060000 +#define SD1_OTAPDLYSEL_SD_HSD 0x00050000 +#define SD1_OTAPDLYSEL_SDR50 0x00030000 +#define SD1_OTAPDLYSEL_SDR104_B0 0x00030000 +#define SD1_OTAPDLYSEL_SDR104_B2 0x00020000 +#define SD1_OTAPDLYSEL_SD_DDR50 0x00040000 +#define SD1_OTAPDLYSEL_MMC_DDR50 0x00060000 + +#define MMC_BANK2 0x2 + +#define MMC_TIMING_UHS_SDR25 1 +#define MMC_TIMING_UHS_SDR50 2 +#define MMC_TIMING_UHS_SDR104 3 +#define MMC_TIMING_UHS_DDR50 4 +#define MMC_TIMING_MMC_HS200 5 +#define MMC_TIMING_SD_HS 6 +#define MMC_TIMING_MMC_DDR52 7 +#define MMC_TIMING_MMC_HS 8 + +void zynqmp_dll_reset(u8 deviceid) +{ + /* Issue DLL Reset */ + if (deviceid == 0) + zynqmp_mmio_write(SD_DLL_CTRL, SD0_DLL_RST_MASK, + SD0_DLL_RST); + else + zynqmp_mmio_write(SD_DLL_CTRL, SD1_DLL_RST_MASK, + SD1_DLL_RST); + + mdelay(1); + + /* Release DLL Reset */ + if (deviceid == 0) + zynqmp_mmio_write(SD_DLL_CTRL, SD0_DLL_RST_MASK, 0x0); + else + zynqmp_mmio_write(SD_DLL_CTRL, SD1_DLL_RST_MASK, 0x0); +} + +static void arasan_zynqmp_tap_sdr104(u8 deviceid, u8 timing, u8 bank) +{ + if (deviceid == 0) { + /* Program OTAP */ + if (bank == MMC_BANK2) + zynqmp_mmio_write(SD_OTAP_DLY, SD0_OTAPDLYSEL_MASK, + SD0_OTAPDLYSEL_SDR104_B2); + else + zynqmp_mmio_write(SD_OTAP_DLY, SD0_OTAPDLYSEL_MASK, + SD0_OTAPDLYSEL_SDR104_B0); + } else { + /* Program OTAP */ + if (bank == MMC_BANK2) + zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK, + SD1_OTAPDLYSEL_SDR104_B2); + else + zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK, + SD1_OTAPDLYSEL_SDR104_B0); + } +} + +static void arasan_zynqmp_tap_hs(u8 deviceid, u8 timing, u8 bank) +{ + if (deviceid == 0) { + /* Program ITAP */ + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPCHGWIN_MASK, + SD0_ITAPCHGWIN); + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPDLYENA_MASK, + SD0_ITAPDLYENA); + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPDLYSEL_MASK, + SD0_ITAPDLYSEL_HSD); + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPCHGWIN_MASK, 0x0); + /* Program OTAP */ + if (timing == MMC_TIMING_MMC_HS) + zynqmp_mmio_write(SD_OTAP_DLY, SD0_OTAPDLYSEL_MASK, + SD0_OTAPDLYSEL_MMC_HSD); + else + zynqmp_mmio_write(SD_OTAP_DLY, SD0_OTAPDLYSEL_MASK, + SD0_OTAPDLYSEL_SD_HSD); + } else { + /* Program ITAP */ + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPCHGWIN_MASK, + SD1_ITAPCHGWIN); + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPDLYENA_MASK, + SD1_ITAPDLYENA); + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPDLYSEL_MASK, + SD1_ITAPDLYSEL_HSD); + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPCHGWIN_MASK, 0x0); + /* Program OTAP */ + if (timing == MMC_TIMING_MMC_HS) + zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK, + SD1_OTAPDLYSEL_MMC_HSD); + else + zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK, + SD1_OTAPDLYSEL_SD_HSD); + } +} + +static void arasan_zynqmp_tap_ddr50(u8 deviceid, u8 timing, u8 bank) +{ + if (deviceid == 0) { + /* Program ITAP */ + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPCHGWIN_MASK, + SD0_ITAPCHGWIN); + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPDLYENA_MASK, + SD0_ITAPDLYENA); + if (timing == MMC_TIMING_UHS_DDR50) + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPDLYSEL_MASK, + SD0_ITAPDLYSEL_SD_DDR50); + else + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPDLYSEL_MASK, + SD0_ITAPDLYSEL_MMC_DDR50); + zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPCHGWIN_MASK, 0x0); + /* Program OTAP */ + if (timing == MMC_TIMING_UHS_DDR50) + zynqmp_mmio_write(SD_OTAP_DLY, SD0_OTAPDLYSEL_MASK, + SD0_OTAPDLYSEL_SD_DDR50); + else + zynqmp_mmio_write(SD_OTAP_DLY, SD0_OTAPDLYSEL_MASK, + SD0_OTAPDLYSEL_MMC_DDR50); + } else { + /* Program ITAP */ + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPCHGWIN_MASK, + SD1_ITAPCHGWIN); + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPDLYENA_MASK, + SD1_ITAPDLYENA); + if (timing == MMC_TIMING_UHS_DDR50) + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPDLYSEL_MASK, + SD1_ITAPDLYSEL_SD_DDR50); + else + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPDLYSEL_MASK, + SD1_ITAPDLYSEL_MMC_DDR50); + zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPCHGWIN_MASK, 0x0); + /* Program OTAP */ + if (timing == MMC_TIMING_UHS_DDR50) + zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK, + SD1_OTAPDLYSEL_SD_DDR50); + else + zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK, + SD1_OTAPDLYSEL_MMC_DDR50); + } +} + +static void arasan_zynqmp_tap_sdr50(u8 deviceid, u8 timing, u8 bank) +{ + if (deviceid == 0) { + /* Program OTAP */ + zynqmp_mmio_write(SD_OTAP_DLY, SD0_OTAPDLYSEL_MASK, + SD0_OTAPDLYSEL_SDR50); + } else { + /* Program OTAP */ + zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK, + SD1_OTAPDLYSEL_SDR50); + } +} + +void arasan_zynqmp_set_tapdelay(u8 deviceid, u8 timing, u8 bank) +{ + if (deviceid == 0) + zynqmp_mmio_write(SD_DLL_CTRL, SD0_DLL_RST_MASK, + SD0_DLL_RST); + else + zynqmp_mmio_write(SD_DLL_CTRL, SD1_DLL_RST_MASK, + SD1_DLL_RST); + + switch (timing) { + case MMC_TIMING_UHS_SDR25: + arasan_zynqmp_tap_hs(deviceid, timing, bank); + break; + case MMC_TIMING_UHS_SDR50: + arasan_zynqmp_tap_sdr50(deviceid, timing, bank); + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + arasan_zynqmp_tap_sdr104(deviceid, timing, bank); + break; + case MMC_TIMING_UHS_DDR50: + arasan_zynqmp_tap_ddr50(deviceid, timing, bank); + break; + } + + if (deviceid == 0) + zynqmp_mmio_write(SD_DLL_CTRL, SD0_DLL_RST_MASK, 0x0); + else + zynqmp_mmio_write(SD_DLL_CTRL, SD1_DLL_RST_MASK, 0x0); +} diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index 0fddb42..34d9b66 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -12,7 +12,9 @@ #include <fdtdec.h> #include <libfdt.h> #include <malloc.h> +#include "mmc_private.h" #include <sdhci.h> +#include <zynqmp_tap_delay.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -26,15 +28,211 @@ struct arasan_sdhci_plat { unsigned int f_max; };
+struct arasan_sdhci_priv { + struct sdhci_host *host; + u8 deviceid; + u8 bank; + u8 no_1p8; + bool pwrseq; +}; + +#if defined(CONFIG_ARCH_ZYNQMP) +static const u8 mode2timing[] = { + [UHS_SDR12] = UHS_SDR12_BUS_SPEED, + [UHS_SDR25] = UHS_SDR25_BUS_SPEED, + [UHS_SDR50] = UHS_SDR50_BUS_SPEED, + [UHS_SDR104] = UHS_SDR104_BUS_SPEED, + [UHS_DDR50] = UHS_DDR50_BUS_SPEED, +}; + +#define SDHCI_HOST_CTRL2 0x3E +#define SDHCI_CTRL2_MODE_MASK 0x7 +#define SDHCI_18V_SIGNAL 0x8 +#define SDHCI_CTRL_EXEC_TUNING 0x0040 +#define SDHCI_CTRL_TUNED_CLK 0x80 + +static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u8 deviceid) +{ + u16 clk; + unsigned long timeout; + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~(SDHCI_CLOCK_CARD_EN); + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + /* Issue DLL Reset */ + zynqmp_dll_reset(deviceid); + + /* Wait max 20 ms */ + timeout = 100; + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + dev_err(mmc_dev(host->mmc), + ": Internal clock never stabilised.\n"); + return; + } + timeout--; + udelay(1000); + } + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +static int arasan_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) +{ + struct mmc_cmd cmd; + struct mmc_data data; + u32 ctrl; + struct sdhci_host *host; + struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev); + u8 tuning_loop_counter = 40; + u8 deviceid; + + printf("%s\n", __func__); + + host = priv->host; + deviceid = priv->deviceid; + + ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); + ctrl |= SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, ctrl, SDHCI_HOST_CTRL2); + + mdelay(1); + + arasan_zynqmp_dll_reset(host, deviceid); + + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); + + do { + cmd.cmdidx = opcode; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + + data.blocksize = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + if (tuning_loop_counter-- == 0) + break; + + if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200 && + mmc->bus_width == 8) + data.blocksize = 128; + + sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, + data.blocksize), + SDHCI_BLOCK_SIZE); + sdhci_writew(host, data.blocks, SDHCI_BLOCK_COUNT); + sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); + + mmc_send_cmd(mmc, &cmd, NULL); + ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); + + if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK) + udelay(1); + + } while (ctrl & SDHCI_CTRL_EXEC_TUNING); + + if (tuning_loop_counter < 0) { + ctrl &= ~SDHCI_CTRL_TUNED_CLK; + sdhci_writel(host, ctrl, SDHCI_HOST_CTRL2); + } + + if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { + printf("%s:Tuning failed\n", __func__); + return -1; + } + + udelay(1); + arasan_zynqmp_dll_reset(host, deviceid); + + /* Enable only interrupts served by the SD controller */ + sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, + SDHCI_INT_ENABLE); + /* Mask all sdhci interrupt sources */ + sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); + + return 0; +} + +static void arasan_sdhci_set_tapdelay(struct sdhci_host *host) +{ + struct arasan_sdhci_priv *priv = dev_get_priv(host->mmc->dev); + struct mmc *mmc = (struct mmc *)host->mmc; + u8 uhsmode; + + if (IS_SD(mmc)) + uhsmode = mode2timing[mmc->selected_mode]; + else + return; + + if (uhsmode >= UHS_SDR25_BUS_SPEED) + arasan_zynqmp_set_tapdelay(priv->deviceid, uhsmode, + priv->bank); +} + +static void arasan_sdhci_set_control_reg(struct sdhci_host *host) +{ + struct mmc *mmc = (struct mmc *)host->mmc; + u32 reg; + + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + reg = sdhci_readw(host, SDHCI_HOST_CTRL2); + reg |= SDHCI_18V_SIGNAL; + sdhci_writew(host, reg, SDHCI_HOST_CTRL2); + } + + if (mmc->selected_mode > SD_HS && + mmc->selected_mode <= UHS_DDR50) { + reg = sdhci_readw(host, SDHCI_HOST_CTRL2); + reg &= ~SDHCI_CTRL2_MODE_MASK; + switch (mmc->selected_mode) { + case UHS_SDR12: + reg |= UHS_SDR12_BUS_SPEED; + break; + case UHS_SDR25: + reg |= UHS_SDR25_BUS_SPEED; + break; + case UHS_SDR50: + reg |= UHS_SDR50_BUS_SPEED; + break; + case UHS_SDR104: + reg |= UHS_SDR104_BUS_SPEED; + break; + case UHS_DDR50: + reg |= UHS_DDR50_BUS_SPEED; + break; + default: + break; + } + sdhci_writew(host, reg, SDHCI_HOST_CTRL2); + } +} +#endif + +#if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP) +const struct sdhci_ops arasan_ops = { + .platform_execute_tuning = &arasan_sdhci_execute_tuning, + .set_delay = &arasan_sdhci_set_tapdelay, + .set_control_reg = &arasan_sdhci_set_control_reg, +}; +#endif + static int arasan_sdhci_probe(struct udevice *dev) { struct arasan_sdhci_plat *plat = dev_get_platdata(dev); struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - struct sdhci_host *host = dev_get_priv(dev); + struct arasan_sdhci_priv *priv = dev_get_priv(dev); + struct sdhci_host *host; struct clk clk; unsigned long clock; int ret;
+ host = priv->host; + ret = clk_get_by_index(dev, 0, &clk); if (ret < 0) { dev_err(dev, "failed to get clock\n"); @@ -46,7 +244,6 @@ static int arasan_sdhci_probe(struct udevice *dev) dev_err(dev, "failed to get rate\n"); return clock; } - debug("%s: CLK %ld\n", __func__, clock);
ret = clk_enable(&clk); if (ret && ret != -ENOSYS) { @@ -61,6 +258,9 @@ static int arasan_sdhci_probe(struct udevice *dev) host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; #endif
+ if (priv->no_1p8) + host->quirks |= SDHCI_QUIRK_NO_1_8_V; + host->max_clk = clock;
ret = sdhci_setup_cfg(&plat->cfg, host, plat->f_max, @@ -78,10 +278,28 @@ static int arasan_sdhci_probe(struct udevice *dev) static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev) { struct arasan_sdhci_plat *plat = dev_get_platdata(dev); - struct sdhci_host *host = dev_get_priv(dev); + struct arasan_sdhci_priv *priv = dev_get_priv(dev); + + priv->host = calloc(1, sizeof(struct sdhci_host)); + if (!priv->host) + return -1;
- host->name = dev->name; - host->ioaddr = (void *)devfdt_get_addr(dev); + priv->host->name = dev->name; + priv->host->ioaddr = (void *)devfdt_get_addr(dev); + + priv->deviceid = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "xlnx,device_id", -1); + priv->bank = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "xlnx,mio_bank", -1); + if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), + "no-1-8-v", NULL)) + priv->no_1p8 = 1; + else + priv->no_1p8 = 0; + +#if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP) + priv->host->ops = &arasan_ops; +#endif
plat->f_max = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "max-frequency", CONFIG_ZYNQ_SDHCI_MAX_FREQ); @@ -109,6 +327,6 @@ U_BOOT_DRIVER(arasan_sdhci_drv) = { .ops = &sdhci_ops, .bind = arasan_sdhci_bind, .probe = arasan_sdhci_probe, - .priv_auto_alloc_size = sizeof(struct sdhci_host), + .priv_auto_alloc_size = sizeof(struct arasan_sdhci_priv), .platdata_auto_alloc_size = sizeof(struct arasan_sdhci_plat), };

This patch enables UHS support for ZynqMP zcu102 rev 1.0 board.
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com --- configs/xilinx_zynqmp_zcu102_rev1_0_defconfig | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/configs/xilinx_zynqmp_zcu102_rev1_0_defconfig b/configs/xilinx_zynqmp_zcu102_rev1_0_defconfig index bf27ca4..d03a760 100644 --- a/configs/xilinx_zynqmp_zcu102_rev1_0_defconfig +++ b/configs/xilinx_zynqmp_zcu102_rev1_0_defconfig @@ -51,6 +51,8 @@ CONFIG_CMD_PCA953X=y CONFIG_DM_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_ZYNQ=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_BAR=y CONFIG_SPI_FLASH_SPANSION=y
participants (1)
-
Siva Durga Prasad Paladugu