[U-Boot] [RFC PATCH 05/13] mmc: add MMC (glue) driver for Nexell SoCs

Andre Przywara
2017-11-30T01:25:03Z

From: Amit Singh Tomar <amittomer25@gmail.com>

The Nexell SoCs contain multiple MMC devices, which can be driven by
U-Boot's DesignWare MMC driver, if supported by the required glue driver
file.
Provide that file along with the Makefile/Kconfig changes.

Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/mmc/Kconfig | 8 +++
drivers/mmc/Makefile | 1 +
drivers/mmc/nexell_dw_mmc.c | 159 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 168 insertions(+)
create mode 100644 drivers/mmc/nexell_dw_mmc.c

diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 62ce0af7d3..243878aa65 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -91,6 +91,14 @@ config MMC_DW_K3
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Hisilicon K3 SoC's.

+config MMC_DW_NEXELL
+ bool "NEXELL SD/MMC controller support"
+ depends on ARCH_NEXELL && DM_MMC && OF_CONTROL
+ depends on MMC_DW
+ help
+ This enables support for the Nexell SD/MMM controller, which is
+ based on Designware IP.
+
config MMC_DW_ROCKCHIP
bool "Rockchip SD/MMC controller support"
depends on DM_MMC && OF_CONTROL
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index d505f37f01..0fb6eb7803 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_EXYNOS) += exynos_dw_mmc.o
obj-$(CONFIG_MMC_DW_K3) += hi6220_dw_mmc.o
+obj-$(CONFIG_MMC_DW_NEXELL) += nexell_dw_mmc.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += rockchip_dw_mmc.o
obj-$(CONFIG_MMC_DW_SOCFPGA) += socfpga_dw_mmc.o
obj-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
diff --git a/drivers/mmc/nexell_dw_mmc.c b/drivers/mmc/nexell_dw_mmc.c
new file mode 100644
index 0000000000..e96395cdaf
--- /dev/null
+++ b/drivers/mmc/nexell_dw_mmc.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 Amit Singh Tomar <amittomer25@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <dwmmc.h>
+#include <errno.h>
+#include <mapmem.h>
+#include <linux/err.h>
+#include <reset.h>
+#include <asm/arch/clk.h>
+
+#define SDMMCCLKENB 0xC00C5000
+#define SDMMCCLKGEN0L 0xC00C5004
+#define PLL_SEL_MASK GENMASK(4, 2)
+#define CLK_DIV_MASK GENMASK(12, 5)
+#define PLLSEL_SHIFT 0x2
+#define PLL0_SEL 0
+#define PLL1_SEL 1
+#define PLL2_SEL 2
+#define SDMMC_CLK_ENB 0xc /* Magic bit to enable/generate SDMMC clock */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct nexell_mmc_plat {
+ struct mmc_config cfg;
+ struct mmc mmc;
+};
+
+struct nexell_dwmmc_priv {
+ struct clk clk;
+ struct dwmci_host host;
+ struct reset_ctl reset_ctl;
+ int fifo_depth;
+ bool fifo_mode;
+};
+
+/* Should this be done from CCF ? */
+static void nexell_dwmci_clksel(struct dwmci_host *host)
+{
+ u32 val;
+
+ /* Enable SDMMC clock */
+ val = readl(SDMMCCLKENB);
+ val |= SDMMC_CLK_ENB;
+ writel(val, SDMMCCLKENB);
+
+ /* Select PLL1 as clock source */
+ val = readl(SDMMCCLKGEN0L);
+ val = val & ~(PLL_SEL_MASK);
+ val |= (PLL1_SEL << PLLSEL_SHIFT) & PLL_SEL_MASK;
+ writel(val, SDMMCCLKGEN0L);
+}
+
+static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev)
+{
+ struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ struct dwmci_host *host = &priv->host;
+ int fifo_depth, ret;
+
+ ret = reset_get_by_name(dev, "mmc", &priv->reset_ctl);
+ if (ret) {
+ printf("reset_get_by_name(rst) failed: %d", ret);
+ return ret;
+ }
+
+ fifo_depth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+ "fifo-depth", 0);
+ if (fifo_depth < 0) {
+ printf("DWMMC: Can't get FIFO depth\n");
+ return -EINVAL;
+ }
+
+ host->name = dev->name;
+ host->ioaddr = (void *)devfdt_get_addr(dev);
+ host->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+ "bus-width", 4);
+
+ ret = reset_assert(&priv->reset_ctl);
+ if (ret)
+ return ret;
+
+ host->clksel = nexell_dwmci_clksel;
+
+ ret = reset_deassert(&priv->reset_ctl);
+ if (ret)
+ return ret;
+
+ host->dev_index = 0;
+ host->bus_hz = get_mmc_clk(host->dev_index);
+ host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_depth / 2 - 1) |
+ TX_WMARK(fifo_depth / 2);
+ host->priv = priv;
+
+ return 0;
+}
+
+static int nexell_dwmmc_probe(struct udevice *dev)
+{
+#ifdef CONFIG_BLK
+ struct nexell_mmc_plat *plat = dev_get_platdata(dev);
+#endif
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ struct dwmci_host *host = &priv->host;
+
+#ifdef CONFIG_BLK
+ dwmci_setup_cfg(&plat->cfg, host, host->bus_hz, 400000);
+ host->mmc = &plat->mmc;
+#else
+ int ret;
+
+ ret = add_dwmci(host, host->bus_hz, 400000);
+ if (ret)
+ return ret;
+#endif
+
+ host->mmc->priv = &priv->host;
+ upriv->mmc = host->mmc;
+ host->mmc->dev = dev;
+
+ return 0;
+}
+
+static int nexell_dwmmc_bind(struct udevice *dev)
+{
+#ifdef CONFIG_BLK
+ struct nexell_mmc_plat *plat = dev_get_platdata(dev);
+ int ret;
+
+ ret = dwmci_bind(dev, &plat->mmc, &plat->cfg);
+ if (ret)
+ return ret;
+#endif
+
+ return 0;
+}
+
+static const struct udevice_id nexell_dwmmc_ids[] = {
+ { .compatible = "nexell,s5p6818-dw-mshc" },
+ { }
+};
+
+U_BOOT_DRIVER(nexell_dwmmc_drv) = {
+ .name = "nexell_s5p6818_dw_mshc",
+ .id = UCLASS_MMC,
+ .of_match = nexell_dwmmc_ids,
+ .ops = &dm_dwmci_ops,
+ .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata,
+ .bind = nexell_dwmmc_bind,
+ .probe = nexell_dwmmc_probe,
+ .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv),
+ .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat),
+};
--
2.14.1