uboot/drivers/mmc/iproc_sdhci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2019 Broadcom.
   4 *
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <errno.h>
  10#include <malloc.h>
  11#include <sdhci.h>
  12
  13DECLARE_GLOBAL_DATA_PTR;
  14
  15struct sdhci_iproc_host {
  16        struct sdhci_host host;
  17        u32 shadow_cmd;
  18        u32 shadow_blk;
  19};
  20
  21#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
  22
  23static inline struct sdhci_iproc_host *to_iproc(struct sdhci_host *host)
  24{
  25        return (struct sdhci_iproc_host *)host;
  26}
  27
  28#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
  29static u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
  30{
  31        u32 val = readl(host->ioaddr + reg);
  32#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
  33        printf("%s %d: readl [0x%02x] 0x%08x\n",
  34               host->name, host->index, reg, val);
  35#endif
  36        return val;
  37}
  38
  39static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
  40{
  41        u32 val = sdhci_iproc_readl(host, (reg & ~3));
  42        u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
  43        return word;
  44}
  45
  46static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
  47{
  48        u32 val = sdhci_iproc_readl(host, (reg & ~3));
  49        u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
  50        return byte;
  51}
  52
  53static void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
  54{
  55        u32 clock = 0;
  56#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
  57        printf("%s %d: writel [0x%02x] 0x%08x\n",
  58               host->name, host->index, reg, val);
  59#endif
  60        writel(val, host->ioaddr + reg);
  61
  62        if (host->mmc)
  63                clock = host->mmc->clock;
  64        if (clock <= 400000) {
  65                /* Round up to micro-second four SD clock delay */
  66                if (clock)
  67                        udelay((4 * 1000000 + clock - 1) / clock);
  68                else
  69                        udelay(10);
  70        }
  71}
  72
  73/*
  74 * The Arasan has a bugette whereby it may lose the content of successive
  75 * writes to the same register that are within two SD-card clock cycles of
  76 * each other (a clock domain crossing problem). The data
  77 * register does not have this problem, which is just as well - otherwise we'd
  78 * have to nobble the DMA engine too.
  79 *
  80 * This wouldn't be a problem with the code except that we can only write the
  81 * controller with 32-bit writes.  So two different 16-bit registers are
  82 * written back to back creates the problem.
  83 *
  84 * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
  85 * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
  86 * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
  87 * the work around can be further optimized. We can keep shadow values of
  88 * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
  89 * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
  90 * by the TRANSFER+COMMAND in another 32-bit write.
  91 */
  92static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
  93{
  94        struct sdhci_iproc_host *iproc_host = to_iproc(host);
  95        u32 word_shift = REG_OFFSET_IN_BITS(reg);
  96        u32 mask = 0xffff << word_shift;
  97        u32 oldval, newval;
  98
  99        if (reg == SDHCI_COMMAND) {
 100                /* Write the block now as we are issuing a command */
 101                if (iproc_host->shadow_blk != 0) {
 102                        sdhci_iproc_writel(host, iproc_host->shadow_blk,
 103                                           SDHCI_BLOCK_SIZE);
 104                        iproc_host->shadow_blk = 0;
 105                }
 106                oldval = iproc_host->shadow_cmd;
 107        } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
 108                /* Block size and count are stored in shadow reg */
 109                oldval = iproc_host->shadow_blk;
 110        } else {
 111                /* Read reg, all other registers are not shadowed */
 112                oldval = sdhci_iproc_readl(host, (reg & ~3));
 113        }
 114        newval = (oldval & ~mask) | (val << word_shift);
 115
 116        if (reg == SDHCI_TRANSFER_MODE) {
 117                /* Save the transfer mode until the command is issued */
 118                iproc_host->shadow_cmd = newval;
 119        } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
 120                /* Save the block info until the command is issued */
 121                iproc_host->shadow_blk = newval;
 122        } else {
 123                /* Command or other regular 32-bit write */
 124                sdhci_iproc_writel(host, newval, reg & ~3);
 125        }
 126}
 127
 128static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
 129{
 130        u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
 131        u32 byte_shift = REG_OFFSET_IN_BITS(reg);
 132        u32 mask = 0xff << byte_shift;
 133        u32 newval = (oldval & ~mask) | (val << byte_shift);
 134
 135        sdhci_iproc_writel(host, newval, reg & ~3);
 136}
 137#endif
 138
 139static void sdhci_iproc_set_ios_post(struct sdhci_host *host)
 140{
 141        u32 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 142
 143        /* Reset UHS mode bits */
 144        ctrl &= ~SDHCI_CTRL_UHS_MASK;
 145
 146        if (host->mmc->ddr_mode)
 147                ctrl |= UHS_DDR50_BUS_SPEED;
 148
 149        sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
 150}
 151
 152static struct sdhci_ops sdhci_platform_ops = {
 153#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
 154        .read_l = sdhci_iproc_readl,
 155        .read_w = sdhci_iproc_readw,
 156        .read_b = sdhci_iproc_readb,
 157        .write_l = sdhci_iproc_writel,
 158        .write_w = sdhci_iproc_writew,
 159        .write_b = sdhci_iproc_writeb,
 160#endif
 161        .set_ios_post = sdhci_iproc_set_ios_post,
 162};
 163
 164struct iproc_sdhci_plat {
 165        struct mmc_config cfg;
 166        struct mmc mmc;
 167};
 168
 169static int iproc_sdhci_probe(struct udevice *dev)
 170{
 171        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 172        struct iproc_sdhci_plat *plat = dev_get_platdata(dev);
 173        struct sdhci_host *host = dev_get_priv(dev);
 174        struct sdhci_iproc_host *iproc_host;
 175        int node = dev_of_offset(dev);
 176        u32 f_min_max[2];
 177        int ret;
 178
 179        iproc_host = (struct sdhci_iproc_host *)
 180                        malloc(sizeof(struct sdhci_iproc_host));
 181        if (!iproc_host) {
 182                printf("%s: sdhci host malloc fail!\n", __func__);
 183                return -ENOMEM;
 184        }
 185        iproc_host->shadow_cmd = 0;
 186        iproc_host->shadow_blk = 0;
 187
 188        host->name = dev->name;
 189        host->ioaddr = (void *)devfdt_get_addr(dev);
 190        host->voltages = MMC_VDD_165_195 |
 191                         MMC_VDD_32_33 | MMC_VDD_33_34;
 192        host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE;
 193        host->host_caps = MMC_MODE_DDR_52MHz;
 194        host->index = fdtdec_get_uint(gd->fdt_blob, node, "index", 0);
 195        host->ops = &sdhci_platform_ops;
 196        host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
 197        ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
 198                                   "clock-freq-min-max", f_min_max, 2);
 199        if (ret) {
 200                printf("sdhci: clock-freq-min-max not found\n");
 201                return ret;
 202        }
 203        host->max_clk = f_min_max[1];
 204        host->bus_width = fdtdec_get_int(gd->fdt_blob,
 205                                         dev_of_offset(dev), "bus-width", 4);
 206
 207        /* Update host_caps for 8 bit bus width */
 208        if (host->bus_width == 8)
 209                host->host_caps |= MMC_MODE_8BIT;
 210
 211        memcpy(&iproc_host->host, host, sizeof(struct sdhci_host));
 212
 213        ret = sdhci_setup_cfg(&plat->cfg, &iproc_host->host,
 214                              f_min_max[1], f_min_max[0]);
 215        if (ret)
 216                return ret;
 217
 218        iproc_host->host.mmc = &plat->mmc;
 219        iproc_host->host.mmc->dev = dev;
 220        iproc_host->host.mmc->priv = &iproc_host->host;
 221        upriv->mmc = iproc_host->host.mmc;
 222
 223        return sdhci_probe(dev);
 224}
 225
 226static int iproc_sdhci_bind(struct udevice *dev)
 227{
 228        struct iproc_sdhci_plat *plat = dev_get_platdata(dev);
 229
 230        return sdhci_bind(dev, &plat->mmc, &plat->cfg);
 231}
 232
 233static const struct udevice_id iproc_sdhci_ids[] = {
 234        { .compatible = "brcm,iproc-sdhci" },
 235        { }
 236};
 237
 238U_BOOT_DRIVER(iproc_sdhci_drv) = {
 239        .name = "iproc_sdhci",
 240        .id = UCLASS_MMC,
 241        .of_match = iproc_sdhci_ids,
 242        .ops = &sdhci_ops,
 243        .bind = iproc_sdhci_bind,
 244        .probe = iproc_sdhci_probe,
 245        .priv_auto_alloc_size = sizeof(struct sdhci_host),
 246        .platdata_auto_alloc_size = sizeof(struct iproc_sdhci_plat),
 247};
 248