Add a driver for the Cadence SD4HC SD/SDIO/eMMC Controller. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>master
parent
3fd0a9ba8c
commit
e5e7a7c204
@ -0,0 +1,125 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Socionext Inc. |
||||||
|
* Author: Masahiro Yamada <yamada.masahiro@socionext.com> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <linux/io.h> |
||||||
|
#include <linux/sizes.h> |
||||||
|
#include <dm/device.h> |
||||||
|
#include <mmc.h> |
||||||
|
#include <sdhci.h> |
||||||
|
|
||||||
|
/* HRS - Host Register Set (specific to Cadence) */ |
||||||
|
#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */ |
||||||
|
#define SDHCI_CDNS_HRS04_ACK BIT(26) |
||||||
|
#define SDHCI_CDNS_HRS04_RD BIT(25) |
||||||
|
#define SDHCI_CDNS_HRS04_WR BIT(24) |
||||||
|
#define SDHCI_CDNS_HRS04_RDATA_SHIFT 12 |
||||||
|
#define SDHCI_CDNS_HRS04_WDATA_SHIFT 8 |
||||||
|
#define SDHCI_CDNS_HRS04_ADDR_SHIFT 0 |
||||||
|
|
||||||
|
/* SRS - Slot Register Set (SDHCI-compatible) */ |
||||||
|
#define SDHCI_CDNS_SRS_BASE 0x200 |
||||||
|
|
||||||
|
/* PHY */ |
||||||
|
#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07 |
||||||
|
#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08 |
||||||
|
|
||||||
|
struct sdhci_cdns_plat { |
||||||
|
struct mmc_config cfg; |
||||||
|
struct mmc mmc; |
||||||
|
void __iomem *hrs_addr; |
||||||
|
}; |
||||||
|
|
||||||
|
static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat, |
||||||
|
u8 addr, u8 data) |
||||||
|
{ |
||||||
|
void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04; |
||||||
|
u32 tmp; |
||||||
|
|
||||||
|
tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) | |
||||||
|
(addr << SDHCI_CDNS_HRS04_ADDR_SHIFT); |
||||||
|
writel(tmp, reg); |
||||||
|
|
||||||
|
tmp |= SDHCI_CDNS_HRS04_WR; |
||||||
|
writel(tmp, reg); |
||||||
|
|
||||||
|
tmp &= ~SDHCI_CDNS_HRS04_WR; |
||||||
|
writel(tmp, reg); |
||||||
|
} |
||||||
|
|
||||||
|
static void sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat) |
||||||
|
{ |
||||||
|
sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_HS, 4); |
||||||
|
sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_DEFAULT, 4); |
||||||
|
sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_LEGACY, 9); |
||||||
|
sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_SDR, 2); |
||||||
|
sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_DDR, 3); |
||||||
|
} |
||||||
|
|
||||||
|
static int sdhci_cdns_bind(struct udevice *dev) |
||||||
|
{ |
||||||
|
struct sdhci_cdns_plat *plat = dev_get_platdata(dev); |
||||||
|
|
||||||
|
return sdhci_bind(dev, &plat->mmc, &plat->cfg); |
||||||
|
} |
||||||
|
|
||||||
|
static int sdhci_cdns_probe(struct udevice *dev) |
||||||
|
{ |
||||||
|
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
||||||
|
struct sdhci_cdns_plat *plat = dev_get_platdata(dev); |
||||||
|
struct sdhci_host *host = dev_get_priv(dev); |
||||||
|
fdt_addr_t base; |
||||||
|
int ret; |
||||||
|
|
||||||
|
base = dev_get_addr(dev); |
||||||
|
if (base == FDT_ADDR_T_NONE) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
plat->hrs_addr = devm_ioremap(dev, base, SZ_1K); |
||||||
|
if (!plat->hrs_addr) |
||||||
|
return -ENOMEM; |
||||||
|
|
||||||
|
host->name = dev->name; |
||||||
|
host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE; |
||||||
|
host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD; |
||||||
|
|
||||||
|
sdhci_cdns_phy_init(plat); |
||||||
|
|
||||||
|
ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
|
||||||
|
upriv->mmc = &plat->mmc; |
||||||
|
host->mmc = &plat->mmc; |
||||||
|
host->mmc->priv = host; |
||||||
|
|
||||||
|
return sdhci_probe(dev); |
||||||
|
} |
||||||
|
|
||||||
|
static const struct udevice_id sdhci_cdns_match[] = { |
||||||
|
{ .compatible = "socionext,uniphier-sd4hc" }, |
||||||
|
{ .compatible = "cdns,sd4hc" }, |
||||||
|
{ /* sentinel */ } |
||||||
|
}; |
||||||
|
|
||||||
|
U_BOOT_DRIVER(sdhci_cdns) = { |
||||||
|
.name = "sdhci-cdns", |
||||||
|
.id = UCLASS_MMC, |
||||||
|
.of_match = sdhci_cdns_match, |
||||||
|
.bind = sdhci_cdns_bind, |
||||||
|
.probe = sdhci_cdns_probe, |
||||||
|
.priv_auto_alloc_size = sizeof(struct sdhci_host), |
||||||
|
.platdata_auto_alloc_size = sizeof(struct sdhci_cdns_plat), |
||||||
|
.ops = &sdhci_ops, |
||||||
|
}; |
Loading…
Reference in new issue