summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/dw_mmc.c12
-rw-r--r--drivers/mmc/exynos_dw_mmc.c205
-rw-r--r--drivers/mmc/mmc.c16
-rw-r--r--drivers/mmc/s5p_sdhci.c42
4 files changed, 172 insertions, 103 deletions
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index eb4e2be514..5bf36a0309 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -284,8 +284,8 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
static void dwmci_set_ios(struct mmc *mmc)
{
- struct dwmci_host *host = mmc->priv;
- u32 ctype;
+ struct dwmci_host *host = (struct dwmci_host *)mmc->priv;
+ u32 ctype, regs;
debug("Buswidth = %d, clock: %d\n",mmc->bus_width, mmc->clock);
@@ -304,6 +304,14 @@ static void dwmci_set_ios(struct mmc *mmc)
dwmci_writel(host, DWMCI_CTYPE, ctype);
+ regs = dwmci_readl(host, DWMCI_UHS_REG);
+ if (mmc->card_caps & MMC_MODE_DDR_52MHz)
+ regs |= DWMCI_DDR_MODE;
+ else
+ regs &= DWMCI_DDR_MODE;
+
+ dwmci_writel(host, DWMCI_UHS_REG, regs);
+
if (host->clksel)
host->clksel(host);
}
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index de8cdcc42b..d96dfe16a5 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -13,6 +13,8 @@
#include <asm/arch/dwmmc.h>
#include <asm/arch/clk.h>
#include <asm/arch/pinmux.h>
+#include <asm/gpio.h>
+#include <asm-generic/errno.h>
#define DWMMC_MAX_CH_NUM 4
#define DWMMC_MAX_FREQ 52000000
@@ -44,7 +46,11 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host)
& DWMCI_DIVRATIO_MASK) + 1;
sclk = get_mmc_clk(host->dev_index);
- return sclk / clk_div;
+ /*
+ * Assume to know divider value.
+ * When clock unit is broken, need to set "host->div"
+ */
+ return sclk / clk_div / (host->div + 1);
}
static void exynos_dwmci_board_init(struct dwmci_host *host)
@@ -60,48 +66,36 @@ static void exynos_dwmci_board_init(struct dwmci_host *host)
}
}
-/*
- * This function adds the mmc channel to be registered with mmc core.
- * index - mmc channel number.
- * regbase - register base address of mmc channel specified in 'index'.
- * bus_width - operating bus width of mmc channel specified in 'index'.
- * clksel - value to be written into CLKSEL register in case of FDT.
- * NULL in case od non-FDT.
- */
-int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
+static int exynos_dwmci_core_init(struct dwmci_host *host, int index)
{
- struct dwmci_host *host = NULL;
unsigned int div;
unsigned long freq, sclk;
- host = malloc(sizeof(struct dwmci_host));
- if (!host) {
- printf("dwmci_host malloc fail!\n");
- return 1;
- }
+
+ if (host->bus_hz)
+ freq = host->bus_hz;
+ else
+ freq = DWMMC_MAX_FREQ;
+
/* request mmc clock vlaue of 52MHz. */
- freq = 52000000;
sclk = get_mmc_clk(index);
div = DIV_ROUND_UP(sclk, freq);
/* set the clock divisor for mmc */
set_mmc_clk(index, div);
host->name = "EXYNOS DWMMC";
- host->ioaddr = (void *)regbase;
- host->buswidth = bus_width;
#ifdef CONFIG_EXYNOS5420
host->quirks = DWMCI_QUIRK_DISABLE_SMU;
#endif
host->board_init = exynos_dwmci_board_init;
- if (clksel) {
- host->clksel_val = clksel;
- } else {
- if (0 == index)
+ if (!host->clksel_val) {
+ if (index == 0)
host->clksel_val = DWMMC_MMC0_CLKSEL_VAL;
- if (2 == index)
+ else if (index == 2)
host->clksel_val = DWMMC_MMC2_CLKSEL_VAL;
}
+ host->caps = MMC_MODE_DDR_52MHz;
host->clksel = exynos_dwmci_clksel;
host->dev_index = index;
host->get_mmc_clk = exynos_dwmci_get_clk;
@@ -113,69 +107,134 @@ int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
return 0;
}
+/*
+ * This function adds the mmc channel to be registered with mmc core.
+ * index - mmc channel number.
+ * regbase - register base address of mmc channel specified in 'index'.
+ * bus_width - operating bus width of mmc channel specified in 'index'.
+ * clksel - value to be written into CLKSEL register in case of FDT.
+ * NULL in case od non-FDT.
+ */
+int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
+{
+ struct dwmci_host *host = NULL;
+
+ host = malloc(sizeof(struct dwmci_host));
+ if (!host) {
+ error("dwmci_host malloc fail!\n");
+ return -ENOMEM;
+ }
+
+ host->ioaddr = (void *)regbase;
+ host->buswidth = bus_width;
+
+ if (clksel)
+ host->clksel_val = clksel;
+
+ return exynos_dwmci_core_init(host, index);
+}
+
#ifdef CONFIG_OF_CONTROL
-int exynos_dwmmc_init(const void *blob)
+static struct dwmci_host dwmci_host[DWMMC_MAX_CH_NUM];
+
+static int do_dwmci_init(struct dwmci_host *host)
{
- int index, bus_width;
- int node_list[DWMMC_MAX_CH_NUM];
- int err = 0, dev_id, flag, count, i;
- u32 clksel_val, base, timing[3];
+ int index, flag, err;
- count = fdtdec_find_aliases_for_id(blob, "mmc",
- COMPAT_SAMSUNG_EXYNOS5_DWMMC, node_list,
- DWMMC_MAX_CH_NUM);
+ index = host->dev_index;
- for (i = 0; i < count; i++) {
- int node = node_list[i];
+ flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
+ err = exynos_pinmux_config(host->dev_id, flag);
+ if (err) {
+ debug("DWMMC not configure\n");
+ return err;
+ }
- if (node <= 0)
- continue;
+ return exynos_dwmci_core_init(host, index);
+}
- /* Extract device id for each mmc channel */
- dev_id = pinmux_decode_periph_id(blob, node);
+static int exynos_dwmci_get_config(const void *blob, int node,
+ struct dwmci_host *host)
+{
+ int err = 0;
+ u32 base, clksel_val, timing[3];
- /* Get the bus width from the device node */
- bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
- if (bus_width <= 0) {
- debug("DWMMC: Can't get bus-width\n");
- return -1;
- }
- if (8 == bus_width)
- flag = PINMUX_FLAG_8BIT_MODE;
- else
- flag = PINMUX_FLAG_NONE;
+ /* Extract device id for each mmc channel */
+ host->dev_id = pinmux_decode_periph_id(blob, node);
- /* config pinmux for each mmc channel */
- err = exynos_pinmux_config(dev_id, flag);
- if (err) {
- debug("DWMMC not configured\n");
- return err;
- }
+ /* Get the bus width from the device node */
+ host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
+ if (host->buswidth <= 0) {
+ debug("DWMMC: Can't get bus-width\n");
+ return -EINVAL;
+ }
- index = dev_id - PERIPH_ID_SDMMC0;
+ host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id);
+ if (host->dev_index == host->dev_id)
+ host->dev_index = host->dev_id - PERIPH_ID_SDMMC0;
- /* Get the base address from the device node */
- base = fdtdec_get_addr(blob, node, "reg");
- if (!base) {
- debug("DWMMC: Can't get base address\n");
- return -1;
- }
- /* Extract the timing info from the node */
- err = fdtdec_get_int_array(blob, node, "samsung,timing",
- timing, 3);
+ /* Set the base address from the device node */
+ base = fdtdec_get_addr(blob, node, "reg");
+ if (!base) {
+ debug("DWMMC: Can't get base address\n");
+ return -EINVAL;
+ }
+ host->ioaddr = (void *)base;
+
+ /* Extract the timing info from the node */
+ err = fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3);
+ if (err) {
+ debug("Can't get sdr-timings for devider\n");
+ return -EINVAL;
+ }
+
+ clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) |
+ DWMCI_SET_DRV_CLK(timing[1]) |
+ DWMCI_SET_DIV_RATIO(timing[2]));
+ if (clksel_val)
+ host->clksel_val = clksel_val;
+
+ host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0);
+ host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0);
+ host->div = fdtdec_get_int(blob, node, "div", 0);
+
+ return 0;
+}
+
+static int exynos_dwmci_process_node(const void *blob,
+ int node_list[], int count)
+{
+ struct dwmci_host *host;
+ int i, node, err;
+
+ for (i = 0; i < count; i++) {
+ node = node_list[i];
+ if (node <= 0)
+ continue;
+ host = &dwmci_host[i];
+ err = exynos_dwmci_get_config(blob, node, host);
if (err) {
- debug("Can't get sdr-timings for divider\n");
- return -1;
+ debug("%s: failed to decode dev %d\n", __func__, i);
+ return err;
}
- clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) |
- DWMCI_SET_DRV_CLK(timing[1]) |
- DWMCI_SET_DIV_RATIO(timing[2]));
- /* Initialise each mmc channel */
- err = exynos_dwmci_add_port(index, base, bus_width, clksel_val);
- if (err)
- debug("dwmmc Channel-%d init failed\n", index);
+ do_dwmci_init(host);
}
return 0;
}
+
+int exynos_dwmmc_init(const void *blob)
+{
+ int compat_id;
+ int node_list[DWMMC_MAX_CH_NUM];
+ int err = 0, count;
+
+ compat_id = COMPAT_SAMSUNG_EXYNOS_DWMMC;
+
+ count = fdtdec_find_aliases_for_id(blob, "mmc",
+ compat_id, node_list, DWMMC_MAX_CH_NUM);
+ err = exynos_dwmci_process_node(blob, node_list, count);
+
+ return err;
+}
#endif
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 8b53ead98f..55c2c68cdb 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -160,6 +160,9 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
{
struct mmc_cmd cmd;
+ if (mmc->card_caps & MMC_MODE_DDR_52MHz)
+ return 0;
+
cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = len;
@@ -516,10 +519,13 @@ static int mmc_change_freq(struct mmc *mmc)
return 0;
/* High Speed is set, there are two types: 52MHz and 26MHz */
- if (cardtype & MMC_HS_52MHZ)
+ if (cardtype & EXT_CSD_CARD_TYPE_52) {
+ if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
+ mmc->card_caps |= MMC_MODE_DDR_52MHz;
mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
- else
+ } else {
mmc->card_caps |= MMC_MODE_HS;
+ }
return 0;
}
@@ -1082,6 +1088,8 @@ static int mmc_startup(struct mmc *mmc)
/* An array of possible bus widths in order of preference */
static unsigned ext_csd_bits[] = {
+ EXT_CSD_DDR_BUS_WIDTH_8,
+ EXT_CSD_DDR_BUS_WIDTH_4,
EXT_CSD_BUS_WIDTH_8,
EXT_CSD_BUS_WIDTH_4,
EXT_CSD_BUS_WIDTH_1,
@@ -1089,13 +1097,15 @@ static int mmc_startup(struct mmc *mmc)
/* An array to map CSD bus widths to host cap bits */
static unsigned ext_to_hostcaps[] = {
+ [EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz,
+ [EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz,
[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
};
/* An array to map chosen bus width to an integer */
static unsigned widths[] = {
- 8, 4, 1,
+ 8, 4, 8, 4, 1,
};
for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
index ccae4ccae1..2ff0ec2a42 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -65,17 +65,9 @@ static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
sdhci_writel(host, ctrl, SDHCI_CONTROL2);
}
-int s5p_sdhci_init(u32 regbase, int index, int bus_width)
+static int s5p_sdhci_core_init(struct sdhci_host *host)
{
- struct sdhci_host *host = NULL;
- host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
- if (!host) {
- printf("sdhci__host malloc fail!\n");
- return 1;
- }
-
host->name = S5P_NAME;
- host->ioaddr = (void *)regbase;
host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
@@ -85,15 +77,28 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width)
host->set_control_reg = &s5p_sdhci_set_control_reg;
host->set_clock = set_mmc_clk;
- host->index = index;
host->host_caps = MMC_MODE_HC;
- if (bus_width == 8)
+ if (host->bus_width == 8)
host->host_caps |= MMC_MODE_8BIT;
return add_sdhci(host, 52000000, 400000);
}
+int s5p_sdhci_init(u32 regbase, int index, int bus_width)
+{
+ struct sdhci_host *host = malloc(sizeof(struct sdhci_host));
+ if (!host) {
+ printf("sdhci__host malloc fail!\n");
+ return 1;
+ }
+ host->ioaddr = (void *)regbase;
+ host->index = index;
+ host->bus_width = bus_width;
+
+ return s5p_sdhci_core_init(host);
+}
+
#ifdef CONFIG_OF_CONTROL
struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS];
@@ -126,20 +131,7 @@ static int do_sdhci_init(struct sdhci_host *host)
}
}
- host->name = S5P_NAME;
-
- host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
- SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
- SDHCI_QUIRK_WAIT_SEND_CMD;
- host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
- host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
-
- host->set_control_reg = &s5p_sdhci_set_control_reg;
- host->set_clock = set_mmc_clk;
-
- host->host_caps = MMC_MODE_HC;
-
- return add_sdhci(host, 52000000, 400000);
+ return s5p_sdhci_core_init(host);
}
static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)