]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/mmc/sunxi_mmc.c
sunxi: mmc: Add 'sunxi_' prefix to the static functions
[karo-tx-uboot.git] / drivers / mmc / sunxi_mmc.c
index d4e574fe191b0e8edd587b733be4b9cf5ec6ee24..ebfec7cf079bb6f55c53c31bb8b7e2219403ebe4 100644 (file)
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
 #include <asm/arch/mmc.h>
+#include <asm-generic/gpio.h>
 
 struct sunxi_mmc_host {
        unsigned mmc_no;
        uint32_t *mclkreg;
-       unsigned database;
        unsigned fatal_err;
-       unsigned mod_clk;
        struct sunxi_mmc *reg;
        struct mmc_config cfg;
 };
@@ -29,10 +29,22 @@ struct sunxi_mmc_host {
 /* support 4 mmc hosts */
 struct sunxi_mmc_host mmc_host[4];
 
+static int sunxi_mmc_getcd_gpio(int sdc_no)
+{
+       switch (sdc_no) {
+       case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN);
+       case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN);
+       case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN);
+       case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN);
+       }
+       return -1;
+}
+
 static int mmc_resource_init(int sdc_no)
 {
        struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
        struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+       int cd_pin, ret = 0;
 
        debug("init mmc %d resource\n", sdc_no);
 
@@ -57,16 +69,80 @@ static int mmc_resource_init(int sdc_no)
                printf("Wrong mmc number %d\n", sdc_no);
                return -1;
        }
-       mmchost->database = (unsigned int)mmchost->reg + 0x100;
        mmchost->mmc_no = sdc_no;
 
+       cd_pin = sunxi_mmc_getcd_gpio(sdc_no);
+       if (cd_pin != -1) {
+               ret = gpio_request(cd_pin, "mmc_cd");
+               if (!ret)
+                       ret = gpio_direction_input(cd_pin);
+       }
+
+       return ret;
+}
+
+static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz)
+{
+       unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
+
+       if (hz <= 24000000) {
+               pll = CCM_MMC_CTRL_OSCM24;
+               pll_hz = 24000000;
+       } else {
+#ifdef CONFIG_MACH_SUN9I
+               pll = CCM_MMC_CTRL_PLL_PERIPH0;
+               pll_hz = clock_get_pll4_periph0();
+#else
+               pll = CCM_MMC_CTRL_PLL6;
+               pll_hz = clock_get_pll6();
+#endif
+       }
+
+       div = pll_hz / hz;
+       if (pll_hz % hz)
+               div++;
+
+       n = 0;
+       while (div > 16) {
+               n++;
+               div = (div + 1) / 2;
+       }
+
+       if (n > 3) {
+               printf("mmc %u error cannot set clock to %u\n",
+                      mmchost->mmc_no, hz);
+               return -1;
+       }
+
+       /* determine delays */
+       if (hz <= 400000) {
+               oclk_dly = 0;
+               sclk_dly = 7;
+       } else if (hz <= 25000000) {
+               oclk_dly = 0;
+               sclk_dly = 5;
+       } else if (hz <= 50000000) {
+               oclk_dly = 3;
+               sclk_dly = 5;
+       } else {
+               /* hz > 50000000 */
+               oclk_dly = 2;
+               sclk_dly = 4;
+       }
+
+       writel(CCM_MMC_CTRL_ENABLE | pll | CCM_MMC_CTRL_SCLK_DLY(sclk_dly) |
+              CCM_MMC_CTRL_N(n) | CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
+              CCM_MMC_CTRL_M(div), mmchost->mclkreg);
+
+       debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n",
+             mmchost->mmc_no, hz, pll_hz, 1u << n, div,
+             pll_hz / (1u << n) / div);
+
        return 0;
 }
 
 static int mmc_clk_io_on(int sdc_no)
 {
-       unsigned int pll_clk;
-       unsigned int divider;
        struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
        struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 
@@ -75,15 +151,18 @@ static int mmc_clk_io_on(int sdc_no)
        /* config ahb clock */
        setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
 
-       /* config mod clock */
-       pll_clk = clock_get_pll6();
-       /* should be close to 100 MHz but no more, so round up */
-       divider = ((pll_clk + 99999999) / 100000000) - 1;
-       writel(CCM_MMC_CTRL_ENABLE | CCM_MMC_CTRL_PLL6 | divider,
-              mmchost->mclkreg);
-       mmchost->mod_clk = pll_clk / (divider + 1);
-
-       return 0;
+#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) || \
+    defined(CONFIG_MACH_SUN9I)
+       /* unassert reset */
+       setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
+#endif
+#if defined(CONFIG_MACH_SUN9I)
+       /* sun9i has a mmc-common module, also set the gate and reset there */
+       writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
+              SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
+#endif
+
+       return mmc_set_mod_clk(mmchost, 24000000);
 }
 
 static int mmc_update_clk(struct mmc *mmc)
@@ -108,7 +187,7 @@ static int mmc_update_clk(struct mmc *mmc)
        return 0;
 }
 
-static int mmc_config_clock(struct mmc *mmc, unsigned div)
+static int mmc_config_clock(struct mmc *mmc)
 {
        struct sunxi_mmc_host *mmchost = mmc->priv;
        unsigned rval = readl(&mmchost->reg->clkcr);
@@ -119,37 +198,34 @@ static int mmc_config_clock(struct mmc *mmc, unsigned div)
        if (mmc_update_clk(mmc))
                return -1;
 
-       /* Change Divider Factor */
+       /* Set mod_clk to new rate */
+       if (mmc_set_mod_clk(mmchost, mmc->clock))
+               return -1;
+
+       /* Clear internal divider */
        rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
-       rval |= div;
        writel(rval, &mmchost->reg->clkcr);
-       if (mmc_update_clk(mmc))
-               return -1;
+
        /* Re-enable Clock */
        rval |= SUNXI_MMC_CLK_ENABLE;
        writel(rval, &mmchost->reg->clkcr);
-
        if (mmc_update_clk(mmc))
                return -1;
 
        return 0;
 }
 
-static void mmc_set_ios(struct mmc *mmc)
+static void sunxi_mmc_set_ios(struct mmc *mmc)
 {
        struct sunxi_mmc_host *mmchost = mmc->priv;
-       unsigned int clkdiv = 0;
 
-       debug("set ios: bus_width: %x, clock: %d, mod_clk: %d\n",
-             mmc->bus_width, mmc->clock, mmchost->mod_clk);
+       debug("set ios: bus_width: %x, clock: %d\n",
+             mmc->bus_width, mmc->clock);
 
        /* Change clock first */
-       clkdiv = (mmchost->mod_clk + (mmc->clock >> 1)) / mmc->clock / 2;
-       if (mmc->clock) {
-               if (mmc_config_clock(mmc, clkdiv)) {
-                       mmchost->fatal_err = 1;
-                       return;
-               }
+       if (mmc->clock && mmc_config_clock(mmc) != 0) {
+               mmchost->fatal_err = 1;
+               return;
        }
 
        /* Change bus width */
@@ -161,7 +237,7 @@ static void mmc_set_ios(struct mmc *mmc)
                writel(0x0, &mmchost->reg->width);
 }
 
-static int mmc_core_init(struct mmc *mmc)
+static int sunxi_mmc_core_init(struct mmc *mmc)
 {
        struct sunxi_mmc_host *mmchost = mmc->priv;
 
@@ -194,9 +270,9 @@ static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data)
                }
 
                if (reading)
-                       buff[i] = readl(mmchost->database);
+                       buff[i] = readl(&mmchost->reg->fifo);
                else
-                       writel(buff[i], mmchost->database);
+                       writel(buff[i], &mmchost->reg->fifo);
        }
 
        return 0;
@@ -222,8 +298,8 @@ static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs,
        return 0;
 }
 
-static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
-                       struct mmc_data *data)
+static int sunxi_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+                             struct mmc_data *data)
 {
        struct sunxi_mmc_host *mmchost = mmc->priv;
        unsigned int cmdval = SUNXI_MMC_CMD_START;
@@ -290,7 +366,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
                }
        }
 
-       error = mmc_rint_wait(mmc, 0xfffff, SUNXI_MMC_RINT_COMMAND_DONE, "cmd");
+       error = mmc_rint_wait(mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE, "cmd");
        if (error)
                goto out;
 
@@ -343,13 +419,26 @@ out:
        return error;
 }
 
+static int sunxi_mmc_getcd(struct mmc *mmc)
+{
+       struct sunxi_mmc_host *mmchost = mmc->priv;
+       int cd_pin;
+
+       cd_pin = sunxi_mmc_getcd_gpio(mmchost->mmc_no);
+       if (cd_pin == -1)
+               return 1;
+
+       return !gpio_get_value(cd_pin);
+}
+
 static const struct mmc_ops sunxi_mmc_ops = {
-       .send_cmd       = mmc_send_cmd,
-       .set_ios        = mmc_set_ios,
-       .init           = mmc_core_init,
+       .send_cmd       = sunxi_mmc_send_cmd,
+       .set_ios        = sunxi_mmc_set_ios,
+       .init           = sunxi_mmc_core_init,
+       .getcd          = sunxi_mmc_getcd,
 };
 
-int sunxi_mmc_init(int sdc_no)
+struct mmc *sunxi_mmc_init(int sdc_no)
 {
        struct mmc_config *cfg = &mmc_host[sdc_no].cfg;
 
@@ -361,16 +450,19 @@ int sunxi_mmc_init(int sdc_no)
        cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
        cfg->host_caps = MMC_MODE_4BIT;
        cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
+    defined(CONFIG_MACH_SUN8I) || defined(CONFIG_MACH_SUN9I)
+       cfg->host_caps |= MMC_MODE_HC;
+#endif
        cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
 
        cfg->f_min = 400000;
        cfg->f_max = 52000000;
 
-       mmc_resource_init(sdc_no);
-       mmc_clk_io_on(sdc_no);
+       if (mmc_resource_init(sdc_no) != 0)
+               return NULL;
 
-       if (mmc_create(cfg, &mmc_host[sdc_no]) == NULL)
-               return -1;
+       mmc_clk_io_on(sdc_no);
 
-       return 0;
+       return mmc_create(cfg, &mmc_host[sdc_no]);
 }