From patchwork Tue Oct 28 20:35:59 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pantelis Antoniou X-Patchwork-Id: 404337 Return-Path: X-Original-To: incoming-dt@patchwork.ozlabs.org Delivered-To: patchwork-incoming-dt@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 99FC8140077 for ; Wed, 29 Oct 2014 07:43:16 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751505AbaJ1UnP (ORCPT ); Tue, 28 Oct 2014 16:43:15 -0400 Received: from mail-wg0-f44.google.com ([74.125.82.44]:61599 "EHLO mail-wg0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751027AbaJ1UnP (ORCPT ); Tue, 28 Oct 2014 16:43:15 -0400 Received: by mail-wg0-f44.google.com with SMTP id y10so1838664wgg.3 for ; Tue, 28 Oct 2014 13:43:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=acCyryphFL0RKN0nsFG6WRX8ZyWbhJAGFcfBl15utls=; b=I3PhnQ+NCsHBL9HVDvu+v8HKWcMSzg9LqYkt65gvpLwCvNu+9j5a0omhoxGuHP7JA0 cxcuTJXeL6omTMHRSu8W/g0CTvdwZaSAzB5qzHiQ4GO8+D8h0ezcm32ZAgXl2nP446S1 +V37nlQrRBGJq8ms3k8y6qUfq1oxUz/CJ1/YJrcHCfDyLVC25jlv2LE/ri44zl1zKM/R WnxLiIHSNZ/xjTZQ7gavrv5YgLmUuU1YdrEcABJNRVE1Lsck3UaBacwqUL2D68WWb64f QfK3VhQY9c8EucTT4nbfVAPyj4hRuNDpAZ43F05V/zUe82xABhh/CXD+/r9mXdQ76kKg M3pw== X-Gm-Message-State: ALoCoQlw/6TCUZ10DN2ax/rLK9csjbX8dhGnJI/LcG1KbehNB6B10stGcGErygK3xmvBdhgP0kuZ X-Received: by 10.194.123.69 with SMTP id ly5mr6859035wjb.9.1414528583144; Tue, 28 Oct 2014 13:36:23 -0700 (PDT) Received: from sles11esa.localdomain ([195.97.110.117]) by mx.google.com with ESMTPSA id td9sm3371380wic.15.2014.10.28.13.36.18 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Tue, 28 Oct 2014 13:36:22 -0700 (PDT) From: Pantelis Antoniou To: Grant Likely Cc: Rob Herring , Stephen Warren , Matt Porter , Koen Kooi , Greg Kroah-Hartman , Alison Chaiken , Dinh Nguyen , Jan Lubbe , Alexander Sverdlin , Michael Stickel , Guenter Roeck , Dirk Behme , Alan Tull , Sascha Hauer , Michael Bohan , Ionut Nicu , Michal Simek , Matt Ranostay , Joel Becker , devicetree@vger.kernel.org, Wolfram Sang , linux-i2c@vger.kernel.org, Mark Brown , linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org, Pete Popov , Dan Malek , Georgi Vlaev , Pantelis Antoniou , Pantelis Antoniou Subject: [PATCH v8 2/8] OF: selftest: Add overlay self-test support. (v2) Date: Tue, 28 Oct 2014 22:35:59 +0200 Message-Id: <1414528565-10907-3-git-send-email-pantelis.antoniou@konsulko.com> X-Mailer: git-send-email 1.7.12 In-Reply-To: <1414528565-10907-1-git-send-email-pantelis.antoniou@konsulko.com> References: <1414528565-10907-1-git-send-email-pantelis.antoniou@konsulko.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org 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 --- 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 #include #include +#include +#include #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"; + }; + }; + }; + + }; +};