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#include <asm/global_data.h>
  13#include "mmc_private.h"
  14#include <linux/delay.h>
  15
  16#define MAX_TUNING_LOOP 40
  17
  18DECLARE_GLOBAL_DATA_PTR;
  19
  20struct sdhci_iproc_host {
  21        struct sdhci_host host;
  22        u32 shadow_cmd;
  23        u32 shadow_blk;
  24};
  25
  26#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
  27
  28static inline struct sdhci_iproc_host *to_iproc(struct sdhci_host *host)
  29{
  30        return (struct sdhci_iproc_host *)host;
  31}
  32
  33#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
  34static u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
  35{
  36        u32 val = readl(host->ioaddr + reg);
  37#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
  38        printf("%s %d: readl [0x%02x] 0x%08x\n",
  39               host->name, host->index, reg, val);
  40#endif
  41        return val;
  42}
  43
  44static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
  45{
  46        u32 val = sdhci_iproc_readl(host, (reg & ~3));
  47        u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
  48        return word;
  49}
  50
  51static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
  52{
  53        u32 val = sdhci_iproc_readl(host, (reg & ~3));
  54        u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
  55        return byte;
  56}
  57
  58static void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
  59{
  60        u32 clock = 0;
  61#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
  62        printf("%s %d: writel [0x%02x] 0x%08x\n",
  63               host->name, host->index, reg, val);
  64#endif
  65        writel(val, host->ioaddr + reg);
  66
  67        if (host->mmc)
  68                clock = host->mmc->clock;
  69        if (clock <= 400000) {
  70                /* Round up to micro-second four SD clock delay */
  71                if (clock)
  72                        udelay((4 * 1000000 + clock - 1) / clock);
  73                else
  74                        udelay(10);
  75        }
  76}
  77
  78/*
  79 * The Arasan has a bugette whereby it may lose the content of successive
  80 * writes to the same register that are within two SD-card clock cycles of
  81 * each other (a clock domain crossing problem). The data
  82 * register does not have this problem, which is just as well - otherwise we'd
  83 * have to nobble the DMA engine too.
  84 *
  85 * This wouldn't be a problem with the code except that we can only write the
  86 * controller with 32-bit writes.  So two different 16-bit registers are
  87 * written back to back creates the problem.
  88 *
  89 * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
  90 * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
  91 * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
  92 * the work around can be further optimized. We can keep shadow values of
  93 * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
  94 * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
  95 * by the TRANSFER+COMMAND in another 32-bit write.
  96 */
  97static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
  98{
  99        struct sdhci_iproc_host *iproc_host = to_iproc(host);
 100        u32 word_shift = REG_OFFSET_IN_BITS(reg);
 101        u32 mask = 0xffff << word_shift;
 102        u32 oldval, newval;
 103
 104        if (reg == SDHCI_COMMAND) {
 105                /* Write the block now as we are issuing a command */
 106                if (iproc_host->shadow_blk != 0) {
 107                        sdhci_iproc_writel(host, iproc_host->shadow_blk,
 108                                           SDHCI_BLOCK_SIZE);
 109                        iproc_host->shadow_blk = 0;
 110                }
 111                oldval = iproc_host->shadow_cmd;
 112        } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
 113                /* Block size and count are stored in shadow reg */
 114                oldval = iproc_host->shadow_blk;
 115        } else {
 116                /* Read reg, all other registers are not shadowed */
 117                oldval = sdhci_iproc_readl(host, (reg & ~3));
 118        }
 119        newval = (oldval & ~mask) | (val << word_shift);
 120
 121        if (reg == SDHCI_TRANSFER_MODE) {
 122                /* Save the transfer mode until the command is issued */
 123                iproc_host->shadow_cmd = newval;
 124        } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
 125                /* Save the block info until the command is issued */
 126                iproc_host->shadow_blk = newval;
 127        } else {
 128                /* Command or other regular 32-bit write */
 129                sdhci_iproc_writel(host, newval, reg & ~3);
 130        }
 131}
 132
 133static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
 134{
 135        u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
 136        u32 byte_shift = REG_OFFSET_IN_BITS(reg);
 137        u32 mask = 0xff << byte_shift;
 138        u32 newval = (oldval & ~mask) | (val << byte_shift);
 139
 140        sdhci_iproc_writel(host, newval, reg & ~3);
 141}
 142#endif
 143
 144static int sdhci_iproc_set_ios_post(struct sdhci_host *host)
 145{
 146        struct mmc *mmc = (struct mmc *)host->mmc;
 147        u32 ctrl;
 148
 149        if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
 150                ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 151                ctrl |= SDHCI_CTRL_VDD_180;
 152                sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
 153        }
 154
 155        sdhci_set_uhs_timing(host);
 156        return 0;
 157}
 158
 159static void sdhci_start_tuning(struct sdhci_host *host)
 160{
 161        u32 ctrl;
 162
 163        ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 164        ctrl |= SDHCI_CTRL_EXEC_TUNING;
 165        sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
 166
 167        sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
 168        sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
 169}
 170
 171static void sdhci_end_tuning(struct sdhci_host *host)
 172{
 173        /* Enable only interrupts served by the SD controller */
 174        sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
 175                     SDHCI_INT_ENABLE);
 176        /* Mask all sdhci interrupt sources */
 177        sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);
 178}
 179
 180static int sdhci_iproc_execute_tuning(struct mmc *mmc, u8 opcode)
 181{
 182        struct mmc_cmd cmd;
 183        u32 ctrl;
 184        u32 blocksize = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 64);
 185        struct sdhci_host *host = dev_get_priv(mmc->dev);
 186        char tuning_loop_counter = MAX_TUNING_LOOP;
 187        int ret = 0;
 188
 189        sdhci_start_tuning(host);
 190
 191        cmd.cmdidx = opcode;
 192        cmd.resp_type = MMC_RSP_R1;
 193        cmd.cmdarg = 0;
 194
 195        if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200 && mmc->bus_width == 8)
 196                blocksize = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 128);
 197
 198        sdhci_writew(host, blocksize, SDHCI_BLOCK_SIZE);
 199        sdhci_writew(host, 1, SDHCI_BLOCK_COUNT);
 200        sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
 201
 202        do {
 203                mmc_send_cmd(mmc, &cmd, NULL);
 204                if (opcode == MMC_CMD_SEND_TUNING_BLOCK)
 205                        /*
 206                         * For tuning command, do not do busy loop. As tuning
 207                         * is happening (CLK-DATA latching for setup/hold time
 208                         * requirements), give time to complete
 209                         */
 210                        udelay(1);
 211
 212                ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 213
 214                if (tuning_loop_counter-- == 0)
 215                        break;
 216
 217        } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
 218
 219        if (tuning_loop_counter < 0 || (!(ctrl & SDHCI_CTRL_TUNED_CLK))) {
 220                ctrl &= ~(SDHCI_CTRL_TUNED_CLK | SDHCI_CTRL_EXEC_TUNING);
 221                sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL2);
 222                printf("%s:Tuning failed, opcode = 0x%02x\n", __func__, opcode);
 223                ret = -EIO;
 224        }
 225
 226        sdhci_end_tuning(host);
 227
 228        return ret;
 229}
 230
 231static struct sdhci_ops sdhci_platform_ops = {
 232#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
 233        .read_l = sdhci_iproc_readl,
 234        .read_w = sdhci_iproc_readw,
 235        .read_b = sdhci_iproc_readb,
 236        .write_l = sdhci_iproc_writel,
 237        .write_w = sdhci_iproc_writew,
 238        .write_b = sdhci_iproc_writeb,
 239#endif
 240        .set_ios_post = sdhci_iproc_set_ios_post,
 241        .platform_execute_tuning = sdhci_iproc_execute_tuning,
 242};
 243
 244struct iproc_sdhci_plat {
 245        struct mmc_config cfg;
 246        struct mmc mmc;
 247};
 248
 249static int iproc_sdhci_probe(struct udevice *dev)
 250{
 251        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 252        struct iproc_sdhci_plat *plat = dev_get_plat(dev);
 253        struct sdhci_host *host = dev_get_priv(dev);
 254        struct sdhci_iproc_host *iproc_host;
 255        int node = dev_of_offset(dev);
 256        u32 f_min_max[2];
 257        int ret;
 258
 259        iproc_host = malloc(sizeof(struct sdhci_iproc_host));
 260        if (!iproc_host) {
 261                printf("%s: sdhci host malloc fail!\n", __func__);
 262                return -ENOMEM;
 263        }
 264        iproc_host->shadow_cmd = 0;
 265        iproc_host->shadow_blk = 0;
 266
 267        host->name = dev->name;
 268        host->ioaddr = dev_read_addr_ptr(dev);
 269        host->quirks = SDHCI_QUIRK_BROKEN_R1B;
 270        host->host_caps = MMC_MODE_DDR_52MHz;
 271        host->index = fdtdec_get_uint(gd->fdt_blob, node, "index", 0);
 272        host->ops = &sdhci_platform_ops;
 273        host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
 274        ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
 275                                   "clock-freq-min-max", f_min_max, 2);
 276        if (ret) {
 277                printf("sdhci: clock-freq-min-max not found\n");
 278                free(iproc_host);
 279                return ret;
 280        }
 281        host->max_clk = f_min_max[1];
 282        host->bus_width = fdtdec_get_int(gd->fdt_blob,
 283                                         dev_of_offset(dev), "bus-width", 4);
 284
 285        /* Update host_caps for 8 bit bus width */
 286        if (host->bus_width == 8)
 287                host->host_caps |= MMC_MODE_8BIT;
 288
 289        memcpy(&iproc_host->host, host, sizeof(struct sdhci_host));
 290
 291        iproc_host->host.mmc = &plat->mmc;
 292        iproc_host->host.mmc->dev = dev;
 293        iproc_host->host.mmc->priv = &iproc_host->host;
 294        upriv->mmc = iproc_host->host.mmc;
 295
 296        ret = sdhci_setup_cfg(&plat->cfg, &iproc_host->host,
 297                              f_min_max[1], f_min_max[0]);
 298        if (ret) {
 299                free(iproc_host);
 300                return ret;
 301        }
 302
 303        return sdhci_probe(dev);
 304}
 305
 306static int iproc_sdhci_bind(struct udevice *dev)
 307{
 308        struct iproc_sdhci_plat *plat = dev_get_plat(dev);
 309
 310        return sdhci_bind(dev, &plat->mmc, &plat->cfg);
 311}
 312
 313static const struct udevice_id iproc_sdhci_ids[] = {
 314        { .compatible = "brcm,iproc-sdhci" },
 315        { }
 316};
 317
 318U_BOOT_DRIVER(iproc_sdhci_drv) = {
 319        .name = "iproc_sdhci",
 320        .id = UCLASS_MMC,
 321        .of_match = iproc_sdhci_ids,
 322        .ops = &sdhci_ops,
 323        .bind = iproc_sdhci_bind,
 324        .probe = iproc_sdhci_probe,
 325        .priv_auto      = sizeof(struct sdhci_host),
 326        .plat_auto      = sizeof(struct iproc_sdhci_plat),
 327};
 328