diff options
Diffstat (limited to 'drivers/mmc/core/slot-gpio.c')
| -rw-r--r-- | drivers/mmc/core/slot-gpio.c | 180 | 
1 files changed, 140 insertions, 40 deletions
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 46596b71a32f..f7650b899e3d 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -10,6 +10,7 @@  #include <linux/err.h>  #include <linux/gpio.h> +#include <linux/gpio/consumer.h>  #include <linux/interrupt.h>  #include <linux/jiffies.h>  #include <linux/mmc/host.h> @@ -18,8 +19,10 @@  #include <linux/slab.h>  struct mmc_gpio { -	int ro_gpio; -	int cd_gpio; +	struct gpio_desc *ro_gpio; +	struct gpio_desc *cd_gpio; +	bool override_ro_active_level; +	bool override_cd_active_level;  	char *ro_label;  	char cd_label[0];  }; @@ -57,8 +60,6 @@ static int mmc_gpio_alloc(struct mmc_host *host)  			ctx->ro_label = ctx->cd_label + len;  			snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));  			snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); -			ctx->cd_gpio = -EINVAL; -			ctx->ro_gpio = -EINVAL;  			host->slot.handler_priv = ctx;  		}  	} @@ -72,11 +73,14 @@ int mmc_gpio_get_ro(struct mmc_host *host)  {  	struct mmc_gpio *ctx = host->slot.handler_priv; -	if (!ctx || !gpio_is_valid(ctx->ro_gpio)) +	if (!ctx || !ctx->ro_gpio)  		return -ENOSYS; -	return !gpio_get_value_cansleep(ctx->ro_gpio) ^ -		!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); +	if (ctx->override_ro_active_level) +		return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^ +			!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); + +	return gpiod_get_value_cansleep(ctx->ro_gpio);  }  EXPORT_SYMBOL(mmc_gpio_get_ro); @@ -84,11 +88,14 @@ int mmc_gpio_get_cd(struct mmc_host *host)  {  	struct mmc_gpio *ctx = host->slot.handler_priv; -	if (!ctx || !gpio_is_valid(ctx->cd_gpio)) +	if (!ctx || !ctx->cd_gpio)  		return -ENOSYS; -	return !gpio_get_value_cansleep(ctx->cd_gpio) ^ -		!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); +	if (ctx->override_cd_active_level) +		return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^ +			!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); + +	return gpiod_get_value_cansleep(ctx->cd_gpio);  }  EXPORT_SYMBOL(mmc_gpio_get_cd); @@ -125,12 +132,47 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)  	if (ret < 0)  		return ret; -	ctx->ro_gpio = gpio; +	ctx->override_ro_active_level = true; +	ctx->ro_gpio = gpio_to_desc(gpio);  	return 0;  }  EXPORT_SYMBOL(mmc_gpio_request_ro); +void mmc_gpiod_request_cd_irq(struct mmc_host *host) +{ +	struct mmc_gpio *ctx = host->slot.handler_priv; +	int ret, irq; + +	if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio) +		return; + +	irq = gpiod_to_irq(ctx->cd_gpio); + +	/* +	 * Even if gpiod_to_irq() returns a valid IRQ number, the platform might +	 * still prefer to poll, e.g., because that IRQ number is already used +	 * by another unit and cannot be shared. +	 */ +	if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) +		irq = -EINVAL; + +	if (irq >= 0) { +		ret = devm_request_threaded_irq(&host->class_dev, irq, +			NULL, mmc_gpio_cd_irqt, +			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +			ctx->cd_label, host); +		if (ret < 0) +			irq = ret; +	} + +	host->slot.cd_irq = irq; + +	if (irq < 0) +		host->caps |= MMC_CAP_NEEDS_POLL; +} +EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); +  /**   * mmc_gpio_request_cd - request a gpio for card-detection   * @host: mmc host @@ -154,7 +196,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,  			unsigned int debounce)  {  	struct mmc_gpio *ctx; -	int irq = gpio_to_irq(gpio);  	int ret;  	ret = mmc_gpio_alloc(host); @@ -179,29 +220,10 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,  			return ret;  	} -	/* -	 * Even if gpio_to_irq() returns a valid IRQ number, the platform might -	 * still prefer to poll, e.g., because that IRQ number is already used -	 * by another unit and cannot be shared. -	 */ -	if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) -		irq = -EINVAL; - -	if (irq >= 0) { -		ret = devm_request_threaded_irq(&host->class_dev, irq, -			NULL, mmc_gpio_cd_irqt, -			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, -			ctx->cd_label, host); -		if (ret < 0) -			irq = ret; -	} - -	host->slot.cd_irq = irq; - -	if (irq < 0) -		host->caps |= MMC_CAP_NEEDS_POLL; +	ctx->override_cd_active_level = true; +	ctx->cd_gpio = gpio_to_desc(gpio); -	ctx->cd_gpio = gpio; +	mmc_gpiod_request_cd_irq(host);  	return 0;  } @@ -219,11 +241,11 @@ void mmc_gpio_free_ro(struct mmc_host *host)  	struct mmc_gpio *ctx = host->slot.handler_priv;  	int gpio; -	if (!ctx || !gpio_is_valid(ctx->ro_gpio)) +	if (!ctx || !ctx->ro_gpio)  		return; -	gpio = ctx->ro_gpio; -	ctx->ro_gpio = -EINVAL; +	gpio = desc_to_gpio(ctx->ro_gpio); +	ctx->ro_gpio = NULL;  	devm_gpio_free(&host->class_dev, gpio);  } @@ -241,7 +263,7 @@ void mmc_gpio_free_cd(struct mmc_host *host)  	struct mmc_gpio *ctx = host->slot.handler_priv;  	int gpio; -	if (!ctx || !gpio_is_valid(ctx->cd_gpio)) +	if (!ctx || !ctx->cd_gpio)  		return;  	if (host->slot.cd_irq >= 0) { @@ -249,9 +271,87 @@ void mmc_gpio_free_cd(struct mmc_host *host)  		host->slot.cd_irq = -EINVAL;  	} -	gpio = ctx->cd_gpio; -	ctx->cd_gpio = -EINVAL; +	gpio = desc_to_gpio(ctx->cd_gpio); +	ctx->cd_gpio = NULL;  	devm_gpio_free(&host->class_dev, gpio);  }  EXPORT_SYMBOL(mmc_gpio_free_cd); + +/** + * mmc_gpiod_request_cd - request a gpio descriptor for card-detection + * @host: mmc host + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * @override_active_level: ignore %GPIO_ACTIVE_LOW flag + * @debounce: debounce time in microseconds + * + * Use this function in place of mmc_gpio_request_cd() to use the GPIO + * descriptor API.  Note that it is paired with mmc_gpiod_free_cd() not + * mmc_gpio_free_cd().  Note also that it must be called prior to mmc_add_host() + * otherwise the caller must also call mmc_gpiod_request_cd_irq(). + * + * Returns zero on success, else an error. + */ +int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, +			 unsigned int idx, bool override_active_level, +			 unsigned int debounce) +{ +	struct mmc_gpio *ctx; +	struct gpio_desc *desc; +	int ret; + +	ret = mmc_gpio_alloc(host); +	if (ret < 0) +		return ret; + +	ctx = host->slot.handler_priv; + +	if (!con_id) +		con_id = ctx->cd_label; + +	desc = devm_gpiod_get_index(host->parent, con_id, idx); +	if (IS_ERR(desc)) +		return PTR_ERR(desc); + +	ret = gpiod_direction_input(desc); +	if (ret < 0) +		return ret; + +	if (debounce) { +		ret = gpiod_set_debounce(desc, debounce); +		if (ret < 0) +			return ret; +	} + +	ctx->override_cd_active_level = override_active_level; +	ctx->cd_gpio = desc; + +	return 0; +} +EXPORT_SYMBOL(mmc_gpiod_request_cd); + +/** + * mmc_gpiod_free_cd - free the card-detection gpio descriptor + * @host: mmc host + * + * It's provided only for cases that client drivers need to manually free + * up the card-detection gpio requested by mmc_gpiod_request_cd(). + */ +void mmc_gpiod_free_cd(struct mmc_host *host) +{ +	struct mmc_gpio *ctx = host->slot.handler_priv; + +	if (!ctx || !ctx->cd_gpio) +		return; + +	if (host->slot.cd_irq >= 0) { +		devm_free_irq(&host->class_dev, host->slot.cd_irq, host); +		host->slot.cd_irq = -EINVAL; +	} + +	devm_gpiod_put(&host->class_dev, ctx->cd_gpio); + +	ctx->cd_gpio = NULL; +} +EXPORT_SYMBOL(mmc_gpiod_free_cd);  | 
