mmc: Add support for ADI SC5XX-family processor SDHCI peripherals

Co-developed-by: Greg Malysa <malysagreg@gmail.com>
Signed-off-by: Greg Malysa <malysagreg@gmail.com>
Co-developed-by: Ian Roberts <ian.roberts@timesys.com>
Signed-off-by: Ian Roberts <ian.roberts@timesys.com>
Signed-off-by: Vasileios Bimpikas <vasileios.bimpikas@analog.com>
Signed-off-by: Utsav Agarwal <utsav.agarwal@analog.com>
Signed-off-by: Arturs Artamonovs <arturs.artamonovs@analog.com>
Signed-off-by: Oliver Gaskell <Oliver.Gaskell@analog.com>
Signed-off-by: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
This commit is contained in:
Nathan Barrett-Morrison 2025-02-26 12:30:34 -05:00 committed by Tom Rini
parent 7535a9280b
commit 036118ebd8
4 changed files with 159 additions and 0 deletions

View file

@ -636,6 +636,7 @@ F: drivers/dma/adi_dma.c
F: drivers/gpio/adp5588_gpio.c
F: drivers/gpio/gpio-adi-adsp.c
F: drivers/i2c/adi_i2c.c
F: drivers/mmc/adi_sdhci.c
F: drivers/net/dwc_eth_qos_adi.c
F: drivers/pinctrl/pinctrl-adi-adsp.c
F: drivers/remoteproc/adi_sc5xx_rproc.c

View file

@ -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

View file

@ -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

148
drivers/mmc/adi_sdhci.c Normal file
View file

@ -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),
};