diff options
Diffstat (limited to 'arch/arm/mach-s5pv210/setup-sdhci.c')
-rw-r--r-- | arch/arm/mach-s5pv210/setup-sdhci.c | 226 |
1 files changed, 213 insertions, 13 deletions
diff --git a/arch/arm/mach-s5pv210/setup-sdhci.c b/arch/arm/mach-s5pv210/setup-sdhci.c index a83b6c9..04ec6af 100644 --- a/arch/arm/mach-s5pv210/setup-sdhci.c +++ b/arch/arm/mach-s5pv210/setup-sdhci.c @@ -15,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/irq.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> @@ -22,6 +23,13 @@ #include <plat/regs-sdhci.h> #include <plat/sdhci.h> +#include <plat/gpio-cfg.h> +#include <plat/devs.h> +#include <mach/regs-gpio.h> +#include <mach/gpio.h> + +#include "herring.h" + /* clock sources for the mmc bus clock, order as for the ctrl2[5..4] */ char *s5pv210_hsmmc_clksrcs[4] = { @@ -31,33 +39,225 @@ char *s5pv210_hsmmc_clksrcs[4] = { /* [3] = NULL, - reserved */ }; +#define S3C_SDHCI_CTRL3_FCSELTX_INVERT (0) +#define S3C_SDHCI_CTRL3_FCSELTX_BASIC \ + (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2) +#define S3C_SDHCI_CTRL3_FCSELRX_INVERT (0) +#define S3C_SDHCI_CTRL3_FCSELRX_BASIC \ + (S3C_SDHCI_CTRL3_FCSEL1 | S3C_SDHCI_CTRL3_FCSEL0) + void s5pv210_setup_sdhci_cfg_card(struct platform_device *dev, void __iomem *r, struct mmc_ios *ios, struct mmc_card *card) { - u32 ctrl2, ctrl3; - - /* don't need to alter anything according to card-type */ - - writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA, r + S3C64XX_SDHCI_CONTROL4); + u32 ctrl2; + u32 ctrl3; ctrl2 = readl(r + S3C_SDHCI_CONTROL2); ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK; ctrl2 |= (S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR | S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK | - S3C_SDHCI_CTRL2_ENFBCLKRX | S3C_SDHCI_CTRL2_DFCNT_NONE | S3C_SDHCI_CTRL2_ENCLKOUTHOLD); - if (ios->clock < 25 * 1000000) - ctrl3 = (S3C_SDHCI_CTRL3_FCSEL3 | - S3C_SDHCI_CTRL3_FCSEL2 | - S3C_SDHCI_CTRL3_FCSEL1 | - S3C_SDHCI_CTRL3_FCSEL0); - else - ctrl3 = (S3C_SDHCI_CTRL3_FCSEL1 | S3C_SDHCI_CTRL3_FCSEL0); + if (ios->clock <= (400 * 1000)) { + ctrl2 &= ~(S3C_SDHCI_CTRL2_ENFBCLKTX | + S3C_SDHCI_CTRL2_ENFBCLKRX); + ctrl3 = 0; + } else { + u32 range_start; + u32 range_end; + + ctrl2 |= S3C_SDHCI_CTRL2_ENFBCLKTX | + S3C_SDHCI_CTRL2_ENFBCLKRX; + + if (card->type == MMC_TYPE_MMC) /* MMC */ + range_start = 20 * 1000 * 1000; + //else /* SD, SDIO */ + // range_start = 25 * 1000 * 1000; + + range_end = 37 * 1000 * 1000; + + if ((ios->clock > range_start) && (ios->clock < range_end)) + ctrl3 = S3C_SDHCI_CTRL3_FCSELTX_BASIC | + S3C_SDHCI_CTRL3_FCSELRX_BASIC; + else if (machine_is_herring() && herring_is_cdma_wimax_dev() && + dev->id == 2) { + ctrl3 = S3C_SDHCI_CTRL3_FCSELTX_BASIC; + //if(card->type & MMC_TYPE_SDIO) + ctrl3 |= S3C_SDHCI_CTRL3_FCSELRX_BASIC; + //else + // ctrl3 |= S3C_SDHCI_CTRL3_FCSELRX_INVERT; + } else + ctrl3 = S3C_SDHCI_CTRL3_FCSELTX_BASIC | + S3C_SDHCI_CTRL3_FCSELRX_INVERT; + } + + + writel(ctrl2, r + S3C_SDHCI_CONTROL2); + writel(ctrl3, r + S3C_SDHCI_CONTROL3); +} + +void s5pv210_adjust_sdhci_cfg_card(struct s3c_sdhci_platdata *pdata, + void __iomem *r, int rw) +{ + u32 ctrl2, ctrl3; + + ctrl2 = readl(r + S3C_SDHCI_CONTROL2); + ctrl3 = readl(r + S3C_SDHCI_CONTROL3); + + if (rw == 0) { + pdata->rx_cfg++; + if (pdata->rx_cfg == 1) { + ctrl2 |= S3C_SDHCI_CTRL2_ENFBCLKRX; + ctrl3 |= S3C_SDHCI_CTRL3_FCSELRX_BASIC; + } else if (pdata->rx_cfg == 2) { + ctrl2 |= S3C_SDHCI_CTRL2_ENFBCLKRX; + ctrl3 &= ~S3C_SDHCI_CTRL3_FCSELRX_BASIC; + } else if (pdata->rx_cfg == 3) { + ctrl2 &= ~(S3C_SDHCI_CTRL2_ENFBCLKTX | + S3C_SDHCI_CTRL2_ENFBCLKRX); + pdata->rx_cfg = 0; + } + } else if (rw == 1) { + pdata->tx_cfg++; + if (pdata->tx_cfg == 1) { + if (ctrl2 & S3C_SDHCI_CTRL2_ENFBCLKRX) { + ctrl2 |= S3C_SDHCI_CTRL2_ENFBCLKTX; + ctrl3 |= S3C_SDHCI_CTRL3_FCSELTX_BASIC; + } else { + ctrl2 &= ~S3C_SDHCI_CTRL2_ENFBCLKTX; + } + } else if (pdata->tx_cfg == 2) { + ctrl2 &= ~S3C_SDHCI_CTRL2_ENFBCLKTX; + pdata->tx_cfg = 0; + } + } else { + printk(KERN_ERR "%s, unknown value rw:%d\n", __func__, rw); + return; + } writel(ctrl2, r + S3C_SDHCI_CONTROL2); writel(ctrl3, r + S3C_SDHCI_CONTROL3); } + +void universal_sdhci2_cfg_ext_cd(void) +{ + printk(KERN_DEBUG "Universal :SD Detect configuration\n"); + s3c_gpio_setpull(S5PV210_GPH3(4), S3C_GPIO_PULL_NONE); + irq_set_irq_type(IRQ_EINT(28), IRQ_TYPE_EDGE_BOTH); +} + +static struct s3c_sdhci_platdata hsmmc0_platdata = { +#if defined(CONFIG_S5PV210_SD_CH0_8BIT) + .max_width = 8, + .host_caps = MMC_CAP_8_BIT_DATA, +#endif +}; + +#if defined(CONFIG_S3C_DEV_HSMMC2) +static struct s3c_sdhci_platdata hsmmc2_platdata = { +#if defined(CONFIG_S5PV210_SD_CH2_8BIT) + .max_width = 8, + .host_caps = MMC_CAP_8_BIT_DATA, +#endif +}; +#endif + +#if defined(CONFIG_S3C_DEV_HSMMC3) +static struct s3c_sdhci_platdata hsmmc3_platdata = { 0 }; +#endif + +static DEFINE_MUTEX(notify_lock); + +#define DEFINE_MMC_CARD_NOTIFIER(num) \ +static void (*hsmmc##num##_notify_func)(struct platform_device *, int state); \ +static int ext_cd_init_hsmmc##num(void (*notify_func)( \ + struct platform_device *, int state)) \ +{ \ + mutex_lock(¬ify_lock); \ + WARN_ON(hsmmc##num##_notify_func); \ + hsmmc##num##_notify_func = notify_func; \ + mutex_unlock(¬ify_lock); \ + return 0; \ +} \ +static int ext_cd_cleanup_hsmmc##num(void (*notify_func)( \ + struct platform_device *, int state)) \ +{ \ + mutex_lock(¬ify_lock); \ + WARN_ON(hsmmc##num##_notify_func != notify_func); \ + hsmmc##num##_notify_func = NULL; \ + mutex_unlock(¬ify_lock); \ + return 0; \ +} + +#ifdef CONFIG_S3C_DEV_HSMMC2 +DEFINE_MMC_CARD_NOTIFIER(2) +#endif +#ifdef CONFIG_S3C_DEV_HSMMC3 +DEFINE_MMC_CARD_NOTIFIER(3) +#endif + +/* + * call this when you need sd stack to recognize insertion or removal of card + * that can't be told by SDHCI regs + */ +void sdhci_s3c_force_presence_change(struct platform_device *pdev) +{ + void (*notify_func)(struct platform_device *, int state) = NULL; + mutex_lock(¬ify_lock); +#ifdef CONFIG_S3C_DEV_HSMMC2 + if (pdev == &s3c_device_hsmmc2) + notify_func = hsmmc2_notify_func; +#endif +#ifdef CONFIG_S3C_DEV_HSMMC3 + if (pdev == &s3c_device_hsmmc3) + notify_func = hsmmc3_notify_func; +#endif + + if (notify_func) + notify_func(pdev, 1); + else + pr_warn("%s: called for device with no notifier\n", __func__); + mutex_unlock(¬ify_lock); +} +EXPORT_SYMBOL_GPL(sdhci_s3c_force_presence_change); + +void s3c_sdhci_set_platdata(void) +{ +#if defined(CONFIG_S3C_DEV_HSMMC) + if (machine_is_herring()) { /* TODO: move to mach-herring.c */ + hsmmc0_platdata.cd_type = S3C_SDHCI_CD_PERMANENT; + } + s3c_sdhci0_set_platdata(&hsmmc0_platdata); +#endif +#if defined(CONFIG_S3C_DEV_HSMMC2) + if (machine_is_herring()) { + if (herring_is_cdma_wimax_dev()) { + hsmmc2_platdata.cd_type = S3C_SDHCI_CD_EXTERNAL; + hsmmc2_platdata.ext_cd_init = ext_cd_init_hsmmc2; + hsmmc2_platdata.ext_cd_cleanup = ext_cd_cleanup_hsmmc2; + hsmmc2_platdata.built_in = 1; + hsmmc2_platdata.must_maintain_clock = 1; + hsmmc2_platdata.enable_intr_on_resume = 1; + } else { + hsmmc2_platdata.cd_type = S3C_SDHCI_CD_GPIO; + hsmmc2_platdata.ext_cd_gpio = S5PV210_GPH3(4); + hsmmc2_platdata.ext_cd_gpio_invert = true; + universal_sdhci2_cfg_ext_cd(); + } + } + + s3c_sdhci2_set_platdata(&hsmmc2_platdata); +#endif +#if defined(CONFIG_S3C_DEV_HSMMC3) + if (machine_is_herring()) { + hsmmc3_platdata.cd_type = S3C_SDHCI_CD_EXTERNAL; + hsmmc3_platdata.ext_cd_init = ext_cd_init_hsmmc3; + hsmmc3_platdata.ext_cd_cleanup = ext_cd_cleanup_hsmmc3; + hsmmc3_platdata.built_in = 1; + } + s3c_sdhci3_set_platdata(&hsmmc3_platdata); +#endif +}; |