of: unittest: add overlay gpio test to catch gpio hog problem

[ 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>
This commit is contained in:
Frank Rowand 2020-02-20 12:40:20 -06:00 committed by Greg Kroah-Hartman
parent 89485251f6
commit f6997a2416
8 changed files with 370 additions and 1 deletions

View 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 += -@

View File

@ -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";
};
};
};

View File

@ -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";
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};

View File

@ -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";
};
};
};

View File

@ -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),