Merge branch 'master' of git://git.denx.de/u-boot-samsung
[platform/kernel/u-boot.git] / drivers / mmc / sh_sdhi.c
index e9d99a2..d181b63 100644 (file)
@@ -3,7 +3,7 @@
  *
  * SD/MMC driver for Renesas rmobile ARM SoCs.
  *
- * Copyright (C) 2011,2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2011,2013-2017 Renesas Electronics Corporation
  * Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
  * Copyright (C) 2008-2009 Renesas Solutions Corp.
  *
@@ -13,7 +13,7 @@
 #include <common.h>
 #include <malloc.h>
 #include <mmc.h>
-#include <asm/errno.h>
+#include <linux/errno.h>
 #include <asm/io.h>
 #include <asm/arch/rmobile.h>
 #include <asm/arch/sh_sdhi.h>
@@ -29,6 +29,17 @@ struct sh_sdhi_host {
        unsigned char sd_error;
        unsigned char detect_waiting;
 };
+
+static inline void sh_sdhi_writeq(struct sh_sdhi_host *host, int reg, u64 val)
+{
+       writeq(val, host->addr + (reg << host->bus_shift));
+}
+
+static inline u64 sh_sdhi_readq(struct sh_sdhi_host *host, int reg)
+{
+       return readq(host->addr + (reg << host->bus_shift));
+}
+
 static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
 {
        writew(val, host->addr + (reg << host->bus_shift));
@@ -232,7 +243,7 @@ static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
        e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
        if (e_state2 & ERR_STS2_SYS_ERROR) {
                if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
-                       ret = TIMEOUT;
+                       ret = -ETIMEDOUT;
                else
                        ret = -EILSEQ;
                debug("%s: ERR_STS2 = %04x\n",
@@ -246,7 +257,7 @@ static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
        if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
                ret = -EILSEQ;
        else
-               ret = TIMEOUT;
+               ret = -ETIMEDOUT;
 
        debug("%s: ERR_STS1 = %04x\n",
              DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
@@ -261,6 +272,7 @@ static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
        long time;
        unsigned short blocksize, i;
        unsigned short *p = (unsigned short *)data->dest;
+       u64 *q = (u64 *)data->dest;
 
        if ((unsigned long)p & 0x00000001) {
                debug(DRIVER_NAME": %s: The data pointer is unaligned.",
@@ -281,8 +293,12 @@ static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
 
        host->wait_int = 0;
        blocksize = sh_sdhi_readw(host, SDHI_SIZE);
-       for (i = 0; i < blocksize / 2; i++)
-               *p++ = sh_sdhi_readw(host, SDHI_BUF0);
+       if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
+               for (i = 0; i < blocksize / 8; i++)
+                       *q++ = sh_sdhi_readq(host, SDHI_BUF0);
+       else
+               for (i = 0; i < blocksize / 2; i++)
+                       *p++ = sh_sdhi_readw(host, SDHI_BUF0);
 
        time = sh_sdhi_wait_interrupt_flag(host);
        if (time == 0 || host->sd_error != 0)
@@ -297,6 +313,7 @@ static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
        long time;
        unsigned short blocksize, i, sec;
        unsigned short *p = (unsigned short *)data->dest;
+       u64 *q = (u64 *)data->dest;
 
        if ((unsigned long)p & 0x00000001) {
                debug(DRIVER_NAME": %s: The data pointer is unaligned.",
@@ -319,8 +336,12 @@ static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
 
                host->wait_int = 0;
                blocksize = sh_sdhi_readw(host, SDHI_SIZE);
-               for (i = 0; i < blocksize / 2; i++)
-                       *p++ = sh_sdhi_readw(host, SDHI_BUF0);
+               if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
+                       for (i = 0; i < blocksize / 8; i++)
+                               *q++ = sh_sdhi_readq(host, SDHI_BUF0);
+               else
+                       for (i = 0; i < blocksize / 2; i++)
+                               *p++ = sh_sdhi_readw(host, SDHI_BUF0);
        }
 
        return 0;
@@ -332,6 +353,7 @@ static int sh_sdhi_single_write(struct sh_sdhi_host *host,
        long time;
        unsigned short blocksize, i;
        const unsigned short *p = (const unsigned short *)data->src;
+       const u64 *q = (const u64 *)data->src;
 
        if ((unsigned long)p & 0x00000001) {
                debug(DRIVER_NAME": %s: The data pointer is unaligned.",
@@ -356,8 +378,12 @@ static int sh_sdhi_single_write(struct sh_sdhi_host *host,
 
        host->wait_int = 0;
        blocksize = sh_sdhi_readw(host, SDHI_SIZE);
-       for (i = 0; i < blocksize / 2; i++)
-               sh_sdhi_writew(host, SDHI_BUF0, *p++);
+       if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
+               for (i = 0; i < blocksize / 8; i++)
+                       sh_sdhi_writeq(host, SDHI_BUF0, *q++);
+       else
+               for (i = 0; i < blocksize / 2; i++)
+                       sh_sdhi_writew(host, SDHI_BUF0, *p++);
 
        time = sh_sdhi_wait_interrupt_flag(host);
        if (time == 0 || host->sd_error != 0)
@@ -372,6 +398,7 @@ static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
        long time;
        unsigned short i, sec, blocksize;
        const unsigned short *p = (const unsigned short *)data->src;
+       const u64 *q = (const u64 *)data->src;
 
        debug("%s: blocks = %d, blocksize = %d\n",
              __func__, data->blocks, data->blocksize);
@@ -388,8 +415,12 @@ static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
 
                host->wait_int = 0;
                blocksize = sh_sdhi_readw(host, SDHI_SIZE);
-               for (i = 0; i < blocksize / 2; i++)
-                       sh_sdhi_writew(host, SDHI_BUF0, *p++);
+               if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
+                       for (i = 0; i < blocksize / 8; i++)
+                               sh_sdhi_writeq(host, SDHI_BUF0, *q++);
+               else
+                       for (i = 0; i < blocksize / 2; i++)
+                               sh_sdhi_writew(host, SDHI_BUF0, *p++);
        }
 
        return 0;
@@ -458,6 +489,13 @@ static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
                else /* SD_SWITCH */
                        opc = SDHI_SD_SWITCH;
                break;
+       case MMC_CMD_SEND_OP_COND:
+               opc = SDHI_MMC_SEND_OP_COND;
+               break;
+       case MMC_CMD_SEND_EXT_CSD:
+               if (data)
+                       opc = SDHI_MMC_SEND_EXT_CSD;
+               break;
        default:
                break;
        }
@@ -482,6 +520,7 @@ static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
        case MMC_CMD_READ_SINGLE_BLOCK:
        case SDHI_SD_APP_SEND_SCR:
        case SDHI_SD_SWITCH: /* SD_SWITCH */
+       case SDHI_MMC_SEND_EXT_CSD:
                ret = sh_sdhi_single_read(host, data);
                break;
        default:
@@ -546,8 +585,6 @@ static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
                        break;
        }
 
-       sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
-
        host->wait_int = 0;
        sh_sdhi_writew(host, SDHI_INFO1_MASK,
                       ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
@@ -557,6 +594,8 @@ static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
                       INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
                       sh_sdhi_readw(host, SDHI_INFO2_MASK));
 
+       sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
+
        time = sh_sdhi_wait_interrupt_flag(host);
        if (!time)
                return sh_sdhi_error_manage(host);
@@ -567,7 +606,7 @@ static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
                case MMC_CMD_SELECT_CARD:
                case SD_CMD_SEND_IF_COND:
                case MMC_CMD_APP_CMD:
-                       ret = TIMEOUT;
+                       ret = -ETIMEDOUT;
                        break;
                default:
                        debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
@@ -608,23 +647,31 @@ static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
        return ret;
 }
 
-static void sh_sdhi_set_ios(struct mmc *mmc)
+static int sh_sdhi_set_ios(struct mmc *mmc)
 {
        int ret;
        struct sh_sdhi_host *host = mmc_priv(mmc);
 
        ret = sh_sdhi_clock_control(host, mmc->clock);
        if (ret)
-               return;
+               return -EINVAL;
 
-       if (mmc->bus_width == 4)
-               sh_sdhi_writew(host, SDHI_OPTION, ~OPT_BUS_WIDTH_1 &
-                              sh_sdhi_readw(host, SDHI_OPTION));
+       if (mmc->bus_width == 8)
+               sh_sdhi_writew(host, SDHI_OPTION,
+                              OPT_BUS_WIDTH_8 | (~OPT_BUS_WIDTH_M &
+                              sh_sdhi_readw(host, SDHI_OPTION)));
+       else if (mmc->bus_width == 4)
+               sh_sdhi_writew(host, SDHI_OPTION,
+                              OPT_BUS_WIDTH_4 | (~OPT_BUS_WIDTH_M &
+                              sh_sdhi_readw(host, SDHI_OPTION)));
        else
-               sh_sdhi_writew(host, SDHI_OPTION, OPT_BUS_WIDTH_1 |
-                              sh_sdhi_readw(host, SDHI_OPTION));
+               sh_sdhi_writew(host, SDHI_OPTION,
+                              OPT_BUS_WIDTH_1 | (~OPT_BUS_WIDTH_M &
+                              sh_sdhi_readw(host, SDHI_OPTION)));
 
        debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
+
+       return 0;
 }
 
 static int sh_sdhi_initialize(struct mmc *mmc)
@@ -651,6 +698,19 @@ static const struct mmc_ops sh_sdhi_ops = {
        .init           = sh_sdhi_initialize,
 };
 
+#ifdef CONFIG_RCAR_GEN3
+static struct mmc_config sh_sdhi_cfg = {
+       .name           = DRIVER_NAME,
+       .ops            = &sh_sdhi_ops,
+       .f_min          = CLKDEV_INIT,
+       .f_max          = CLKDEV_HS_DATA,
+       .voltages       = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
+       .host_caps      = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HS |
+                         MMC_MODE_HS_52MHz,
+       .part_type      = PART_TYPE_DOS,
+       .b_max          = CONFIG_SYS_MMC_MAX_BLK_COUNT,
+};
+#else
 static struct mmc_config sh_sdhi_cfg = {
        .name           = DRIVER_NAME,
        .ops            = &sh_sdhi_ops,
@@ -661,6 +721,7 @@ static struct mmc_config sh_sdhi_cfg = {
        .part_type      = PART_TYPE_DOS,
        .b_max          = CONFIG_SYS_MMC_MAX_BLK_COUNT,
 };
+#endif
 
 int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
 {
@@ -685,7 +746,9 @@ int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
        host->addr = addr;
        host->quirks = quirks;
 
-       if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
+       if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
+               host->bus_shift = 2;
+       else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
                host->bus_shift = 1;
 
        return ret;