diff options
Diffstat (limited to 'roms/u-boot/drivers/mmc/rockchip_dw_mmc.c')
-rw-r--r-- | roms/u-boot/drivers/mmc/rockchip_dw_mmc.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/mmc/rockchip_dw_mmc.c b/roms/u-boot/drivers/mmc/rockchip_dw_mmc.c new file mode 100644 index 000000000..d7d5361fd --- /dev/null +++ b/roms/u-boot/drivers/mmc/rockchip_dw_mmc.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <dwmmc.h> +#include <errno.h> +#include <log.h> +#include <mapmem.h> +#include <pwrseq.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/periph.h> +#include <linux/delay.h> +#include <linux/err.h> + +struct rockchip_mmc_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3288_dw_mshc dtplat; +#endif + struct mmc_config cfg; + struct mmc mmc; +}; + +struct rockchip_dwmmc_priv { + struct clk clk; + struct dwmci_host host; + int fifo_depth; + bool fifo_mode; + u32 minmax[2]; +}; + +static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) +{ + struct udevice *dev = host->priv; + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); + int ret; + + ret = clk_set_rate(&priv->clk, freq); + if (ret < 0) { + debug("%s: err=%d\n", __func__, ret); + return ret; + } + + return freq; +} + +static int rockchip_dwmmc_of_to_plat(struct udevice *dev) +{ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + + host->name = dev->name; + host->ioaddr = dev_read_addr_ptr(dev); + host->buswidth = dev_read_u32_default(dev, "bus-width", 4); + host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; + host->priv = dev; + + /* use non-removeable as sdcard and emmc as judgement */ + if (dev_read_bool(dev, "non-removable")) + host->dev_index = 0; + else + host->dev_index = 1; + + priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); + + if (priv->fifo_depth < 0) + return -EINVAL; + priv->fifo_mode = dev_read_bool(dev, "fifo-mode"); + +#ifdef CONFIG_SPL_BUILD + if (!priv->fifo_mode) + priv->fifo_mode = dev_read_bool(dev, "u-boot,spl-fifo-mode"); +#endif + + /* + * 'clock-freq-min-max' is deprecated + * (see https://github.com/torvalds/linux/commit/b023030f10573de738bbe8df63d43acab64c9f7b) + */ + if (dev_read_u32_array(dev, "clock-freq-min-max", priv->minmax, 2)) { + int val = dev_read_u32_default(dev, "max-frequency", -EINVAL); + + if (val < 0) + return val; + + priv->minmax[0] = 400000; /* 400 kHz */ + priv->minmax[1] = val; + } else { + debug("%s: 'clock-freq-min-max' property was deprecated.\n", + __func__); + } +#endif + return 0; +} + +static int rockchip_dwmmc_probe(struct udevice *dev) +{ + struct rockchip_mmc_plat *plat = dev_get_plat(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + int ret; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat; + + host->name = dev->name; + host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); + host->buswidth = dtplat->bus_width; + host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; + host->priv = dev; + host->dev_index = 0; + priv->fifo_depth = dtplat->fifo_depth; + priv->fifo_mode = 0; + priv->minmax[0] = 400000; /* 400 kHz */ + priv->minmax[1] = dtplat->max_frequency; + + ret = clk_get_by_driver_info(dev, dtplat->clocks, &priv->clk); + if (ret < 0) + return ret; +#else + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret < 0) + return ret; +#endif + host->fifoth_val = MSIZE(0x2) | + RX_WMARK(priv->fifo_depth / 2 - 1) | + TX_WMARK(priv->fifo_depth / 2); + + host->fifo_mode = priv->fifo_mode; + +#ifdef CONFIG_MMC_PWRSEQ + /* Enable power if needed */ + ret = mmc_pwrseq_get_power(dev, &plat->cfg); + if (!ret) { + ret = pwrseq_set_power(plat->cfg.pwr_dev, true); + if (ret) + return ret; + } +#endif + dwmci_setup_cfg(&plat->cfg, host, priv->minmax[1], priv->minmax[0]); + host->mmc = &plat->mmc; + host->mmc->priv = &priv->host; + host->mmc->dev = dev; + upriv->mmc = host->mmc; + + return dwmci_probe(dev); +} + +static int rockchip_dwmmc_bind(struct udevice *dev) +{ + struct rockchip_mmc_plat *plat = dev_get_plat(dev); + + return dwmci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id rockchip_dwmmc_ids[] = { + { .compatible = "rockchip,rk2928-dw-mshc" }, + { .compatible = "rockchip,rk3288-dw-mshc" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3288_dw_mshc) = { + .name = "rockchip_rk3288_dw_mshc", + .id = UCLASS_MMC, + .of_match = rockchip_dwmmc_ids, + .of_to_plat = rockchip_dwmmc_of_to_plat, + .ops = &dm_dwmci_ops, + .bind = rockchip_dwmmc_bind, + .probe = rockchip_dwmmc_probe, + .priv_auto = sizeof(struct rockchip_dwmmc_priv), + .plat_auto = sizeof(struct rockchip_mmc_plat), +}; + +DM_DRIVER_ALIAS(rockchip_rk3288_dw_mshc, rockchip_rk3328_dw_mshc) +DM_DRIVER_ALIAS(rockchip_rk3288_dw_mshc, rockchip_rk3368_dw_mshc) |