mmc: slot-gpio: Allow host driver to provide isr for card-detect interrupts
[deliverable/linux.git] / drivers / mmc / core / slot-gpio.c
index 69bbf2adb329db5570b0c0b698152110fb2ccee4..27117ba4707312dad5f06bbf142ffe50c34efa22 100644 (file)
 #include <linux/module.h>
 #include <linux/slab.h>
 
+#include "slot-gpio.h"
+
 struct mmc_gpio {
        struct gpio_desc *ro_gpio;
        struct gpio_desc *cd_gpio;
        bool override_ro_active_level;
        bool override_cd_active_level;
+       irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
        char *ro_label;
        char cd_label[0];
 };
@@ -38,32 +41,20 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int mmc_gpio_alloc(struct mmc_host *host)
+int mmc_gpio_alloc(struct mmc_host *host)
 {
        size_t len = strlen(dev_name(host->parent)) + 4;
-       struct mmc_gpio *ctx;
-
-       mutex_lock(&host->slot.lock);
-
-       ctx = host->slot.handler_priv;
-       if (!ctx) {
-               /*
-                * devm_kzalloc() can be called after device_initialize(), even
-                * before device_add(), i.e., between mmc_alloc_host() and
-                * mmc_add_host()
-                */
-               ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len,
-                                  GFP_KERNEL);
-               if (ctx) {
-                       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));
-                       host->slot.handler_priv = ctx;
-               }
+       struct mmc_gpio *ctx = devm_kzalloc(host->parent,
+                               sizeof(*ctx) + 2 * len, GFP_KERNEL);
+
+       if (ctx) {
+               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));
+               host->slot.handler_priv = ctx;
+               host->slot.cd_irq = -EINVAL;
        }
 
-       mutex_unlock(&host->slot.lock);
-
        return ctx ? 0 : -ENOMEM;
 }
 
@@ -103,29 +94,19 @@ EXPORT_SYMBOL(mmc_gpio_get_cd);
  * @gpio: gpio number requested
  *
  * As devm_* managed functions are used in mmc_gpio_request_ro(), client
- * drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up,
- * if the requesting and freeing are only needed at probing and unbinding time
- * for once.  However, if client drivers do something special like runtime
- * switching for write-protection, they are responsible for calling
- * mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own.
+ * drivers do not need to worry about freeing up memory.
  *
  * Returns zero on success, else an error.
  */
 int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
 {
-       struct mmc_gpio *ctx;
+       struct mmc_gpio *ctx = host->slot.handler_priv;
        int ret;
 
        if (!gpio_is_valid(gpio))
                return -EINVAL;
 
-       ret = mmc_gpio_alloc(host);
-       if (ret < 0)
-               return ret;
-
-       ctx = host->slot.handler_priv;
-
-       ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
+       ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
                                    ctx->ro_label);
        if (ret < 0)
                return ret;
@@ -156,8 +137,10 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
                irq = -EINVAL;
 
        if (irq >= 0) {
-               ret = devm_request_threaded_irq(&host->class_dev, irq,
-                       NULL, mmc_gpio_cd_irqt,
+               if (!ctx->cd_gpio_isr)
+                       ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
+               ret = devm_request_threaded_irq(host->parent, irq,
+                       NULL, ctx->cd_gpio_isr,
                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                        ctx->cd_label, host);
                if (ret < 0)
@@ -171,6 +154,19 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
 }
 EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
 
+/* Register an alternate interrupt service routine for
+ * the card-detect GPIO.
+ */
+void mmc_gpio_set_cd_isr(struct mmc_host *host,
+                        irqreturn_t (*isr)(int irq, void *dev_id))
+{
+       struct mmc_gpio *ctx = host->slot.handler_priv;
+
+       WARN_ON(ctx->cd_gpio_isr);
+       ctx->cd_gpio_isr = isr;
+}
+EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
+
 /**
  * mmc_gpio_request_cd - request a gpio for card-detection
  * @host: mmc host
@@ -178,11 +174,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
  * @debounce: debounce time in microseconds
  *
  * As devm_* managed functions are used in mmc_gpio_request_cd(), client
- * drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up,
- * if the requesting and freeing are only needed at probing and unbinding time
- * for once.  However, if client drivers do something special like runtime
- * switching for card-detection, they are responsible for calling
- * mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own.
+ * drivers do not need to worry about freeing up memory.
  *
  * If GPIO debouncing is desired, set the debounce parameter to a non-zero
  * value. The caller is responsible for ensuring that the GPIO driver associated
@@ -193,16 +185,10 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
 int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
                        unsigned int debounce)
 {
-       struct mmc_gpio *ctx;
+       struct mmc_gpio *ctx = host->slot.handler_priv;
        int ret;
 
-       ret = mmc_gpio_alloc(host);
-       if (ret < 0)
-               return ret;
-
-       ctx = host->slot.handler_priv;
-
-       ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
+       ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
                                    ctx->cd_label);
        if (ret < 0)
                /*
@@ -225,55 +211,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
 }
 EXPORT_SYMBOL(mmc_gpio_request_cd);
 
-/**
- * mmc_gpio_free_ro - free the write-protection gpio
- * @host: mmc host
- *
- * It's provided only for cases that client drivers need to manually free
- * up the write-protection gpio requested by mmc_gpio_request_ro().
- */
-void mmc_gpio_free_ro(struct mmc_host *host)
-{
-       struct mmc_gpio *ctx = host->slot.handler_priv;
-       int gpio;
-
-       if (!ctx || !ctx->ro_gpio)
-               return;
-
-       gpio = desc_to_gpio(ctx->ro_gpio);
-       ctx->ro_gpio = NULL;
-
-       devm_gpio_free(&host->class_dev, gpio);
-}
-EXPORT_SYMBOL(mmc_gpio_free_ro);
-
-/**
- * mmc_gpio_free_cd - free the card-detection gpio
- * @host: mmc host
- *
- * It's provided only for cases that client drivers need to manually free
- * up the card-detection gpio requested by mmc_gpio_request_cd().
- */
-void mmc_gpio_free_cd(struct mmc_host *host)
-{
-       struct mmc_gpio *ctx = host->slot.handler_priv;
-       int gpio;
-
-       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;
-       }
-
-       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
@@ -285,8 +222,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
  * to NULL to ignore
  *
  * 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()
+ * descriptor API.  Note 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.
@@ -295,16 +231,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
                         unsigned int idx, bool override_active_level,
                         unsigned int debounce, bool *gpio_invert)
 {
-       struct mmc_gpio *ctx;
+       struct mmc_gpio *ctx = host->slot.handler_priv;
        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;
 
@@ -339,8 +269,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd);
  * set to NULL to ignore
  *
  * Use this function in place of mmc_gpio_request_ro() to use the GPIO
- * descriptor API.  Note that it is paired with mmc_gpiod_free_ro() not
- * mmc_gpio_free_ro().
+ * descriptor API.
  *
  * Returns zero on success, else an error.
  */
@@ -348,16 +277,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
                         unsigned int idx, bool override_active_level,
                         unsigned int debounce, bool *gpio_invert)
 {
-       struct mmc_gpio *ctx;
+       struct mmc_gpio *ctx = host->slot.handler_priv;
        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->ro_label;
 
@@ -380,28 +303,3 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
        return 0;
 }
 EXPORT_SYMBOL(mmc_gpiod_request_ro);
-
-/**
- * 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->parent, ctx->cd_gpio);
-
-       ctx->cd_gpio = NULL;
-}
-EXPORT_SYMBOL(mmc_gpiod_free_cd);
This page took 0.149891 seconds and 5 git commands to generate.