mmc: pwrseq_simple: Add support for a reset GPIO pin

The need for reset GPIOs has several times been pointed out from
erlier posted patchsets. Especially some WLAN chips which are
attached to an SDIO interface may use a GPIO reset.

The reset GPIO is asserted at initialization and prior we start the
power up procedure. The GPIO will be de-asserted right after the power
has been provided to the card, from the ->post_power_on() callback.

Note, the reset GPIO is optional. Thus we don't return an error even if
we can't find a GPIO for the consumer.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Reviewed-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
This commit is contained in:
Ulf Hansson 2014-12-15 16:07:11 +01:00
Родитель 8c96f89c62
Коммит 862b5dcf9f
1 изменённых файлов: 38 добавлений и 0 удалений

Просмотреть файл

@ -11,6 +11,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
@ -18,31 +19,68 @@
struct mmc_pwrseq_simple { struct mmc_pwrseq_simple {
struct mmc_pwrseq pwrseq; struct mmc_pwrseq pwrseq;
struct gpio_desc *reset_gpio;
}; };
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);
if (!IS_ERR(pwrseq->reset_gpio))
gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
}
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);
if (!IS_ERR(pwrseq->reset_gpio))
gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
}
static void mmc_pwrseq_simple_free(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 = container_of(host->pwrseq,
struct mmc_pwrseq_simple, pwrseq); struct mmc_pwrseq_simple, pwrseq);
if (!IS_ERR(pwrseq->reset_gpio))
gpiod_put(pwrseq->reset_gpio);
kfree(pwrseq); kfree(pwrseq);
host->pwrseq = NULL; host->pwrseq = NULL;
} }
static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { 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_pre_power_on,
.free = mmc_pwrseq_simple_free, .free = mmc_pwrseq_simple_free,
}; };
int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
{ {
struct mmc_pwrseq_simple *pwrseq; struct mmc_pwrseq_simple *pwrseq;
int ret = 0;
pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple), GFP_KERNEL); pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple), GFP_KERNEL);
if (!pwrseq) if (!pwrseq)
return -ENOMEM; return -ENOMEM;
pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_HIGH);
if (IS_ERR(pwrseq->reset_gpio) &&
PTR_ERR(pwrseq->reset_gpio) != -ENOENT &&
PTR_ERR(pwrseq->reset_gpio) != -ENOSYS) {
ret = PTR_ERR(pwrseq->reset_gpio);
goto free;
}
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
host->pwrseq = &pwrseq->pwrseq; host->pwrseq = &pwrseq->pwrseq;
return 0; return 0;
free:
kfree(pwrseq);
return ret;
} }