From 863981e96738983919de841ec669e157e6bdaeb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Fabian=20Silva=20Delgado?= Date: Sun, 11 Sep 2016 04:34:46 -0300 Subject: Linux-libre 4.7.1-gnu --- drivers/mmc/core/Kconfig | 21 ++++++++ drivers/mmc/core/Makefile | 4 +- drivers/mmc/core/core.c | 12 ++++- drivers/mmc/core/host.c | 46 ++++++++++++++--- drivers/mmc/core/mmc.c | 61 +++++++++++----------- drivers/mmc/core/pwrseq.c | 108 ++++++++++++++++++--------------------- drivers/mmc/core/pwrseq.h | 19 ++++--- drivers/mmc/core/pwrseq_emmc.c | 81 ++++++++++++++++++----------- drivers/mmc/core/pwrseq_simple.c | 91 ++++++++++++++++++--------------- drivers/mmc/core/sdio_cis.c | 7 ++- 10 files changed, 271 insertions(+), 179 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 4c33d7690..250f223aa 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -1,3 +1,24 @@ # # MMC core configuration # +config PWRSEQ_EMMC + tristate "HW reset support for eMMC" + default y + depends on OF + help + This selects Hardware reset support aka pwrseq-emmc for eMMC + devices. By default this option is set to y. + + This driver can also be built as a module. If so, the module + will be called pwrseq_emmc. + +config PWRSEQ_SIMPLE + tristate "Simple HW reset support for MMC" + default y + depends on OF + help + This selects simple hardware reset support aka pwrseq-simple for MMC + devices. By default this option is set to y. + + This driver can also be built as a module. If so, the module + will be called pwrseq_simple. diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 2c25138f2..f007151df 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,7 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ quirks.o slot-gpio.o -mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o +mmc_core-$(CONFIG_OF) += pwrseq.o +obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o +obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9eba56c68..8b4dfd454 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -36,6 +36,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #include "core.h" #include "bus.h" #include "host.h" @@ -140,6 +143,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) cmd->retries = 0; } + trace_mmc_request_done(host, mrq); + if (err && cmd->retries && !mmc_card_removed(host->card)) { /* * Request starter must handle retries - see @@ -215,6 +220,8 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) } } + trace_mmc_request_start(host, mrq); + host->ops->request(host, mrq); } @@ -2449,8 +2456,9 @@ int mmc_hw_reset(struct mmc_host *host) ret = host->bus_ops->reset(host); mmc_bus_put(host); - if (ret != -EOPNOTSUPP) - pr_warn("%s: tried to reset card\n", mmc_hostname(host)); + if (ret) + pr_warn("%s: tried to reset card, got error %d\n", + mmc_hostname(host), ret); return ret; } diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 6e4c55a4a..1be42fab1 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -33,14 +33,14 @@ #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) -static DEFINE_IDR(mmc_host_idr); +static DEFINE_IDA(mmc_host_ida); static DEFINE_SPINLOCK(mmc_host_lock); static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); spin_lock(&mmc_host_lock); - idr_remove(&mmc_host_idr, host->index); + ida_remove(&mmc_host_ida, host->index); spin_unlock(&mmc_host_lock); kfree(host); } @@ -68,8 +68,32 @@ void mmc_retune_enable(struct mmc_host *host) jiffies + host->retune_period * HZ); } +/* + * Pause re-tuning for a small set of operations. The pause begins after the + * next command and after first doing re-tuning. + */ +void mmc_retune_pause(struct mmc_host *host) +{ + if (!host->retune_paused) { + host->retune_paused = 1; + mmc_retune_needed(host); + mmc_retune_hold(host); + } +} +EXPORT_SYMBOL(mmc_retune_pause); + +void mmc_retune_unpause(struct mmc_host *host) +{ + if (host->retune_paused) { + host->retune_paused = 0; + mmc_retune_release(host); + } +} +EXPORT_SYMBOL(mmc_retune_unpause); + void mmc_retune_disable(struct mmc_host *host) { + mmc_retune_unpause(host); host->can_retune = 0; del_timer_sync(&host->retune_timer); host->retune_now = 0; @@ -321,14 +345,20 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) /* scanning will be enabled when we're ready */ host->rescan_disable = 1; - idr_preload(GFP_KERNEL); + +again: + if (!ida_pre_get(&mmc_host_ida, GFP_KERNEL)) { + kfree(host); + return NULL; + } + spin_lock(&mmc_host_lock); - err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT); - if (err >= 0) - host->index = err; + err = ida_get_new(&mmc_host_ida, &host->index); spin_unlock(&mmc_host_lock); - idr_preload_end(); - if (err < 0) { + + if (err == -EAGAIN) { + goto again; + } else if (err) { kfree(host); return NULL; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 80169643d..5d438ad3e 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1251,10 +1251,11 @@ static int mmc_select_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; bool send_status = true; - unsigned int old_timing; + unsigned int old_timing, old_signal_voltage; int err = -EINVAL; u8 val; + old_signal_voltage = host->ios.signal_voltage; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); @@ -1263,7 +1264,7 @@ static int mmc_select_hs200(struct mmc_card *card) /* If fails try again during next card power cycle */ if (err) - goto err; + return err; mmc_select_driver_type(card); @@ -1275,7 +1276,7 @@ static int mmc_select_hs200(struct mmc_card *card) * switch to HS200 mode if bus width is set successfully. */ err = mmc_select_bus_width(card); - if (!IS_ERR_VALUE(err)) { + if (err >= 0) { val = EXT_CSD_TIMING_HS200 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -1297,9 +1298,14 @@ static int mmc_select_hs200(struct mmc_card *card) } } err: - if (err) + if (err) { + /* fall back to the old signal voltage, if fails report error */ + if (__mmc_set_signal_voltage(host, old_signal_voltage)) + err = -EIO; + pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), __func__, err); + } return err; } @@ -1321,21 +1327,13 @@ static int mmc_select_timing(struct mmc_card *card) if (err && err != -EBADMSG) return err; - if (err) { - pr_warn("%s: switch to %s failed\n", - mmc_card_hs(card) ? "high-speed" : - (mmc_card_hs200(card) ? "hs200" : ""), - mmc_hostname(card->host)); - err = 0; - } - bus_speed: /* * Set the bus speed to the selected bus timing. * If timing is not selected, backward compatible is the default. */ mmc_set_bus_speed(card); - return err; + return 0; } /* @@ -1490,12 +1488,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (err) goto free_card; - /* If doing byte addressing, check if required to do sector + /* + * If doing byte addressing, check if required to do sector * addressing. Handle the case of <2GB cards needing sector * addressing. See section 8.1 JEDEC Standard JED84-A441; * ocr register has bit 30 set for sector addressing. */ - if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30))) + if (rocr & BIT(30)) mmc_card_set_blockaddr(card); /* Erase size depends on CSD and Extended CSD */ @@ -1584,7 +1583,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } else if (mmc_card_hs(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); - if (!IS_ERR_VALUE(err)) { + if (err >= 0) { err = mmc_select_hs_ddr(card); if (err) goto free_card; @@ -1964,19 +1963,23 @@ static int mmc_reset(struct mmc_host *host) { struct mmc_card *card = host->card; - if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) - return -EOPNOTSUPP; - - if (!mmc_can_reset(card)) - return -EOPNOTSUPP; - - mmc_set_clock(host, host->f_init); - - host->ops->hw_reset(host); - - /* Set initial state and call mmc_set_ios */ - mmc_set_initial_state(host); - + /* + * In the case of recovery, we can't expect flushing the cache to work + * always, but we have a go and ignore errors. + */ + mmc_flush_cache(host->card); + + if ((host->caps & MMC_CAP_HW_RESET) && host->ops->hw_reset && + mmc_can_reset(card)) { + /* If the card accept RST_n signal, send it. */ + mmc_set_clock(host, host->f_init); + host->ops->hw_reset(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); + } else { + /* Do a brute force power cycle */ + mmc_power_cycle(host, card->ocr); + } return mmc_init_card(host, card->ocr, card); } diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index 4c1d1757d..9386c4771 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -8,88 +8,55 @@ * MMC power sequence management */ #include -#include #include +#include #include -#include #include #include "pwrseq.h" -struct mmc_pwrseq_match { - const char *compatible; - struct mmc_pwrseq *(*alloc)(struct mmc_host *host, struct device *dev); -}; - -static struct mmc_pwrseq_match pwrseq_match[] = { - { - .compatible = "mmc-pwrseq-simple", - .alloc = mmc_pwrseq_simple_alloc, - }, { - .compatible = "mmc-pwrseq-emmc", - .alloc = mmc_pwrseq_emmc_alloc, - }, -}; - -static struct mmc_pwrseq_match *mmc_pwrseq_find(struct device_node *np) -{ - struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV); - int i; - - for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) { - if (of_device_is_compatible(np, pwrseq_match[i].compatible)) { - match = &pwrseq_match[i]; - break; - } - } - - return match; -} +static DEFINE_MUTEX(pwrseq_list_mutex); +static LIST_HEAD(pwrseq_list); int mmc_pwrseq_alloc(struct mmc_host *host) { - struct platform_device *pdev; struct device_node *np; - struct mmc_pwrseq_match *match; - struct mmc_pwrseq *pwrseq; - int ret = 0; + struct mmc_pwrseq *p; np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0); if (!np) return 0; - pdev = of_find_device_by_node(np); - if (!pdev) { - ret = -ENODEV; - goto err; - } + mutex_lock(&pwrseq_list_mutex); + list_for_each_entry(p, &pwrseq_list, pwrseq_node) { + if (p->dev->of_node == np) { + if (!try_module_get(p->owner)) + dev_err(host->parent, + "increasing module refcount failed\n"); + else + host->pwrseq = p; - match = mmc_pwrseq_find(np); - if (IS_ERR(match)) { - ret = PTR_ERR(match); - goto err; + break; + } } - pwrseq = match->alloc(host, &pdev->dev); - if (IS_ERR(pwrseq)) { - ret = PTR_ERR(pwrseq); - goto err; - } + of_node_put(np); + mutex_unlock(&pwrseq_list_mutex); + + if (!host->pwrseq) + return -EPROBE_DEFER; - host->pwrseq = pwrseq; dev_info(host->parent, "allocated mmc-pwrseq\n"); -err: - of_node_put(np); - return ret; + return 0; } void mmc_pwrseq_pre_power_on(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->pre_power_on) + if (pwrseq && pwrseq->ops->pre_power_on) pwrseq->ops->pre_power_on(host); } @@ -97,7 +64,7 @@ void mmc_pwrseq_post_power_on(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->post_power_on) + if (pwrseq && pwrseq->ops->post_power_on) pwrseq->ops->post_power_on(host); } @@ -105,7 +72,7 @@ void mmc_pwrseq_power_off(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->power_off) + if (pwrseq && pwrseq->ops->power_off) pwrseq->ops->power_off(host); } @@ -113,8 +80,31 @@ void mmc_pwrseq_free(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->free) - pwrseq->ops->free(host); + if (pwrseq) { + module_put(pwrseq->owner); + host->pwrseq = NULL; + } +} + +int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq) +{ + if (!pwrseq || !pwrseq->ops || !pwrseq->dev) + return -EINVAL; - host->pwrseq = NULL; + mutex_lock(&pwrseq_list_mutex); + list_add(&pwrseq->pwrseq_node, &pwrseq_list); + mutex_unlock(&pwrseq_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mmc_pwrseq_register); + +void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) +{ + if (pwrseq) { + mutex_lock(&pwrseq_list_mutex); + list_del(&pwrseq->pwrseq_node); + mutex_unlock(&pwrseq_list_mutex); + } } +EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister); diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h index 133de0426..d69e751f1 100644 --- a/drivers/mmc/core/pwrseq.h +++ b/drivers/mmc/core/pwrseq.h @@ -8,32 +8,39 @@ #ifndef _MMC_CORE_PWRSEQ_H #define _MMC_CORE_PWRSEQ_H +#include + struct mmc_pwrseq_ops { void (*pre_power_on)(struct mmc_host *host); void (*post_power_on)(struct mmc_host *host); void (*power_off)(struct mmc_host *host); - void (*free)(struct mmc_host *host); }; struct mmc_pwrseq { const struct mmc_pwrseq_ops *ops; + struct device *dev; + struct list_head pwrseq_node; + struct module *owner; }; #ifdef CONFIG_OF +int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq); +void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq); + int mmc_pwrseq_alloc(struct mmc_host *host); void mmc_pwrseq_pre_power_on(struct mmc_host *host); void mmc_pwrseq_post_power_on(struct mmc_host *host); void mmc_pwrseq_power_off(struct mmc_host *host); void mmc_pwrseq_free(struct mmc_host *host); -struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, - struct device *dev); -struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, - struct device *dev); - #else +static inline int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq) +{ + return -ENOSYS; +} +static inline void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) {} static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; } static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {} static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {} diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c index 4a82bc77f..adc9c0c61 100644 --- a/drivers/mmc/core/pwrseq_emmc.c +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -9,6 +9,9 @@ */ #include #include +#include +#include +#include #include #include #include @@ -25,6 +28,8 @@ struct mmc_pwrseq_emmc { struct gpio_desc *reset_gpio; }; +#define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq) + static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) { gpiod_set_value(pwrseq->reset_gpio, 1); @@ -35,27 +40,11 @@ static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) static void mmc_pwrseq_emmc_reset(struct mmc_host *host) { - struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_emmc, pwrseq); + struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); __mmc_pwrseq_emmc_reset(pwrseq); } -static void mmc_pwrseq_emmc_free(struct mmc_host *host) -{ - struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_emmc, pwrseq); - - unregister_restart_handler(&pwrseq->reset_nb); - gpiod_put(pwrseq->reset_gpio); - kfree(pwrseq); -} - -static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { - .post_power_on = mmc_pwrseq_emmc_reset, - .free = mmc_pwrseq_emmc_free, -}; - static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, unsigned long mode, void *cmd) { @@ -66,21 +55,22 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, return NOTIFY_DONE; } -struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, - struct device *dev) +static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { + .post_power_on = mmc_pwrseq_emmc_reset, +}; + +static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) { struct mmc_pwrseq_emmc *pwrseq; - int ret = 0; + struct device *dev = &pdev->dev; - pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); + pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - pwrseq->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(pwrseq->reset_gpio)) { - ret = PTR_ERR(pwrseq->reset_gpio); - goto free; - } + pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(pwrseq->reset_gpio)) + return PTR_ERR(pwrseq->reset_gpio); /* * register reset handler to ensure emmc reset also from @@ -92,9 +82,38 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, register_restart_handler(&pwrseq->reset_nb); pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; + pwrseq->pwrseq.dev = dev; + pwrseq->pwrseq.owner = THIS_MODULE; + platform_set_drvdata(pdev, pwrseq); + + return mmc_pwrseq_register(&pwrseq->pwrseq); +} + +static int mmc_pwrseq_emmc_remove(struct platform_device *pdev) +{ + struct mmc_pwrseq_emmc *pwrseq = platform_get_drvdata(pdev); + + unregister_restart_handler(&pwrseq->reset_nb); + mmc_pwrseq_unregister(&pwrseq->pwrseq); - return &pwrseq->pwrseq; -free: - kfree(pwrseq); - return ERR_PTR(ret); + return 0; } + +static const struct of_device_id mmc_pwrseq_emmc_of_match[] = { + { .compatible = "mmc-pwrseq-emmc",}, + {/* sentinel */}, +}; + +MODULE_DEVICE_TABLE(of, mmc_pwrseq_emmc_of_match); + +static struct platform_driver mmc_pwrseq_emmc_driver = { + .probe = mmc_pwrseq_emmc_probe, + .remove = mmc_pwrseq_emmc_remove, + .driver = { + .name = "pwrseq_emmc", + .of_match_table = mmc_pwrseq_emmc_of_match, + }, +}; + +module_platform_driver(mmc_pwrseq_emmc_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index bc173e18b..450d907c6 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -8,7 +8,10 @@ * Simple MMC power sequence management */ #include +#include #include +#include +#include #include #include #include @@ -25,6 +28,8 @@ struct mmc_pwrseq_simple { struct gpio_descs *reset_gpios; }; +#define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) + static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, int value) { @@ -44,8 +49,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) { - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) { clk_prepare_enable(pwrseq->ext_clk); @@ -57,16 +61,14 @@ static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) { - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); } static void mmc_pwrseq_simple_power_off(struct mmc_host *host) { - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); @@ -76,59 +78,64 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host) } } -static void mmc_pwrseq_simple_free(struct mmc_host *host) -{ - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); - - if (!IS_ERR(pwrseq->reset_gpios)) - gpiod_put_array(pwrseq->reset_gpios); - - if (!IS_ERR(pwrseq->ext_clk)) - clk_put(pwrseq->ext_clk); - - kfree(pwrseq); -} - static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { .pre_power_on = mmc_pwrseq_simple_pre_power_on, .post_power_on = mmc_pwrseq_simple_post_power_on, .power_off = mmc_pwrseq_simple_power_off, - .free = mmc_pwrseq_simple_free, }; -struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, - struct device *dev) +static const struct of_device_id mmc_pwrseq_simple_of_match[] = { + { .compatible = "mmc-pwrseq-simple",}, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match); + +static int mmc_pwrseq_simple_probe(struct platform_device *pdev) { struct mmc_pwrseq_simple *pwrseq; - int ret = 0; + struct device *dev = &pdev->dev; - pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL); + pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - pwrseq->ext_clk = clk_get(dev, "ext_clock"); - if (IS_ERR(pwrseq->ext_clk) && - PTR_ERR(pwrseq->ext_clk) != -ENOENT) { - ret = PTR_ERR(pwrseq->ext_clk); - goto free; - } + pwrseq->ext_clk = devm_clk_get(dev, "ext_clock"); + if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) + return PTR_ERR(pwrseq->ext_clk); - pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); + pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", + GPIOD_OUT_HIGH); if (IS_ERR(pwrseq->reset_gpios) && PTR_ERR(pwrseq->reset_gpios) != -ENOENT && PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { - ret = PTR_ERR(pwrseq->reset_gpios); - goto clk_put; + return PTR_ERR(pwrseq->reset_gpios); } + pwrseq->pwrseq.dev = dev; pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; + pwrseq->pwrseq.owner = THIS_MODULE; + platform_set_drvdata(pdev, pwrseq); - return &pwrseq->pwrseq; -clk_put: - if (!IS_ERR(pwrseq->ext_clk)) - clk_put(pwrseq->ext_clk); -free: - kfree(pwrseq); - return ERR_PTR(ret); + return mmc_pwrseq_register(&pwrseq->pwrseq); } + +static int mmc_pwrseq_simple_remove(struct platform_device *pdev) +{ + struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev); + + mmc_pwrseq_unregister(&pwrseq->pwrseq); + + return 0; +} + +static struct platform_driver mmc_pwrseq_simple_driver = { + .probe = mmc_pwrseq_simple_probe, + .remove = mmc_pwrseq_simple_remove, + .driver = { + .name = "pwrseq_simple", + .of_match_table = mmc_pwrseq_simple_of_match, + }, +}; + +module_platform_driver(mmc_pwrseq_simple_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index 6f6fc527a..dcb3dee59 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -177,8 +177,13 @@ static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func, vsn = func->card->cccr.sdio_vsn; min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; - if (size < min_size) + if (size == 28 && vsn == SDIO_SDIO_REV_1_10) { + pr_warn("%s: card has broken SDIO 1.1 CIS, forcing SDIO 1.0\n", + mmc_hostname(card->host)); + vsn = SDIO_SDIO_REV_1_00; + } else if (size < min_size) { return -EINVAL; + } /* TPLFE_MAX_BLK_SIZE */ func->max_blksize = buf[12] | (buf[13] << 8); -- cgit v1.2.3