OSDN Git Service

of: unittest: add overlay gpio test to catch gpio hog problem
authorFrank Rowand <frank.rowand@sony.com>
Thu, 20 Feb 2020 18:40:20 +0000 (12:40 -0600)
committerRob Herring <robh@kernel.org>
Wed, 26 Feb 2020 16:42:04 +0000 (10:42 -0600)
Geert reports that gpio hog nodes are not properly processed when
the gpio hog node is added via an overlay reply and provides an
RFC patch to fix the problem [1].

Add a unittest that shows the problem.  Unittest will report "1 failed"
test before applying Geert's RFC patch and "0 failed" after applying
Geert's RFC patch.

[1] https://lore.kernel.org/linux-devicetree/20191230133852.5890-1-geert+renesas@glider.be/

Signed-off-by: Frank Rowand <frank.rowand@sony.com>
Signed-off-by: Rob Herring <robh@kernel.org>
drivers/of/unittest-data/Makefile
drivers/of/unittest-data/overlay_gpio_01.dts [new file with mode: 0644]
drivers/of/unittest-data/overlay_gpio_02a.dts [new file with mode: 0644]
drivers/of/unittest-data/overlay_gpio_02b.dts [new file with mode: 0644]
drivers/of/unittest-data/overlay_gpio_03.dts [new file with mode: 0644]
drivers/of/unittest-data/overlay_gpio_04a.dts [new file with mode: 0644]
drivers/of/unittest-data/overlay_gpio_04b.dts [new file with mode: 0644]
drivers/of/unittest.c

index 9b68070..009f404 100644 (file)
@@ -21,7 +21,13 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
                            overlay_bad_add_dup_prop.dtb.o \
                            overlay_bad_phandle.dtb.o \
                            overlay_bad_symbol.dtb.o \
-                           overlay_base.dtb.o
+                           overlay_base.dtb.o \
+                           overlay_gpio_01.dtb.o \
+                           overlay_gpio_02a.dtb.o \
+                           overlay_gpio_02b.dtb.o \
+                           overlay_gpio_03.dtb.o \
+                           overlay_gpio_04a.dtb.o \
+                           overlay_gpio_04b.dtb.o
 
 # enable creation of __symbols__ node
 DTC_FLAGS_overlay += -@
diff --git a/drivers/of/unittest-data/overlay_gpio_01.dts b/drivers/of/unittest-data/overlay_gpio_01.dts
new file mode 100644 (file)
index 0000000..699ff10
--- /dev/null
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&unittest_test_bus {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       gpio@0 {
+               compatible = "unittest-gpio";
+               reg = <0>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               ngpios = <2>;
+               gpio-line-names = "line-A", "line-B";
+
+               line-b {
+                       gpio-hog;
+                       gpios = <2 0>;
+                       input;
+                       line-name = "line-B-input";
+               };
+       };
+};
diff --git a/drivers/of/unittest-data/overlay_gpio_02a.dts b/drivers/of/unittest-data/overlay_gpio_02a.dts
new file mode 100644 (file)
index 0000000..ec59aff
--- /dev/null
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&unittest_test_bus {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       gpio@2 {
+               compatible = "unittest-gpio";
+               reg = <2>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               ngpios = <2>;
+               gpio-line-names = "line-A", "line-B";
+       };
+};
diff --git a/drivers/of/unittest-data/overlay_gpio_02b.dts b/drivers/of/unittest-data/overlay_gpio_02b.dts
new file mode 100644 (file)
index 0000000..43ce111
--- /dev/null
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&unittest_test_bus {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       gpio@2 {
+               line-a {
+                       gpio-hog;
+                       gpios = <1 0>;
+                       input;
+                       line-name = "line-A-input";
+               };
+       };
+};
diff --git a/drivers/of/unittest-data/overlay_gpio_03.dts b/drivers/of/unittest-data/overlay_gpio_03.dts
new file mode 100644 (file)
index 0000000..6e03123
--- /dev/null
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&unittest_test_bus {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       gpio@3 {
+               compatible = "unittest-gpio";
+               reg = <3>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               ngpios = <2>;
+               gpio-line-names = "line-A", "line-B", "line-C", "line-D";
+
+               line-d {
+                       gpio-hog;
+                       gpios = <4 0>;
+                       input;
+                       line-name = "line-D-input";
+               };
+       };
+};
diff --git a/drivers/of/unittest-data/overlay_gpio_04a.dts b/drivers/of/unittest-data/overlay_gpio_04a.dts
new file mode 100644 (file)
index 0000000..7b1e04e
--- /dev/null
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&unittest_test_bus {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       gpio@4 {
+               compatible = "unittest-gpio";
+               reg = <4>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               ngpios = <2>;
+               gpio-line-names = "line-A", "line-B", "line-C", "line-D";
+       };
+};
diff --git a/drivers/of/unittest-data/overlay_gpio_04b.dts b/drivers/of/unittest-data/overlay_gpio_04b.dts
new file mode 100644 (file)
index 0000000..a14e95c
--- /dev/null
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&unittest_test_bus {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       gpio@4 {
+               line-c {
+                       gpio-hog;
+                       gpios = <3 0>;
+                       input;
+                       line-name = "line-C-input";
+               };
+       };
+};
index 68b8758..6059bb3 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
+#include <linux/gpio/driver.h>
 
 #include <linux/bitops.h>
 
@@ -46,6 +47,97 @@ static struct unittest_results {
        failed; \
 })
 
+/*
+ * Expected message may have a message level other than KERN_INFO.
+ * Print the expected message only if the current loglevel will allow
+ * the actual message to print.
+ */
+#define EXPECT_BEGIN(level, fmt, ...) \
+       printk(level pr_fmt("EXPECT \\ : ") fmt, ##__VA_ARGS__)
+
+#define EXPECT_END(level, fmt, ...) \
+       printk(level pr_fmt("EXPECT / : ") fmt, ##__VA_ARGS__)
+
+struct unittest_gpio_dev {
+       struct gpio_chip chip;
+};
+
+static int unittest_gpio_chip_request_count;
+static int unittest_gpio_probe_count;
+static int unittest_gpio_probe_pass_count;
+
+static int unittest_gpio_chip_request(struct gpio_chip *chip, unsigned int offset)
+{
+       unittest_gpio_chip_request_count++;
+
+       pr_debug("%s(): %s %d %d\n", __func__, chip->label, offset,
+                unittest_gpio_chip_request_count);
+       return 0;
+}
+
+static int unittest_gpio_probe(struct platform_device *pdev)
+{
+       struct unittest_gpio_dev *devptr;
+       int ret;
+
+       unittest_gpio_probe_count++;
+
+       devptr = kzalloc(sizeof(*devptr), GFP_KERNEL);
+       if (!devptr)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, devptr);
+
+       devptr->chip.of_node = pdev->dev.of_node;
+       devptr->chip.label = "of-unittest-gpio";
+       devptr->chip.base = -1; /* dynamic allocation */
+       devptr->chip.ngpio = 5;
+       devptr->chip.request = unittest_gpio_chip_request;
+
+       ret = gpiochip_add_data(&devptr->chip, NULL);
+
+       unittest(!ret,
+                "gpiochip_add_data() for node @%pOF failed, ret = %d\n", devptr->chip.of_node, ret);
+
+       if (!ret)
+               unittest_gpio_probe_pass_count++;
+       return ret;
+}
+
+static int unittest_gpio_remove(struct platform_device *pdev)
+{
+       struct unittest_gpio_dev *gdev = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       struct device_node *np = pdev->dev.of_node;
+
+       dev_dbg(dev, "%s for node @%pOF\n", __func__, np);
+
+       if (!gdev)
+               return -EINVAL;
+
+       if (gdev->chip.base != -1)
+               gpiochip_remove(&gdev->chip);
+
+       platform_set_drvdata(pdev, NULL);
+       kfree(pdev);
+
+       return 0;
+}
+
+static const struct of_device_id unittest_gpio_id[] = {
+       { .compatible = "unittest-gpio", },
+       {}
+};
+
+static struct platform_driver unittest_gpio_driver = {
+       .probe  = unittest_gpio_probe,
+       .remove = unittest_gpio_remove,
+       .driver = {
+               .name           = "unittest-gpio",
+               .of_match_table = of_match_ptr(unittest_gpio_id),
+       },
+};
+
 static void __init of_unittest_find_node_by_name(void)
 {
        struct device_node *np;
@@ -2183,6 +2275,153 @@ static inline void of_unittest_overlay_i2c_15(void) { }
 
 #endif
 
+static void __init of_unittest_overlay_gpio(void)
+{
+       int chip_request_count;
+       int probe_pass_count;
+       int ret;
+
+       /*
+        * tests: apply overlays before registering driver
+        * Similar to installing a driver as a module, the
+        * driver is registered after applying the overlays.
+        *
+        * - apply overlay_gpio_01
+        * - apply overlay_gpio_02a
+        * - apply overlay_gpio_02b
+        * - register driver
+        *
+        * register driver will result in
+        *   - probe and processing gpio hog for overlay_gpio_01
+        *   - probe for overlay_gpio_02a
+        *   - processing gpio for overlay_gpio_02b
+        */
+
+       probe_pass_count = unittest_gpio_probe_pass_count;
+       chip_request_count = unittest_gpio_chip_request_count;
+
+       /*
+        * overlay_gpio_01 contains gpio node and child gpio hog node
+        * overlay_gpio_02a contains gpio node
+        * overlay_gpio_02b contains child gpio hog node
+        */
+
+       unittest(overlay_data_apply("overlay_gpio_01", NULL),
+                "Adding overlay 'overlay_gpio_01' failed\n");
+
+       unittest(overlay_data_apply("overlay_gpio_02a", NULL),
+                "Adding overlay 'overlay_gpio_02a' failed\n");
+
+       unittest(overlay_data_apply("overlay_gpio_02b", NULL),
+                "Adding overlay 'overlay_gpio_02b' failed\n");
+
+       /*
+        * messages are the result of the probes, after the
+        * driver is registered
+        */
+
+       EXPECT_BEGIN(KERN_INFO,
+                    "GPIO line <<int>> (line-B-input) hogged as input\n");
+
+       EXPECT_BEGIN(KERN_INFO,
+                    "GPIO line <<int>> (line-A-input) hogged as input\n");
+
+       ret = platform_driver_register(&unittest_gpio_driver);
+       if (unittest(ret == 0, "could not register unittest gpio driver\n"))
+               return;
+
+       EXPECT_END(KERN_INFO,
+                  "GPIO line <<int>> (line-A-input) hogged as input\n");
+       EXPECT_END(KERN_INFO,
+                  "GPIO line <<int>> (line-B-input) hogged as input\n");
+
+       unittest(probe_pass_count + 2 == unittest_gpio_probe_pass_count,
+                "unittest_gpio_probe() failed or not called\n");
+
+       unittest(chip_request_count + 2 == unittest_gpio_chip_request_count,
+                "unittest_gpio_chip_request() called %d times (expected 1 time)\n",
+                unittest_gpio_chip_request_count - chip_request_count);
+
+       /*
+        * tests: apply overlays after registering driver
+        *
+        * Similar to a driver built-in to the kernel, the
+        * driver is registered before applying the overlays.
+        *
+        * overlay_gpio_03 contains gpio node and child gpio hog node
+        *
+        * - apply overlay_gpio_03
+        *
+        * apply overlay will result in
+        *   - probe and processing gpio hog.
+        */
+
+       probe_pass_count = unittest_gpio_probe_pass_count;
+       chip_request_count = unittest_gpio_chip_request_count;
+
+       EXPECT_BEGIN(KERN_INFO,
+                    "GPIO line <<int>> (line-D-input) hogged as input\n");
+
+       /* overlay_gpio_03 contains gpio node and child gpio hog node */
+
+       unittest(overlay_data_apply("overlay_gpio_03", NULL),
+                "Adding overlay 'overlay_gpio_03' failed\n");
+
+       EXPECT_END(KERN_INFO,
+                  "GPIO line <<int>> (line-D-input) hogged as input\n");
+
+       unittest(probe_pass_count + 1 == unittest_gpio_probe_pass_count,
+                "unittest_gpio_probe() failed or not called\n");
+
+       unittest(chip_request_count + 1 == unittest_gpio_chip_request_count,
+                "unittest_gpio_chip_request() called %d times (expected 1 time)\n",
+                unittest_gpio_chip_request_count - chip_request_count);
+
+       /*
+        * overlay_gpio_04a contains gpio node
+        *
+        * - apply overlay_gpio_04a
+        *
+        * apply the overlay will result in
+        *   - probe for overlay_gpio_04a
+        */
+
+       probe_pass_count = unittest_gpio_probe_pass_count;
+       chip_request_count = unittest_gpio_chip_request_count;
+
+       /* overlay_gpio_04a contains gpio node */
+
+       unittest(overlay_data_apply("overlay_gpio_04a", NULL),
+                "Adding overlay 'overlay_gpio_04a' failed\n");
+
+       unittest(probe_pass_count + 1 == unittest_gpio_probe_pass_count,
+                "unittest_gpio_probe() failed or not called\n");
+
+       /*
+        * overlay_gpio_04b contains child gpio hog node
+        *
+        * - apply overlay_gpio_04b
+        *
+        * apply the overlay will result in
+        *   - processing gpio for overlay_gpio_04b
+        */
+
+       EXPECT_BEGIN(KERN_INFO,
+                    "GPIO line <<int>> (line-C-input) hogged as input\n");
+
+       /* overlay_gpio_04b contains child gpio hog node */
+
+       unittest(overlay_data_apply("overlay_gpio_04b", NULL),
+                "Adding overlay 'overlay_gpio_04b' failed\n");
+
+       EXPECT_END(KERN_INFO,
+                  "GPIO line <<int>> (line-C-input) hogged as input\n");
+
+       unittest(chip_request_count + 1 == unittest_gpio_chip_request_count,
+                "unittest_gpio_chip_request() called %d times (expected 1 time)\n",
+                unittest_gpio_chip_request_count - chip_request_count);
+}
+
 static void __init of_unittest_overlay(void)
 {
        struct device_node *bus_np = NULL;
@@ -2242,6 +2481,8 @@ static void __init of_unittest_overlay(void)
        of_unittest_overlay_i2c_cleanup();
 #endif
 
+       of_unittest_overlay_gpio();
+
        of_unittest_destroy_tracked_overlays();
 
 out:
@@ -2295,6 +2536,12 @@ OVERLAY_INFO_EXTERN(overlay_11);
 OVERLAY_INFO_EXTERN(overlay_12);
 OVERLAY_INFO_EXTERN(overlay_13);
 OVERLAY_INFO_EXTERN(overlay_15);
+OVERLAY_INFO_EXTERN(overlay_gpio_01);
+OVERLAY_INFO_EXTERN(overlay_gpio_02a);
+OVERLAY_INFO_EXTERN(overlay_gpio_02b);
+OVERLAY_INFO_EXTERN(overlay_gpio_03);
+OVERLAY_INFO_EXTERN(overlay_gpio_04a);
+OVERLAY_INFO_EXTERN(overlay_gpio_04b);
 OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node);
 OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop);
 OVERLAY_INFO_EXTERN(overlay_bad_phandle);
@@ -2319,6 +2566,12 @@ static struct overlay_info overlays[] = {
        OVERLAY_INFO(overlay_12, 0),
        OVERLAY_INFO(overlay_13, 0),
        OVERLAY_INFO(overlay_15, 0),
+       OVERLAY_INFO(overlay_gpio_01, 0),
+       OVERLAY_INFO(overlay_gpio_02a, 0),
+       OVERLAY_INFO(overlay_gpio_02b, 0),
+       OVERLAY_INFO(overlay_gpio_03, 0),
+       OVERLAY_INFO(overlay_gpio_04a, 0),
+       OVERLAY_INFO(overlay_gpio_04b, 0),
        OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL),
        OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL),
        OVERLAY_INFO(overlay_bad_phandle, -EINVAL),