Adds a driver to support shdci on bcm63158.

Signed-off-by: Philippe Reynes <philippe.rey...@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.rey...@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),
+};
-- 
2.25.1

Reply via email to