diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mmc/adi_sdhci.c | 148 | ||||
| -rw-r--r-- | drivers/mmc/fsl_esdhc_imx.c | 8 | ||||
| -rw-r--r-- | drivers/mmc/msm_sdhci.c | 10 | ||||
| -rw-r--r-- | drivers/mmc/sunxi_mmc.c | 35 | ||||
| -rw-r--r-- | drivers/mmc/sunxi_mmc.h | 18 | 
7 files changed, 220 insertions, 15 deletions
| diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 4827834b4aa..6740591a653 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -292,6 +292,15 @@ config MMC_DW_ROCKCHIP  	  SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well  	  as removeable SD and micro-SD cards. +config MMC_SDHCI_ADI +	bool "ADI SD/MMC controller support" +	depends on ARCH_SC5XX +	depends on DM_MMC && OF_CONTROL +	depends on MMC_SDHCI && MMC_SDHCI_ADMA +	help +	  This enables support for the SD/MMC controller included in some Analog +	  Devices SC5XX Socs. +  config MMC_DW_SOCFPGA  	bool "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"  	depends on ARCH_SOCFPGA @@ -919,6 +928,12 @@ config ESDHC_DETECT_QUIRK  	bool "QIXIS-based eSDHC quirk detection"  	depends on FSL_ESDHC && FSL_QIXIS +config ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE +	bool +	depends on FSL_ESDHC || FSL_ESDHC_IMX +	def_bool y if ARCH_T1024 || ARCH_T1040 || ARCH_T1042 || ARCH_T2080 \ +		|| FSL_ESDHC_IMX +  config FSL_ESDHC_IMX  	bool "Freescale/NXP i.MX eSDHC controller support"  	help diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 90e76f90769..94ed28ead71 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_MMC_SDHCI_MV)		+= mv_sdhci.o  obj-$(CONFIG_MMC_SDHCI_NPCM)            += npcm_sdhci.o  obj-$(CONFIG_MMC_SDHCI_PIC32)		+= pic32_sdhci.o  obj-$(CONFIG_MMC_SDHCI_ROCKCHIP)	+= rockchip_sdhci.o +obj-$(CONFIG_MMC_SDHCI_ADI)		+= adi_sdhci.o  obj-$(CONFIG_MMC_SDHCI_S5P)		+= s5p_sdhci.o  obj-$(CONFIG_MMC_SDHCI_SNPS)		+= snps_sdhci.o  obj-$(CONFIG_MMC_SDHCI_STI)		+= sti_sdhci.o diff --git a/drivers/mmc/adi_sdhci.c b/drivers/mmc/adi_sdhci.c new file mode 100644 index 00000000000..65a22cefb71 --- /dev/null +++ b/drivers/mmc/adi_sdhci.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * Based on Rockchip's sdhci.c file + */ + +#include <clk.h> +#include <dm.h> +#include <malloc.h> +#include <sdhci.h> +#include <asm/cache.h> + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define EMMC_MIN_FREQ	400000 + +/* Check if an operation crossed a boundary of size ADMA_BOUNDARY_ALIGN */ +#define ADMA_BOUNDARY_ALGN SZ_128M +#define BOUNDARY_OK(addr, len) \ +	(((addr) | (ADMA_BOUNDARY_ALGN - 1)) == (((addr) + (len) - 1) | \ +	(ADMA_BOUNDARY_ALGN - 1))) + +/* We split a descriptor for every crossing of the ADMA alignment boundary, + * so we need an additional descriptor for every expected crossing. + * As I understand it, the max expected transaction size is: + *  CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN + * + * With the way the SDHCI-ADMA driver is implemented, if ADMA_MAX_LEN was a + * clean power of two, we'd only ever need +1 descriptor as the first + * descriptor that got split would then bring the remaining DMA + * destination addresses into alignment. Unfortunately, it's currently + * hardcoded to a non-power-of-two value. + * + * If that ever becomes parameterized, ADMA max length can be set to + * 0x10000, and set this to 1. + */ +#define ADMA_POTENTIAL_CROSSINGS \ +	DIV_ROUND_UP((CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN), \ +		 ADMA_BOUNDARY_ALGN) +/* +1 descriptor for each crossing. + */ +#define ADMA_TABLE_EXTRA_SZ (ADMA_POTENTIAL_CROSSINGS * ADMA_DESC_LEN) + +struct adi_sdhc_plat { +	struct mmc_config cfg; +	struct mmc mmc; +}; + +void adi_dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, +				 dma_addr_t addr, int len, bool end) +{ +	int tmplen, offset; + +	if (likely(!len || BOUNDARY_OK(addr, len))) { +		sdhci_adma_write_desc(host, desc, addr, len, end); +		return; +	} + +	offset = addr & (ADMA_BOUNDARY_ALGN - 1); +	tmplen = ADMA_BOUNDARY_ALGN - offset; +	sdhci_adma_write_desc(host, desc, addr, tmplen, false); + +	addr += tmplen; +	len -= tmplen; +	sdhci_adma_write_desc(host, desc, addr, len, end); +} + +struct sdhci_ops adi_dwcmshc_sdhci_ops = { +	.adma_write_desc = adi_dwcmshc_adma_write_desc, +}; + +static int adi_dwcmshc_sdhci_probe(struct udevice *dev) +{ +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); +	struct adi_sdhc_plat *plat = dev_get_plat(dev); +	struct sdhci_host *host = dev_get_priv(dev); +	int max_frequency, ret; +	struct clk clk; + +	max_frequency = dev_read_u32_default(dev, "max-frequency", 0); +	ret = clk_get_by_index(dev, 0, &clk); + +	host->quirks = 0; +	host->max_clk = max_frequency; +	/* +	 * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg +	 * doesn't allow us to clear MMC_MODE_4BIT.  Consequently, we don't +	 * check for other bus-width values. +	 */ +	if (host->bus_width == 8) +		host->host_caps |= MMC_MODE_8BIT; + +	host->mmc = &plat->mmc; +	host->mmc->priv = host; +	host->mmc->dev = dev; +	upriv->mmc = host->mmc; + +	host->ops = &adi_dwcmshc_sdhci_ops; +	host->adma_desc_table = memalign(ARCH_DMA_MINALIGN, +					 ADMA_TABLE_SZ + ADMA_TABLE_EXTRA_SZ); +	host->adma_addr = virt_to_phys(host->adma_desc_table); + +	ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ); +	if (ret) +		return ret; + +	return sdhci_probe(dev); +} + +static int adi_dwcmshc_sdhci_of_to_plat(struct udevice *dev) +{ +	struct sdhci_host *host = dev_get_priv(dev); + +	host->name = dev->name; +	host->ioaddr = dev_read_addr_ptr(dev); +	host->bus_width = dev_read_u32_default(dev, "bus-width", 4); + +	return 0; +} + +static int adi_sdhci_bind(struct udevice *dev) +{ +	struct adi_sdhc_plat *plat = dev_get_plat(dev); + +	return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id adi_dwcmshc_sdhci_ids[] = { +	{ .compatible = "adi,dwc-sdhci" }, +	{ } +}; + +U_BOOT_DRIVER(adi_dwcmshc_sdhci_drv) = { +	.name		= "adi_sdhci", +	.id		= UCLASS_MMC, +	.of_match	= adi_dwcmshc_sdhci_ids, +	.of_to_plat	= adi_dwcmshc_sdhci_of_to_plat, +	.ops		= &sdhci_ops, +	.bind		= adi_sdhci_bind, +	.probe		= adi_dwcmshc_sdhci_probe, +	.priv_auto	= sizeof(struct sdhci_host), +	.plat_auto	= sizeof(struct adi_sdhc_plat), +}; diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index d7a45ef0ad0..926113f79d3 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -40,12 +40,6 @@  #include <linux/iopoll.h>  #include <linux/dma-mapping.h> -#ifndef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE -#ifdef CONFIG_FSL_USDHC -#define ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE	1 -#endif -#endif -  DECLARE_GLOBAL_DATA_PTR;  #define SDHCI_IRQ_EN_BITS		(IRQSTATEN_CC | IRQSTATEN_TC | \ @@ -376,7 +370,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc,  	    (timeout == 4 || timeout == 8 || timeout == 12))  		timeout++; -	if (IS_ENABLED(ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE)) +	if (IS_ENABLED(CONFIG_ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE))  		timeout = 0xE;  	esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 27bb7052fca..ac77fb06bf7 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -10,6 +10,7 @@  #include <clk.h>  #include <dm.h>  #include <malloc.h> +#include <reset.h>  #include <sdhci.h>  #include <wait_bit.h>  #include <asm/global_data.h> @@ -153,9 +154,18 @@ static int msm_sdc_probe(struct udevice *dev)  	const struct msm_sdhc_variant_info *var_info;  	struct sdhci_host *host = &prv->host;  	u32 core_version, core_minor, core_major; +	struct reset_ctl bcr_rst;  	u32 caps;  	int ret; +	ret = reset_get_by_index(dev, 0, &bcr_rst); +	if (!ret) { +		reset_assert(&bcr_rst); +		udelay(200); +		reset_deassert(&bcr_rst); +		udelay(200); +	} +  	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B;  	host->max_clk = 0; diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 0b56d1405be..951e6acd34d 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -92,6 +92,13 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)  		pll = CCM_MMC_CTRL_PLL6;  		pll_hz = clock_get_pll6();  #endif +		/* +		 * On the D1/R528/T113 mux source 1 refers to PLL_PERIPH0(1x), +		 * like for the older SoCs. However we still have the hidden +		 * divider of 2x, so compensate for that here. +		 */ +		if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) +			pll_hz /= 2;  	}  	div = pll_hz / hz; @@ -442,6 +449,26 @@ out:  	return error;  } +static void sunxi_mmc_reset(void *regs) +{ +	/* Reset controller */ +	writel(SUNXI_MMC_GCTRL_RESET, regs + SUNXI_MMC_GCTRL); +	udelay(1000); + +	if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) { +		/* Reset card */ +		writel(SUNXI_MMC_HWRST_ASSERT, regs + SUNXI_MMC_HWRST); +		udelay(10); +		writel(SUNXI_MMC_HWRST_DEASSERT, regs + SUNXI_MMC_HWRST); +		udelay(300); + +		/* Setup FIFO R/W threshold. Needed on H616. */ +		writel(SUNXI_MMC_THLDC_READ_THLD(512) | +		       SUNXI_MMC_THLDC_WRITE_EN | +		       SUNXI_MMC_THLDC_READ_EN, regs + SUNXI_MMC_THLDC); +	} +} +  /* non-DM code here is used by the (ARM) SPL only */  #if !CONFIG_IS_ENABLED(DM_MMC) @@ -489,9 +516,7 @@ static int sunxi_mmc_core_init(struct mmc *mmc)  {  	struct sunxi_mmc_priv *priv = mmc->priv; -	/* Reset controller */ -	writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); -	udelay(1000); +	sunxi_mmc_reset(priv->reg);  	return 0;  } @@ -684,9 +709,7 @@ static int sunxi_mmc_probe(struct udevice *dev)  	upriv->mmc = &plat->mmc; -	/* Reset controller */ -	writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); -	udelay(1000); +	sunxi_mmc_reset(priv->reg);  	return 0;  } diff --git a/drivers/mmc/sunxi_mmc.h b/drivers/mmc/sunxi_mmc.h index f4ae5a790c8..71865160319 100644 --- a/drivers/mmc/sunxi_mmc.h +++ b/drivers/mmc/sunxi_mmc.h @@ -37,7 +37,9 @@ struct sunxi_mmc {  	u32 res0;		/* 0x54 reserved */  	u32 a12a;		/* 0x58 Auto command 12 argument */  	u32 ntsr;		/* 0x5c	New timing set register */ -	u32 res1[8]; +	u32 res1[6]; +	u32 hwrst;		/* 0x78 Hardware Reset */ +	u32 res5;  	u32 dmac;		/* 0x80 internal DMA control */  	u32 dlba;		/* 0x84 internal DMA descr list base address */  	u32 idst;		/* 0x88 internal DMA status */ @@ -46,7 +48,8 @@ struct sunxi_mmc {  	u32 cbda;		/* 0x94 */  	u32 res2[26];  #if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2) -	u32 res3[17]; +	u32 thldc;		/* 0x100 Threshold control */ +	u32 res3[16];  	u32 samp_dl;  	u32 res4[46];  #endif @@ -57,6 +60,7 @@ struct sunxi_mmc {  #define SUNXI_MMC_CLK_ENABLE		(0x1 << 16)  #define SUNXI_MMC_CLK_DIVIDER_MASK	(0xff) +#define SUNXI_MMC_GCTRL		0x000  #define SUNXI_MMC_GCTRL_SOFT_RESET	(0x1 << 0)  #define SUNXI_MMC_GCTRL_FIFO_RESET	(0x1 << 1)  #define SUNXI_MMC_GCTRL_DMA_RESET	(0x1 << 2) @@ -123,6 +127,10 @@ struct sunxi_mmc {  #define SUNXI_MMC_NTSR_MODE_SEL_NEW		(0x1 << 31) +#define SUNXI_MMC_HWRST		0x078 +#define SUNXI_MMC_HWRST_ASSERT		(0x0 << 0) +#define SUNXI_MMC_HWRST_DEASSERT	(0x1 << 0) +  #define SUNXI_MMC_IDMAC_RESET		(0x1 << 0)  #define SUNXI_MMC_IDMAC_FIXBURST	(0x1 << 1)  #define SUNXI_MMC_IDMAC_ENABLE		(0x1 << 7) @@ -133,6 +141,12 @@ struct sunxi_mmc {  #define SUNXI_MMC_COMMON_CLK_GATE		(1 << 16)  #define SUNXI_MMC_COMMON_RESET			(1 << 18) +#define SUNXI_MMC_THLDC		0x100 +#define SUNXI_MMC_THLDC_READ_EN		(0x1 << 0) +#define SUNXI_MMC_THLDC_BSY_CLR_INT_EN	(0x1 << 1) +#define SUNXI_MMC_THLDC_WRITE_EN	(0x1 << 2) +#define SUNXI_MMC_THLDC_READ_THLD(x)	(((x) & 0xfff) << 16) +  #define SUNXI_MMC_CAL_DL_SW_EN		(0x1 << 7)  #endif /* _SUNXI_MMC_H */ | 
