uboot/drivers/mmc/arm_pl180_mmci.c
<<
>>
Prefs
   1/*
   2 * ARM PrimeCell MultiMedia Card Interface - PL180
   3 *
   4 * Copyright (C) ST-Ericsson SA 2010
   5 *
   6 * Author: Ulf Hansson <ulf.hansson@stericsson.com>
   7 * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com>
   8 * Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org>
   9 *
  10 * SPDX-License-Identifier:     GPL-2.0+
  11 */
  12
  13/* #define DEBUG */
  14
  15#include <asm/io.h>
  16#include "common.h"
  17#include <errno.h>
  18#include <mmc.h>
  19#include "arm_pl180_mmci.h"
  20#include <malloc.h>
  21
  22static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
  23{
  24        u32 hoststatus, statusmask;
  25        struct pl180_mmc_host *host = dev->priv;
  26
  27        statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL;
  28        if ((cmd->resp_type & MMC_RSP_PRESENT))
  29                statusmask |= SDI_STA_CMDREND;
  30        else
  31                statusmask |= SDI_STA_CMDSENT;
  32
  33        do
  34                hoststatus = readl(&host->base->status) & statusmask;
  35        while (!hoststatus);
  36
  37        writel(statusmask, &host->base->status_clear);
  38        if (hoststatus & SDI_STA_CTIMEOUT) {
  39                debug("CMD%d time out\n", cmd->cmdidx);
  40                return -ETIMEDOUT;
  41        } else if ((hoststatus & SDI_STA_CCRCFAIL) &&
  42                   (cmd->resp_type & MMC_RSP_CRC)) {
  43                printf("CMD%d CRC error\n", cmd->cmdidx);
  44                return -EILSEQ;
  45        }
  46
  47        if (cmd->resp_type & MMC_RSP_PRESENT) {
  48                cmd->response[0] = readl(&host->base->response0);
  49                cmd->response[1] = readl(&host->base->response1);
  50                cmd->response[2] = readl(&host->base->response2);
  51                cmd->response[3] = readl(&host->base->response3);
  52                debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, "
  53                        "response[2]:0x%08X, response[3]:0x%08X\n",
  54                        cmd->cmdidx, cmd->response[0], cmd->response[1],
  55                        cmd->response[2], cmd->response[3]);
  56        }
  57
  58        return 0;
  59}
  60
  61/* send command to the mmc card and wait for results */
  62static int do_command(struct mmc *dev, struct mmc_cmd *cmd)
  63{
  64        int result;
  65        u32 sdi_cmd = 0;
  66        struct pl180_mmc_host *host = dev->priv;
  67
  68        sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN);
  69
  70        if (cmd->resp_type) {
  71                sdi_cmd |= SDI_CMD_WAITRESP;
  72                if (cmd->resp_type & MMC_RSP_136)
  73                        sdi_cmd |= SDI_CMD_LONGRESP;
  74        }
  75
  76        writel((u32)cmd->cmdarg, &host->base->argument);
  77        udelay(COMMAND_REG_DELAY);
  78        writel(sdi_cmd, &host->base->command);
  79        result = wait_for_command_end(dev, cmd);
  80
  81        /* After CMD2 set RCA to a none zero value. */
  82        if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID))
  83                dev->rca = 10;
  84
  85        /* After CMD3 open drain is switched off and push pull is used. */
  86        if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) {
  87                u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD;
  88                writel(sdi_pwr, &host->base->power);
  89        }
  90
  91        return result;
  92}
  93
  94static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize)
  95{
  96        u32 *tempbuff = dest;
  97        u64 xfercount = blkcount * blksize;
  98        struct pl180_mmc_host *host = dev->priv;
  99        u32 status, status_err;
 100
 101        debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
 102
 103        status = readl(&host->base->status);
 104        status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT |
 105                               SDI_STA_RXOVERR);
 106        while ((!status_err) && (xfercount >= sizeof(u32))) {
 107                if (status & SDI_STA_RXDAVL) {
 108                        *(tempbuff) = readl(&host->base->fifo);
 109                        tempbuff++;
 110                        xfercount -= sizeof(u32);
 111                }
 112                status = readl(&host->base->status);
 113                status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT |
 114                                       SDI_STA_RXOVERR);
 115        }
 116
 117        status_err = status &
 118                (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND |
 119                 SDI_STA_RXOVERR);
 120        while (!status_err) {
 121                status = readl(&host->base->status);
 122                status_err = status &
 123                        (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND |
 124                         SDI_STA_RXOVERR);
 125        }
 126
 127        if (status & SDI_STA_DTIMEOUT) {
 128                printf("Read data timed out, xfercount: %llu, status: 0x%08X\n",
 129                        xfercount, status);
 130                return -ETIMEDOUT;
 131        } else if (status & SDI_STA_DCRCFAIL) {
 132                printf("Read data bytes CRC error: 0x%x\n", status);
 133                return -EILSEQ;
 134        } else if (status & SDI_STA_RXOVERR) {
 135                printf("Read data RX overflow error\n");
 136                return -EIO;
 137        }
 138
 139        writel(SDI_ICR_MASK, &host->base->status_clear);
 140
 141        if (xfercount) {
 142                printf("Read data error, xfercount: %llu\n", xfercount);
 143                return -ENOBUFS;
 144        }
 145
 146        return 0;
 147}
 148
 149static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize)
 150{
 151        u32 *tempbuff = src;
 152        int i;
 153        u64 xfercount = blkcount * blksize;
 154        struct pl180_mmc_host *host = dev->priv;
 155        u32 status, status_err;
 156
 157        debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
 158
 159        status = readl(&host->base->status);
 160        status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
 161        while (!status_err && xfercount) {
 162                if (status & SDI_STA_TXFIFOBW) {
 163                        if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) {
 164                                for (i = 0; i < SDI_FIFO_BURST_SIZE; i++)
 165                                        writel(*(tempbuff + i),
 166                                                &host->base->fifo);
 167                                tempbuff += SDI_FIFO_BURST_SIZE;
 168                                xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32);
 169                        } else {
 170                                while (xfercount >= sizeof(u32)) {
 171                                        writel(*(tempbuff), &host->base->fifo);
 172                                        tempbuff++;
 173                                        xfercount -= sizeof(u32);
 174                                }
 175                        }
 176                }
 177                status = readl(&host->base->status);
 178                status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
 179        }
 180
 181        status_err = status &
 182                (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
 183        while (!status_err) {
 184                status = readl(&host->base->status);
 185                status_err = status &
 186                        (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
 187        }
 188
 189        if (status & SDI_STA_DTIMEOUT) {
 190                printf("Write data timed out, xfercount:%llu,status:0x%08X\n",
 191                       xfercount, status);
 192                return -ETIMEDOUT;
 193        } else if (status & SDI_STA_DCRCFAIL) {
 194                printf("Write data CRC error\n");
 195                return -EILSEQ;
 196        }
 197
 198        writel(SDI_ICR_MASK, &host->base->status_clear);
 199
 200        if (xfercount) {
 201                printf("Write data error, xfercount:%llu", xfercount);
 202                return -ENOBUFS;
 203        }
 204
 205        return 0;
 206}
 207
 208static int do_data_transfer(struct mmc *dev,
 209                            struct mmc_cmd *cmd,
 210                            struct mmc_data *data)
 211{
 212        int error = -ETIMEDOUT;
 213        struct pl180_mmc_host *host = dev->priv;
 214        u32 blksz = 0;
 215        u32 data_ctrl = 0;
 216        u32 data_len = (u32) (data->blocks * data->blocksize);
 217
 218        if (!host->version2) {
 219                blksz = (ffs(data->blocksize) - 1);
 220                data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK);
 221        } else {
 222                blksz = data->blocksize;
 223                data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT);
 224        }
 225        data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE;
 226
 227        writel(SDI_DTIMER_DEFAULT, &host->base->datatimer);
 228        writel(data_len, &host->base->datalength);
 229        udelay(DATA_REG_DELAY);
 230
 231        if (data->flags & MMC_DATA_READ) {
 232                data_ctrl |= SDI_DCTRL_DTDIR_IN;
 233                writel(data_ctrl, &host->base->datactrl);
 234
 235                error = do_command(dev, cmd);
 236                if (error)
 237                        return error;
 238
 239                error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks,
 240                                   (u32)data->blocksize);
 241        } else if (data->flags & MMC_DATA_WRITE) {
 242                error = do_command(dev, cmd);
 243                if (error)
 244                        return error;
 245
 246                writel(data_ctrl, &host->base->datactrl);
 247                error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks,
 248                                                        (u32)data->blocksize);
 249        }
 250
 251        return error;
 252}
 253
 254static int host_request(struct mmc *dev,
 255                        struct mmc_cmd *cmd,
 256                        struct mmc_data *data)
 257{
 258        int result;
 259
 260        if (data)
 261                result = do_data_transfer(dev, cmd, data);
 262        else
 263                result = do_command(dev, cmd);
 264
 265        return result;
 266}
 267
 268/* MMC uses open drain drivers in the enumeration phase */
 269static int mmc_host_reset(struct mmc *dev)
 270{
 271        struct pl180_mmc_host *host = dev->priv;
 272
 273        writel(host->pwr_init, &host->base->power);
 274
 275        return 0;
 276}
 277
 278static int  host_set_ios(struct mmc *dev)
 279{
 280        struct pl180_mmc_host *host = dev->priv;
 281        u32 sdi_clkcr;
 282
 283        sdi_clkcr = readl(&host->base->clock);
 284
 285        /* Ramp up the clock rate */
 286        if (dev->clock) {
 287                u32 clkdiv = 0;
 288                u32 tmp_clock;
 289
 290                if (dev->clock >= dev->cfg->f_max) {
 291                        clkdiv = 0;
 292                        dev->clock = dev->cfg->f_max;
 293                } else {
 294                        clkdiv = (host->clock_in / dev->clock) - 2;
 295                }
 296
 297                tmp_clock = host->clock_in / (clkdiv + 2);
 298                while (tmp_clock > dev->clock) {
 299                        clkdiv++;
 300                        tmp_clock = host->clock_in / (clkdiv + 2);
 301                }
 302
 303                if (clkdiv > SDI_CLKCR_CLKDIV_MASK)
 304                        clkdiv = SDI_CLKCR_CLKDIV_MASK;
 305
 306                tmp_clock = host->clock_in / (clkdiv + 2);
 307                dev->clock = tmp_clock;
 308                sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK);
 309                sdi_clkcr |= clkdiv;
 310        }
 311
 312        /* Set the bus width */
 313        if (dev->bus_width) {
 314                u32 buswidth = 0;
 315
 316                switch (dev->bus_width) {
 317                case 1:
 318                        buswidth |= SDI_CLKCR_WIDBUS_1;
 319                        break;
 320                case 4:
 321                        buswidth |= SDI_CLKCR_WIDBUS_4;
 322                        break;
 323                case 8:
 324                        buswidth |= SDI_CLKCR_WIDBUS_8;
 325                        break;
 326                default:
 327                        printf("Invalid bus width: %d\n", dev->bus_width);
 328                        break;
 329                }
 330                sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK);
 331                sdi_clkcr |= buswidth;
 332        }
 333
 334        writel(sdi_clkcr, &host->base->clock);
 335        udelay(CLK_CHANGE_DELAY);
 336
 337        return 0;
 338}
 339
 340static const struct mmc_ops arm_pl180_mmci_ops = {
 341        .send_cmd = host_request,
 342        .set_ios = host_set_ios,
 343        .init = mmc_host_reset,
 344};
 345
 346/*
 347 * mmc_host_init - initialize the mmc controller.
 348 * Set initial clock and power for mmc slot.
 349 * Initialize mmc struct and register with mmc framework.
 350 */
 351int arm_pl180_mmci_init(struct pl180_mmc_host *host)
 352{
 353        struct mmc *mmc;
 354        u32 sdi_u32;
 355
 356        writel(host->pwr_init, &host->base->power);
 357        writel(host->clkdiv_init, &host->base->clock);
 358        udelay(CLK_CHANGE_DELAY);
 359
 360        /* Disable mmc interrupts */
 361        sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK;
 362        writel(sdi_u32, &host->base->mask0);
 363
 364        host->cfg.name = host->name;
 365        host->cfg.ops = &arm_pl180_mmci_ops;
 366        /* TODO remove the duplicates */
 367        host->cfg.host_caps = host->caps;
 368        host->cfg.voltages = host->voltages;
 369        host->cfg.f_min = host->clock_min;
 370        host->cfg.f_max = host->clock_max;
 371        if (host->b_max != 0)
 372                host->cfg.b_max = host->b_max;
 373        else
 374                host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
 375
 376        mmc = mmc_create(&host->cfg, host);
 377        if (mmc == NULL)
 378                return -1;
 379
 380        debug("registered mmc interface number is:%d\n", mmc->block_dev.devnum);
 381
 382        return 0;
 383}
 384