
Adds a driver to support shdci on bcm63158.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- drivers/mmc/Kconfig | 12 +++ drivers/mmc/Makefile | 1 + drivers/mmc/bcm63158_sdhci.c | 153 +++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 drivers/mmc/bcm63158_sdhci.c
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index f04cc44e19..a08e8245cf 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -520,6 +520,18 @@ config MMC_SDHCI_BCM2835
If unsure, say N.
+config MMC_SDHCI_BCM63158 + bool "SDHCI support for the BCM63158 SD/MMC Controller" + depends on ARCH_BCM63158 + depends on MMC_SDHCI + help + This selects the BCM63158 SD/MMC controller. + + If you have a BCM63158 platform with SD or MMC devices, + say Y here. + + If unsure, say N. + config MMC_SDHCI_BCMSTB tristate "SDHCI support for the BCMSTB SD/MMC Controller" depends on MMC_SDHCI diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 17ebc04203..5cebd55549 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_ASPEED) += aspeed_sdhci.o obj-$(CONFIG_MMC_SDHCI_ATMEL) += atmel_sdhci.o obj-$(CONFIG_MMC_SDHCI_BCM2835) += bcm2835_sdhci.o +obj-$(CONFIG_MMC_SDHCI_BCM63158) += bcm63158_sdhci.o obj-$(CONFIG_MMC_SDHCI_BCMSTB) += bcmstb_sdhci.o obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o obj-$(CONFIG_MMC_SDHCI_AM654) += am654_sdhci.o diff --git a/drivers/mmc/bcm63158_sdhci.c b/drivers/mmc/bcm63158_sdhci.c new file mode 100644 index 0000000000..42295d113d --- /dev/null +++ b/drivers/mmc/bcm63158_sdhci.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Philippe Reynes philippe.reynes@softathome.com + * + * based on: + * drivers/mmc/bcmstb_sdhci.c + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <sdhci.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/err.h> +#include <dm/device_compat.h> + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define MIN_FREQ 400000 + +#define BCM63158_MMC_BOOT_MAIN_CTL_REG 0x0 +#define BCM63158_MMC_BOOT_STATUS_REG 0x4 +#define BCM63158_MMC_BOOT_MODE_MASK 1 + +struct sdhci_bcm63158_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +static int sdhci_bcm63158_bind(struct udevice *dev) +{ + struct sdhci_bcm63158_plat *plat = dev_get_plat(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static int sdhci_bcm63158_set_normal_mode(void *boot_regs) +{ + void *boot_main_ctl_reg = boot_regs + BCM63158_MMC_BOOT_MAIN_CTL_REG; + void *boot_status_reg = boot_regs + BCM63158_MMC_BOOT_STATUS_REG; + u32 status; + int i, max_retry = 10; + int ret = -1; + + status = readl(boot_status_reg); + if ((status & BCM63158_MMC_BOOT_MODE_MASK) == 0) { + ret = 0; + goto out; + } + + clrbits_32(boot_main_ctl_reg, BCM63158_MMC_BOOT_MODE_MASK); + + for (i = 0; i < max_retry; i++) { + status = readl(boot_status_reg); + if ((status & BCM63158_MMC_BOOT_MODE_MASK) == 0) { + ret = 0; + goto out; + } + + mdelay(10); + } + + log_err("%s: can't set mode normal\n", __func__); + + out: + return ret; +} + +static int sdhci_bcm63158_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct sdhci_bcm63158_plat *plat = dev_get_plat(dev); + struct sdhci_host *host = dev_get_priv(dev); + struct resource res; + void *boot_regs; + int ret; + + host->name = dev->name; + + /* Get sdhci controller base address */ + ret = dev_read_resource_byname(dev, "sdhci-base", &res); + if (ret) { + dev_err(dev, "can't get regs sdhci-base address(ret = %d)!\n", ret); + return ret; + } + + host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD; + host->ioaddr = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(host->ioaddr)) + return PTR_ERR(host->ioaddr); + + /* Get sdhci boot controller base address */ + ret = dev_read_resource_byname(dev, "sdhci-boot", &res); + if (ret) { + dev_err(dev, "can't get regs sdhci-boot address(ret = %d)!\n", ret); + return ret; + } + + boot_regs = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(boot_regs)) + return PTR_ERR(boot_regs); + + /* Set normal mode instead of boot mode */ + ret = sdhci_bcm63158_set_normal_mode(boot_regs); + if (ret) + return ret; + + ret = mmc_of_parse(dev, &plat->cfg); + if (ret) + return ret; + + /* + * see commit: + * 425d83346d7 ("mmc: bcm: fix uninitialized pointer deref on probe") + * + * Since commit + * 3d296365e4e8 ("mmc: sdhci: Add support for sdhci-caps-mask") + * the function sdhci_setup_cfg() xpects a valid sdhci_host mmc field. + */ + host->mmc = &plat->mmc; + host->mmc->dev = dev; + + /* Use default max frequency from caps register */ + ret = sdhci_setup_cfg(&plat->cfg, host, + 0, + MIN_FREQ); + 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_bcm63158_match[] = { + { .compatible = "brcm,bcm63158-sdhci" }, + { } +}; + +U_BOOT_DRIVER(sdhci_bcm63158) = { + .name = "sdhci-bcm63158", + .id = UCLASS_MMC, + .of_match = sdhci_bcm63158_match, + .ops = &sdhci_ops, + .bind = sdhci_bcm63158_bind, + .probe = sdhci_bcm63158_probe, + .priv_auto = sizeof(struct sdhci_host), + .plat_auto = sizeof(struct sdhci_bcm63158_plat), +};