mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-16 09:54:35 +00:00
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:
parent
7535a9280b
commit
036118ebd8
4 changed files with 159 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
148
drivers/mmc/adi_sdhci.c
Normal 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),
|
||||
};
|
Loading…
Add table
Reference in a new issue