uboot/drivers/mmc/socfpga_dw_mmc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2013 Altera Corporation <www.altera.com>
   4 */
   5
   6#include <common.h>
   7#include <log.h>
   8#include <asm/arch/clock_manager.h>
   9#include <asm/arch/system_manager.h>
  10#include <clk.h>
  11#include <dm.h>
  12#include <dwmmc.h>
  13#include <errno.h>
  14#include <fdtdec.h>
  15#include <dm/device_compat.h>
  16#include <linux/libfdt.h>
  17#include <linux/err.h>
  18#include <malloc.h>
  19#include <reset.h>
  20
  21DECLARE_GLOBAL_DATA_PTR;
  22
  23struct socfpga_dwmci_plat {
  24        struct mmc_config cfg;
  25        struct mmc mmc;
  26};
  27
  28/* socfpga implmentation specific driver private data */
  29struct dwmci_socfpga_priv_data {
  30        struct dwmci_host       host;
  31        unsigned int            drvsel;
  32        unsigned int            smplsel;
  33};
  34
  35static void socfpga_dwmci_reset(struct udevice *dev)
  36{
  37        struct reset_ctl_bulk reset_bulk;
  38        int ret;
  39
  40        ret = reset_get_bulk(dev, &reset_bulk);
  41        if (ret) {
  42                dev_warn(dev, "Can't get reset: %d\n", ret);
  43                return;
  44        }
  45
  46        reset_deassert_bulk(&reset_bulk);
  47}
  48
  49static void socfpga_dwmci_clksel(struct dwmci_host *host)
  50{
  51        struct dwmci_socfpga_priv_data *priv = host->priv;
  52        u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) |
  53                         ((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT);
  54
  55        /* Disable SDMMC clock. */
  56        clrbits_le32(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_EN,
  57                     CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK);
  58
  59        debug("%s: drvsel %d smplsel %d\n", __func__,
  60              priv->drvsel, priv->smplsel);
  61        writel(sdmmc_mask, socfpga_get_sysmgr_addr() + SYSMGR_SDMMC);
  62
  63        debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__,
  64                readl(socfpga_get_sysmgr_addr() + SYSMGR_SDMMC));
  65
  66        /* Enable SDMMC clock */
  67        setbits_le32(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_EN,
  68                     CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK);
  69}
  70
  71static int socfpga_dwmmc_get_clk_rate(struct udevice *dev)
  72{
  73        struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev);
  74        struct dwmci_host *host = &priv->host;
  75#if CONFIG_IS_ENABLED(CLK)
  76        struct clk clk;
  77        int ret;
  78
  79        ret = clk_get_by_index(dev, 1, &clk);
  80        if (ret)
  81                return ret;
  82
  83        host->bus_hz = clk_get_rate(&clk);
  84
  85        clk_free(&clk);
  86#else
  87        /* Fixed clock divide by 4 which due to the SDMMC wrapper */
  88        host->bus_hz = cm_get_mmc_controller_clk_hz();
  89#endif
  90        if (host->bus_hz == 0) {
  91                printf("DWMMC: MMC clock is zero!");
  92                return -EINVAL;
  93        }
  94
  95        return 0;
  96}
  97
  98static int socfpga_dwmmc_ofdata_to_platdata(struct udevice *dev)
  99{
 100        struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev);
 101        struct dwmci_host *host = &priv->host;
 102        int fifo_depth;
 103
 104        fifo_depth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
 105                                    "fifo-depth", 0);
 106        if (fifo_depth < 0) {
 107                printf("DWMMC: Can't get FIFO depth\n");
 108                return -EINVAL;
 109        }
 110
 111        host->name = dev->name;
 112        host->ioaddr = dev_read_addr_ptr(dev);
 113        host->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
 114                                        "bus-width", 4);
 115        host->clksel = socfpga_dwmci_clksel;
 116
 117        /*
 118         * TODO(sjg@chromium.org): Remove the need for this hack.
 119         * We only have one dwmmc block on gen5 SoCFPGA.
 120         */
 121        host->dev_index = 0;
 122        host->fifoth_val = MSIZE(0x2) |
 123                RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2);
 124        priv->drvsel = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
 125                                       "drvsel", 3);
 126        priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
 127                                        "smplsel", 0);
 128        host->priv = priv;
 129
 130        return 0;
 131}
 132
 133static int socfpga_dwmmc_probe(struct udevice *dev)
 134{
 135#ifdef CONFIG_BLK
 136        struct socfpga_dwmci_plat *plat = dev_get_platdata(dev);
 137#endif
 138        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 139        struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev);
 140        struct dwmci_host *host = &priv->host;
 141        int ret;
 142
 143        ret = socfpga_dwmmc_get_clk_rate(dev);
 144        if (ret)
 145                return ret;
 146
 147        socfpga_dwmci_reset(dev);
 148
 149#ifdef CONFIG_BLK
 150        dwmci_setup_cfg(&plat->cfg, host, host->bus_hz, 400000);
 151        host->mmc = &plat->mmc;
 152#else
 153
 154        ret = add_dwmci(host, host->bus_hz, 400000);
 155        if (ret)
 156                return ret;
 157#endif
 158        host->mmc->priv = &priv->host;
 159        upriv->mmc = host->mmc;
 160        host->mmc->dev = dev;
 161
 162        return dwmci_probe(dev);
 163}
 164
 165static int socfpga_dwmmc_bind(struct udevice *dev)
 166{
 167#ifdef CONFIG_BLK
 168        struct socfpga_dwmci_plat *plat = dev_get_platdata(dev);
 169        int ret;
 170
 171        ret = dwmci_bind(dev, &plat->mmc, &plat->cfg);
 172        if (ret)
 173                return ret;
 174#endif
 175
 176        return 0;
 177}
 178
 179static const struct udevice_id socfpga_dwmmc_ids[] = {
 180        { .compatible = "altr,socfpga-dw-mshc" },
 181        { }
 182};
 183
 184U_BOOT_DRIVER(socfpga_dwmmc_drv) = {
 185        .name           = "socfpga_dwmmc",
 186        .id             = UCLASS_MMC,
 187        .of_match       = socfpga_dwmmc_ids,
 188        .ofdata_to_platdata = socfpga_dwmmc_ofdata_to_platdata,
 189        .ops            = &dm_dwmci_ops,
 190        .bind           = socfpga_dwmmc_bind,
 191        .probe          = socfpga_dwmmc_probe,
 192        .priv_auto_alloc_size = sizeof(struct dwmci_socfpga_priv_data),
 193        .platdata_auto_alloc_size = sizeof(struct socfpga_dwmci_plat),
 194};
 195