diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-s3c.c')
| -rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 416 |
1 files changed, 211 insertions, 205 deletions
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index a50c205ea20..fa5954a0544 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> +#include <linux/platform_data/mmc-sdhci-s3c.h> #include <linux/slab.h> #include <linux/clk.h> #include <linux/io.h> @@ -27,9 +28,7 @@ #include <linux/mmc/host.h> -#include <plat/sdhci.h> -#include <plat/regs-sdhci.h> - +#include "sdhci-s3c-regs.h" #include "sdhci.h" #define MAX_BUS_CLK (4) @@ -49,12 +48,15 @@ struct sdhci_s3c { struct platform_device *pdev; struct resource *ioarea; struct s3c_sdhci_platdata *pdata; - unsigned int cur_clk; + int cur_clk; int ext_cd_irq; int ext_cd_gpio; struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; + unsigned long clk_rates[MAX_BUS_CLK]; + + bool no_divider; }; /** @@ -67,6 +69,7 @@ struct sdhci_s3c { */ struct sdhci_s3c_drv_data { unsigned int sdhci_quirks; + bool no_divider; }; static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) @@ -75,32 +78,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) } /** - * get_curclk - convert ctrl2 register to clock source number - * @ctrl2: Control2 register value. - */ -static u32 get_curclk(u32 ctrl2) -{ - ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK; - ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; - - return ctrl2; -} - -static void sdhci_s3c_check_sclk(struct sdhci_host *host) -{ - struct sdhci_s3c *ourhost = to_s3c(host); - u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2); - - if (get_curclk(tmp) != ourhost->cur_clk) { - dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n"); - - tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; - tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; - writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2); - } -} - -/** * sdhci_s3c_get_max_clk - callback to get maximum clock frequency. * @host: The SDHCI host instance. * @@ -109,20 +86,11 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host) static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host) { struct sdhci_s3c *ourhost = to_s3c(host); - struct clk *busclk; - unsigned int rate, max; - int clk; - - /* note, a reset will reset the clock source */ - - sdhci_s3c_check_sclk(host); - - for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) { - busclk = ourhost->clk_bus[clk]; - if (!busclk) - continue; + unsigned long rate, max = 0; + int src; - rate = clk_get_rate(busclk); + for (src = 0; src < MAX_BUS_CLK; src++) { + rate = ourhost->clk_rates[src]; if (rate > max) max = rate; } @@ -142,31 +110,38 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, { unsigned long rate; struct clk *clksrc = ourhost->clk_bus[src]; - int div; + int shift; - if (!clksrc) + if (IS_ERR(clksrc)) return UINT_MAX; /* * If controller uses a non-standard clock division, find the best clock * speed possible with selected clock source and skip the division. */ - if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (ourhost->no_divider) { rate = clk_round_rate(clksrc, wanted); return wanted - rate; } - rate = clk_get_rate(clksrc); + rate = ourhost->clk_rates[src]; - for (div = 1; div < 256; div *= 2) { - if ((rate / div) <= wanted) + for (shift = 0; shift <= 8; ++shift) { + if ((rate >> shift) <= wanted) break; } + if (shift > 8) { + dev_dbg(&ourhost->pdev->dev, + "clk %d: rate %ld, min rate %lu > wanted %u\n", + src, rate, rate / 256, wanted); + return UINT_MAX; + } + dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n", - src, rate, wanted, rate / div); + src, rate, wanted, rate >> shift); - return (wanted - (rate / div)); + return wanted - (rate >> shift); } /** @@ -186,9 +161,13 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) int src; u32 ctrl; + host->mmc->actual_clock = 0; + /* don't bother if the clock is going off. */ - if (clock == 0) + if (clock == 0) { + sdhci_set_clock(host, clock); return; + } for (src = 0; src < MAX_BUS_CLK; src++) { delta = sdhci_s3c_consider_clock(ourhost, src, clock); @@ -203,22 +182,26 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) best_src, clock, best); /* select the new clock source */ - if (ourhost->cur_clk != best_src) { struct clk *clk = ourhost->clk_bus[best_src]; - /* turn clock off to card before changing clock source */ - writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + clk_prepare_enable(clk); + if (ourhost->cur_clk >= 0) + clk_disable_unprepare( + ourhost->clk_bus[ourhost->cur_clk]); ourhost->cur_clk = best_src; - host->max_clk = clk_get_rate(clk); - - ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); - ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; - ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; - writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2); + host->max_clk = ourhost->clk_rates[best_src]; } + /* turn clock off to card before changing clock source */ + writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + + ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); + ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; + ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; + writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2); + /* reprogram default hardware configuration */ writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA, host->ioaddr + S3C64XX_SDHCI_CONTROL4); @@ -236,6 +219,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) if (clock < 25 * 1000000) ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2); writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3); + + sdhci_set_clock(host, clock); } /** @@ -250,17 +235,17 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host) { struct sdhci_s3c *ourhost = to_s3c(host); - unsigned int delta, min = UINT_MAX; + unsigned long rate, min = ULONG_MAX; int src; for (src = 0; src < MAX_BUS_CLK; src++) { - delta = sdhci_s3c_consider_clock(ourhost, src, 0); - if (delta == UINT_MAX) + rate = ourhost->clk_rates[src] / 256; + if (!rate) continue; - /* delta is a negative value in this case */ - if (-delta < min) - min = -delta; + if (rate < min) + min = rate; } + return min; } @@ -268,39 +253,66 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host) static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host) { struct sdhci_s3c *ourhost = to_s3c(host); + unsigned long rate, max = 0; + int src; + + for (src = 0; src < MAX_BUS_CLK; src++) { + struct clk *clk; - return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX); + clk = ourhost->clk_bus[src]; + if (IS_ERR(clk)) + continue; + + rate = clk_round_rate(clk, ULONG_MAX); + if (rate > max) + max = rate; + } + + return max; } /* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */ static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host) { struct sdhci_s3c *ourhost = to_s3c(host); + unsigned long rate, min = ULONG_MAX; + int src; - /* - * initial clock can be in the frequency range of - * 100KHz-400KHz, so we set it as max value. - */ - return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000); + for (src = 0; src < MAX_BUS_CLK; src++) { + struct clk *clk; + + clk = ourhost->clk_bus[src]; + if (IS_ERR(clk)) + continue; + + rate = clk_round_rate(clk, 0); + if (rate < min) + min = rate; + } + + return min; } /* sdhci_cmu_set_clock - callback on clock change.*/ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_s3c *ourhost = to_s3c(host); + struct device *dev = &ourhost->pdev->dev; unsigned long timeout; u16 clk = 0; - /* don't bother if the clock is going off */ - if (clock == 0) + host->mmc->actual_clock = 0; + + /* If the clock is going off, set to 0 at clock control register */ + if (clock == 0) { + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); return; + } sdhci_s3c_set_clock(host, clock); clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); - host->clock = clock; - clk = SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); @@ -309,8 +321,8 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { if (timeout == 0) { - printk(KERN_ERR "%s: Internal clock never " - "stabilised.\n", mmc_hostname(host->mmc)); + dev_err(dev, "%s: Internal clock never stabilised.\n", + mmc_hostname(host->mmc)); return; } timeout--; @@ -322,14 +334,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) } /** - * sdhci_s3c_platform_8bit_width - support 8bit buswidth + * sdhci_s3c_set_bus_width - support 8bit buswidth * @host: The SDHCI host being queried * @width: MMC_BUS_WIDTH_ macro for the bus width being requested * * We have 8-bit width support but is not a v3 controller. - * So we add platform_8bit_width() and support 8bit width. + * So we add platform_bus_width() and support 8bit width. */ -static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width) +static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -351,82 +363,72 @@ static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width) } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - - return 0; } static struct sdhci_ops sdhci_s3c_ops = { .get_max_clock = sdhci_s3c_get_max_clk, .set_clock = sdhci_s3c_set_clock, .get_min_clock = sdhci_s3c_get_min_clock, - .platform_8bit_width = sdhci_s3c_platform_8bit_width, + .set_bus_width = sdhci_s3c_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; -static void sdhci_s3c_notify_change(struct platform_device *dev, int state) +#ifdef CONFIG_OF +static int sdhci_s3c_parse_dt(struct device *dev, + struct sdhci_host *host, struct s3c_sdhci_platdata *pdata) { - struct sdhci_host *host = platform_get_drvdata(dev); - unsigned long flags; - - if (host) { - spin_lock_irqsave(&host->lock, flags); - if (state) { - dev_dbg(&dev->dev, "card inserted.\n"); - host->flags &= ~SDHCI_DEVICE_DEAD; - host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; - } else { - dev_dbg(&dev->dev, "card removed.\n"); - host->flags |= SDHCI_DEVICE_DEAD; - host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; - } - tasklet_schedule(&host->card_tasklet); - spin_unlock_irqrestore(&host->lock, flags); + struct device_node *node = dev->of_node; + u32 max_width; + + /* if the bus-width property is not specified, assume width as 1 */ + if (of_property_read_u32(node, "bus-width", &max_width)) + max_width = 1; + pdata->max_width = max_width; + + /* get the card detection method */ + if (of_get_property(node, "broken-cd", NULL)) { + pdata->cd_type = S3C_SDHCI_CD_NONE; + return 0; } -} -static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) -{ - struct sdhci_s3c *sc = dev_id; - int status = gpio_get_value(sc->ext_cd_gpio); - if (sc->pdata->ext_cd_gpio_invert) - status = !status; - sdhci_s3c_notify_change(sc->pdev, status); - return IRQ_HANDLED; -} + if (of_get_property(node, "non-removable", NULL)) { + pdata->cd_type = S3C_SDHCI_CD_PERMANENT; + return 0; + } + + if (of_get_named_gpio(node, "cd-gpios", 0)) + return 0; -static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) + /* assuming internal card detect that will be configured by pinctrl */ + pdata->cd_type = S3C_SDHCI_CD_INTERNAL; + return 0; +} +#else +static int sdhci_s3c_parse_dt(struct device *dev, + struct sdhci_host *host, struct s3c_sdhci_platdata *pdata) { - struct s3c_sdhci_platdata *pdata = sc->pdata; - struct device *dev = &sc->pdev->dev; - - if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { - sc->ext_cd_gpio = pdata->ext_cd_gpio; - sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); - if (sc->ext_cd_irq && - request_threaded_irq(sc->ext_cd_irq, NULL, - sdhci_s3c_gpio_card_detect_thread, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), sc) == 0) { - int status = gpio_get_value(sc->ext_cd_gpio); - if (pdata->ext_cd_gpio_invert) - status = !status; - sdhci_s3c_notify_change(sc->pdev, status); - } else { - dev_warn(dev, "cannot request irq for card detect\n"); - sc->ext_cd_irq = 0; - } - } else { - dev_err(dev, "cannot request gpio for card detect\n"); - } + return -EINVAL; } +#endif + +static const struct of_device_id sdhci_s3c_dt_match[]; static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data( struct platform_device *pdev) { +#ifdef CONFIG_OF + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(sdhci_s3c_dt_match, pdev->dev.of_node); + return (struct sdhci_s3c_drv_data *)match->data; + } +#endif return (struct sdhci_s3c_drv_data *) platform_get_device_id(pdev)->driver_data; } -static int __devinit sdhci_s3c_probe(struct platform_device *pdev) +static int sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata; struct sdhci_s3c_drv_data *drv_data; @@ -436,7 +438,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) struct resource *res; int ret, irq, ptr, clks; - if (!pdev->dev.platform_data) { + if (!pdev->dev.platform_data && !pdev->dev.of_node) { dev_err(dev, "no device data specified\n"); return -ENOENT; } @@ -452,57 +454,55 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } + sc = sdhci_priv(host); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { ret = -ENOMEM; - goto err_io_clk; + goto err_pdata_io_clk; + } + + if (pdev->dev.of_node) { + ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); + if (ret) + goto err_pdata_io_clk; + } else { + memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); + sc->ext_cd_gpio = -1; /* invalid gpio number */ } - memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); drv_data = sdhci_s3c_get_driver_data(pdev); - sc = sdhci_priv(host); sc->host = host; sc->pdev = pdev; sc->pdata = pdata; - sc->ext_cd_gpio = -1; /* invalid gpio number */ + sc->cur_clk = -1; platform_set_drvdata(pdev, host); - sc->clk_io = clk_get(dev, "hsmmc"); + sc->clk_io = devm_clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(sc->clk_io); - goto err_io_clk; + goto err_pdata_io_clk; } /* enable the local io clock and keep it running for the moment. */ - clk_enable(sc->clk_io); + clk_prepare_enable(sc->clk_io); for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { - struct clk *clk; char name[14]; snprintf(name, 14, "mmc_busclk.%d", ptr); - clk = clk_get(dev, name); - if (IS_ERR(clk)) { + sc->clk_bus[ptr] = devm_clk_get(dev, name); + if (IS_ERR(sc->clk_bus[ptr])) continue; - } clks++; - sc->clk_bus[ptr] = clk; - - /* - * save current clock index to know which clock bus - * is used later in overriding functions. - */ - sc->cur_clk = ptr; - - clk_enable(clk); + sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]); dev_info(dev, "clock source %d: %s (%ld Hz)\n", - ptr, name, clk_get_rate(clk)); + ptr, name, sc->clk_rates[ptr]); } if (clks == 0) { @@ -512,10 +512,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->ioaddr = devm_request_and_ioremap(&pdev->dev, res); - if (!host->ioaddr) { - dev_err(dev, "failed to map registers\n"); - ret = -ENXIO; + host->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->ioaddr)) { + ret = PTR_ERR(host->ioaddr); goto err_req_regs; } @@ -526,13 +525,16 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; + host->quirks2 = 0; host->irq = irq; /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; - if (drv_data) + if (drv_data) { host->quirks |= drv_data->sdhci_quirks; + sc->no_divider = drv_data->no_divider; + } #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -581,7 +583,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (sc->no_divider) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; @@ -599,6 +601,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); + mmc_of_parse(host->mmc); + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); @@ -607,66 +611,42 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) goto err_req_regs; } - /* The following two methods of card detection might call - sdhci_s3c_notify_change() immediately, so they can be called - only after sdhci_add_host(). Setup errors are ignored. */ - if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) - pdata->ext_cd_init(&sdhci_s3c_notify_change); - if (pdata->cd_type == S3C_SDHCI_CD_GPIO && - gpio_is_valid(pdata->ext_cd_gpio)) - sdhci_s3c_setup_card_detect_gpio(sc); - +#ifdef CONFIG_PM_RUNTIME + if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) + clk_disable_unprepare(sc->clk_io); +#endif return 0; err_req_regs: - for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { - if (sc->clk_bus[ptr]) { - clk_disable(sc->clk_bus[ptr]); - clk_put(sc->clk_bus[ptr]); - } - } - err_no_busclks: - clk_disable(sc->clk_io); - clk_put(sc->clk_io); + clk_disable_unprepare(sc->clk_io); - err_io_clk: + err_pdata_io_clk: sdhci_free_host(host); return ret; } -static int __devexit sdhci_s3c_remove(struct platform_device *pdev) +static int sdhci_s3c_remove(struct platform_device *pdev) { - struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_s3c *sc = sdhci_priv(host); - int ptr; - - if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) - pdata->ext_cd_cleanup(&sdhci_s3c_notify_change); if (sc->ext_cd_irq) free_irq(sc->ext_cd_irq, sc); - if (gpio_is_valid(sc->ext_cd_gpio)) - gpio_free(sc->ext_cd_gpio); - +#ifdef CONFIG_PM_RUNTIME + if (sc->pdata->cd_type != S3C_SDHCI_CD_INTERNAL) + clk_prepare_enable(sc->clk_io); +#endif sdhci_remove_host(host, 1); + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); - for (ptr = 0; ptr < 3; ptr++) { - if (sc->clk_bus[ptr]) { - clk_disable(sc->clk_bus[ptr]); - clk_put(sc->clk_bus[ptr]); - } - } - clk_disable(sc->clk_io); - clk_put(sc->clk_io); + clk_disable_unprepare(sc->clk_io); sdhci_free_host(host); - platform_set_drvdata(pdev, NULL); return 0; } @@ -691,15 +671,30 @@ static int sdhci_s3c_resume(struct device *dev) static int sdhci_s3c_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_s3c *ourhost = to_s3c(host); + struct clk *busclk = ourhost->clk_io; + int ret; + + ret = sdhci_runtime_suspend_host(host); - return sdhci_runtime_suspend_host(host); + if (ourhost->cur_clk >= 0) + clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); + clk_disable_unprepare(busclk); + return ret; } static int sdhci_s3c_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_s3c *ourhost = to_s3c(host); + struct clk *busclk = ourhost->clk_io; + int ret; - return sdhci_runtime_resume_host(host); + clk_prepare_enable(busclk); + if (ourhost->cur_clk >= 0) + clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]); + ret = sdhci_runtime_resume_host(host); + return ret; } #endif @@ -718,7 +713,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = { #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { - .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, + .no_divider = true, }; #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data) #else @@ -737,13 +732,24 @@ static struct platform_device_id sdhci_s3c_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids); +#ifdef CONFIG_OF +static const struct of_device_id sdhci_s3c_dt_match[] = { + { .compatible = "samsung,s3c6410-sdhci", }, + { .compatible = "samsung,exynos4210-sdhci", + .data = (void *)EXYNOS4_SDHCI_DRV_DATA }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match); +#endif + static struct platform_driver sdhci_s3c_driver = { .probe = sdhci_s3c_probe, - .remove = __devexit_p(sdhci_s3c_remove), + .remove = sdhci_s3c_remove, .id_table = sdhci_s3c_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-sdhci", + .of_match_table = of_match_ptr(sdhci_s3c_dt_match), .pm = SDHCI_S3C_PMOPS, }, }; |
