diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mmc/core/Makefile | 4 | ||||
-rw-r--r-- | drivers/mmc/core/pwrseq.c | 92 | ||||
-rw-r--r-- | drivers/mmc/core/pwrseq.h | 25 | ||||
-rw-r--r-- | drivers/mmc/core/pwrseq_emmc.c | 91 | ||||
-rw-r--r-- | drivers/mmc/core/pwrseq_simple.c | 118 |
6 files changed, 213 insertions, 125 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index f771bc3496a4..0525054f4078 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -26,3 +26,11 @@ config MMC_PARANOID_SD_INIT about re-trying SD init requests. This can be a useful work-around for buggy controllers and hardware. Enable if you are experiencing issues with SD detection. + +config PWRSEQ_EMMC + tristate "PwrSeq EMMC" + depends on OF + +config PWRSEQ_SIMPLE + tristate "PwrSeq Simple" + depends on OF diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 2c25138f28b7..b281675edb92 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 mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o +obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o +obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index 4c1d1757dbf9..9af53a66e646 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -8,50 +8,28 @@ * MMC power sequence management */ #include <linux/kernel.h> -#include <linux/platform_device.h> #include <linux/err.h> #include <linux/of.h> -#include <linux/of_platform.h> - #include <linux/mmc/host.h> - #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) +static DEFINE_MUTEX(pwrseq_list_mutex); +static LIST_HEAD(pwrseq_list); + +static struct mmc_pwrseq *of_find_mmc_pwrseq(struct device_node *np) { - struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV); - int i; + struct mmc_pwrseq *p; - for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) { - if (of_device_is_compatible(np, pwrseq_match[i].compatible)) { - match = &pwrseq_match[i]; - break; - } - } + list_for_each_entry(p, &pwrseq_list, list) + if (p->dev->of_node == np) + return p; - return match; + return NULL; } 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; @@ -59,25 +37,18 @@ int mmc_pwrseq_alloc(struct mmc_host *host) if (!np) return 0; - pdev = of_find_device_by_node(np); - if (!pdev) { - ret = -ENODEV; - goto err; - } - - match = mmc_pwrseq_find(np); - if (IS_ERR(match)) { - ret = PTR_ERR(match); - goto err; - } + pwrseq = of_find_mmc_pwrseq(np); - pwrseq = match->alloc(host, &pdev->dev); - if (IS_ERR(pwrseq)) { - ret = PTR_ERR(pwrseq); - goto err; + if (pwrseq && pwrseq->ops && pwrseq->ops->alloc) { + host->pwrseq = pwrseq; + ret = pwrseq->ops->alloc(host); + if (IS_ERR_VALUE(ret)) { + host->pwrseq = NULL; + goto err; + } } + pwrseq->users++; - host->pwrseq = pwrseq; dev_info(host->parent, "allocated mmc-pwrseq\n"); err: @@ -116,5 +87,32 @@ void mmc_pwrseq_free(struct mmc_host *host) if (pwrseq && pwrseq->ops && pwrseq->ops->free) pwrseq->ops->free(host); + pwrseq->users--; host->pwrseq = NULL; } + +int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq) +{ + if (!pwrseq || !pwrseq->ops || !pwrseq->dev) + return -EINVAL; + + mutex_lock(&pwrseq_list_mutex); + list_add(&pwrseq->list, &pwrseq_list); + mutex_unlock(&pwrseq_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mmc_pwrseq_register); + +int mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) +{ + if (!pwrseq->users) { + mutex_lock(&pwrseq_list_mutex); + list_del(&pwrseq->list); + mutex_unlock(&pwrseq_list_mutex); + return 0; + } + + return -EBUSY; +} +EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister); diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h index 096da48c6a7e..462c0abfb651 100644 --- a/drivers/mmc/core/pwrseq.h +++ b/drivers/mmc/core/pwrseq.h @@ -8,7 +8,10 @@ #ifndef _MMC_CORE_PWRSEQ_H #define _MMC_CORE_PWRSEQ_H +#include <linux/mmc/host.h> + struct mmc_pwrseq_ops { + int (*alloc)(struct mmc_host *host); void (*pre_power_on)(struct mmc_host *host); void (*post_power_on)(struct mmc_host *host); void (*power_off)(struct mmc_host *host); @@ -17,23 +20,35 @@ struct mmc_pwrseq_ops { struct mmc_pwrseq { struct mmc_pwrseq_ops *ops; + struct device *dev; + struct list_head list; + int users; + }; #ifdef CONFIG_OF +int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq); +int 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 int mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) +{ + return -ENOSYS; +} + 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 9d6d2fb21796..0b12bd79ba8e 100644 --- a/drivers/mmc/core/pwrseq_emmc.c +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -9,6 +9,8 @@ */ #include <linux/delay.h> #include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/err.h> @@ -16,7 +18,6 @@ #include <linux/reboot.h> #include <linux/mmc/host.h> - #include "pwrseq.h" struct mmc_pwrseq_emmc { @@ -25,6 +26,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,52 +38,37 @@ 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); + struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); unregister_restart_handler(&pwrseq->reset_nb); gpiod_put(pwrseq->reset_gpio); - kfree(pwrseq); } -static 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) { struct mmc_pwrseq_emmc *pwrseq = container_of(this, - struct mmc_pwrseq_emmc, reset_nb); + struct mmc_pwrseq_emmc, reset_nb); __mmc_pwrseq_emmc_reset(pwrseq); return NOTIFY_DONE; } -struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, - struct device *dev) +static int mmc_pwrseq_emmc_alloc(struct mmc_host *host) { - struct mmc_pwrseq_emmc *pwrseq; - int ret = 0; - - pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); - if (!pwrseq) - return ERR_PTR(-ENOMEM); - - pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); - if (IS_ERR(pwrseq->reset_gpio)) { - ret = PTR_ERR(pwrseq->reset_gpio); - goto free; - } + struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); + + pwrseq->reset_gpio = gpiod_get_index(host->pwrseq->dev, + "reset", 0, GPIOD_OUT_LOW); + if (IS_ERR(pwrseq->reset_gpio)) + return PTR_ERR(pwrseq->reset_gpio); /* * register reset handler to ensure emmc reset also from @@ -91,10 +79,53 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, pwrseq->reset_nb.priority = 129; register_restart_handler(&pwrseq->reset_nb); + return 0; +} + +static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { + .alloc = mmc_pwrseq_emmc_alloc, + .post_power_on = mmc_pwrseq_emmc_reset, + .free = mmc_pwrseq_emmc_free, +}; + +static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) +{ + struct mmc_pwrseq_emmc *pwrseq; + struct device *dev = &pdev->dev; + + pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); + if (!pwrseq) + return -ENOMEM; + pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; + pwrseq->pwrseq.dev = dev; + + platform_set_drvdata(pdev, pwrseq); - return &pwrseq->pwrseq; -free: - kfree(pwrseq); - return ERR_PTR(ret); + return mmc_pwrseq_register(&pwrseq->pwrseq); } + +static int mmc_pwrseq_emmc_remove(struct platform_device *pdev) +{ + struct mmc_pwrseq_emmc *spwrseq = platform_get_drvdata(pdev); + + return mmc_pwrseq_unregister(&spwrseq->pwrseq); +} + +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 0b14b83a53d6..f2a7613fd1e5 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -9,6 +9,7 @@ */ #include <linux/clk.h> #include <linux/kernel.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/err.h> @@ -27,6 +28,8 @@ struct mmc_pwrseq_simple { struct gpio_desc *reset_gpios[0]; }; +#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) { @@ -39,8 +42,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); @@ -52,16 +54,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); @@ -73,8 +73,7 @@ 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); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); int i; for (i = 0; i < pwrseq->nr_gpios; i++) @@ -84,39 +83,22 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host) if (!IS_ERR(pwrseq->ext_clk)) clk_put(pwrseq->ext_clk); - kfree(pwrseq); } -static 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) +int mmc_pwrseq_simple_alloc(struct mmc_host *host) { - struct mmc_pwrseq_simple *pwrseq; - int i, nr_gpios, ret = 0; - - nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios"); - if (nr_gpios < 0) - nr_gpios = 0; - - pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios * - sizeof(struct gpio_desc *), GFP_KERNEL); - if (!pwrseq) - return ERR_PTR(-ENOMEM); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); + struct device *dev = host->pwrseq->dev; + int i, ret = 0; 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; + return PTR_ERR(pwrseq->ext_clk); + } - for (i = 0; i < nr_gpios; i++) { + for (i = 0; i < pwrseq->nr_gpios; i++) { pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i, GPIOD_OUT_HIGH); if (IS_ERR(pwrseq->reset_gpios[i]) && @@ -127,18 +109,70 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, while (i--) gpiod_put(pwrseq->reset_gpios[i]); - goto clk_put; + if (!IS_ERR(pwrseq->ext_clk)) + clk_put(pwrseq->ext_clk); + + return -EINVAL; } } - pwrseq->nr_gpios = nr_gpios; - pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; - return &pwrseq->pwrseq; -clk_put: - if (!IS_ERR(pwrseq->ext_clk)) - clk_put(pwrseq->ext_clk); -free: - kfree(pwrseq); - return ERR_PTR(ret); + return 0; +} + +static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { + .alloc = mmc_pwrseq_simple_alloc, + .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, +}; + +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 *spwrseq; + struct device *dev = &pdev->dev; + int nr_gpios; + + nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios"); + if (nr_gpios < 0) + nr_gpios = 0; + + spwrseq = devm_kzalloc(dev, sizeof(struct mmc_pwrseq_simple) + nr_gpios * + sizeof(struct gpio_desc *), GFP_KERNEL); + if (!spwrseq) + return -ENOMEM; + + spwrseq->pwrseq.dev = dev; + spwrseq->nr_gpios = nr_gpios; + spwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; + + platform_set_drvdata(pdev, spwrseq); + + return mmc_pwrseq_register(&spwrseq->pwrseq); } + +static int mmc_pwrseq_simple_remove(struct platform_device *pdev) +{ + struct mmc_pwrseq_simple *spwrseq = platform_get_drvdata(pdev); + + return mmc_pwrseq_unregister(&spwrseq->pwrseq); +} + +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"); |