Commit dae0f5c644c0f76e67306bd49c09d95373b7357a

Authored by Marek Vasut
Committed by Pantelis Antoniou
1 parent 7eca6b6327

mmc: s3c: Add SD driver

Implement SD driver for the S3C24xx family. This implementation
is currently only capable of using the PIO transfers, DMA is not
supported.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Lukasz Majewski <l.majewski@samsung.com>
Cc: Minkyu Kang <mk7.kang@samsung.com>
Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
Cc: Vladimir Zapolskiy <vz@mleia.com>
Acked-by: Pantelis Antoniou <panto@antoniou-consulting.com>

Showing 3 changed files with 328 additions and 0 deletions Side-by-side Diff

arch/arm/include/asm/arch-s3c24x0/s3c24x0.h
... ... @@ -700,5 +700,11 @@
700 700 #endif
701 701 };
702 702  
  703 +#ifdef CONFIG_CMD_MMC
  704 +#include <mmc.h>
  705 +int s3cmmc_initialize(bd_t *bis, int (*getcd)(struct mmc *),
  706 + int (*getwp)(struct mmc *));
  707 +#endif
  708 +
703 709 #endif /*__S3C24X0_H__*/
drivers/mmc/Makefile
... ... @@ -22,6 +22,7 @@
22 22 obj-$(CONFIG_SDHCI) += sdhci.o
23 23 obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o
24 24 obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o
  25 +obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
25 26 obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
26 27 obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
27 28 obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o
drivers/mmc/s3c_sdi.c
  1 +/*
  2 + * S3C24xx SD/MMC driver
  3 + *
  4 + * Based on OpenMoko S3C24xx driver by Harald Welte <laforge@openmoko.org>
  5 + *
  6 + * Copyright (C) 2014 Marek Vasut <marex@denx.de>
  7 + *
  8 + * SPDX-License-Identifier: GPL-2.0+
  9 + */
  10 +
  11 +#include <common.h>
  12 +#include <malloc.h>
  13 +#include <mmc.h>
  14 +#include <errno.h>
  15 +#include <asm/arch/s3c24x0_cpu.h>
  16 +#include <asm/io.h>
  17 +#include <asm/unaligned.h>
  18 +
  19 +#define S3C2440_SDICON_SDRESET (1 << 8)
  20 +#define S3C2410_SDICON_FIFORESET (1 << 1)
  21 +#define S3C2410_SDICON_CLOCKTYPE (1 << 0)
  22 +
  23 +#define S3C2410_SDICMDCON_LONGRSP (1 << 10)
  24 +#define S3C2410_SDICMDCON_WAITRSP (1 << 9)
  25 +#define S3C2410_SDICMDCON_CMDSTART (1 << 8)
  26 +#define S3C2410_SDICMDCON_SENDERHOST (1 << 6)
  27 +#define S3C2410_SDICMDCON_INDEX 0x3f
  28 +
  29 +#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12)
  30 +#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11)
  31 +#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10)
  32 +#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9)
  33 +
  34 +#define S3C2440_SDIDCON_DS_WORD (2 << 22)
  35 +#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20)
  36 +#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19)
  37 +#define S3C2410_SDIDCON_BLOCKMODE (1 << 17)
  38 +#define S3C2410_SDIDCON_WIDEBUS (1 << 16)
  39 +#define S3C2440_SDIDCON_DATSTART (1 << 14)
  40 +#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12)
  41 +#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12)
  42 +#define S3C2410_SDIDCON_BLKNUM 0x7ff
  43 +
  44 +#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8)
  45 +#define S3C2410_SDIDSTA_CRCFAIL (1 << 7)
  46 +#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6)
  47 +#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5)
  48 +#define S3C2410_SDIDSTA_XFERFINISH (1 << 4)
  49 +
  50 +#define S3C2410_SDIFSTA_TFHALF (1 << 11)
  51 +#define S3C2410_SDIFSTA_COUNTMASK 0x7f
  52 +
  53 +/*
  54 + * WARNING: We only support one SD IP block.
  55 + * NOTE: It's not likely there will ever exist an S3C24xx with two,
  56 + * at least not in this universe all right.
  57 + */
  58 +static int wide_bus;
  59 +
  60 +static int
  61 +s3cmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
  62 +{
  63 + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
  64 + uint32_t sdiccon, sdicsta, sdidcon, sdidsta, sdidat, sdifsta;
  65 + uint32_t sdicsta_wait_bit = S3C2410_SDICMDSTAT_CMDSENT;
  66 + unsigned int timeout = 100000;
  67 + int ret = 0, xfer_len, data_offset = 0;
  68 + const uint32_t sdidsta_err_mask = S3C2410_SDIDSTA_FIFOFAIL |
  69 + S3C2410_SDIDSTA_CRCFAIL | S3C2410_SDIDSTA_RXCRCFAIL |
  70 + S3C2410_SDIDSTA_DATATIMEOUT;
  71 +
  72 +
  73 + writel(0xffffffff, &sdi_regs->sdicsta);
  74 + writel(0xffffffff, &sdi_regs->sdidsta);
  75 + writel(0xffffffff, &sdi_regs->sdifsta);
  76 +
  77 + /* Set up data transfer (if applicable). */
  78 + if (data) {
  79 + writel(data->blocksize, &sdi_regs->sdibsize);
  80 +
  81 + sdidcon = data->blocks & S3C2410_SDIDCON_BLKNUM;
  82 + sdidcon |= S3C2410_SDIDCON_BLOCKMODE;
  83 +#if defined(CONFIG_S3C2440)
  84 + sdidcon |= S3C2440_SDIDCON_DS_WORD | S3C2440_SDIDCON_DATSTART;
  85 +#endif
  86 + if (wide_bus)
  87 + sdidcon |= S3C2410_SDIDCON_WIDEBUS;
  88 +
  89 + if (data->flags & MMC_DATA_READ) {
  90 + sdidcon |= S3C2410_SDIDCON_RXAFTERCMD;
  91 + sdidcon |= S3C2410_SDIDCON_XFER_RXSTART;
  92 + } else {
  93 + sdidcon |= S3C2410_SDIDCON_TXAFTERRESP;
  94 + sdidcon |= S3C2410_SDIDCON_XFER_TXSTART;
  95 + }
  96 +
  97 + writel(sdidcon, &sdi_regs->sdidcon);
  98 + }
  99 +
  100 + /* Write CMD arg. */
  101 + writel(cmd->cmdarg, &sdi_regs->sdicarg);
  102 +
  103 + /* Write CMD index. */
  104 + sdiccon = cmd->cmdidx & S3C2410_SDICMDCON_INDEX;
  105 + sdiccon |= S3C2410_SDICMDCON_SENDERHOST;
  106 + sdiccon |= S3C2410_SDICMDCON_CMDSTART;
  107 +
  108 + /* Command with short response. */
  109 + if (cmd->resp_type & MMC_RSP_PRESENT) {
  110 + sdiccon |= S3C2410_SDICMDCON_WAITRSP;
  111 + sdicsta_wait_bit = S3C2410_SDICMDSTAT_RSPFIN;
  112 + }
  113 +
  114 + /* Command with long response. */
  115 + if (cmd->resp_type & MMC_RSP_136)
  116 + sdiccon |= S3C2410_SDICMDCON_LONGRSP;
  117 +
  118 + /* Start the command. */
  119 + writel(sdiccon, &sdi_regs->sdiccon);
  120 +
  121 + /* Wait for the command to complete or for response. */
  122 + for (timeout = 100000; timeout; timeout--) {
  123 + sdicsta = readl(&sdi_regs->sdicsta);
  124 + if (sdicsta & sdicsta_wait_bit)
  125 + break;
  126 +
  127 + if (sdicsta & S3C2410_SDICMDSTAT_CMDTIMEOUT)
  128 + timeout = 1;
  129 + }
  130 +
  131 + /* Clean the status bits. */
  132 + setbits_le32(&sdi_regs->sdicsta, 0xf << 9);
  133 +
  134 + if (!timeout) {
  135 + puts("S3C SDI: Command timed out!\n");
  136 + ret = TIMEOUT;
  137 + goto error;
  138 + }
  139 +
  140 + /* Read out the response. */
  141 + if (cmd->resp_type & MMC_RSP_136) {
  142 + cmd->response[0] = readl(&sdi_regs->sdirsp0);
  143 + cmd->response[1] = readl(&sdi_regs->sdirsp1);
  144 + cmd->response[2] = readl(&sdi_regs->sdirsp2);
  145 + cmd->response[3] = readl(&sdi_regs->sdirsp3);
  146 + } else {
  147 + cmd->response[0] = readl(&sdi_regs->sdirsp0);
  148 + }
  149 +
  150 + /* If there are no data, we're done. */
  151 + if (!data)
  152 + return 0;
  153 +
  154 + xfer_len = data->blocksize * data->blocks;
  155 +
  156 + while (xfer_len > 0) {
  157 + sdidsta = readl(&sdi_regs->sdidsta);
  158 + sdifsta = readl(&sdi_regs->sdifsta);
  159 +
  160 + if (sdidsta & sdidsta_err_mask) {
  161 + printf("S3C SDI: Data error (sdta=0x%08x)\n", sdidsta);
  162 + ret = -EIO;
  163 + goto error;
  164 + }
  165 +
  166 + if (data->flags & MMC_DATA_READ) {
  167 + if ((sdifsta & S3C2410_SDIFSTA_COUNTMASK) < 4)
  168 + continue;
  169 + sdidat = readl(&sdi_regs->sdidat);
  170 + put_unaligned_le32(sdidat, data->dest + data_offset);
  171 + } else { /* Write */
  172 + /* TX FIFO half full. */
  173 + if (!(sdifsta & S3C2410_SDIFSTA_TFHALF))
  174 + continue;
  175 +
  176 + /* TX FIFO is below 32b full, write. */
  177 + sdidat = get_unaligned_le32(data->src + data_offset);
  178 + writel(sdidat, &sdi_regs->sdidat);
  179 + }
  180 + data_offset += 4;
  181 + xfer_len -= 4;
  182 + }
  183 +
  184 + /* Wait for the command to complete or for response. */
  185 + for (timeout = 100000; timeout; timeout--) {
  186 + sdidsta = readl(&sdi_regs->sdidsta);
  187 + if (sdidsta & S3C2410_SDIDSTA_XFERFINISH)
  188 + break;
  189 +
  190 + if (sdidsta & S3C2410_SDIDSTA_DATATIMEOUT)
  191 + timeout = 1;
  192 + }
  193 +
  194 + /* Clear status bits. */
  195 + writel(0x6f8, &sdi_regs->sdidsta);
  196 +
  197 + if (!timeout) {
  198 + puts("S3C SDI: Command timed out!\n");
  199 + ret = TIMEOUT;
  200 + goto error;
  201 + }
  202 +
  203 + writel(0, &sdi_regs->sdidcon);
  204 +
  205 + return 0;
  206 +error:
  207 + return ret;
  208 +}
  209 +
  210 +static void s3cmmc_set_ios(struct mmc *mmc)
  211 +{
  212 + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
  213 + uint32_t divider = 0;
  214 +
  215 + wide_bus = (mmc->bus_width == 4);
  216 +
  217 + if (!mmc->clock)
  218 + return;
  219 +
  220 + divider = DIV_ROUND_UP(get_PCLK(), mmc->clock);
  221 + if (divider)
  222 + divider--;
  223 +
  224 + writel(divider, &sdi_regs->sdipre);
  225 + mdelay(125);
  226 +}
  227 +
  228 +static int s3cmmc_init(struct mmc *mmc)
  229 +{
  230 + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
  231 + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
  232 +
  233 + /* Start the clock. */
  234 + setbits_le32(&clk_power->clkcon, 1 << 9);
  235 +
  236 +#if defined(CONFIG_S3C2440)
  237 + writel(S3C2440_SDICON_SDRESET, &sdi_regs->sdicon);
  238 + mdelay(10);
  239 + writel(0x7fffff, &sdi_regs->sdidtimer);
  240 +#else
  241 + writel(0xffff, &sdi_regs->sdidtimer);
  242 +#endif
  243 + writel(MMC_MAX_BLOCK_LEN, &sdi_regs->sdibsize);
  244 + writel(0x0, &sdi_regs->sdiimsk);
  245 +
  246 + writel(S3C2410_SDICON_FIFORESET | S3C2410_SDICON_CLOCKTYPE,
  247 + &sdi_regs->sdicon);
  248 +
  249 + mdelay(125);
  250 +
  251 + return 0;
  252 +}
  253 +
  254 +struct s3cmmc_priv {
  255 + struct mmc_config cfg;
  256 + int (*getcd)(struct mmc *);
  257 + int (*getwp)(struct mmc *);
  258 +};
  259 +
  260 +static int s3cmmc_getcd(struct mmc *mmc)
  261 +{
  262 + struct s3cmmc_priv *priv = mmc->priv;
  263 + if (priv->getcd)
  264 + return priv->getcd(mmc);
  265 + else
  266 + return 0;
  267 +}
  268 +
  269 +static int s3cmmc_getwp(struct mmc *mmc)
  270 +{
  271 + struct s3cmmc_priv *priv = mmc->priv;
  272 + if (priv->getwp)
  273 + return priv->getwp(mmc);
  274 + else
  275 + return 0;
  276 +}
  277 +
  278 +static const struct mmc_ops s3cmmc_ops = {
  279 + .send_cmd = s3cmmc_send_cmd,
  280 + .set_ios = s3cmmc_set_ios,
  281 + .init = s3cmmc_init,
  282 + .getcd = s3cmmc_getcd,
  283 + .getwp = s3cmmc_getwp,
  284 +};
  285 +
  286 +int s3cmmc_initialize(bd_t *bis, int (*getcd)(struct mmc *),
  287 + int (*getwp)(struct mmc *))
  288 +{
  289 + struct s3cmmc_priv *priv;
  290 + struct mmc *mmc;
  291 + struct mmc_config *cfg;
  292 +
  293 + priv = calloc(1, sizeof(*priv));
  294 + if (!priv)
  295 + return -ENOMEM;
  296 + cfg = &priv->cfg;
  297 +
  298 + cfg->name = "S3C MMC";
  299 + cfg->ops = &s3cmmc_ops;
  300 + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
  301 + cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HC | MMC_MODE_HS;
  302 + cfg->f_min = 400000;
  303 + cfg->f_max = get_PCLK() / 2;
  304 + cfg->b_max = 0x80;
  305 +
  306 +#if defined(CONFIG_S3C2410)
  307 + /*
  308 + * S3C2410 has some bug that prevents reliable
  309 + * operation at higher speed
  310 + */
  311 + cfg->f_max /= 2;
  312 +#endif
  313 +
  314 + mmc = mmc_create(cfg, priv);
  315 + if (!mmc) {
  316 + free(priv);
  317 + return -ENOMEM;
  318 + }
  319 +
  320 + return 0;
  321 +}