Merge remote-tracking branch 'remotes/tegra/android-tegra-2.6.36-honeycomb-mr1' into...
[firefly-linux-kernel-4.4.55.git] / drivers / gpio / gpiolib.c
index 6a6bd569e1f8c80851a553b9a4b1c98a76de9faa..9fda464d7db0a24a47f7e31bc95175a31f19d7b2 100644 (file)
@@ -57,9 +57,9 @@ struct gpio_desc {
 #define FLAG_TRIG_RISE 6       /* trigger on rising edge */
 #define FLAG_ACTIVE_LOW        7       /* sysfs value has active low */
 
-#define PDESC_ID_SHIFT 16      /* add new flags before this one */
+#define ID_SHIFT       16      /* add new flags before this one */
 
-#define GPIO_FLAGS_MASK                ((1 << PDESC_ID_SHIFT) - 1)
+#define GPIO_FLAGS_MASK                ((1 << ID_SHIFT) - 1)
 #define GPIO_TRIGGER_MASK      (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
 
 #ifdef CONFIG_DEBUG_FS
@@ -69,12 +69,7 @@ struct gpio_desc {
 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
 
 #ifdef CONFIG_GPIO_SYSFS
-struct poll_desc {
-       struct work_struct      work;
-       struct sysfs_dirent     *value_sd;
-};
-
-static struct idr pdesc_idr;
+static DEFINE_IDR(dirent_idr);
 #endif
 
 static inline void desc_set_label(struct gpio_desc *d, const char *label)
@@ -325,24 +320,16 @@ static const DEVICE_ATTR(value, 0644,
 
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
-       struct work_struct      *work = priv;
+       struct sysfs_dirent     *value_sd = priv;
 
-       schedule_work(work);
+       sysfs_notify_dirent(value_sd);
        return IRQ_HANDLED;
 }
 
-static void gpio_notify_sysfs(struct work_struct *work)
-{
-       struct poll_desc        *pdesc;
-
-       pdesc = container_of(work, struct poll_desc, work);
-       sysfs_notify_dirent(pdesc->value_sd);
-}
-
 static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
                unsigned long gpio_flags)
 {
-       struct poll_desc        *pdesc;
+       struct sysfs_dirent     *value_sd;
        unsigned long           irq_flags;
        int                     ret, irq, id;
 
@@ -353,18 +340,16 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
        if (irq < 0)
                return -EIO;
 
-       id = desc->flags >> PDESC_ID_SHIFT;
-       pdesc = idr_find(&pdesc_idr, id);
-       if (pdesc) {
-               free_irq(irq, &pdesc->work);
-               cancel_work_sync(&pdesc->work);
-       }
+       id = desc->flags >> ID_SHIFT;
+       value_sd = idr_find(&dirent_idr, id);
+       if (value_sd)
+               free_irq(irq, value_sd);
 
        desc->flags &= ~GPIO_TRIGGER_MASK;
 
        if (!gpio_flags) {
                ret = 0;
-               goto free_sd;
+               goto free_id;
        }
 
        irq_flags = IRQF_SHARED;
@@ -375,55 +360,46 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
                irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
                        IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
 
-       if (!pdesc) {
-               pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
-               if (!pdesc) {
-                       ret = -ENOMEM;
+       if (!value_sd) {
+               value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+               if (!value_sd) {
+                       ret = -ENODEV;
                        goto err_out;
                }
 
                do {
                        ret = -ENOMEM;
-                       if (idr_pre_get(&pdesc_idr, GFP_KERNEL))
-                               ret = idr_get_new_above(&pdesc_idr,
-                                               pdesc, 1, &id);
+                       if (idr_pre_get(&dirent_idr, GFP_KERNEL))
+                               ret = idr_get_new_above(&dirent_idr, value_sd,
+                                                       1, &id);
                } while (ret == -EAGAIN);
 
                if (ret)
-                       goto free_mem;
+                       goto free_sd;
 
                desc->flags &= GPIO_FLAGS_MASK;
-               desc->flags |= (unsigned long)id << PDESC_ID_SHIFT;
+               desc->flags |= (unsigned long)id << ID_SHIFT;
 
-               if (desc->flags >> PDESC_ID_SHIFT != id) {
+               if (desc->flags >> ID_SHIFT != id) {
                        ret = -ERANGE;
                        goto free_id;
                }
-
-               pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
-               if (!pdesc->value_sd) {
-                       ret = -ENODEV;
-                       goto free_id;
-               }
-               INIT_WORK(&pdesc->work, gpio_notify_sysfs);
        }
 
-       ret = request_irq(irq, gpio_sysfs_irq, irq_flags,
-                       "gpiolib", &pdesc->work);
-       if (ret)
-               goto free_sd;
+       ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags,
+                               "gpiolib", value_sd);
+       if (ret < 0)
+               goto free_id;
 
        desc->flags |= gpio_flags;
        return 0;
 
-free_sd:
-       if (pdesc)
-               sysfs_put(pdesc->value_sd);
 free_id:
-       idr_remove(&pdesc_idr, id);
+       idr_remove(&dirent_idr, id);
        desc->flags &= GPIO_FLAGS_MASK;
-free_mem:
-       kfree(pdesc);
+free_sd:
+       if (value_sd)
+               sysfs_put(value_sd);
 err_out:
        return ret;
 }
@@ -994,8 +970,6 @@ static int __init gpiolib_sysfs_init(void)
        unsigned long   flags;
        unsigned        gpio;
 
-       idr_init(&pdesc_idr);
-
        status = class_register(&gpio_class);
        if (status < 0)
                return status;
@@ -1272,7 +1246,7 @@ void gpio_free(unsigned gpio)
        if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
                if (chip->free) {
                        spin_unlock_irqrestore(&gpio_lock, flags);
-                       might_sleep_if(extra_checks && chip->can_sleep);
+                       might_sleep_if(chip->can_sleep);
                        chip->free(chip, gpio - chip->base);
                        spin_lock_irqsave(&gpio_lock, flags);
                }
@@ -1410,7 +1384,7 @@ int gpio_direction_input(unsigned gpio)
 
        spin_unlock_irqrestore(&gpio_lock, flags);
 
-       might_sleep_if(extra_checks && chip->can_sleep);
+       might_sleep_if(chip->can_sleep);
 
        if (status) {
                status = chip->request(chip, gpio);
@@ -1447,6 +1421,8 @@ int gpio_direction_output(unsigned gpio, int value)
 
        spin_lock_irqsave(&gpio_lock, flags);
 
+       if (value !=0 && value !=1)
+               goto fail;
        if (!gpio_is_valid(gpio))
                goto fail;
        chip = desc->chip;
@@ -1463,7 +1439,7 @@ int gpio_direction_output(unsigned gpio, int value)
 
        spin_unlock_irqrestore(&gpio_lock, flags);
 
-       might_sleep_if(extra_checks && chip->can_sleep);
+       might_sleep_if(chip->can_sleep);
 
        if (status) {
                status = chip->request(chip, gpio);
@@ -1491,6 +1467,67 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_output);
 
+/* 
+gpio pull up or pull down
+value = 0, normal
+value = 1, pull up
+value = 2, pull down
+*/
+int gpio_pull_updown(unsigned gpio, unsigned value)
+{
+       unsigned long           flags;
+       struct gpio_chip        *chip;
+       struct gpio_desc        *desc = &gpio_desc[gpio];
+       int                     status = -EINVAL;
+
+       spin_lock_irqsave(&gpio_lock, flags);
+       
+       if (value >3)
+               goto fail;
+       if (!gpio_is_valid(gpio))
+               goto fail;
+       chip = desc->chip;
+       if (!chip || !chip->get || !chip->pull_updown)
+               goto fail;
+       gpio -= chip->base;
+       if (gpio >= chip->ngpio)
+               goto fail;
+       status = gpio_ensure_requested(desc, gpio);
+       if (status < 0)
+               goto fail;
+
+       /* now we know the gpio is valid and chip won't vanish */
+
+       spin_unlock_irqrestore(&gpio_lock, flags);
+
+       might_sleep_if(extra_checks && chip->can_sleep);
+
+       if (status) {
+               status = chip->request(chip, gpio);
+               if (status < 0) {
+                       pr_debug("GPIO-%d: chip request fail, %d\n",
+                               chip->base + gpio, status);
+                       /* and it's not available to anyone else ...
+                        * gpio_request() is the fully clean solution.
+                        */
+                       goto lose;
+               }
+       }
+       status = chip->pull_updown(chip, gpio,value);
+       if (status == 0)
+               clear_bit(FLAG_IS_OUT, &desc->flags);
+       
+lose:
+       return status;
+fail:
+       spin_unlock_irqrestore(&gpio_lock, flags);
+       if (status)
+               pr_debug("%s: gpio-%d status %d\n",
+                       __func__, gpio, status);
+       return status;
+}
+EXPORT_SYMBOL_GPL(gpio_pull_updown);
+
 /**
  * gpio_set_debounce - sets @debounce time for a @gpio
  * @gpio: the gpio to set debounce time
@@ -1521,7 +1558,7 @@ int gpio_set_debounce(unsigned gpio, unsigned debounce)
 
        spin_unlock_irqrestore(&gpio_lock, flags);
 
-       might_sleep_if(extra_checks && chip->can_sleep);
+       might_sleep_if(chip->can_sleep);
 
        return chip->set_debounce(chip, gpio, debounce);
 
@@ -1569,9 +1606,11 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce);
 int __gpio_get_value(unsigned gpio)
 {
        struct gpio_chip        *chip;
-
+       
+       if (!gpio_is_valid(gpio))
+                return -1;
        chip = gpio_to_chip(gpio);
-       WARN_ON(extra_checks && chip->can_sleep);
+       WARN_ON(chip->can_sleep);
        return chip->get ? chip->get(chip, gpio - chip->base) : 0;
 }
 EXPORT_SYMBOL_GPL(__gpio_get_value);
@@ -1589,8 +1628,12 @@ void __gpio_set_value(unsigned gpio, int value)
 {
        struct gpio_chip        *chip;
 
+       if(value !=0 && value !=1)
+               return;
+       if (!gpio_is_valid(gpio))
+                return;
        chip = gpio_to_chip(gpio);
-       WARN_ON(extra_checks && chip->can_sleep);
+       WARN_ON(chip->can_sleep);
        chip->set(chip, gpio - chip->base, value);
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);
@@ -1626,9 +1669,13 @@ EXPORT_SYMBOL_GPL(__gpio_cansleep);
 int __gpio_to_irq(unsigned gpio)
 {
        struct gpio_chip        *chip;
-
+       
+       if (!gpio_is_valid(gpio))
+                return -1;
+       
        chip = gpio_to_chip(gpio);
-       return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO;
+       
+       return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -1;
 }
 EXPORT_SYMBOL_GPL(__gpio_to_irq);