From patchwork Fri May 29 21:38:07 2020
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 246850
List-Id: U-Boot discussion
From: p.yadav at ti.com (Pratyush Yadav)
Date: Sat, 30 May 2020 03:08:07 +0530
Subject: [PATCH v2 1/2] drivers: gpio: Add a managed API to get a GPIO from
the device-tree
In-Reply-To: <20200529213808.2815-1-p.yadav@ti.com>
References: <20200529213808.2815-1-p.yadav@ti.com>
Message-ID: <20200529213808.2815-2-p.yadav@ti.com>
From: Jean-Jacques Hiblot
Add managed functions to get a gpio from the devce-tree, based on a
property name (minus the '-gpios' suffix) and optionally an index.
When the device is unbound, the GPIO is automatically released and the
data structure is freed.
Signed-off-by: Jean-Jacques Hiblot
---
drivers/gpio/gpio-uclass.c | 70 ++++++++++++++++++++++++++++++++++++++
include/asm-generic/gpio.h | 47 +++++++++++++++++++++++++
2 files changed, 117 insertions(+)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 9eeab22eef..fd868608fc 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -6,6 +6,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -1141,6 +1143,74 @@ int gpio_dev_request_index(struct udevice *dev, const char *nodename,
flags, 0, dev);
}
+static void devm_gpiod_release(struct udevice *dev, void *res)
+{
+ dm_gpio_free(dev, res);
+}
+
+static int devm_gpiod_match(struct udevice *dev, void *res, void *data)
+{
+ return res == data;
+}
+
+struct gpio_desc *devm_gpiod_get_index(struct udevice *dev, const char *id,
+ unsigned int index, int flags)
+{
+ int rc;
+ struct gpio_desc *desc;
+ char *propname;
+ static const char suffix[] = "-gpios";
+
+ propname = malloc(strlen(id) + sizeof(suffix));
+ if (!propname) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ strcpy(propname, id);
+ strcat(propname, suffix);
+
+ desc = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc),
+ __GFP_ZERO);
+ if (unlikely(!desc)) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ rc = gpio_request_by_name(dev, propname, index, desc, flags);
+
+end:
+ if (propname)
+ free(propname);
+
+ if (rc)
+ return ERR_PTR(rc);
+
+ devres_add(dev, desc);
+ return desc;
+}
+
+struct gpio_desc *devm_gpiod_get_index_optional(struct udevice *dev,
+ const char *id,
+ unsigned int index,
+ int flags)
+{
+ struct gpio_desc *desc = devm_gpiod_get_index(dev, id, index, flags);
+
+ if (IS_ERR(desc))
+ return NULL;
+
+ return desc;
+}
+
+void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_gpiod_release, devm_gpiod_match, desc);
+ WARN_ON(rc);
+}
+
static int gpio_post_bind(struct udevice *dev)
{
struct udevice *child;
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index e16c2f31d9..76e0e902c4 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -674,4 +674,51 @@ int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
*/
int gpio_get_number(const struct gpio_desc *desc);
+/**
+ * devm_gpiod_get_index - Resource-managed gpiod_get()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @index: index of the GPIO to obtain in the consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get(). GPIO descriptors returned from this function are
+ * automatically disposed on driver detach.
+ * Return the GPIO descriptor corresponding to the function con_id of device
+ * dev, -ENOENT if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
+ */
+struct gpio_desc *devm_gpiod_get_index(struct udevice *dev, const char *id,
+ unsigned int index, int flags);
+
+#define devm_gpiod_get(dev, id, flags) devm_gpiod_get_index(dev, id, 0, flags)
+/**
+ * gpiod_get_optional - obtain an optional GPIO for a given GPIO function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @index: index of the GPIO to obtain in the consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This is equivalent to devm_gpiod_get(), except that when no GPIO was
+ * assigned to the requested function it will return NULL. This is convenient
+ * for drivers that need to handle optional GPIOs.
+ */
+struct gpio_desc *devm_gpiod_get_index_optional(struct udevice *dev,
+ const char *id,
+ unsigned int index,
+ int flags);
+
+#define devm_gpiod_get_optional(dev, id, flags) \
+ devm_gpiod_get_index_optional(dev, id, 0, flags)
+
+/**
+ * devm_gpiod_put - Resource-managed gpiod_put()
+ * @dev: GPIO consumer
+ * @desc: GPIO descriptor to dispose of
+ *
+ * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or
+ * devm_gpiod_get_index(). Normally this function will not be called as the GPIO
+ * will be disposed of by the resource management code.
+ */
+void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc);
+
#endif /* _ASM_GENERIC_GPIO_H_ */
From patchwork Fri May 29 21:38:08 2020
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 246849
List-Id: U-Boot discussion
From: p.yadav at ti.com (Pratyush Yadav)
Date: Sat, 30 May 2020 03:08:08 +0530
Subject: [PATCH v2 2/2] test: gpio: Add tests for the managed API
In-Reply-To: <20200529213808.2815-1-p.yadav@ti.com>
References: <20200529213808.2815-1-p.yadav@ti.com>
Message-ID: <20200529213808.2815-3-p.yadav@ti.com>
From: Jean-Jacques Hiblot
Add a test to verify that GPIOs can be acquired/released using the managed
API. Also check that the GPIOs are released when the consumer device is
removed.
Signed-off-by: Jean-Jacques Hiblot
---
arch/sandbox/dts/test.dts | 10 ++++
test/dm/gpio.c | 102 ++++++++++++++++++++++++++++++++++++++
2 files changed, 112 insertions(+)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 5ce5e28476..a8618ccade 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -107,6 +107,9 @@
<&gpio_c 5 GPIO_IN>,
<&gpio_c 6 (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN)>,
<&gpio_c 7 (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_SOURCE)>;
+ test4-gpios = <&gpio_a 14>, <&gpio_b 4 1 3 2 1>;
+ test5-gpios = <&gpio_a 19>;
+
int-value = <1234>;
uint-value = <(-1234)>;
int64-value = /bits/ 64 <0x1111222233334444>;
@@ -114,6 +117,13 @@
interrupts-extended = <&irq 3 0>;
};
+ another-test {
+ reg = <0 2>;
+ compatible = "denx,u-boot-fdt-test";
+ test4-gpios = <&gpio_a 14>, <&gpio_b 4 1 3 2 1>;
+ test5-gpios = <&gpio_a 19>;
+ };
+
junk {
reg = <1 1>;
compatible = "not,compatible";
diff --git a/test/dm/gpio.c b/test/dm/gpio.c
index b5ee4e4f87..40bea32b13 100644
--- a/test/dm/gpio.c
+++ b/test/dm/gpio.c
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -385,3 +386,104 @@ static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_gpio_get_dir_flags, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test that we can get/release GPIOs using managed API */
+static int dm_test_gpio_devm(struct unit_test_state *uts)
+{
+ static const u32 flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE;
+ struct gpio_desc *desc1, *desc2, *desc3, *desc_err;
+ struct udevice *dev;
+ struct udevice *dev2;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+ &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "another-test",
+ &dev2));
+
+ /* Get 3 GPIOs from 'a-test' dev */
+ desc1 = devm_gpiod_get_index(dev, "test4", 0, flags);
+ ut_assert(!IS_ERR(desc1));
+ desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+ desc3 = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
+ ut_assert(!IS_ERR(desc3));
+ ut_assert(desc3);
+
+ /*
+ * Try get the same 3 GPIOs from 'a-test' and 'another-test' devices.
+ * check that it fails
+ */
+ desc_err = devm_gpiod_get_index(dev, "test4", 0, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev, "test4", 1, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+ desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+
+ /* Try get GPIOs outside of the list */
+ desc_err = devm_gpiod_get_index(dev, "test4", 2, flags);
+ ut_assert(IS_ERR(desc_err));
+ desc_err = devm_gpiod_get_index_optional(dev, "test5", 1, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+
+ /* Manipulate the GPIOs */
+ ut_assertok(dm_gpio_set_value(desc1, 1));
+ ut_asserteq(1, dm_gpio_get_value(desc1));
+ ut_assertok(dm_gpio_set_value(desc1, 0));
+ ut_asserteq(0, dm_gpio_get_value(desc1));
+
+ ut_assertok(dm_gpio_set_value(desc2, 1));
+ ut_asserteq(1, dm_gpio_get_value(desc2));
+ ut_assertok(dm_gpio_set_value(desc2, 0));
+ ut_asserteq(0, dm_gpio_get_value(desc2));
+
+ ut_assertok(dm_gpio_set_value(desc3, 1));
+ ut_asserteq(1, dm_gpio_get_value(desc3));
+ ut_assertok(dm_gpio_set_value(desc3, 0));
+ ut_asserteq(0, dm_gpio_get_value(desc3));
+
+ /* Check that the GPIO cannot be owned by more than one device */
+ desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+
+ /*
+ * Release one GPIO and check that we can get it back using
+ * 'another-test' and then 'a-test'
+ */
+ devm_gpiod_put(dev, desc2);
+ desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+
+ devm_gpiod_put(dev2, desc2);
+ desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+
+ /* Release one GPIO before removing the 'a-test' dev. */
+ devm_gpiod_put(dev, desc2);
+ device_remove(dev, DM_REMOVE_NORMAL);
+
+ /* All the GPIOs must have been freed. We should be able to claim
+ * them with the 'another-test' device.
+ */
+ desc1 = devm_gpiod_get_index(dev2, "test4", 0, flags);
+ ut_assert(!IS_ERR(desc1));
+ desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+ desc3 = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+ ut_assert(!IS_ERR(desc3));
+ ut_assert(desc3);
+
+ device_remove(dev2, DM_REMOVE_NORMAL);
+ return 0;
+}
+DM_TEST(dm_test_gpio_devm, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);