lkml.org 
[lkml]   [2014]   [Oct]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v8 2/8] OF: selftest: Add overlay self-test support. (v2)
    Date
    This patch adds overlay tests to the OF selftest.

    It tests overlay device addition/removal and whether
    the apply revert sequence is correct.

    Changes since V1:
    * Added local fixups entries.

    Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
    ---
    Documentation/devicetree/bindings/selftest.txt | 14 +
    drivers/of/selftest.c | 481 +++++++++++++++++++++++++
    drivers/of/testcase-data/testcases.dts | 16 +
    drivers/of/testcase-data/tests-overlay.dtsi | 180 +++++++++
    4 files changed, 691 insertions(+)
    create mode 100644 Documentation/devicetree/bindings/selftest.txt
    create mode 100644 drivers/of/testcase-data/tests-overlay.dtsi

    diff --git a/Documentation/devicetree/bindings/selftest.txt b/Documentation/devicetree/bindings/selftest.txt
    new file mode 100644
    index 0000000..0f92a22
    --- /dev/null
    +++ b/Documentation/devicetree/bindings/selftest.txt
    @@ -0,0 +1,14 @@
    +* OF selftest platform device
    +
    +** selftest
    +
    +Required properties:
    +- compatible: must be "selftest"
    +
    +All other properties are optional.
    +
    +Example:
    + selftest {
    + compatible = "selftest";
    + status = "okay";
    + };
    diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
    index 16102fd..63aa0b2 100644
    --- a/drivers/of/selftest.c
    +++ b/drivers/of/selftest.c
    @@ -17,6 +17,8 @@
    #include <linux/mutex.h>
    #include <linux/slab.h>
    #include <linux/device.h>
    +#include <linux/platform_device.h>
    +#include <linux/of_platform.h>

    #include "of_private.h"

    @@ -858,6 +860,484 @@ static void selftest_data_remove(void)
    }
    }

    +#ifdef CONFIG_OF_OVERLAY
    +
    +static int selftest_probe(struct platform_device *pdev)
    +{
    + struct device *dev = &pdev->dev;
    + struct device_node *np = dev->of_node;
    +
    + if (np == NULL) {
    + dev_err(dev, "No OF data for device\n");
    + return -EINVAL;
    +
    + }
    +
    + dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
    + return 0;
    +}
    +
    +static int selftest_remove(struct platform_device *pdev)
    +{
    + struct device *dev = &pdev->dev;
    + struct device_node *np = dev->of_node;
    +
    + dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
    + return 0;
    +}
    +
    +static struct of_device_id selftest_match[] = {
    + { .compatible = "selftest", },
    + {},
    +};
    +MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
    +
    +static struct platform_driver selftest_driver = {
    + .probe = selftest_probe,
    + .remove = selftest_remove,
    + .driver = {
    + .name = "selftest",
    + .owner = THIS_MODULE,
    + .of_match_table = of_match_ptr(selftest_match),
    + },
    +};
    +
    +/* get the platform device instantiated at the path */
    +static struct platform_device *of_path_to_platform_device(const char *path)
    +{
    + struct device_node *np;
    + struct platform_device *pdev;
    +
    + np = of_find_node_by_path(path);
    + if (np == NULL)
    + return NULL;
    +
    + pdev = of_find_device_by_node(np);
    + of_node_put(np);
    +
    + return pdev;
    +}
    +
    +/* find out if a platform device exists at that path */
    +static int of_path_platform_device_exists(const char *path)
    +{
    + struct platform_device *pdev;
    +
    + pdev = of_path_to_platform_device(path);
    + platform_device_put(pdev);
    + return pdev != NULL;
    +}
    +
    +static const char *selftest_path(int nr)
    +{
    + static char buf[256];
    +
    + snprintf(buf, sizeof(buf) - 1,
    + "/testcase-data/overlay-node/test-bus/test-selftest%d", nr);
    + buf[sizeof(buf) - 1] = '\0';
    +
    + return buf;
    +}
    +
    +static const char *overlay_path(int nr)
    +{
    + static char buf[256];
    +
    + snprintf(buf, sizeof(buf) - 1,
    + "/testcase-data/overlay%d", nr);
    + buf[sizeof(buf) - 1] = '\0';
    +
    + return buf;
    +}
    +
    +static const char *bus_path = "/testcase-data/overlay-node/test-bus";
    +
    +static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr,
    + int *overlay_id)
    +{
    + struct device_node *np = NULL;
    + int ret, id = -1;
    +
    + np = of_find_node_by_path(overlay_path(overlay_nr));
    + if (np == NULL) {
    + selftest(0, "could not find overlay node @\"%s\"\n",
    + overlay_path(overlay_nr));
    + ret = -EINVAL;
    + goto out;
    + }
    +
    + ret = of_overlay_create(np);
    + if (ret < 0) {
    + selftest(0, "could not create overlay from \"%s\"\n",
    + overlay_path(overlay_nr));
    + goto out;
    + }
    + id = ret;
    +
    + ret = 0;
    +
    +out:
    + of_node_put(np);
    +
    + if (overlay_id)
    + *overlay_id = id;
    +
    + return ret;
    +}
    +
    +/* apply an overlay while checking before and after states */
    +static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr,
    + int before, int after)
    +{
    + int ret;
    +
    + /* selftest device must not be in before state */
    + if (of_path_platform_device_exists(selftest_path(selftest_nr))
    + != before) {
    + selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
    + overlay_path(overlay_nr),
    + selftest_path(selftest_nr),
    + !before ? "enabled" : "disabled");
    + return -EINVAL;
    + }
    +
    + ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, NULL);
    + if (ret != 0) {
    + /* of_selftest_apply_overlay already called selftest() */
    + return ret;
    + }
    +
    + /* selftest device must be to set to after state */
    + if (of_path_platform_device_exists(selftest_path(selftest_nr))
    + != after) {
    + selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
    + overlay_path(overlay_nr),
    + selftest_path(selftest_nr),
    + !after ? "enabled" : "disabled");
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +/* apply an overlay and then revert it while checking before, after states */
    +static int of_selftest_apply_revert_overlay_check(int overlay_nr,
    + int selftest_nr, int before, int after)
    +{
    + int ret, ov_id;
    +
    + /* selftest device must be in before state */
    + if (of_path_platform_device_exists(selftest_path(selftest_nr))
    + != before) {
    + selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
    + overlay_path(overlay_nr),
    + selftest_path(selftest_nr),
    + !before ? "enabled" : "disabled");
    + return -EINVAL;
    + }
    +
    + /* apply the overlay */
    + ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, &ov_id);
    + if (ret != 0) {
    + /* of_selftest_apply_overlay already called selftest() */
    + return ret;
    + }
    +
    + /* selftest device must be in after state */
    + if (of_path_platform_device_exists(selftest_path(selftest_nr))
    + != after) {
    + selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
    + overlay_path(overlay_nr),
    + selftest_path(selftest_nr),
    + !after ? "enabled" : "disabled");
    + return -EINVAL;
    + }
    +
    + ret = of_overlay_destroy(ov_id);
    + if (ret != 0) {
    + selftest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n",
    + overlay_path(overlay_nr),
    + selftest_path(selftest_nr));
    + return ret;
    + }
    +
    + /* selftest device must be again in before state */
    + if (of_path_platform_device_exists(selftest_path(selftest_nr))
    + != before) {
    + selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
    + overlay_path(overlay_nr),
    + selftest_path(selftest_nr),
    + !before ? "enabled" : "disabled");
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +/* test activation of device */
    +static void of_selftest_overlay_0(void)
    +{
    + int ret;
    +
    + /* device should enable */
    + ret = of_selftest_apply_overlay_check(0, 0, 0, 1);
    + if (ret != 0)
    + return;
    +
    + selftest(1, "overlay test %d passed\n", 0);
    +}
    +
    +/* test deactivation of device */
    +static void of_selftest_overlay_1(void)
    +{
    + int ret;
    +
    + /* device should disable */
    + ret = of_selftest_apply_overlay_check(1, 1, 1, 0);
    + if (ret != 0)
    + return;
    +
    + selftest(1, "overlay test %d passed\n", 1);
    +}
    +
    +/* test activation of device */
    +static void of_selftest_overlay_2(void)
    +{
    + int ret;
    +
    + /* device should enable */
    + ret = of_selftest_apply_overlay_check(2, 2, 0, 1);
    + if (ret != 0)
    + return;
    +
    + selftest(1, "overlay test %d passed\n", 2);
    +}
    +
    +/* test deactivation of device */
    +static void of_selftest_overlay_3(void)
    +{
    + int ret;
    +
    + /* device should disable */
    + ret = of_selftest_apply_overlay_check(3, 3, 1, 0);
    + if (ret != 0)
    + return;
    +
    + selftest(1, "overlay test %d passed\n", 3);
    +}
    +
    +/* test activation of a full device node */
    +static void of_selftest_overlay_4(void)
    +{
    + int ret;
    +
    + /* device should disable */
    + ret = of_selftest_apply_overlay_check(4, 4, 0, 1);
    + if (ret != 0)
    + return;
    +
    + selftest(1, "overlay test %d passed\n", 4);
    +}
    +
    +/* test overlay apply/revert sequence */
    +static void of_selftest_overlay_5(void)
    +{
    + int ret;
    +
    + /* device should disable */
    + ret = of_selftest_apply_revert_overlay_check(5, 5, 0, 1);
    + if (ret != 0)
    + return;
    +
    + selftest(1, "overlay test %d passed\n", 5);
    +}
    +
    +/* test overlay application in sequence */
    +static void of_selftest_overlay_6(void)
    +{
    + struct device_node *np;
    + int ret, i, ov_id[2];
    + int overlay_nr = 6, selftest_nr = 6;
    + int before = 0, after = 1;
    +
    + /* selftest device must be in before state */
    + for (i = 0; i < 2; i++) {
    + if (of_path_platform_device_exists(
    + selftest_path(selftest_nr + i))
    + != before) {
    + selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
    + overlay_path(overlay_nr + i),
    + selftest_path(selftest_nr + i),
    + !before ? "enabled" : "disabled");
    + return;
    + }
    + }
    +
    + /* apply the overlays */
    + for (i = 0; i < 2; i++) {
    +
    + np = of_find_node_by_path(overlay_path(overlay_nr + i));
    + if (np == NULL) {
    + selftest(0, "could not find overlay node @\"%s\"\n",
    + overlay_path(overlay_nr + i));
    + return;
    + }
    +
    + ret = of_overlay_create(np);
    + if (ret < 0) {
    + selftest(0, "could not create overlay from \"%s\"\n",
    + overlay_path(overlay_nr + i));
    + return;
    + }
    + ov_id[i] = ret;
    + }
    +
    + for (i = 0; i < 2; i++) {
    + /* selftest device must be in after state */
    + if (of_path_platform_device_exists(
    + selftest_path(selftest_nr + i))
    + != after) {
    + selftest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
    + overlay_path(overlay_nr + i),
    + selftest_path(selftest_nr + i),
    + !after ? "enabled" : "disabled");
    + return;
    + }
    + }
    +
    + for (i = 1; i >= 0; i--) {
    + ret = of_overlay_destroy(ov_id[i]);
    + if (ret != 0) {
    + selftest(0, "overlay @\"%s\" failed destroy @\"%s\"\n",
    + overlay_path(overlay_nr + i),
    + selftest_path(selftest_nr + i));
    + return;
    + }
    + }
    +
    + for (i = 0; i < 2; i++) {
    + /* selftest device must be again in before state */
    + if (of_path_platform_device_exists(
    + selftest_path(selftest_nr + i))
    + != before) {
    + selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
    + overlay_path(overlay_nr + i),
    + selftest_path(selftest_nr + i),
    + !before ? "enabled" : "disabled");
    + return;
    + }
    + }
    +
    + selftest(1, "overlay test %d passed\n", 6);
    +}
    +
    +/* test overlay application in sequence */
    +static void of_selftest_overlay_8(void)
    +{
    + struct device_node *np;
    + int ret, i, ov_id[2];
    + int overlay_nr = 8, selftest_nr = 8;
    +
    + /* we don't care about device state in this test */
    +
    + /* apply the overlays */
    + for (i = 0; i < 2; i++) {
    +
    + np = of_find_node_by_path(overlay_path(overlay_nr + i));
    + if (np == NULL) {
    + selftest(0, "could not find overlay node @\"%s\"\n",
    + overlay_path(overlay_nr + i));
    + return;
    + }
    +
    + ret = of_overlay_create(np);
    + if (ret < 0) {
    + selftest(0, "could not create overlay from \"%s\"\n",
    + overlay_path(overlay_nr + i));
    + return;
    + }
    + ov_id[i] = ret;
    + }
    +
    + /* now try to remove first overlay (it should fail) */
    + ret = of_overlay_destroy(ov_id[0]);
    + if (ret == 0) {
    + selftest(0, "overlay @\"%s\" was destroyed @\"%s\"\n",
    + overlay_path(overlay_nr + 0),
    + selftest_path(selftest_nr));
    + return;
    + }
    +
    + /* removing them in order should work */
    + for (i = 1; i >= 0; i--) {
    + ret = of_overlay_destroy(ov_id[i]);
    + if (ret != 0) {
    + selftest(0, "overlay @\"%s\" not destroyed @\"%s\"\n",
    + overlay_path(overlay_nr + i),
    + selftest_path(selftest_nr));
    + return;
    + }
    + }
    +
    + selftest(1, "overlay test %d passed\n", 8);
    +}
    +
    +static void __init of_selftest_overlay(void)
    +{
    + struct device_node *bus_np = NULL;
    + int ret;
    +
    + ret = platform_driver_register(&selftest_driver);
    + if (ret != 0) {
    + selftest(0, "could not register selftest driver\n");
    + goto out;
    + }
    +
    + bus_np = of_find_node_by_path(bus_path);
    + if (bus_np == NULL) {
    + selftest(0, "could not find bus_path \"%s\"\n", bus_path);
    + goto out;
    + }
    +
    + ret = of_platform_populate(bus_np, of_default_bus_match_table,
    + NULL, NULL);
    + if (ret != 0) {
    + selftest(0, "could not populate bus @ \"%s\"\n", bus_path);
    + goto out;
    + }
    +
    + if (!of_path_platform_device_exists(selftest_path(100))) {
    + selftest(0, "could not find selftest0 @ \"%s\"\n",
    + selftest_path(100));
    + goto out;
    + }
    +
    + if (of_path_platform_device_exists(selftest_path(101))) {
    + selftest(0, "selftest1 @ \"%s\" should not exist\n",
    + selftest_path(101));
    + goto out;
    + }
    +
    + selftest(1, "basic infrastructure of overlays passed");
    +
    + /* tests in sequence */
    + of_selftest_overlay_0();
    + of_selftest_overlay_1();
    + of_selftest_overlay_2();
    + of_selftest_overlay_3();
    + of_selftest_overlay_4();
    + of_selftest_overlay_5();
    + of_selftest_overlay_6();
    + of_selftest_overlay_8();
    +
    +out:
    + of_node_put(bus_np);
    +}
    +
    +#else
    +static inline void __init of_selftest_overlay(void) { }
    +#endif
    +
    static int __init of_selftest(void)
    {
    struct device_node *np;
    @@ -888,6 +1368,7 @@ static int __init of_selftest(void)
    of_selftest_parse_interrupts_extended();
    of_selftest_match_node();
    of_selftest_platform_populate();
    + of_selftest_overlay();

    /* removing selftest data from live tree */
    selftest_data_remove();
    diff --git a/drivers/of/testcase-data/testcases.dts b/drivers/of/testcase-data/testcases.dts
    index b6bc41b..12f7c3d 100644
    --- a/drivers/of/testcase-data/testcases.dts
    +++ b/drivers/of/testcase-data/testcases.dts
    @@ -13,6 +13,7 @@
    #include "tests-interrupts.dtsi"
    #include "tests-match.dtsi"
    #include "tests-platform.dtsi"
    +#include "tests-overlay.dtsi"

    /*
    * phandle fixup data - generated by dtc patches that aren't upstream.
    @@ -59,5 +60,20 @@
    testcase-device2 {
    interrupt-parent = <0x00000000>;
    };
    + overlay2 {
    + fragment@0 {
    + target = <0x00000000>;
    + };
    + };
    + overlay3 {
    + fragment@0 {
    + target = <0x00000000>;
    + };
    + };
    + overlay4 {
    + fragment@0 {
    + target = <0x00000000>;
    + };
    + };
    };
    }; };
    diff --git a/drivers/of/testcase-data/tests-overlay.dtsi b/drivers/of/testcase-data/tests-overlay.dtsi
    new file mode 100644
    index 0000000..75976da
    --- /dev/null
    +++ b/drivers/of/testcase-data/tests-overlay.dtsi
    @@ -0,0 +1,180 @@
    +
    +/ {
    + testcase-data {
    + overlay-node {
    +
    + /* test bus */
    + selftestbus: test-bus {
    + compatible = "simple-bus";
    + #address-cells = <1>;
    + #size-cells = <0>;
    +
    + selftest100: test-selftest100 {
    + compatible = "selftest";
    + status = "okay";
    + reg = <100>;
    + };
    +
    + selftest101: test-selftest101 {
    + compatible = "selftest";
    + status = "disabled";
    + reg = <101>;
    + };
    +
    + selftest0: test-selftest0 {
    + compatible = "selftest";
    + status = "disabled";
    + reg = <0>;
    + };
    +
    + selftest1: test-selftest1 {
    + compatible = "selftest";
    + status = "okay";
    + reg = <1>;
    + };
    +
    + selftest2: test-selftest2 {
    + compatible = "selftest";
    + status = "disabled";
    + reg = <2>;
    + };
    +
    + selftest3: test-selftest3 {
    + compatible = "selftest";
    + status = "okay";
    + reg = <3>;
    + };
    +
    + selftest5: test-selftest5 {
    + compatible = "selftest";
    + status = "disabled";
    + reg = <5>;
    + };
    +
    + selftest6: test-selftest6 {
    + compatible = "selftest";
    + status = "disabled";
    + reg = <6>;
    + };
    +
    + selftest7: test-selftest7 {
    + compatible = "selftest";
    + status = "disabled";
    + reg = <7>;
    + };
    +
    + selftest8: test-selftest8 {
    + compatible = "selftest";
    + status = "disabled";
    + reg = <8>;
    + };
    + };
    + };
    +
    + /* test enable using absolute target path */
    + overlay0 {
    + fragment@0 {
    + target-path = "/testcase-data/overlay-node/test-bus/test-selftest0";
    + __overlay__ {
    + status = "okay";
    + };
    + };
    + };
    +
    + /* test disable using absolute target path */
    + overlay1 {
    + fragment@0 {
    + target-path = "/testcase-data/overlay-node/test-bus/test-selftest1";
    + __overlay__ {
    + status = "disabled";
    + };
    + };
    + };
    +
    + /* test enable using label */
    + overlay2 {
    + fragment@0 {
    + target = <&selftest2>;
    + __overlay__ {
    + status = "okay";
    + };
    + };
    + };
    +
    + /* test disable using label */
    + overlay3 {
    + fragment@0 {
    + target = <&selftest3>;
    + __overlay__ {
    + status = "disabled";
    + };
    + };
    + };
    +
    + /* test insertion of a full node */
    + overlay4 {
    + fragment@0 {
    + target = <&selftestbus>;
    + __overlay__ {
    +
    + /* suppress DTC warning */
    + #address-cells = <1>;
    + #size-cells = <0>;
    +
    + test-selftest4 {
    + compatible = "selftest";
    + status = "okay";
    + reg = <4>;
    + };
    + };
    + };
    + };
    +
    + /* test overlay apply revert */
    + overlay5 {
    + fragment@0 {
    + target-path = "/testcase-data/overlay-node/test-bus/test-selftest5";
    + __overlay__ {
    + status = "okay";
    + };
    + };
    + };
    +
    + /* test overlays application and removal in sequence */
    + overlay6 {
    + fragment@0 {
    + target-path = "/testcase-data/overlay-node/test-bus/test-selftest6";
    + __overlay__ {
    + status = "okay";
    + };
    + };
    + };
    + overlay7 {
    + fragment@0 {
    + target-path = "/testcase-data/overlay-node/test-bus/test-selftest7";
    + __overlay__ {
    + status = "okay";
    + };
    + };
    + };
    +
    + /* test overlays application and removal in bad sequence */
    + overlay8 {
    + fragment@0 {
    + target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
    + __overlay__ {
    + status = "okay";
    + };
    + };
    + };
    + overlay9 {
    + fragment@0 {
    + target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
    + __overlay__ {
    + property-foo = "bar";
    + };
    + };
    + };
    +
    + };
    +};
    --
    1.7.12


    \
     
     \ /
      Last update: 2014-10-28 21:41    [W:4.181 / U:1.032 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site