summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorAmar <amarendra.xt@samsung.com>2013-04-27 11:42:55 +0530
committerMinkyu Kang <mk7.kang@samsung.com>2013-06-13 17:35:14 +0900
commita082a2dde06142bb599551ed3dad324923a130e0 (patch)
tree7376dc7eb064ad614f16ee9f3cc19d2f4564f24c /drivers/mmc
parent9c50e35ff2f8ead8df5781988927b51ea03c6192 (diff)
downloadu-boot-imx-a082a2dde06142bb599551ed3dad324923a130e0.zip
u-boot-imx-a082a2dde06142bb599551ed3dad324923a130e0.tar.gz
u-boot-imx-a082a2dde06142bb599551ed3dad324923a130e0.tar.bz2
EXYNOS5: DWMMC: Added FDT support for DWMMC
This patch adds FDT support for DWMMC, by reading the DWMMC node data from the device tree and initialising DWMMC channels as per data obtained from the node. Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com> Signed-off-by: Amar <amarendra.xt@samsung.com> Acked-by: Simon Glass <sjg@chromium.org> Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/exynos_dw_mmc.c127
1 files changed, 117 insertions, 10 deletions
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 72a31b7..4238dd9 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -19,39 +19,146 @@
*/
#include <common.h>
-#include <malloc.h>
#include <dwmmc.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <malloc.h>
#include <asm/arch/dwmmc.h>
#include <asm/arch/clk.h>
+#include <asm/arch/pinmux.h>
-static char *EXYNOS_NAME = "EXYNOS DWMMC";
+#define DWMMC_MAX_CH_NUM 4
+#define DWMMC_MAX_FREQ 52000000
+#define DWMMC_MIN_FREQ 400000
+#define DWMMC_MMC0_CLKSEL_VAL 0x03030001
+#define DWMMC_MMC2_CLKSEL_VAL 0x03020001
+/*
+ * Function used as callback function to initialise the
+ * CLKSEL register for every mmc channel.
+ */
static void exynos_dwmci_clksel(struct dwmci_host *host)
{
- u32 val;
- val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
- DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(0);
+ dwmci_writel(host, DWMCI_CLKSEL, host->clksel_val);
+}
- dwmci_writel(host, DWMCI_CLKSEL, val);
+unsigned int exynos_dwmci_get_clk(int dev_index)
+{
+ return get_mmc_clk(dev_index);
}
-int exynos_dwmci_init(u32 regbase, int bus_width, int index)
+/*
+ * 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;
+ unsigned int div;
+ unsigned long freq, sclk;
host = malloc(sizeof(struct dwmci_host));
if (!host) {
printf("dwmci_host malloc fail!\n");
return 1;
}
+ /* 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_NAME;
+ host->name = "EXYNOS DWMMC";
host->ioaddr = (void *)regbase;
host->buswidth = bus_width;
+
+ if (clksel) {
+ host->clksel_val = clksel;
+ } else {
+ if (0 == index)
+ host->clksel_val = DWMMC_MMC0_CLKSEL_VAL;
+ if (2 == index)
+ host->clksel_val = DWMMC_MMC2_CLKSEL_VAL;
+ }
+
host->clksel = exynos_dwmci_clksel;
host->dev_index = index;
+ host->mmc_clk = exynos_dwmci_get_clk;
+ /* Add the mmc channel to be registered with mmc core */
+ if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) {
+ debug("dwmmc%d registration failed\n", index);
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_OF_CONTROL
+int exynos_dwmmc_init(const void *blob)
+{
+ 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];
+
+ count = fdtdec_find_aliases_for_id(blob, "mmc",
+ COMPAT_SAMSUNG_EXYNOS5_DWMMC, node_list,
+ DWMMC_MAX_CH_NUM);
+
+ for (i = 0; i < count; i++) {
+ int node = node_list[i];
+
+ if (node <= 0)
+ continue;
- add_dwmci(host, 52000000, 400000);
+ /* Extract device id for each mmc channel */
+ dev_id = pinmux_decode_periph_id(blob, node);
+ /* 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;
+
+ /* config pinmux for each mmc channel */
+ err = exynos_pinmux_config(dev_id, flag);
+ if (err) {
+ debug("DWMMC not configured\n");
+ return err;
+ }
+
+ index = 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);
+ if (err) {
+ debug("Can't get sdr-timings for divider\n");
+ return -1;
+ }
+
+ 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);
+ }
return 0;
}
-
+#endif