From: Amit Singh Tomar <amittome...@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 <amittome...@gmail.com> Signed-off-by: Andre Przywara <andre.przyw...@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 <amittome...@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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot