mmc: Implement card detection.
[oweals/u-boot.git] / drivers / mmc / s5p_mmc.c
index 669b1d0d2f91c2755689c9ce1d857761dc802c6a..4ae3aaf7738d8ced7d51dd5500022f39759b88a0 100644 (file)
 #include <mmc.h>
 #include <asm/io.h>
 #include <asm/arch/mmc.h>
-
-#ifdef DEBUG_S5P_HSMMC
-#define dbg(x...)       printf(x)
-#else
-#define dbg(x...)       do { } while (0)
-#endif
+#include <asm/arch/clk.h>
 
 /* support 4 mmc hosts */
 struct mmc mmc_dev[4];
@@ -36,18 +31,14 @@ struct mmc_host mmc_host[4];
 static inline struct s5p_mmc *s5p_get_base_mmc(int dev_index)
 {
        unsigned long offset = dev_index * sizeof(struct s5p_mmc);
-
-       if (cpu_is_s5pc100())
-               return (struct s5p_mmc *)(S5PC100_MMC_BASE + offset);
-       else
-               return (struct s5p_mmc *)(S5PC110_MMC_BASE + offset);
+       return (struct s5p_mmc *)(samsung_get_base_mmc() + offset);
 }
 
 static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
 {
        unsigned char ctrl;
 
-       dbg("data->dest: %08x\n", (u32)data->dest);
+       debug("data->dest: %08x\n", (u32)data->dest);
        writel((u32)data->dest, &host->reg->sysad);
        /*
         * DMASEL[4:3]
@@ -61,7 +52,7 @@ static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
        writeb(ctrl, &host->reg->hostctl);
 
        /* We do not handle DMA boundaries, so set it to max (512 KiB) */
-       writew((7 << 12) | (512 << 0), &host->reg->blksize);
+       writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize);
        writew(data->blocks, &host->reg->blkcnt);
 }
 
@@ -128,7 +119,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
        if (data)
                mmc_prepare_data(host, data);
 
-       dbg("cmd->arg: %08x\n", cmd->cmdarg);
+       debug("cmd->arg: %08x\n", cmd->cmdarg);
        writel(cmd->cmdarg, &host->reg->argument);
 
        if (data)
@@ -165,7 +156,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
        if (data)
                flags |= (1 << 5);
 
-       dbg("cmd: %d\n", cmd->cmdidx);
+       debug("cmd: %d\n", cmd->cmdidx);
 
        writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg);
 
@@ -186,11 +177,11 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 
        if (mask & (1 << 16)) {
                /* Timeout Error */
-               dbg("timeout: %08x cmd %d\n", mask, cmd->cmdidx);
+               debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx);
                return TIMEOUT;
        } else if (mask & (1 << 15)) {
                /* Error Interrupt */
-               dbg("error: %08x cmd %d\n", mask, cmd->cmdidx);
+               debug("error: %08x cmd %d\n", mask, cmd->cmdidx);
                return -1;
        }
 
@@ -206,7 +197,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
                                        cmd->response[i] |=
                                                readb(offset - 1);
                                }
-                               dbg("cmd->resp[%d]: %08x\n",
+                               debug("cmd->resp[%d]: %08x\n",
                                                i, cmd->response[i]);
                        }
                } else if (cmd->resp_type & MMC_RSP_BUSY) {
@@ -223,10 +214,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
                        }
 
                        cmd->response[0] = readl(&host->reg->rspreg0);
-                       dbg("cmd->resp[0]: %08x\n", cmd->response[0]);
+                       debug("cmd->resp[0]: %08x\n", cmd->response[0]);
                } else {
                        cmd->response[0] = readl(&host->reg->rspreg0);
-                       dbg("cmd->resp[0]: %08x\n", cmd->response[0]);
+                       debug("cmd->resp[0]: %08x\n", cmd->response[0]);
                }
        }
 
@@ -241,12 +232,18 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
                                                __func__, mask);
                                return -1;
                        } else if (mask & (1 << 3)) {
-                               /* DMA Interrupt */
-                               dbg("DMA end\n");
-                               break;
+                               /*
+                                * DMA Interrupt, restart the transfer where
+                                * it was interrupted.
+                                */
+                               unsigned int address = readl(&host->reg->sysad);
+
+                               debug("DMA end\n");
+                               writel((1 << 3), &host->reg->norintsts);
+                               writel(address, &host->reg->sysad);
                        } else if (mask & (1 << 1)) {
                                /* Transfer Complete */
-                               dbg("r/w is done\n");
+                               debug("r/w is done\n");
                                break;
                        }
                }
@@ -288,7 +285,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)
                div = 2;
        else
                div = 1;
-       dbg("div: %d\n", div);
+       debug("div: %d\n", div);
 
        div >>= 1;
        /*
@@ -301,6 +298,8 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)
        clk = (div << 8) | (1 << 0);
        writew(clk, &host->reg->clkcon);
 
+       set_mmc_clk(host->dev_index, div);
+
        /* Wait max 10 ms */
        timeout = 10;
        while (!(readw(&host->reg->clkcon) & (1 << 1))) {
@@ -325,7 +324,7 @@ static void mmc_set_ios(struct mmc *mmc)
        unsigned char ctrl;
        unsigned long val;
 
-       dbg("set_ios: bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
+       debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
 
        /*
         * SELCLKPADDS[17:16]
@@ -362,11 +361,16 @@ static void mmc_set_ios(struct mmc *mmc)
        ctrl = readb(&host->reg->hostctl);
 
        /*
+        * WIDE8[5]
+        * 0 = Depend on WIDE4
+        * 1 = 8-bit mode
         * WIDE4[1]
         * 1 = 4-bit mode
         * 0 = 1-bit mode
         */
-       if (mmc->bus_width == 4)
+       if (mmc->bus_width == 8)
+               ctrl |= (1 << 5);
+       else if (mmc->bus_width == 4)
                ctrl |= (1 << 1);
        else
                ctrl &= ~(1 << 1);
@@ -427,12 +431,13 @@ static int mmc_core_init(struct mmc *mmc)
         * NORMAL Interrupt Status Enable Register init
         * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable
         * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable
+        * [3] ENSTADMAINT : DMA Interrupt Status Enable
         * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable
         * [0] ENSTACMDCMPLT : Command Complete Status Enable
-       */
+        */
        mask = readl(&host->reg->norintstsen);
        mask &= ~(0xffff);
-       mask |= (1 << 5) | (1 << 4) | (1 << 1) | (1 << 0);
+       mask |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1) | (1 << 0);
        writel(mask, &host->reg->norintstsen);
 
        /*
@@ -447,7 +452,7 @@ static int mmc_core_init(struct mmc *mmc)
        return 0;
 }
 
-static int s5p_mmc_initialize(int dev_index)
+static int s5p_mmc_initialize(int dev_index, int bus_width)
 {
        struct mmc *mmc;
 
@@ -458,21 +463,28 @@ static int s5p_mmc_initialize(int dev_index)
        mmc->send_cmd = mmc_send_cmd;
        mmc->set_ios = mmc_set_ios;
        mmc->init = mmc_core_init;
+       mmc->getcd = NULL;
 
        mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
-       mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS;
+       if (bus_width == 8)
+               mmc->host_caps = MMC_MODE_8BIT;
+       else
+               mmc->host_caps = MMC_MODE_4BIT;
+       mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
 
        mmc->f_min = 400000;
        mmc->f_max = 52000000;
 
+       mmc_host[dev_index].dev_index = dev_index;
        mmc_host[dev_index].clock = 0;
        mmc_host[dev_index].reg = s5p_get_base_mmc(dev_index);
+       mmc->b_max = 0;
        mmc_register(mmc);
 
        return 0;
 }
 
-int s5p_mmc_init(int dev_index)
+int s5p_mmc_init(int dev_index, int bus_width)
 {
-       return s5p_mmc_initialize(dev_index);
+       return s5p_mmc_initialize(dev_index, bus_width);
 }