| From 13755d596283c2cd558c7be2ea182544674ab498 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 20 Feb 2020 12:40:20 -0600 |
| Subject: of: unittest: add overlay gpio test to catch gpio hog problem |
| |
| From: Frank Rowand <frank.rowand@sony.com> |
| |
| [ Upstream commit f4056e705b2ef7f123a188f6aee23ade70e7d793 ] |
| |
| 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> |
| Stable-dep-of: 607aad1e4356 ("of: unittest: Fix compile in the non-dynamic case") |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/of/unittest-data/Makefile | 8 +- |
| drivers/of/unittest-data/overlay_gpio_01.dts | 23 ++ |
| drivers/of/unittest-data/overlay_gpio_02a.dts | 16 ++ |
| drivers/of/unittest-data/overlay_gpio_02b.dts | 16 ++ |
| drivers/of/unittest-data/overlay_gpio_03.dts | 23 ++ |
| drivers/of/unittest-data/overlay_gpio_04a.dts | 16 ++ |
| drivers/of/unittest-data/overlay_gpio_04b.dts | 16 ++ |
| drivers/of/unittest.c | 253 ++++++++++++++++++ |
| 8 files changed, 370 insertions(+), 1 deletion(-) |
| create mode 100644 drivers/of/unittest-data/overlay_gpio_01.dts |
| create mode 100644 drivers/of/unittest-data/overlay_gpio_02a.dts |
| create mode 100644 drivers/of/unittest-data/overlay_gpio_02b.dts |
| create mode 100644 drivers/of/unittest-data/overlay_gpio_03.dts |
| create mode 100644 drivers/of/unittest-data/overlay_gpio_04a.dts |
| create mode 100644 drivers/of/unittest-data/overlay_gpio_04b.dts |
| |
| diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile |
| index 9b6807065827..009f4045c8e4 100644 |
| --- a/drivers/of/unittest-data/Makefile |
| +++ b/drivers/of/unittest-data/Makefile |
| @@ -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 |
| index 000000000000..699ff104ae10 |
| --- /dev/null |
| +++ b/drivers/of/unittest-data/overlay_gpio_01.dts |
| @@ -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 |
| index 000000000000..ec59aff6ed47 |
| --- /dev/null |
| +++ b/drivers/of/unittest-data/overlay_gpio_02a.dts |
| @@ -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 |
| index 000000000000..43ce111d41ce |
| --- /dev/null |
| +++ b/drivers/of/unittest-data/overlay_gpio_02b.dts |
| @@ -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 |
| index 000000000000..6e0312340a1b |
| --- /dev/null |
| +++ b/drivers/of/unittest-data/overlay_gpio_03.dts |
| @@ -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 |
| index 000000000000..7b1e04ebfa7a |
| --- /dev/null |
| +++ b/drivers/of/unittest-data/overlay_gpio_04a.dts |
| @@ -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 |
| index 000000000000..a14e95c6699a |
| --- /dev/null |
| +++ b/drivers/of/unittest-data/overlay_gpio_04b.dts |
| @@ -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"; |
| + }; |
| + }; |
| +}; |
| diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c |
| index 1ed470b03cd7..7585df5e3daf 100644 |
| --- a/drivers/of/unittest.c |
| +++ b/drivers/of/unittest.c |
| @@ -23,6 +23,7 @@ |
| |
| #include <linux/i2c.h> |
| #include <linux/i2c-mux.h> |
| +#include <linux/gpio/driver.h> |
| |
| #include <linux/bitops.h> |
| |
| @@ -45,6 +46,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; |
| @@ -2114,6 +2206,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; |
| @@ -2173,6 +2412,8 @@ static void __init of_unittest_overlay(void) |
| of_unittest_overlay_i2c_cleanup(); |
| #endif |
| |
| + of_unittest_overlay_gpio(); |
| + |
| of_unittest_destroy_tracked_overlays(); |
| |
| out: |
| @@ -2226,6 +2467,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); |
| @@ -2250,6 +2497,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), |
| -- |
| 2.43.0 |
| |