uboot/drivers/mmc/rockchip_dw_mmc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2013 Google, Inc
   4 */
   5
   6#include <common.h>
   7#include <clk.h>
   8#include <dm.h>
   9#include <dt-structs.h>
  10#include <dwmmc.h>
  11#include <errno.h>
  12#include <mapmem.h>
  13#include <pwrseq.h>
  14#include <syscon.h>
  15#include <asm/gpio.h>
  16#include <asm/arch-rockchip/clock.h>
  17#include <asm/arch-rockchip/periph.h>
  18#include <linux/err.h>
  19
  20struct rockchip_mmc_plat {
  21#if CONFIG_IS_ENABLED(OF_PLATDATA)
  22        struct dtd_rockchip_rk3288_dw_mshc dtplat;
  23#endif
  24        struct mmc_config cfg;
  25        struct mmc mmc;
  26};
  27
  28struct rockchip_dwmmc_priv {
  29        struct clk clk;
  30        struct dwmci_host host;
  31        int fifo_depth;
  32        bool fifo_mode;
  33        u32 minmax[2];
  34};
  35
  36static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq)
  37{
  38        struct udevice *dev = host->priv;
  39        struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
  40        int ret;
  41
  42        ret = clk_set_rate(&priv->clk, freq);
  43        if (ret < 0) {
  44                debug("%s: err=%d\n", __func__, ret);
  45                return ret;
  46        }
  47
  48        return freq;
  49}
  50
  51static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev)
  52{
  53#if !CONFIG_IS_ENABLED(OF_PLATDATA)
  54        struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
  55        struct dwmci_host *host = &priv->host;
  56
  57        host->name = dev->name;
  58        host->ioaddr = dev_read_addr_ptr(dev);
  59        host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
  60        host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
  61        host->priv = dev;
  62
  63        /* use non-removeable as sdcard and emmc as judgement */
  64        if (dev_read_bool(dev, "non-removable"))
  65                host->dev_index = 0;
  66        else
  67                host->dev_index = 1;
  68
  69        priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0);
  70
  71        if (priv->fifo_depth < 0)
  72                return -EINVAL;
  73        priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
  74
  75#ifdef CONFIG_SPL_BUILD
  76        if (!priv->fifo_mode)
  77                priv->fifo_mode = dev_read_bool(dev, "u-boot,spl-fifo-mode");
  78#endif
  79
  80        /*
  81         * 'clock-freq-min-max' is deprecated
  82         * (see https://github.com/torvalds/linux/commit/b023030f10573de738bbe8df63d43acab64c9f7b)
  83         */
  84        if (dev_read_u32_array(dev, "clock-freq-min-max", priv->minmax, 2)) {
  85                int val = dev_read_u32_default(dev, "max-frequency", -EINVAL);
  86
  87                if (val < 0)
  88                        return val;
  89
  90                priv->minmax[0] = 400000;  /* 400 kHz */
  91                priv->minmax[1] = val;
  92        } else {
  93                debug("%s: 'clock-freq-min-max' property was deprecated.\n",
  94                      __func__);
  95        }
  96#endif
  97        return 0;
  98}
  99
 100static int rockchip_dwmmc_probe(struct udevice *dev)
 101{
 102        struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
 103        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 104        struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
 105        struct dwmci_host *host = &priv->host;
 106        struct udevice *pwr_dev __maybe_unused;
 107        int ret;
 108
 109#if CONFIG_IS_ENABLED(OF_PLATDATA)
 110        struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat;
 111
 112        host->name = dev->name;
 113        host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
 114        host->buswidth = dtplat->bus_width;
 115        host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
 116        host->priv = dev;
 117        host->dev_index = 0;
 118        priv->fifo_depth = dtplat->fifo_depth;
 119        priv->fifo_mode = 0;
 120        priv->minmax[0] = 400000;  /*  400 kHz */
 121        priv->minmax[1] = dtplat->max_frequency;
 122
 123        ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk);
 124        if (ret < 0)
 125                return ret;
 126#else
 127        ret = clk_get_by_index(dev, 0, &priv->clk);
 128        if (ret < 0)
 129                return ret;
 130#endif
 131        host->fifoth_val = MSIZE(0x2) |
 132                RX_WMARK(priv->fifo_depth / 2 - 1) |
 133                TX_WMARK(priv->fifo_depth / 2);
 134
 135        host->fifo_mode = priv->fifo_mode;
 136
 137#ifdef CONFIG_PWRSEQ
 138        /* Enable power if needed */
 139        ret = uclass_get_device_by_phandle(UCLASS_PWRSEQ, dev, "mmc-pwrseq",
 140                                           &pwr_dev);
 141        if (!ret) {
 142                ret = pwrseq_set_power(pwr_dev, true);
 143                if (ret)
 144                        return ret;
 145        }
 146#endif
 147        dwmci_setup_cfg(&plat->cfg, host, priv->minmax[1], priv->minmax[0]);
 148        host->mmc = &plat->mmc;
 149        host->mmc->priv = &priv->host;
 150        host->mmc->dev = dev;
 151        upriv->mmc = host->mmc;
 152
 153        return dwmci_probe(dev);
 154}
 155
 156static int rockchip_dwmmc_bind(struct udevice *dev)
 157{
 158        struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
 159
 160        return dwmci_bind(dev, &plat->mmc, &plat->cfg);
 161}
 162
 163static const struct udevice_id rockchip_dwmmc_ids[] = {
 164        { .compatible = "rockchip,rk2928-dw-mshc" },
 165        { .compatible = "rockchip,rk3288-dw-mshc" },
 166        { }
 167};
 168
 169U_BOOT_DRIVER(rockchip_dwmmc_drv) = {
 170        .name           = "rockchip_rk3288_dw_mshc",
 171        .id             = UCLASS_MMC,
 172        .of_match       = rockchip_dwmmc_ids,
 173        .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata,
 174        .ops            = &dm_dwmci_ops,
 175        .bind           = rockchip_dwmmc_bind,
 176        .probe          = rockchip_dwmmc_probe,
 177        .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv),
 178        .platdata_auto_alloc_size = sizeof(struct rockchip_mmc_plat),
 179};
 180
 181#ifdef CONFIG_PWRSEQ
 182static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable)
 183{
 184        struct gpio_desc reset;
 185        int ret;
 186
 187        ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT);
 188        if (ret)
 189                return ret;
 190        dm_gpio_set_value(&reset, 1);
 191        udelay(1);
 192        dm_gpio_set_value(&reset, 0);
 193        udelay(200);
 194
 195        return 0;
 196}
 197
 198static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = {
 199        .set_power      = rockchip_dwmmc_pwrseq_set_power,
 200};
 201
 202static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = {
 203        { .compatible = "mmc-pwrseq-emmc" },
 204        { }
 205};
 206
 207U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = {
 208        .name           = "mmc_pwrseq_emmc",
 209        .id             = UCLASS_PWRSEQ,
 210        .of_match       = rockchip_dwmmc_pwrseq_ids,
 211        .ops            = &rockchip_dwmmc_pwrseq_ops,
 212};
 213#endif
 214