diff options
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 320 |
1 files changed, 141 insertions, 179 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6bd3d1794966..e010ea4eb6f5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -38,11 +38,6 @@ #define DBG(f, x...) \ pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) -#if defined(CONFIG_LEDS_CLASS) || (defined(CONFIG_LEDS_CLASS_MODULE) && \ - defined(CONFIG_MMC_SDHCI_MODULE)) -#define SDHCI_USE_LEDS_CLASS -#endif - #define MAX_TUNING_LOOP 40 static unsigned int debug_quirks = 0; @@ -53,29 +48,7 @@ static void sdhci_finish_data(struct sdhci_host *); static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); -static int sdhci_do_get_cd(struct sdhci_host *host); - -#ifdef CONFIG_PM -static int sdhci_runtime_pm_get(struct sdhci_host *host); -static int sdhci_runtime_pm_put(struct sdhci_host *host); -static void sdhci_runtime_pm_bus_on(struct sdhci_host *host); -static void sdhci_runtime_pm_bus_off(struct sdhci_host *host); -#else -static inline int sdhci_runtime_pm_get(struct sdhci_host *host) -{ - return 0; -} -static inline int sdhci_runtime_pm_put(struct sdhci_host *host) -{ - return 0; -} -static void sdhci_runtime_pm_bus_on(struct sdhci_host *host) -{ -} -static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) -{ -} -#endif +static int sdhci_get_cd(struct mmc_host *mmc); static void sdhci_dumpregs(struct sdhci_host *host) { @@ -171,6 +144,22 @@ static void sdhci_disable_card_detection(struct sdhci_host *host) sdhci_set_card_detection(host, false); } +static void sdhci_runtime_pm_bus_on(struct sdhci_host *host) +{ + if (host->bus_on) + return; + host->bus_on = true; + pm_runtime_get_noresume(host->mmc->parent); +} + +static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) +{ + if (!host->bus_on) + return; + host->bus_on = false; + pm_runtime_put_noidle(host->mmc->parent); +} + void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; @@ -204,7 +193,7 @@ EXPORT_SYMBOL_GPL(sdhci_reset); static void sdhci_do_reset(struct sdhci_host *host, u8 mask) { if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!sdhci_do_get_cd(host)) + if (!sdhci_get_cd(host->mmc)) return; } @@ -252,7 +241,7 @@ static void sdhci_reinit(struct sdhci_host *host) sdhci_enable_card_detection(host); } -static void sdhci_activate_led(struct sdhci_host *host) +static void __sdhci_led_activate(struct sdhci_host *host) { u8 ctrl; @@ -261,7 +250,7 @@ static void sdhci_activate_led(struct sdhci_host *host) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } -static void sdhci_deactivate_led(struct sdhci_host *host) +static void __sdhci_led_deactivate(struct sdhci_host *host) { u8 ctrl; @@ -270,9 +259,9 @@ static void sdhci_deactivate_led(struct sdhci_host *host) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } -#ifdef SDHCI_USE_LEDS_CLASS +#if IS_REACHABLE(CONFIG_LEDS_CLASS) static void sdhci_led_control(struct led_classdev *led, - enum led_brightness brightness) + enum led_brightness brightness) { struct sdhci_host *host = container_of(led, struct sdhci_host, led); unsigned long flags; @@ -283,12 +272,62 @@ static void sdhci_led_control(struct led_classdev *led, goto out; if (brightness == LED_OFF) - sdhci_deactivate_led(host); + __sdhci_led_deactivate(host); else - sdhci_activate_led(host); + __sdhci_led_activate(host); out: spin_unlock_irqrestore(&host->lock, flags); } + +static int sdhci_led_register(struct sdhci_host *host) +{ + struct mmc_host *mmc = host->mmc; + + snprintf(host->led_name, sizeof(host->led_name), + "%s::", mmc_hostname(mmc)); + + host->led.name = host->led_name; + host->led.brightness = LED_OFF; + host->led.default_trigger = mmc_hostname(mmc); + host->led.brightness_set = sdhci_led_control; + + return led_classdev_register(mmc_dev(mmc), &host->led); +} + +static void sdhci_led_unregister(struct sdhci_host *host) +{ + led_classdev_unregister(&host->led); +} + +static inline void sdhci_led_activate(struct sdhci_host *host) +{ +} + +static inline void sdhci_led_deactivate(struct sdhci_host *host) +{ +} + +#else + +static inline int sdhci_led_register(struct sdhci_host *host) +{ + return 0; +} + +static inline void sdhci_led_unregister(struct sdhci_host *host) +{ +} + +static inline void sdhci_led_activate(struct sdhci_host *host) +{ + __sdhci_led_activate(host); +} + +static inline void sdhci_led_deactivate(struct sdhci_host *host) +{ + __sdhci_led_deactivate(host); +} + #endif /*****************************************************************************\ @@ -1091,23 +1130,14 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) return preset; } -void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock, + unsigned int *actual_clock) { int div = 0; /* Initialized for compiler warning */ int real_div = div, clk_mul = 1; u16 clk = 0; - unsigned long timeout; bool switch_base_clk = false; - host->mmc->actual_clock = 0; - - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); - if (host->quirks2 & SDHCI_QUIRK2_NEED_DELAY_AFTER_INT_CLK_RST) - mdelay(1); - - if (clock == 0) - return; - if (host->version >= SDHCI_SPEC_300) { if (host->preset_enabled) { u16 pre_val; @@ -1184,10 +1214,29 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clock_set: if (real_div) - host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; + *actual_clock = (host->max_clk * clk_mul) / real_div; clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; + + return clk; +} +EXPORT_SYMBOL_GPL(sdhci_calc_clk); + +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + unsigned long timeout; + + host->mmc->actual_clock = 0; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return; + + clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); + clk |= SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); @@ -1319,8 +1368,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host = mmc_priv(mmc); - sdhci_runtime_pm_get(host); - /* Firstly check card presence */ present = mmc->ops->get_cd(mmc); @@ -1328,9 +1375,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq != NULL); -#ifndef SDHCI_USE_LEDS_CLASS - sdhci_activate_led(host); -#endif + sdhci_led_activate(host); /* * Ensure we don't send the STOP for non-SET_BLOCK_COUNTED @@ -1405,11 +1450,11 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) } EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); -static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) +static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { + struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; u8 ctrl; - struct mmc_host *mmc = host->mmc; spin_lock_irqsave(&host->lock, flags); @@ -1563,18 +1608,10 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) spin_unlock_irqrestore(&host->lock, flags); } -static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +static int sdhci_get_cd(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); - - sdhci_runtime_pm_get(host); - sdhci_do_set_ios(host, ios); - sdhci_runtime_pm_put(host); -} - -static int sdhci_do_get_cd(struct sdhci_host *host) -{ - int gpio_cd = mmc_gpio_get_cd(host->mmc); + int gpio_cd = mmc_gpio_get_cd(mmc); if (host->flags & SDHCI_DEVICE_DEAD) return 0; @@ -1598,17 +1635,6 @@ static int sdhci_do_get_cd(struct sdhci_host *host) return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); } -static int sdhci_get_cd(struct mmc_host *mmc) -{ - struct sdhci_host *host = mmc_priv(mmc); - int ret; - - sdhci_runtime_pm_get(host); - ret = sdhci_do_get_cd(host); - sdhci_runtime_pm_put(host); - return ret; -} - static int sdhci_check_ro(struct sdhci_host *host) { unsigned long flags; @@ -1633,8 +1659,9 @@ static int sdhci_check_ro(struct sdhci_host *host) #define SAMPLE_COUNT 5 -static int sdhci_do_get_ro(struct sdhci_host *host) +static int sdhci_get_ro(struct mmc_host *mmc) { + struct sdhci_host *host = mmc_priv(mmc); int i, ro_count; if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT)) @@ -1659,17 +1686,6 @@ static void sdhci_hw_reset(struct mmc_host *mmc) host->ops->hw_reset(host); } -static int sdhci_get_ro(struct mmc_host *mmc) -{ - struct sdhci_host *host = mmc_priv(mmc); - int ret; - - sdhci_runtime_pm_get(host); - ret = sdhci_do_get_ro(host); - sdhci_runtime_pm_put(host); - return ret; -} - static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { if (!(host->flags & SDHCI_DEVICE_DEAD)) { @@ -1689,8 +1705,6 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; - sdhci_runtime_pm_get(host); - spin_lock_irqsave(&host->lock, flags); if (enable) host->flags |= SDHCI_SDIO_IRQ_ENABLED; @@ -1699,14 +1713,12 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) sdhci_enable_sdio_irq_nolock(host, enable); spin_unlock_irqrestore(&host->lock, flags); - - sdhci_runtime_pm_put(host); } -static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, - struct mmc_ios *ios) +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) { - struct mmc_host *mmc = host->mmc; + struct sdhci_host *host = mmc_priv(mmc); u16 ctrl; int ret; @@ -1794,29 +1806,13 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, } } -static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, - struct mmc_ios *ios) -{ - struct sdhci_host *host = mmc_priv(mmc); - int err; - - if (host->version < SDHCI_SPEC_300) - return 0; - sdhci_runtime_pm_get(host); - err = sdhci_do_start_signal_voltage_switch(host, ios); - sdhci_runtime_pm_put(host); - return err; -} - static int sdhci_card_busy(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); u32 present_state; - sdhci_runtime_pm_get(host); /* Check whether DAT[3:0] is 0000 */ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); - sdhci_runtime_pm_put(host); return !(present_state & SDHCI_DATA_LVL_MASK); } @@ -1843,7 +1839,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) unsigned int tuning_count = 0; bool hs400_tuning; - sdhci_runtime_pm_get(host); spin_lock_irqsave(&host->lock, flags); hs400_tuning = host->flags & SDHCI_HS400_TUNING; @@ -1879,8 +1874,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) break; case MMC_TIMING_UHS_SDR50: - if (host->flags & SDHCI_SDR50_NEEDS_TUNING || - host->flags & SDHCI_SDR104_NEEDS_TUNING) + if (host->flags & SDHCI_SDR50_NEEDS_TUNING) break; /* FALLTHROUGH */ @@ -1891,7 +1885,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->ops->platform_execute_tuning) { spin_unlock_irqrestore(&host->lock, flags); err = host->ops->platform_execute_tuning(host, opcode); - sdhci_runtime_pm_put(host); return err; } @@ -2023,8 +2016,6 @@ out: sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); out_unlock: spin_unlock_irqrestore(&host->lock, flags); - sdhci_runtime_pm_put(host); - return err; } @@ -2105,7 +2096,7 @@ static void sdhci_card_event(struct mmc_host *mmc) if (host->ops->card_event) host->ops->card_event(host); - present = sdhci_do_get_cd(host); + present = sdhci_get_cd(host->mmc); spin_lock_irqsave(&host->lock, flags); @@ -2214,15 +2205,12 @@ static void sdhci_tasklet_finish(unsigned long param) host->cmd = NULL; host->data = NULL; -#ifndef SDHCI_USE_LEDS_CLASS - sdhci_deactivate_led(host); -#endif + sdhci_led_deactivate(host); mmiowb(); spin_unlock_irqrestore(&host->lock, flags); mmc_request_done(host->mmc, mrq); - sdhci_runtime_pm_put(host); } static void sdhci_timeout_timer(unsigned long data) @@ -2679,7 +2667,7 @@ int sdhci_resume_host(struct sdhci_host *host) sdhci_init(host, 0); host->pwr = 0; host->clock = 0; - sdhci_do_set_ios(host, &host->mmc->ios); + sdhci_set_ios(host->mmc, &host->mmc->ios); } else { sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); mmiowb(); @@ -2703,33 +2691,6 @@ int sdhci_resume_host(struct sdhci_host *host) EXPORT_SYMBOL_GPL(sdhci_resume_host); -static int sdhci_runtime_pm_get(struct sdhci_host *host) -{ - return pm_runtime_get_sync(host->mmc->parent); -} - -static int sdhci_runtime_pm_put(struct sdhci_host *host) -{ - pm_runtime_mark_last_busy(host->mmc->parent); - return pm_runtime_put_autosuspend(host->mmc->parent); -} - -static void sdhci_runtime_pm_bus_on(struct sdhci_host *host) -{ - if (host->bus_on) - return; - host->bus_on = true; - pm_runtime_get_noresume(host->mmc->parent); -} - -static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) -{ - if (!host->bus_on) - return; - host->bus_on = false; - pm_runtime_put_noidle(host->mmc->parent); -} - int sdhci_runtime_suspend_host(struct sdhci_host *host) { unsigned long flags; @@ -2768,8 +2729,8 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) /* Force clock and power re-program */ host->pwr = 0; host->clock = 0; - sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); - sdhci_do_set_ios(host, &host->mmc->ios); + sdhci_start_signal_voltage_switch(host->mmc, &host->mmc->ios); + sdhci_set_ios(host->mmc, &host->mmc->ios); if ((host_flags & SDHCI_PV_ENABLED) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { @@ -3014,7 +2975,8 @@ int sdhci_add_host(struct sdhci_host *host) if (!host->ops->get_max_clock) { pr_err("%s: Hardware doesn't specify base clock frequency.\n", mmc_hostname(mmc)); - return -ENODEV; + ret = -ENODEV; + goto undma; } host->max_clk = host->ops->get_max_clock(host); } @@ -3051,7 +3013,7 @@ int sdhci_add_host(struct sdhci_host *host) } else mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; - if (!mmc->f_max || (mmc->f_max && (mmc->f_max > max_clk))) + if (!mmc->f_max || mmc->f_max > max_clk) mmc->f_max = max_clk; if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { @@ -3064,7 +3026,8 @@ int sdhci_add_host(struct sdhci_host *host) } else { pr_err("%s: Hardware doesn't specify timeout clock frequency.\n", mmc_hostname(mmc)); - return -ENODEV; + ret = -ENODEV; + goto undma; } } @@ -3118,8 +3081,9 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_NEEDS_POLL; /* If there are external regulators, get them */ - if (mmc_regulator_get_supply(mmc) == -EPROBE_DEFER) - return -EPROBE_DEFER; + ret = mmc_regulator_get_supply(mmc); + if (ret == -EPROBE_DEFER) + goto undma; /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { @@ -3174,10 +3138,6 @@ int sdhci_add_host(struct sdhci_host *host) if (caps[1] & SDHCI_USE_SDR50_TUNING) host->flags |= SDHCI_SDR50_NEEDS_TUNING; - /* Does the host need tuning for SDR104 / HS200? */ - if (mmc->caps2 & MMC_CAP2_HS200) - host->flags |= SDHCI_SDR104_NEEDS_TUNING; - /* Driver Type(s) (A, C, D) supported by the host */ if (caps[1] & SDHCI_DRIVER_TYPE_A) mmc->caps |= MMC_CAP_DRIVER_TYPE_A; @@ -3276,7 +3236,8 @@ int sdhci_add_host(struct sdhci_host *host) if (mmc->ocr_avail == 0) { pr_err("%s: Hardware doesn't report any support voltages.\n", mmc_hostname(mmc)); - return -ENODEV; + ret = -ENODEV; + goto unreg; } spin_lock_init(&host->lock); @@ -3360,25 +3321,18 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_dumpregs(host); #endif -#ifdef SDHCI_USE_LEDS_CLASS - snprintf(host->led_name, sizeof(host->led_name), - "%s::", mmc_hostname(mmc)); - host->led.name = host->led_name; - host->led.brightness = LED_OFF; - host->led.default_trigger = mmc_hostname(mmc); - host->led.brightness_set = sdhci_led_control; - - ret = led_classdev_register(mmc_dev(mmc), &host->led); + ret = sdhci_led_register(host); if (ret) { pr_err("%s: Failed to register LED device: %d\n", mmc_hostname(mmc), ret); - goto reset; + goto unirq; } -#endif mmiowb(); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto unled; pr_info("%s: SDHCI controller on %s [%s] using %s\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), @@ -3390,15 +3344,25 @@ int sdhci_add_host(struct sdhci_host *host) return 0; -#ifdef SDHCI_USE_LEDS_CLASS -reset: +unled: + sdhci_led_unregister(host); +unirq: sdhci_do_reset(host, SDHCI_RESET_ALL); sdhci_writel(host, 0, SDHCI_INT_ENABLE); sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); -#endif untasklet: tasklet_kill(&host->finish_tasklet); +unreg: + if (!IS_ERR(mmc->supply.vqmmc)) + regulator_disable(mmc->supply.vqmmc); +undma: + if (host->align_buffer) + dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz + + host->adma_table_sz, host->align_buffer, + host->align_addr); + host->adma_table = NULL; + host->align_buffer = NULL; return ret; } @@ -3430,9 +3394,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) mmc_remove_host(mmc); -#ifdef SDHCI_USE_LEDS_CLASS - led_classdev_unregister(&host->led); -#endif + sdhci_led_unregister(host); if (!dead) sdhci_do_reset(host, SDHCI_RESET_ALL); |