Merge branch 'agust@denx.de' of git://git.denx.de/u-boot-staging
[oweals/u-boot.git] / drivers / mmc / tegra2_mmc.c
index 159cef1e0cc73a630bb734c1326e9843799a7e70..035a8687df724361098b780fc1d7860f314faabd 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <common.h>
 #include <mmc.h>
+#include <asm/gpio.h>
 #include <asm/io.h>
 #include <asm/arch/clk_rst.h>
 #include <asm/arch/clock.h>
@@ -116,34 +117,24 @@ static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data)
        writew(mode, &host->reg->trnmod);
 }
 
-static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
-                       struct mmc_data *data)
+static int mmc_wait_inhibit(struct mmc_host *host,
+                           struct mmc_cmd *cmd,
+                           struct mmc_data *data,
+                           unsigned int timeout)
 {
-       struct mmc_host *host = (struct mmc_host *)mmc->priv;
-       int flags, i;
-       unsigned int timeout;
-       unsigned int mask;
-       unsigned int retry = 0x100000;
-       debug(" mmc_send_cmd called\n");
-
-       /* Wait max 10 ms */
-       timeout = 10;
-
        /*
         * PRNSTS
-        * CMDINHDAT[1] : Command Inhibit (DAT)
-        * CMDINHCMD[0] : Command Inhibit (CMD)
+        * CMDINHDAT[1] : Command Inhibit (DAT)
+        * CMDINHCMD[0] : Command Inhibit (CMD)
         */
-       mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD;
-       if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY))
-               mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT;
+       unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD;
 
        /*
         * We shouldn't wait for data inhibit for stop commands, even
         * though they might use busy signaling
         */
-       if (data)
-               mask &= ~TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT;
+       if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY))
+               mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT;
 
        while (readl(&host->reg->prnsts) & mask) {
                if (timeout == 0) {
@@ -154,6 +145,24 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
                udelay(1000);
        }
 
+       return 0;
+}
+
+static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+                       struct mmc_data *data)
+{
+       struct mmc_host *host = (struct mmc_host *)mmc->priv;
+       int flags, i;
+       int result;
+       unsigned int mask;
+       unsigned int retry = 0x100000;
+       debug(" mmc_send_cmd called\n");
+
+       result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */);
+
+       if (result < 0)
+               return result;
+
        if (data)
                mmc_prepare_data(host, data);
 
@@ -260,6 +269,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
        }
 
        if (data) {
+               unsigned long   start = get_timer(0);
+
                while (1) {
                        mask = readl(&host->reg->norintsts);
 
@@ -284,6 +295,18 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
                                /* Transfer Complete */
                                debug("r/w is done\n");
                                break;
+                       } else if (get_timer(start) > 2000UL) {
+                               writel(mask, &host->reg->norintsts);
+                               printf("%s: MMC Timeout\n"
+                                      "    Interrupt status        0x%08x\n"
+                                      "    Interrupt status enable 0x%08x\n"
+                                      "    Interrupt signal enable 0x%08x\n"
+                                      "    Present status          0x%08x\n",
+                                      __func__, mask,
+                                      readl(&host->reg->norintstsen),
+                                      readl(&host->reg->norintsigen),
+                                      readl(&host->reg->prnsts));
+                               return -1;
                        }
                }
                writel(mask, &host->reg->norintsts);
@@ -451,20 +474,37 @@ static int mmc_core_init(struct mmc *mmc)
        return 0;
 }
 
-static int tegra2_mmc_initialize(int dev_index, int bus_width)
+int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
 {
        struct mmc_host *host;
+       char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */
        struct mmc *mmc;
 
-       debug(" mmc_initialize called\n");
+       debug(" tegra2_mmc_init: index %d, bus width %d "
+               "pwr_gpio %d cd_gpio %d\n",
+               dev_index, bus_width, pwr_gpio, cd_gpio);
 
        host = &mmc_host[dev_index];
 
        host->clock = 0;
+       host->pwr_gpio = pwr_gpio;
+       host->cd_gpio = cd_gpio;
        tegra2_get_setup(host, dev_index);
 
        clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000);
 
+       if (host->pwr_gpio >= 0) {
+               sprintf(gpusage, "SD/MMC%d PWR", dev_index);
+               gpio_request(host->pwr_gpio, gpusage);
+               gpio_direction_output(host->pwr_gpio, 1);
+       }
+
+       if (host->cd_gpio >= 0) {
+               sprintf(gpusage, "SD/MMC%d CD", dev_index);
+               gpio_request(host->cd_gpio, gpusage);
+               gpio_direction_input(host->cd_gpio);
+       }
+
        mmc = &mmc_dev[dev_index];
 
        sprintf(mmc->name, "Tegra2 SD/MMC");
@@ -496,9 +536,21 @@ static int tegra2_mmc_initialize(int dev_index, int bus_width)
        return 0;
 }
 
-int tegra2_mmc_init(int dev_index, int bus_width)
+/* this is a weak define that we are overriding */
+int board_mmc_getcd(u8 *cd, struct mmc *mmc)
 {
-       debug(" tegra2_mmc_init: index %d, bus width %d\n",
-               dev_index, bus_width);
-       return tegra2_mmc_initialize(dev_index, bus_width);
+       struct mmc_host *host = (struct mmc_host *)mmc->priv;
+
+       debug("board_mmc_getcd called\n");
+
+       *cd = 1; /* Assume card is inserted, or eMMC */
+
+       if (IS_SD(mmc)) {
+               if (host->cd_gpio >= 0) {
+                       if (gpio_get_value(host->cd_gpio))
+                               *cd = 0;
+               }
+       }
+
+       return 0;
 }