diff options
Diffstat (limited to 'arch/arm/mach-exynos/dev-dwmci.c')
-rw-r--r-- | arch/arm/mach-exynos/dev-dwmci.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/dev-dwmci.c b/arch/arm/mach-exynos/dev-dwmci.c new file mode 100644 index 0000000..8db1907 --- /dev/null +++ b/arch/arm/mach-exynos/dev-dwmci.c @@ -0,0 +1,239 @@ +/* + * linux/arch/arm/mach-exynos/dev-dwmci.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Platform device for Synopsys DesignWare Mobile Storage IP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/mmc/dw_mmc.h> +#include <linux/mmc/host.h> + +#include <plat/devs.h> +#include <plat/cpu.h> + +#include <mach/map.h> + +#define DWMCI_CLKSEL 0x09c + +static int exynos_dwmci_get_bus_wd(u32 slot_id) +{ + return 4; +} + +static int exynos_dwmci_init(u32 slot_id, irq_handler_t handler, void *data) +{ + struct dw_mci *host = (struct dw_mci *)data; + + /* Set Phase Shift Register */ + if (soc_is_exynos4210()) { + host->pdata->sdr_timing = 0x00010001; + host->pdata->ddr_timing = 0x00020002; + } else if (soc_is_exynos4212() || soc_is_exynos4412()) { + host->pdata->sdr_timing = 0x00010001; + host->pdata->ddr_timing = 0x00010002; + } else if (soc_is_exynos5250()) { + if (samsung_rev() >= EXYNOS5250_REV_1_0) { + switch (host->pdev->id) { + case 0: + host->pdata->sdr_timing = 0x03020001; + host->pdata->ddr_timing = 0x03030002; + break; + case 1: + host->pdata->sdr_timing = 0x03020001; + host->pdata->ddr_timing = 0x03030002; + break; + case 2: + host->pdata->sdr_timing = 0x03020001; + host->pdata->ddr_timing = 0x03030002; + break; + case 3: + host->pdata->sdr_timing = 0x03020001; + host->pdata->ddr_timing = 0x03030002; + break; + default: + host->pdata->sdr_timing = 0x03020001; + host->pdata->ddr_timing = 0x03030002; + break; + } + } else { + host->pdata->sdr_timing = 0x00010000; + host->pdata->ddr_timing = 0x00010000; + } + } +#ifdef CONFIG_SLP + host->pdata->sdr_timing = 0x00020001; + host->pdata->ddr_timing = 0x00020002; +#endif + + return 0; +} + +static void exynos_dwmci_set_io_timing(void *data, unsigned char timing) +{ + struct dw_mci *host = (struct dw_mci *)data; + + if (timing == MMC_TIMING_UHS_DDR50) + __raw_writel(host->pdata->ddr_timing, + host->regs + DWMCI_CLKSEL); + else + __raw_writel(host->pdata->sdr_timing, + host->regs + DWMCI_CLKSEL); +} + +static struct resource exynos_dwmci_resource[] = { + [0] = { + .start = EXYNOS_PA_DWMCI, + .end = EXYNOS_PA_DWMCI + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_DWMCI, + .end = IRQ_DWMCI, + .flags = IORESOURCE_IRQ, + } +}; + +static struct dw_mci_board exynos_dwmci_def_platdata = { + .num_slots = 1, + .quirks = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, + .bus_hz = 80 * 1000 * 1000, + .detect_delay_ms = 200, + .init = exynos_dwmci_init, + .get_bus_wd = exynos_dwmci_get_bus_wd, + .set_io_timing = exynos_dwmci_set_io_timing, +}; + +static u64 exynos_dwmci_dmamask = DMA_BIT_MASK(32); + +struct platform_device exynos_device_dwmci = { + .name = "dw_mmc", + .id = -1, + .num_resources = ARRAY_SIZE(exynos_dwmci_resource), + .resource = exynos_dwmci_resource, + .dev = { + .dma_mask = &exynos_dwmci_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &exynos_dwmci_def_platdata, + }, +}; + +#define EXYNOS5_DWMCI_RESOURCE(_channel) \ +static struct resource exynos5_dwmci##_channel##_resource[] = { \ + [0] = { \ + .start = S3C_PA_HSMMC##_channel, \ + .end = S3C_PA_HSMMC##_channel + SZ_4K - 1, \ + .flags = IORESOURCE_MEM, \ + }, \ + [1] = { \ + .start = IRQ_HSMMC##_channel, \ + .end = IRQ_HSMMC##_channel, \ + .flags = IORESOURCE_IRQ, \ + } \ +}; + +EXYNOS5_DWMCI_RESOURCE(0) +EXYNOS5_DWMCI_RESOURCE(1) +EXYNOS5_DWMCI_RESOURCE(2) +EXYNOS5_DWMCI_RESOURCE(3) + +#define EXYNOS_DWMCI_DEF_PLATDATA(_channel) \ +struct dw_mci_board exynos_dwmci##_channel##_def_platdata = { \ + .num_slots = 1, \ + .quirks = \ + DW_MCI_QUIRK_BROKEN_CARD_DETECTION, \ + .bus_hz = 200 * 1000 * 1000, \ + .detect_delay_ms = 200, \ + .init = exynos_dwmci_init, \ + .get_bus_wd = exynos_dwmci_get_bus_wd, \ + .set_io_timing = exynos_dwmci_set_io_timing, \ + .cd_type = DW_MCI_CD_INTERNAL \ +}; + +EXYNOS_DWMCI_DEF_PLATDATA(0) +EXYNOS_DWMCI_DEF_PLATDATA(1) +EXYNOS_DWMCI_DEF_PLATDATA(2) +EXYNOS_DWMCI_DEF_PLATDATA(3) + +#define EXYNOS_DWMCI_PLATFORM_DEVICE(_channel) \ +struct platform_device exynos_device_dwmci##_channel = \ +{ \ + .name = "dw_mmc", \ + .id = _channel, \ + .num_resources = \ + ARRAY_SIZE(exynos5_dwmci##_channel##_resource), \ + .resource = exynos5_dwmci##_channel##_resource, \ + .dev = { \ + .dma_mask = &exynos_dwmci_dmamask,\ + .coherent_dma_mask = DMA_BIT_MASK(32), \ + .platform_data = \ + &exynos_dwmci##_channel##_def_platdata, \ + }, \ +}; + +EXYNOS_DWMCI_PLATFORM_DEVICE(0) +EXYNOS_DWMCI_PLATFORM_DEVICE(1) +EXYNOS_DWMCI_PLATFORM_DEVICE(2) +EXYNOS_DWMCI_PLATFORM_DEVICE(3) + +void __init exynos_dwmci_set_platdata(struct dw_mci_board *pd, u32 slot_id) +{ + struct dw_mci_board *npd = NULL; + + if ((soc_is_exynos4210()) || + soc_is_exynos4212() || soc_is_exynos4412()) { + npd = s3c_set_platdata(pd, sizeof(struct dw_mci_board), + &exynos_device_dwmci); + } else if (soc_is_exynos5250()) { + if (slot_id == 0) { + if (samsung_rev() < EXYNOS5250_REV_1_0) { + exynos_device_dwmci0.resource[0].start = + EXYNOS_PA_DWMCI; + exynos_device_dwmci0.resource[0].end = + EXYNOS_PA_DWMCI + SZ_4K - 1; + exynos_device_dwmci0.resource[1].start = + IRQ_DWMCI; + exynos_device_dwmci0.resource[1].end = + IRQ_DWMCI; + } + npd = s3c_set_platdata(pd, sizeof(struct dw_mci_board), + &exynos_device_dwmci0); + } else if (slot_id == 1) { + npd = s3c_set_platdata(pd, sizeof(struct dw_mci_board), + &exynos_device_dwmci1); + } else if (slot_id == 2) { + npd = s3c_set_platdata(pd, sizeof(struct dw_mci_board), + &exynos_device_dwmci2); + } else if (slot_id == 3) { + npd = s3c_set_platdata(pd, sizeof(struct dw_mci_board), + &exynos_device_dwmci3); + } else { + pr_err("This channel %d Cannot support.\n", slot_id); + } + } else { + printk("dwmci platform data support only exynos4/5!\n"); +#ifdef CONFIG_SLP + npd = s3c_set_platdata(pd, sizeof(struct dw_mci_board), + &exynos_device_dwmci); +#endif + } + + if (npd) { + if (!npd->init) + npd->init = exynos_dwmci_init; + if (!npd->get_bus_wd) + npd->get_bus_wd = exynos_dwmci_get_bus_wd; + if (!npd->set_io_timing) + npd->set_io_timing = exynos_dwmci_set_io_timing; + } +} |