]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
gpio: gpiolib: Generalise state persistence beyond sleep
authorAndrew Jeffery <andrew@aj.id.au>
Thu, 30 Nov 2017 03:55:24 +0000 (14:25 +1030)
committerLinus Walleij <linus.walleij@linaro.org>
Sat, 2 Dec 2017 21:42:34 +0000 (22:42 +0100)
General support for state persistence is added to gpiolib with the
introduction of a new pinconf parameter to propagate the request to
hardware. The existing persistence support for sleep is adapted to
include hardware support if the GPIO driver provides it. Persistence
continues to be enabled by default; in-kernel consumers can opt out, but
userspace (currently) does not have a choice.

The *_SLEEP_MAY_LOSE_VALUE and *_SLEEP_MAINTAIN_VALUE symbols are
renamed, dropping the SLEEP prefix to reflect that the concept is no
longer sleep-specific.  I feel that renaming to just *_MAY_LOSE_VALUE
could initially be misinterpreted, so I've further changed the symbols
to *_TRANSITORY and *_PERSISTENT to address this.

The sysfs interface is modified only to keep consistency with the
chardev interface in enforcing persistence for userspace exports.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib-sysfs.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
include/dt-bindings/gpio/gpio.h
include/linux/gpio/consumer.h
include/linux/gpio/machine.h
include/linux/of_gpio.h
include/linux/pinctrl/pinconf-generic.h

index e0d59e61b52fa6aa53e7830ae1ab1c59ceec2453..4a2b8d3397c79d8782ed24bbfb732d3bb407c490 100644 (file)
@@ -153,8 +153,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
                        *flags |= GPIO_OPEN_SOURCE;
        }
 
-       if (of_flags & OF_GPIO_SLEEP_MAY_LOSE_VALUE)
-               *flags |= GPIO_SLEEP_MAY_LOSE_VALUE;
+       if (of_flags & OF_GPIO_TRANSITORY)
+               *flags |= GPIO_TRANSITORY;
 
        return desc;
 }
@@ -214,6 +214,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
 
        if (xlate_flags & OF_GPIO_ACTIVE_LOW)
                *lflags |= GPIO_ACTIVE_LOW;
+       if (xlate_flags & OF_GPIO_TRANSITORY)
+               *lflags |= GPIO_TRANSITORY;
 
        if (of_property_read_bool(np, "input"))
                *dflags |= GPIOD_IN;
index 3f454eaf2101fcce2da580216ba8932d31ce70f3..0bd472ffb0720df058bec4ac9ee243e7658aa998 100644 (file)
@@ -474,11 +474,15 @@ static ssize_t export_store(struct class *class,
                        status = -ENODEV;
                goto done;
        }
-       status = gpiod_export(desc, true);
-       if (status < 0)
-               gpiod_free(desc);
-       else
-               set_bit(FLAG_SYSFS, &desc->flags);
+
+       status = gpiod_set_transitory(desc, false);
+       if (!status) {
+               status = gpiod_export(desc, true);
+               if (status < 0)
+                       gpiod_free(desc);
+               else
+                       set_bit(FLAG_SYSFS, &desc->flags);
+       }
 
 done:
        if (status)
index ec0fd95bbf350276929870f412c5fa8378a30c65..56eec094184cd86d80baa91c1ae9118988718fea 100644 (file)
@@ -514,6 +514,10 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
                if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
                        set_bit(FLAG_OPEN_SOURCE, &desc->flags);
 
+               ret = gpiod_set_transitory(desc, false);
+               if (ret < 0)
+                       goto out_free_descs;
+
                /*
                 * Lines have to be requested explicitly for input
                 * or output, else the line will be treated "as is".
@@ -2529,6 +2533,49 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
 }
 EXPORT_SYMBOL_GPL(gpiod_set_debounce);
 
+/**
+ * gpiod_set_transitory - Lose or retain GPIO state on suspend or reset
+ * @desc: descriptor of the GPIO for which to configure persistence
+ * @transitory: True to lose state on suspend or reset, false for persistence
+ *
+ * Returns:
+ * 0 on success, otherwise a negative error code.
+ */
+int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
+{
+       struct gpio_chip *chip;
+       unsigned long packed;
+       int gpio;
+       int rc;
+
+       /*
+        * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for
+        * persistence state.
+        */
+       if (transitory)
+               set_bit(FLAG_TRANSITORY, &desc->flags);
+       else
+               clear_bit(FLAG_TRANSITORY, &desc->flags);
+
+       /* If the driver supports it, set the persistence state now */
+       chip = desc->gdev->chip;
+       if (!chip->set_config)
+               return 0;
+
+       packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE,
+                                         !transitory);
+       gpio = gpio_chip_hwgpio(desc);
+       rc = chip->set_config(chip, gpio, packed);
+       if (rc == -ENOTSUPP) {
+               dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n",
+                               gpio);
+               return 0;
+       }
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(gpiod_set_transitory);
+
 /**
  * gpiod_is_active_low - test whether a GPIO is active-low or not
  * @desc: the gpio descriptor to test
@@ -3116,8 +3163,7 @@ bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset)
        if (offset >= chip->ngpio)
                return false;
 
-       return !test_bit(FLAG_SLEEP_MAY_LOSE_VALUE,
-                        &chip->gpiodev->descs[offset].flags);
+       return !test_bit(FLAG_TRANSITORY, &chip->gpiodev->descs[offset].flags);
 }
 EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent);
 
@@ -3554,8 +3600,10 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
 
        if (lflags & GPIO_OPEN_SOURCE)
                set_bit(FLAG_OPEN_SOURCE, &desc->flags);
-       if (lflags & GPIO_SLEEP_MAY_LOSE_VALUE)
-               set_bit(FLAG_SLEEP_MAY_LOSE_VALUE, &desc->flags);
+
+       status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY));
+       if (status < 0)
+               return status;
 
        /* No particular flag request, return here... */
        if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
@@ -3669,6 +3717,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
        bool active_low = false;
        bool single_ended = false;
        bool open_drain = false;
+       bool transitory = false;
        int ret;
 
        if (!fwnode)
@@ -3683,6 +3732,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
                        active_low = flags & OF_GPIO_ACTIVE_LOW;
                        single_ended = flags & OF_GPIO_SINGLE_ENDED;
                        open_drain = flags & OF_GPIO_OPEN_DRAIN;
+                       transitory = flags & OF_GPIO_TRANSITORY;
                }
        } else if (is_acpi_node(fwnode)) {
                struct acpi_gpio_info info;
@@ -3711,6 +3761,9 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
                        lflags |= GPIO_OPEN_SOURCE;
        }
 
+       if (transitory)
+               lflags |= GPIO_TRANSITORY;
+
        ret = gpiod_configure_flags(desc, propname, lflags, dflags);
        if (ret < 0) {
                gpiod_put(desc);
index cd4622863fe1ec07535ea81dad0c1e209ba6467c..5e1f7cc6eeb6d385958aa455a3cb42967dc27c26 100644 (file)
@@ -209,7 +209,7 @@ struct gpio_desc {
 #define FLAG_OPEN_SOURCE 8     /* Gpio is open source type */
 #define FLAG_USED_AS_IRQ 9     /* GPIO is connected to an IRQ */
 #define FLAG_IS_HOGGED 11      /* GPIO is hogged */
-#define FLAG_SLEEP_MAY_LOSE_VALUE 12   /* GPIO may lose value in sleep */
+#define FLAG_TRANSITORY 12     /* GPIO may lose value in sleep or reset */
 
        /* Connection label */
        const char              *label;
index dd549ff04295367d22d8f75696cf02c0066b6a20..2cc10ae4bbb7ba29bb7503992e1bd5c0036acc27 100644 (file)
@@ -29,8 +29,8 @@
 #define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN)
 #define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE)
 
-/* Bit 3 express GPIO suspend/resume persistence */
-#define GPIO_SLEEP_MAINTAIN_VALUE 0
-#define GPIO_SLEEP_MAY_LOSE_VALUE 8
+/* Bit 3 express GPIO suspend/resume and reset persistence */
+#define GPIO_PERSISTENT 0
+#define GPIO_TRANSITORY 8
 
 #endif
index 7447d85dbe2f8f387d282c83e11da308895f7f14..540b2c142493c64f429d3d1c4a7ef1989a708a86 100644 (file)
@@ -139,6 +139,7 @@ void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
                                        int *value_array);
 
 int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
+int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
 
 int gpiod_is_active_low(const struct gpio_desc *desc);
 int gpiod_cansleep(const struct gpio_desc *desc);
@@ -431,6 +432,13 @@ static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
        return -ENOSYS;
 }
 
+static inline int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
+{
+       /* GPIO can never have been requested */
+       WARN_ON(1);
+       return -ENOSYS;
+}
+
 static inline int gpiod_is_active_low(const struct gpio_desc *desc)
 {
        /* GPIO can never have been requested */
index 846be7c69a52bd6a59c877208befe4efbfd58c77..b2f2dc638463ce9da4c0fc0feaf8f391d11927d3 100644 (file)
@@ -10,8 +10,8 @@ enum gpio_lookup_flags {
        GPIO_ACTIVE_LOW = (1 << 0),
        GPIO_OPEN_DRAIN = (1 << 1),
        GPIO_OPEN_SOURCE = (1 << 2),
-       GPIO_SLEEP_MAINTAIN_VALUE = (0 << 3),
-       GPIO_SLEEP_MAY_LOSE_VALUE = (1 << 3),
+       GPIO_PERSISTENT = (0 << 3),
+       GPIO_TRANSITORY = (1 << 3),
 };
 
 /**
index 1fe205582111489d4efac1cce323a06ab404212c..18a7f03e11826ff545bc979f99983f5eaabbaa9e 100644 (file)
@@ -31,7 +31,7 @@ enum of_gpio_flags {
        OF_GPIO_ACTIVE_LOW = 0x1,
        OF_GPIO_SINGLE_ENDED = 0x2,
        OF_GPIO_OPEN_DRAIN = 0x4,
-       OF_GPIO_SLEEP_MAY_LOSE_VALUE = 0x8,
+       OF_GPIO_TRANSITORY = 0x8,
 };
 
 #ifdef CONFIG_OF_GPIO
index ec6dadcc1fde81cd2ef36d6a055c499275efb9a4..6c06806411084d1e7da6abbf5bce7012dc53c172 100644 (file)
@@ -94,6 +94,7 @@
  *     or latch delay (on outputs) this parameter (in a custom format)
  *     specifies the clock skew or latch delay. It typically controls how
  *     many double inverters are put in front of the line.
+ * @PIN_CONFIG_PERSIST_STATE: retain pin state across sleep or controller reset
  * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if
  *     you need to pass in custom configurations to the pin controller, use
  *     PIN_CONFIG_END+1 as the base offset.
@@ -122,6 +123,7 @@ enum pin_config_param {
        PIN_CONFIG_SLEEP_HARDWARE_STATE,
        PIN_CONFIG_SLEW_RATE,
        PIN_CONFIG_SKEW_DELAY,
+       PIN_CONFIG_PERSIST_STATE,
        PIN_CONFIG_END = 0x7F,
        PIN_CONFIG_MAX = 0xFF,
 };