diff options
-rw-r--r-- | arch/arm/dts/armada-3720-db.dts | 21 | ||||
-rw-r--r-- | arch/arm/dts/armada-37xx.dtsi | 16 | ||||
-rw-r--r-- | arch/arm/dts/armada-7040-db.dts | 14 | ||||
-rw-r--r-- | arch/arm/dts/armada-ap806.dtsi | 8 | ||||
-rw-r--r-- | arch/arm/dts/armada-cp110-master.dtsi | 8 | ||||
-rw-r--r-- | cmd/mvebu/bubt.c | 25 | ||||
-rw-r--r-- | configs/mvebu_db-88f3720_defconfig | 6 | ||||
-rw-r--r-- | configs/mvebu_db-88f7040_defconfig | 5 | ||||
-rw-r--r-- | configs/mvebu_db-88f8040_defconfig | 5 | ||||
-rw-r--r-- | drivers/mmc/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/sdhci.c | 10 | ||||
-rw-r--r-- | drivers/mmc/xenon_sdhci.c | 497 | ||||
-rw-r--r-- | include/configs/mvebu_armada-8k.h | 3 | ||||
-rw-r--r-- | include/configs/mvebu_db-88f3720.h | 3 | ||||
-rw-r--r-- | include/sdhci.h | 1 |
16 files changed, 624 insertions, 10 deletions
diff --git a/arch/arm/dts/armada-3720-db.dts b/arch/arm/dts/armada-3720-db.dts index 83967eeac4..85761afb74 100644 --- a/arch/arm/dts/armada-3720-db.dts +++ b/arch/arm/dts/armada-3720-db.dts @@ -94,6 +94,27 @@ status = "okay"; }; +&sdhci0 { + bus-width = <4>; + status = "okay"; +}; + +&sdhci1 { + non-removable; + bus-width = <8>; + mmc-ddr-1_8v; + mmc-hs400-1_8v; + marvell,pad-type = "fixed-1-8v"; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + mmccard: mmccard@0 { + compatible = "mmc-card"; + reg = <0>; + }; +}; + &spi0 { status = "okay"; diff --git a/arch/arm/dts/armada-37xx.dtsi b/arch/arm/dts/armada-37xx.dtsi index e27eae0362..062f2a69f4 100644 --- a/arch/arm/dts/armada-37xx.dtsi +++ b/arch/arm/dts/armada-37xx.dtsi @@ -133,6 +133,22 @@ }; }; + sdhci0: sdhci@d0000 { + compatible = "marvell,armada-3700-sdhci", + "marvell,sdhci-xenon"; + reg = <0xd0000 0x300 + 0x1e808 0x4>; + status = "disabled"; + }; + + sdhci1: sdhci@d8000 { + compatible = "marvell,armada-3700-sdhci", + "marvell,sdhci-xenon"; + reg = <0xd8000 0x300 + 0x17808 0x4>; + status = "disabled"; + }; + sata: sata@e0000 { compatible = "marvell,armada-3700-ahci"; reg = <0xe0000 0x2000>; diff --git a/arch/arm/dts/armada-7040-db.dts b/arch/arm/dts/armada-7040-db.dts index 466c6dcc3f..63442df8f3 100644 --- a/arch/arm/dts/armada-7040-db.dts +++ b/arch/arm/dts/armada-7040-db.dts @@ -195,3 +195,17 @@ &cpm_utmi1 { status = "okay"; }; + +&ap_sdhci0 { + status = "okay"; + bus-width = <4>; + no-1-8-v; + non-removable; +}; + +&cpm_sdhci0 { + status = "okay"; + bus-width = <4>; + no-1-8-v; + non-removable; +}; diff --git a/arch/arm/dts/armada-ap806.dtsi b/arch/arm/dts/armada-ap806.dtsi index efb383b9f3..3042cb154b 100644 --- a/arch/arm/dts/armada-ap806.dtsi +++ b/arch/arm/dts/armada-ap806.dtsi @@ -234,6 +234,14 @@ }; + ap_sdhci0: sdhci@6e0000 { + compatible = "marvell,armada-8k-sdhci"; + reg = <0x6e0000 0x300>; + interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>; + dma-coherent; + status = "disabled"; + }; + ap_syscon: system-controller@6f4000 { compatible = "marvell,ap806-system-controller", "syscon"; diff --git a/arch/arm/dts/armada-cp110-master.dtsi b/arch/arm/dts/armada-cp110-master.dtsi index d637867615..661a69679e 100644 --- a/arch/arm/dts/armada-cp110-master.dtsi +++ b/arch/arm/dts/armada-cp110-master.dtsi @@ -206,6 +206,14 @@ utmi-port = <UTMI_PHY_TO_USB_HOST1>; status = "disabled"; }; + + cpm_sdhci0: sdhci@780000 { + compatible = "marvell,armada-8k-sdhci"; + reg = <0x780000 0x300>; + interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>; + dma-coherent; + status = "disabled"; + }; }; cpm_pcie0: pcie@f2600000 { diff --git a/cmd/mvebu/bubt.c b/cmd/mvebu/bubt.c index 1cbfcf0863..b752927e8c 100644 --- a/cmd/mvebu/bubt.c +++ b/cmd/mvebu/bubt.c @@ -18,6 +18,9 @@ #include <usb.h> #include <fs.h> #include <mmc.h> +#ifdef CONFIG_BLK +#include <blk.h> +#endif #include <u-boot/sha1.h> #include <u-boot/sha256.h> @@ -116,7 +119,9 @@ static int mmc_burn_image(size_t image_size) ulong blk_written; int err; const u8 mmc_dev_num = CONFIG_SYS_MMC_ENV_DEV; - +#ifdef CONFIG_BLK + struct blk_desc *blk_desc; +#endif mmc = find_mmc_device(mmc_dev_num); if (!mmc) { printf("No SD/MMC/eMMC card found\n"); @@ -144,13 +149,27 @@ static int mmc_burn_image(size_t image_size) * MMC/eMMC boots from LBA-0 */ start_lba = IS_SD(mmc) ? 1 : 0; +#ifdef CONFIG_BLK + blk_count = image_size / mmc->write_bl_len; + if (image_size % mmc->write_bl_len) + blk_count += 1; + + blk_desc = mmc_get_blk_desc(mmc); + if (!blk_desc) { + printf("Error - failed to obtain block descriptor\n"); + return -ENODEV; + } + blk_written = blk_dwrite(blk_desc, start_lba, blk_count, + (void *)get_load_addr()); +#else blk_count = image_size / mmc->block_dev.blksz; if (image_size % mmc->block_dev.blksz) blk_count += 1; blk_written = mmc->block_dev.block_write(mmc_dev_num, - start_lba, blk_count, - (void *)get_load_addr()); + start_lba, blk_count, + (void *)get_load_addr()); +#endif /* CONFIG_BLK */ if (blk_written != blk_count) { printf("Error - written %#lx blocks\n", blk_written); return -ENOSPC; diff --git a/configs/mvebu_db-88f3720_defconfig b/configs/mvebu_db-88f3720_defconfig index 8b5229cc67..7d97e11a1c 100644 --- a/configs/mvebu_db-88f3720_defconfig +++ b/configs/mvebu_db-88f3720_defconfig @@ -2,7 +2,6 @@ CONFIG_ARM=y CONFIG_ARCH_MVEBU=y CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_TARGET_MVEBU_DB_88F3720=y -# CONFIG_MMC is not set CONFIG_DEFAULT_DEVICE_TREE="armada-3720-db" CONFIG_AHCI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set @@ -13,6 +12,7 @@ CONFIG_ARCH_EARLY_INIT_R=y CONFIG_BOARD_EARLY_INIT_F=y # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set +CONFIG_CMD_MMC=y CONFIG_CMD_SF=y CONFIG_CMD_SPI=y CONFIG_CMD_I2C=y @@ -33,6 +33,10 @@ CONFIG_BLOCK_CACHE=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y CONFIG_MISC=y +CONFIG_DM_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_SDMA=y +CONFIG_MMC_SDHCI_XENON=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_MACRONIX=y CONFIG_SPI_FLASH_SPANSION=y diff --git a/configs/mvebu_db-88f7040_defconfig b/configs/mvebu_db-88f7040_defconfig index 4107767be7..d6beb4694b 100644 --- a/configs/mvebu_db-88f7040_defconfig +++ b/configs/mvebu_db-88f7040_defconfig @@ -14,6 +14,7 @@ CONFIG_BOARD_EARLY_INIT_F=y CONFIG_HUSH_PARSER=y # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set +CONFIG_CMD_MMC=y CONFIG_CMD_SF=y CONFIG_CMD_SPI=y CONFIG_CMD_I2C=y @@ -35,7 +36,9 @@ CONFIG_BLOCK_CACHE=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_MVTWSI=y CONFIG_MISC=y -# CONFIG_MMC is not set +CONFIG_DM_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_XENON=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_MACRONIX=y CONFIG_SPI_FLASH_SPANSION=y diff --git a/configs/mvebu_db-88f8040_defconfig b/configs/mvebu_db-88f8040_defconfig index 1699234eb7..dc95fe4f2d 100644 --- a/configs/mvebu_db-88f8040_defconfig +++ b/configs/mvebu_db-88f8040_defconfig @@ -14,6 +14,7 @@ CONFIG_BOARD_EARLY_INIT_F=y CONFIG_HUSH_PARSER=y # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set +CONFIG_CMD_MMC=y CONFIG_CMD_SF=y CONFIG_CMD_SPI=y CONFIG_CMD_I2C=y @@ -35,7 +36,9 @@ CONFIG_BLOCK_CACHE=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_MVTWSI=y CONFIG_MISC=y -# CONFIG_MMC is not set +CONFIG_DM_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_XENON=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_MACRONIX=y CONFIG_SPI_FLASH_SPANSION=y diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 9ed8da39ef..147e52d332 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -287,6 +287,17 @@ config MMC_SDHCI_SPEAR If unsure, say N. +config MMC_SDHCI_XENON + bool "SDHCI support for the Xenon SDHCI controller" + depends on MMC_SDHCI && DM_MMC && OF_CONTROL + help + Support for Xenon SDHCI host controller on Marvell Armada 3700 + 7k/8k ARM SoCs platforms + + If you have a controller with this interface, say Y here. + + If unsure, say N. + config MMC_SDHCI_TEGRA bool "SDHCI platform support for the Tegra SD/MMC Controller" depends on TEGRA diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 4dca09c955..6af7f79ff8 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MMC_SDHCI_MV) += mv_sdhci.o obj-$(CONFIG_MMC_SDHCI_S5P) += s5p_sdhci.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += spear_sdhci.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += tegra_mmc.o +obj-$(CONFIG_MMC_SDHCI_XENON) += xenon_sdhci.o obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o obj-$(CONFIG_MMC_UNIPHIER) += uniphier-sd.o diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 84e05815bf..93cefd89cd 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -295,7 +295,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) { struct sdhci_host *host = mmc->priv; - unsigned int div, clk = 0, timeout, reg; + unsigned int div, clk = 0, timeout; /* Wait max 20 ms */ timeout = 200; @@ -311,9 +311,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) udelay(100); } - reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN); - sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) return 0; @@ -460,6 +458,10 @@ static int sdhci_set_ios(struct mmc *mmc) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + /* If available, call the driver specific "post" set_ios() function */ + if (host->ops && host->ops->set_ios_post) + host->ops->set_ios_post(host); + return 0; } diff --git a/drivers/mmc/xenon_sdhci.c b/drivers/mmc/xenon_sdhci.c new file mode 100644 index 0000000000..828da111c8 --- /dev/null +++ b/drivers/mmc/xenon_sdhci.c @@ -0,0 +1,497 @@ +/* + * Driver for Marvell SOC Platform Group Xenon SDHC as a platform device + * + * Copyright (C) 2016 Marvell, All Rights Reserved. + * + * Author: Victor Gu <xigu@marvell.com> + * Date: 2016-8-24 + * + * Included parts of the Linux driver version which was written by: + * Hu Ziji <huziji@marvell.com> + * + * Ported to from Marvell 2015.01 to mainline U-Boot 2017.01: + * Stefan Roese <sr@denx.de> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <malloc.h> +#include <sdhci.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Register Offset of SD Host Controller SOCP self-defined register */ +#define SDHC_SYS_CFG_INFO 0x0104 +#define SLOT_TYPE_SDIO_SHIFT 24 +#define SLOT_TYPE_EMMC_MASK 0xFF +#define SLOT_TYPE_EMMC_SHIFT 16 +#define SLOT_TYPE_SD_SDIO_MMC_MASK 0xFF +#define SLOT_TYPE_SD_SDIO_MMC_SHIFT 8 +#define NR_SUPPORTED_SLOT_MASK 0x7 + +#define SDHC_SYS_OP_CTRL 0x0108 +#define AUTO_CLKGATE_DISABLE_MASK BIT(20) +#define SDCLK_IDLEOFF_ENABLE_SHIFT 8 +#define SLOT_ENABLE_SHIFT 0 + +#define SDHC_SYS_EXT_OP_CTRL 0x010C +#define MASK_CMD_CONFLICT_ERROR BIT(8) + +#define SDHC_SLOT_RETUNING_REQ_CTRL 0x0144 +/* retuning compatible */ +#define RETUNING_COMPATIBLE 0x1 + +/* Xenon specific Mode Select value */ +#define XENON_SDHCI_CTRL_HS200 0x5 +#define XENON_SDHCI_CTRL_HS400 0x6 + +#define EMMC_PHY_REG_BASE 0x170 +#define EMMC_PHY_TIMING_ADJUST EMMC_PHY_REG_BASE +#define OUTPUT_QSN_PHASE_SELECT BIT(17) +#define SAMPL_INV_QSP_PHASE_SELECT BIT(18) +#define SAMPL_INV_QSP_PHASE_SELECT_SHIFT 18 +#define EMMC_PHY_SLOW_MODE BIT(29) +#define PHY_INITIALIZAION BIT(31) +#define WAIT_CYCLE_BEFORE_USING_MASK 0xf +#define WAIT_CYCLE_BEFORE_USING_SHIFT 12 +#define FC_SYNC_EN_DURATION_MASK 0xf +#define FC_SYNC_EN_DURATION_SHIFT 8 +#define FC_SYNC_RST_EN_DURATION_MASK 0xf +#define FC_SYNC_RST_EN_DURATION_SHIFT 4 +#define FC_SYNC_RST_DURATION_MASK 0xf +#define FC_SYNC_RST_DURATION_SHIFT 0 + +#define EMMC_PHY_FUNC_CONTROL (EMMC_PHY_REG_BASE + 0x4) +#define DQ_ASYNC_MODE BIT(4) +#define DQ_DDR_MODE_SHIFT 8 +#define DQ_DDR_MODE_MASK 0xff +#define CMD_DDR_MODE BIT(16) + +#define EMMC_PHY_PAD_CONTROL (EMMC_PHY_REG_BASE + 0x8) +#define REC_EN_SHIFT 24 +#define REC_EN_MASK 0xf +#define FC_DQ_RECEN BIT(24) +#define FC_CMD_RECEN BIT(25) +#define FC_QSP_RECEN BIT(26) +#define FC_QSN_RECEN BIT(27) +#define OEN_QSN BIT(28) +#define AUTO_RECEN_CTRL BIT(30) + +#define EMMC_PHY_PAD_CONTROL1 (EMMC_PHY_REG_BASE + 0xc) +#define EMMC5_1_FC_QSP_PD BIT(9) +#define EMMC5_1_FC_QSP_PU BIT(25) +#define EMMC5_1_FC_CMD_PD BIT(8) +#define EMMC5_1_FC_CMD_PU BIT(24) +#define EMMC5_1_FC_DQ_PD 0xff +#define EMMC5_1_FC_DQ_PU (0xff << 16) + +#define SDHCI_RETUNE_EVT_INTSIG 0x00001000 + +/* Hyperion only have one slot 0 */ +#define XENON_MMC_SLOT_ID_HYPERION 0 + +#define MMC_TIMING_LEGACY 0 +#define MMC_TIMING_MMC_HS 1 +#define MMC_TIMING_SD_HS 2 +#define MMC_TIMING_UHS_SDR12 3 +#define MMC_TIMING_UHS_SDR25 4 +#define MMC_TIMING_UHS_SDR50 5 +#define MMC_TIMING_UHS_SDR104 6 +#define MMC_TIMING_UHS_DDR50 7 +#define MMC_TIMING_MMC_DDR52 8 +#define MMC_TIMING_MMC_HS200 9 +#define MMC_TIMING_MMC_HS400 10 + +#define XENON_MMC_MAX_CLK 400000000 + +enum soc_pad_ctrl_type { + SOC_PAD_SD, + SOC_PAD_FIXED_1_8V, +}; + +struct xenon_sdhci_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +struct xenon_sdhci_priv { + struct sdhci_host host; + + u8 timing; + + unsigned int clock; + + void *pad_ctrl_reg; + int pad_type; +}; + +static int xenon_mmc_phy_init(struct sdhci_host *host) +{ + struct xenon_sdhci_priv *priv = host->mmc->priv; + u32 clock = priv->clock; + u32 time; + u32 var; + + /* Enable QSP PHASE SELECT */ + var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST); + var |= SAMPL_INV_QSP_PHASE_SELECT; + if ((priv->timing == MMC_TIMING_UHS_SDR50) || + (priv->timing == MMC_TIMING_UHS_SDR25) || + (priv->timing == MMC_TIMING_UHS_SDR12) || + (priv->timing == MMC_TIMING_SD_HS) || + (priv->timing == MMC_TIMING_LEGACY)) + var |= EMMC_PHY_SLOW_MODE; + sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST); + + /* Poll for host MMC PHY clock init to be stable */ + /* Wait up to 10ms */ + time = 100; + while (time--) { + var = sdhci_readl(host, SDHCI_CLOCK_CONTROL); + if (var & SDHCI_CLOCK_INT_STABLE) + break; + + udelay(100); + } + + if (time <= 0) { + error("Failed to enable MMC internal clock in time\n"); + return -ETIMEDOUT; + } + + /* Init PHY */ + var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST); + var |= PHY_INITIALIZAION; + sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST); + + if (clock == 0) { + /* Use the possibly slowest bus frequency value */ + clock = 100000; + } + + /* Poll for host eMMC PHY init to complete */ + /* Wait up to 10ms */ + time = 100; + while (time--) { + var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST); + var &= PHY_INITIALIZAION; + if (!var) + break; + + /* wait for host eMMC PHY init to complete */ + udelay(100); + } + + if (time <= 0) { + error("Failed to init MMC PHY in time\n"); + return -ETIMEDOUT; + } + + return 0; +} + +#define ARMADA_3700_SOC_PAD_1_8V 0x1 +#define ARMADA_3700_SOC_PAD_3_3V 0x0 + +static void armada_3700_soc_pad_voltage_set(struct sdhci_host *host) +{ + struct xenon_sdhci_priv *priv = host->mmc->priv; + + if (priv->pad_type == SOC_PAD_FIXED_1_8V) + writel(ARMADA_3700_SOC_PAD_1_8V, priv->pad_ctrl_reg); + else if (priv->pad_type == SOC_PAD_SD) + writel(ARMADA_3700_SOC_PAD_3_3V, priv->pad_ctrl_reg); +} + +static void xenon_mmc_phy_set(struct sdhci_host *host) +{ + struct xenon_sdhci_priv *priv = host->mmc->priv; + u32 var; + + /* Setup pad, set bit[30], bit[28] and bits[26:24] */ + var = sdhci_readl(host, EMMC_PHY_PAD_CONTROL); + var |= AUTO_RECEN_CTRL | OEN_QSN | FC_QSP_RECEN | + FC_CMD_RECEN | FC_DQ_RECEN; + sdhci_writel(host, var, EMMC_PHY_PAD_CONTROL); + + /* Set CMD and DQ Pull Up */ + var = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1); + var |= (EMMC5_1_FC_CMD_PU | EMMC5_1_FC_DQ_PU); + var &= ~(EMMC5_1_FC_CMD_PD | EMMC5_1_FC_DQ_PD); + sdhci_writel(host, var, EMMC_PHY_PAD_CONTROL1); + + /* + * If timing belongs to high speed, set bit[17] of + * EMMC_PHY_TIMING_ADJUST register + */ + if ((priv->timing == MMC_TIMING_MMC_HS400) || + (priv->timing == MMC_TIMING_MMC_HS200) || + (priv->timing == MMC_TIMING_UHS_SDR50) || + (priv->timing == MMC_TIMING_UHS_SDR104) || + (priv->timing == MMC_TIMING_UHS_DDR50) || + (priv->timing == MMC_TIMING_UHS_SDR25) || + (priv->timing == MMC_TIMING_MMC_DDR52)) { + var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST); + var |= OUTPUT_QSN_PHASE_SELECT; + sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST); + } + + /* + * When setting EMMC_PHY_FUNC_CONTROL register, + * SD clock should be disabled + */ + var = sdhci_readl(host, SDHCI_CLOCK_CONTROL); + var &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, var, SDHCI_CLOCK_CONTROL); + + var = sdhci_readl(host, EMMC_PHY_FUNC_CONTROL); + if (host->mmc->ddr_mode) { + var |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE; + } else { + var &= ~((DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | + CMD_DDR_MODE); + } + sdhci_writel(host, var, EMMC_PHY_FUNC_CONTROL); + + /* Enable bus clock */ + var = sdhci_readl(host, SDHCI_CLOCK_CONTROL); + var |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, var, SDHCI_CLOCK_CONTROL); + + xenon_mmc_phy_init(host); +} + +/* Enable/Disable the Auto Clock Gating function of this slot */ +static void xenon_mmc_set_acg(struct sdhci_host *host, bool enable) +{ + u32 var; + + var = sdhci_readl(host, SDHC_SYS_OP_CTRL); + if (enable) + var &= ~AUTO_CLKGATE_DISABLE_MASK; + else + var |= AUTO_CLKGATE_DISABLE_MASK; + + sdhci_writel(host, var, SDHC_SYS_OP_CTRL); +} + +#define SLOT_MASK(slot) BIT(slot) + +/* Enable specific slot */ +static void xenon_mmc_enable_slot(struct sdhci_host *host, u8 slot) +{ + u32 var; + + var = sdhci_readl(host, SDHC_SYS_OP_CTRL); + var |= SLOT_MASK(slot) << SLOT_ENABLE_SHIFT; + sdhci_writel(host, var, SDHC_SYS_OP_CTRL); +} + +/* Enable Parallel Transfer Mode */ +static void xenon_mmc_enable_parallel_tran(struct sdhci_host *host, u8 slot) +{ + u32 var; + + var = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL); + var |= SLOT_MASK(slot); + sdhci_writel(host, var, SDHC_SYS_EXT_OP_CTRL); +} + +static void xenon_mmc_disable_tuning(struct sdhci_host *host, u8 slot) +{ + u32 var; + + /* Clear the Re-Tuning Request functionality */ + var = sdhci_readl(host, SDHC_SLOT_RETUNING_REQ_CTRL); + var &= ~RETUNING_COMPATIBLE; + sdhci_writel(host, var, SDHC_SLOT_RETUNING_REQ_CTRL); + + /* Clear the Re-tuning Event Signal Enable */ + var = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); + var &= ~SDHCI_RETUNE_EVT_INTSIG; + sdhci_writel(host, var, SDHCI_SIGNAL_ENABLE); +} + +/* Mask command conflict error */ +static void xenon_mask_cmd_conflict_err(struct sdhci_host *host) +{ + u32 reg; + + reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL); + reg |= MASK_CMD_CONFLICT_ERROR; + sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL); +} + +/* Platform specific function for post set_ios configuration */ +static void xenon_sdhci_set_ios_post(struct sdhci_host *host) +{ + struct xenon_sdhci_priv *priv = host->mmc->priv; + uint speed = host->mmc->tran_speed; + int pwr_18v = 0; + + if ((sdhci_readb(host, SDHCI_POWER_CONTROL) & ~SDHCI_POWER_ON) == + SDHCI_POWER_180) + pwr_18v = 1; + + /* Set timing variable according to the configured speed */ + if (IS_SD(host->mmc)) { + /* SD/SDIO */ + if (pwr_18v) { + if (host->mmc->ddr_mode) + priv->timing = MMC_TIMING_UHS_DDR50; + else if (speed <= 25000000) + priv->timing = MMC_TIMING_UHS_SDR25; + else + priv->timing = MMC_TIMING_UHS_SDR50; + } else { + if (speed <= 25000000) + priv->timing = MMC_TIMING_LEGACY; + else + priv->timing = MMC_TIMING_SD_HS; + } + } else { + /* eMMC */ + if (host->mmc->ddr_mode) + priv->timing = MMC_TIMING_MMC_DDR52; + else if (speed <= 26000000) + priv->timing = MMC_TIMING_LEGACY; + else + priv->timing = MMC_TIMING_MMC_HS; + } + + /* Re-init the PHY */ + xenon_mmc_phy_set(host); +} + +/* Install a driver specific handler for post set_ios configuration */ +static const struct sdhci_ops xenon_sdhci_ops = { + .set_ios_post = xenon_sdhci_set_ios_post +}; + +static int xenon_sdhci_probe(struct udevice *dev) +{ + struct xenon_sdhci_plat *plat = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct xenon_sdhci_priv *priv = dev_get_priv(dev); + struct sdhci_host *host = dev_get_priv(dev); + int ret; + + host->mmc = &plat->mmc; + host->mmc->priv = host; + host->mmc->dev = dev; + upriv->mmc = host->mmc; + + /* Set quirks */ + host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_32BIT_DMA_ADDR; + + /* Set default timing */ + priv->timing = MMC_TIMING_LEGACY; + + /* Disable auto clock gating during init */ + xenon_mmc_set_acg(host, false); + + /* Enable slot */ + xenon_mmc_enable_slot(host, XENON_MMC_SLOT_ID_HYPERION); + + /* + * Set default power on SoC PHY PAD register (currently only + * available on the Armada 3700) + */ + if (priv->pad_ctrl_reg) + armada_3700_soc_pad_voltage_set(host); + + host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_DDR_52MHz; + switch (fdtdec_get_int(gd->fdt_blob, dev->of_offset, "bus-width", 1)) { + case 8: + host->host_caps |= MMC_MODE_8BIT; + break; + case 4: + host->host_caps |= MMC_MODE_4BIT; + break; + case 1: + break; + default: + printf("Invalid \"bus-width\" value\n"); + return -EINVAL; + } + + host->ops = &xenon_sdhci_ops; + + ret = sdhci_setup_cfg(&plat->cfg, host, XENON_MMC_MAX_CLK, 0); + if (ret) + return ret; + + ret = sdhci_probe(dev); + if (ret) + return ret; + + /* Enable parallel transfer */ + xenon_mmc_enable_parallel_tran(host, XENON_MMC_SLOT_ID_HYPERION); + + /* Disable tuning functionality of this slot */ + xenon_mmc_disable_tuning(host, XENON_MMC_SLOT_ID_HYPERION); + + /* Enable auto clock gating after init */ + xenon_mmc_set_acg(host, true); + + xenon_mask_cmd_conflict_err(host); + + return ret; +} + +static int xenon_sdhci_ofdata_to_platdata(struct udevice *dev) +{ + struct sdhci_host *host = dev_get_priv(dev); + struct xenon_sdhci_priv *priv = dev_get_priv(dev); + const char *name; + + host->name = dev->name; + host->ioaddr = (void *)dev_get_addr(dev); + + if (of_device_is_compatible(dev, "marvell,armada-3700-sdhci")) + priv->pad_ctrl_reg = (void *)dev_get_addr_index(dev, 1); + + name = fdt_getprop(gd->fdt_blob, dev->of_offset, "marvell,pad-type", + NULL); + if (name) { + if (0 == strncmp(name, "sd", 2)) { + priv->pad_type = SOC_PAD_SD; + } else if (0 == strncmp(name, "fixed-1-8v", 10)) { + priv->pad_type = SOC_PAD_FIXED_1_8V; + } else { + printf("Unsupported SOC PHY PAD ctrl type %s\n", name); + return -EINVAL; + } + } + + return 0; +} + +static int xenon_sdhci_bind(struct udevice *dev) +{ + struct xenon_sdhci_plat *plat = dev_get_platdata(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id xenon_sdhci_ids[] = { + { .compatible = "marvell,armada-8k-sdhci",}, + { .compatible = "marvell,armada-3700-sdhci",}, + { } +}; + +U_BOOT_DRIVER(xenon_sdhci_drv) = { + .name = "xenon_sdhci", + .id = UCLASS_MMC, + .of_match = xenon_sdhci_ids, + .ofdata_to_platdata = xenon_sdhci_ofdata_to_platdata, + .ops = &sdhci_ops, + .bind = xenon_sdhci_bind, + .probe = xenon_sdhci_probe, + .priv_auto_alloc_size = sizeof(struct xenon_sdhci_priv), + .platdata_auto_alloc_size = sizeof(struct xenon_sdhci_plat), +}; diff --git a/include/configs/mvebu_armada-8k.h b/include/configs/mvebu_armada-8k.h index 27dc6282d4..58b88016e2 100644 --- a/include/configs/mvebu_armada-8k.h +++ b/include/configs/mvebu_armada-8k.h @@ -114,6 +114,9 @@ #define CONFIG_SYS_SCSI_MAX_DEVICE (CONFIG_SYS_SCSI_MAX_SCSI_ID * \ CONFIG_SYS_SCSI_MAX_LUN) +/* MMC/SD IP block */ +#define CONFIG_GENERIC_MMC + #define CONFIG_SUPPORT_VFAT /* DISK Partition support */ diff --git a/include/configs/mvebu_db-88f3720.h b/include/configs/mvebu_db-88f3720.h index caaad86221..49a4d89ab9 100644 --- a/include/configs/mvebu_db-88f3720.h +++ b/include/configs/mvebu_db-88f3720.h @@ -126,6 +126,9 @@ #define CONFIG_SYS_SCSI_MAX_DEVICE (CONFIG_SYS_SCSI_MAX_SCSI_ID * \ CONFIG_SYS_SCSI_MAX_LUN) +/* MMC/SD IP block */ +#define CONFIG_GENERIC_MMC + #define CONFIG_SUPPORT_VFAT /* DISK Partition support */ diff --git a/include/sdhci.h b/include/sdhci.h index fdef7c40c9..6a43271e96 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -235,6 +235,7 @@ struct sdhci_ops { #endif int (*get_cd)(struct sdhci_host *host); 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); }; |