diff options
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Kconfig | 4 | ||||
-rw-r--r-- | drivers/of/base.c | 19 | ||||
-rw-r--r-- | drivers/of/fdt.c | 2 | ||||
-rw-r--r-- | drivers/of/irq.c | 10 | ||||
-rw-r--r-- | drivers/of/of_pci.c | 3 | ||||
-rw-r--r-- | drivers/of/of_reserved_mem.c | 2 | ||||
-rw-r--r-- | drivers/of/overlay.c | 3 | ||||
-rw-r--r-- | drivers/of/platform.c | 1 | ||||
-rw-r--r-- | drivers/of/unittest-data/tests-overlay.dtsi | 94 | ||||
-rw-r--r-- | drivers/of/unittest.c | 618 |
10 files changed, 614 insertions, 142 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index b5e0c873d4e1..7bcaeec876c0 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -10,7 +10,6 @@ menu "Device Tree and Open Firmware support" config OF_UNITTEST bool "Device Tree runtime unit tests" depends on OF_IRQ && OF_EARLY_FLATTREE - select OF_DYNAMIC select OF_RESOLVE help This option builds in test cases for the device tree infrastructure @@ -85,8 +84,7 @@ config OF_RESOLVE bool config OF_OVERLAY - bool - depends on OF + bool "Device Tree overlays" select OF_DYNAMIC select OF_RESOLVE diff --git a/drivers/of/base.c b/drivers/of/base.c index 36536b6a8834..8f165b112e03 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -714,16 +714,12 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, const char *path) { struct device_node *child; - int len = strchrnul(path, '/') - path; - int term; + int len; + len = strcspn(path, "/:"); if (!len) return NULL; - term = strchrnul(path, ':') - path; - if (term < len) - len = term; - __for_each_child_of_node(parent, child) { const char *name = strrchr(child->full_name, '/'); if (WARN(!name, "malformed device_node %s\n", child->full_name)) @@ -768,8 +764,12 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt /* The path could begin with an alias */ if (*path != '/') { - char *p = strchrnul(path, '/'); - int len = separator ? separator - path : p - path; + int len; + const char *p = separator; + + if (!p) + p = strchrnul(path, '/'); + len = p - path; /* of_aliases must not be NULL */ if (!of_aliases) @@ -794,6 +794,8 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt path++; /* Increment past '/' delimiter */ np = __of_find_node_by_path(np, path); path = strchrnul(path, '/'); + if (separator && separator < path) + break; } raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; @@ -1303,6 +1305,7 @@ int of_property_read_u64_array(const struct device_node *np, } return 0; } +EXPORT_SYMBOL_GPL(of_property_read_u64_array); /** * of_property_read_string - Find and read a string from a property diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 510074226d57..3a896c9aeb74 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -762,7 +762,7 @@ static inline void early_init_dt_check_for_initrd(unsigned long node) #ifdef CONFIG_SERIAL_EARLYCON extern struct of_device_id __earlycon_of_table[]; -int __init early_init_dt_scan_chosen_serial(void) +static int __init early_init_dt_scan_chosen_serial(void) { int offset; const char *p; diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 0d7765807f49..1a7980692f25 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -290,7 +290,7 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar struct device_node *p; const __be32 *intspec, *tmp, *addr; u32 intsize, intlen; - int i, res = -EINVAL; + int i, res; pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); @@ -323,15 +323,19 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar /* Get size of interrupt specifier */ tmp = of_get_property(p, "#interrupt-cells", NULL); - if (tmp == NULL) + if (tmp == NULL) { + res = -EINVAL; goto out; + } intsize = be32_to_cpu(*tmp); pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); /* Check index */ - if ((index + 1) * intsize > intlen) + if ((index + 1) * intsize > intlen) { + res = -EINVAL; goto out; + } /* Copy intspec into irq structure */ intspec += index * intsize; diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 88471d3d98cd..62426d81a4d6 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -140,6 +140,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, unsigned char busno, unsigned char bus_max, struct list_head *resources, resource_size_t *io_base) { + struct resource_entry *window; struct resource *res; struct resource *bus_range; struct of_pci_range range; @@ -225,6 +226,8 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, conversion_failed: kfree(res); parse_failed: + resource_list_for_each_entry(window, resources) + kfree(window->res); pci_free_resource_list(resources); return err; } diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index dc566b38645f..726ebe792813 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -265,6 +265,7 @@ int of_reserved_mem_device_init(struct device *dev) return ret; } +EXPORT_SYMBOL_GPL(of_reserved_mem_device_init); /** * of_reserved_mem_device_release() - release reserved memory device structures @@ -289,3 +290,4 @@ void of_reserved_mem_device_release(struct device *dev) rmem->ops->device_release(rmem, dev); } +EXPORT_SYMBOL_GPL(of_reserved_mem_device_release); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 352b4f28f82c..dee9270ba547 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -19,6 +19,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/idr.h> #include "of_private.h" @@ -85,7 +86,7 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, struct device_node *target, struct device_node *child) { const char *cname; - struct device_node *tchild, *grandchild; + struct device_node *tchild; int ret = 0; cname = kbasename(child->full_name); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b0d50d70a8a1..b189733a1539 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -526,6 +526,7 @@ static int of_platform_device_destroy(struct device *dev, void *data) amba_device_unregister(to_amba_device(dev)); #endif + of_dma_deconfigure(dev); of_node_clear_flag(dev->of_node, OF_POPULATED); of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); return 0; diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi index a2b687d5f324..244226cbb5a3 100644 --- a/drivers/of/unittest-data/tests-overlay.dtsi +++ b/drivers/of/unittest-data/tests-overlay.dtsi @@ -68,6 +68,48 @@ status = "disabled"; reg = <8>; }; + + i2c-test-bus { + compatible = "selftest-i2c-bus"; + status = "okay"; + reg = <50>; + + #address-cells = <1>; + #size-cells = <0>; + + test-selftest12 { + reg = <8>; + compatible = "selftest-i2c-dev"; + status = "disabled"; + }; + + test-selftest13 { + reg = <9>; + compatible = "selftest-i2c-dev"; + status = "okay"; + }; + + test-selftest14 { + reg = <10>; + compatible = "selftest-i2c-mux"; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + test-mux-dev { + reg = <32>; + compatible = "selftest-i2c-dev"; + status = "okay"; + }; + }; + }; + }; }; }; @@ -231,5 +273,57 @@ }; }; }; + + /* test enable using absolute target path (i2c) */ + overlay12 { + fragment@0 { + target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-selftest12"; + __overlay__ { + status = "okay"; + }; + }; + }; + + /* test disable using absolute target path (i2c) */ + overlay13 { + fragment@0 { + target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-selftest13"; + __overlay__ { + status = "disabled"; + }; + }; + }; + + /* test mux overlay */ + overlay15 { + fragment@0 { + target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus"; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + test-selftest15 { + reg = <11>; + compatible = "selftest-i2c-mux"; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + test-mux-dev { + reg = <32>; + compatible = "selftest-i2c-dev"; + status = "okay"; + }; + }; + }; + }; + }; + }; + }; }; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 41a4a138f53b..52c45c7df07f 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -20,6 +20,9 @@ #include <linux/platform_device.h> #include <linux/of_platform.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> + #include "of_private.h" static struct selftest_results { @@ -27,11 +30,6 @@ static struct selftest_results { int failed; } selftest_results; -#define NO_OF_NODES 3 -static struct device_node *nodes[NO_OF_NODES]; -static int last_node_index; -static bool selftest_live_tree; - #define selftest(result, fmt, ...) ({ \ bool failed = !(result); \ if (failed) { \ @@ -94,6 +92,16 @@ static void __init of_selftest_find_node_by_name(void) "option path test failed\n"); of_node_put(np); + np = of_find_node_opts_by_path("/testcase-data:test/option", &options); + selftest(np && !strcmp("test/option", options), + "option path test, subcase #1 failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path("/testcase-data/testcase-device1:test/option", &options); + selftest(np && !strcmp("test/option", options), + "option path test, subcase #2 failed\n"); + of_node_put(np); + np = of_find_node_opts_by_path("/testcase-data:testoption", NULL); selftest(np, "NULL option path test failed\n"); of_node_put(np); @@ -104,6 +112,12 @@ static void __init of_selftest_find_node_by_name(void) "option alias path test failed\n"); of_node_put(np); + np = of_find_node_opts_by_path("testcase-alias:test/alias/option", + &options); + selftest(np && !strcmp("test/alias/option", options), + "option alias path test, subcase #1 failed\n"); + of_node_put(np); + np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL); selftest(np, "NULL option alias path test failed\n"); of_node_put(np); @@ -380,9 +394,9 @@ static void __init of_selftest_property_string(void) rc = of_property_match_string(np, "phandle-list-names", "first"); selftest(rc == 0, "first expected:0 got:%i\n", rc); rc = of_property_match_string(np, "phandle-list-names", "second"); - selftest(rc == 1, "second expected:0 got:%i\n", rc); + selftest(rc == 1, "second expected:1 got:%i\n", rc); rc = of_property_match_string(np, "phandle-list-names", "third"); - selftest(rc == 2, "third expected:0 got:%i\n", rc); + selftest(rc == 2, "third expected:2 got:%i\n", rc); rc = of_property_match_string(np, "phandle-list-names", "fourth"); selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc); rc = of_property_match_string(np, "missing-property", "blah"); @@ -480,7 +494,6 @@ static void __init of_selftest_changeset(void) struct device_node *n1, *n2, *n21, *nremove, *parent, *np; struct of_changeset chgset; - of_changeset_init(&chgset); n1 = __of_node_dup(NULL, "/testcase-data/changeset/n1"); selftest(n1, "testcase setup failure\n"); n2 = __of_node_dup(NULL, "/testcase-data/changeset/n2"); @@ -822,6 +835,7 @@ static void update_node_properties(struct device_node *np, static int attach_node_and_children(struct device_node *np) { struct device_node *next, *dup, *child; + unsigned long flags; dup = of_find_node_by_path(np->full_name); if (dup) { @@ -829,17 +843,19 @@ static int attach_node_and_children(struct device_node *np) return 0; } - /* Children of the root need to be remembered for removal */ - if (np->parent == of_root) { - if (WARN_ON(last_node_index >= NO_OF_NODES)) - return -EINVAL; - nodes[last_node_index++] = np; - } - child = np->child; np->child = NULL; - np->sibling = NULL; - of_attach_node(np); + + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); + np->sibling = np->parent->child; + np->parent->child = np; + of_node_clear_flag(np, OF_DETACHED); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + __of_attach_node_sysfs(np); + mutex_unlock(&of_mutex); + while (child) { next = child->sibling; attach_node_and_children(child); @@ -889,10 +905,7 @@ static int __init selftest_data_add(void) } if (!of_root) { - /* enabling flag for removing nodes */ - selftest_live_tree = true; of_root = selftest_data_node; - for_each_of_allnodes(np) __of_attach_node_sysfs(np); of_aliases = of_find_node_by_path("/aliases"); @@ -911,59 +924,6 @@ static int __init selftest_data_add(void) return 0; } -/** - * detach_node_and_children - detaches node - * and its children from live tree - * - * @np: Node to detach from live tree - */ -static void detach_node_and_children(struct device_node *np) -{ - while (np->child) - detach_node_and_children(np->child); - of_detach_node(np); -} - -/** - * selftest_data_remove - removes the selftest data - * nodes from the live tree - */ -static void selftest_data_remove(void) -{ - struct device_node *np; - struct property *prop; - - if (selftest_live_tree) { - of_node_put(of_aliases); - of_node_put(of_chosen); - of_aliases = NULL; - of_chosen = NULL; - for_each_child_of_node(of_root, np) - detach_node_and_children(np); - __of_detach_node_sysfs(of_root); - of_root = NULL; - return; - } - - while (last_node_index-- > 0) { - if (nodes[last_node_index]) { - np = of_find_node_by_path(nodes[last_node_index]->full_name); - if (np == nodes[last_node_index]) { - if (of_aliases == np) { - of_node_put(of_aliases); - of_aliases = NULL; - } - detach_node_and_children(np); - } else { - for_each_property_of_node(np, prop) { - if (strcmp(prop->name, "testcase-alias") == 0) - of_remove_property(np, prop); - } - } - } - } -} - #ifdef CONFIG_OF_OVERLAY static int selftest_probe(struct platform_device *pdev) @@ -1034,17 +994,94 @@ static int of_path_platform_device_exists(const char *path) return pdev != NULL; } -static const char *selftest_path(int nr) +#if IS_BUILTIN(CONFIG_I2C) + +/* get the i2c client device instantiated at the path */ +static struct i2c_client *of_path_to_i2c_client(const char *path) +{ + struct device_node *np; + struct i2c_client *client; + + np = of_find_node_by_path(path); + if (np == NULL) + return NULL; + + client = of_find_i2c_device_by_node(np); + of_node_put(np); + + return client; +} + +/* find out if a i2c client device exists at that path */ +static int of_path_i2c_client_exists(const char *path) +{ + struct i2c_client *client; + + client = of_path_to_i2c_client(path); + if (client) + put_device(&client->dev); + return client != NULL; +} +#else +static int of_path_i2c_client_exists(const char *path) +{ + return 0; +} +#endif + +enum overlay_type { + PDEV_OVERLAY, + I2C_OVERLAY +}; + +static int of_path_device_type_exists(const char *path, + enum overlay_type ovtype) { + switch (ovtype) { + case PDEV_OVERLAY: + return of_path_platform_device_exists(path); + case I2C_OVERLAY: + return of_path_i2c_client_exists(path); + } + return 0; +} + +static const char *selftest_path(int nr, enum overlay_type ovtype) +{ + const char *base; static char buf[256]; - snprintf(buf, sizeof(buf) - 1, - "/testcase-data/overlay-node/test-bus/test-selftest%d", nr); + switch (ovtype) { + case PDEV_OVERLAY: + base = "/testcase-data/overlay-node/test-bus"; + break; + case I2C_OVERLAY: + base = "/testcase-data/overlay-node/test-bus/i2c-test-bus"; + break; + default: + buf[0] = '\0'; + return buf; + } + snprintf(buf, sizeof(buf) - 1, "%s/test-selftest%d", base, nr); buf[sizeof(buf) - 1] = '\0'; - return buf; } +static int of_selftest_device_exists(int selftest_nr, enum overlay_type ovtype) +{ + const char *path; + + path = selftest_path(selftest_nr, ovtype); + + switch (ovtype) { + case PDEV_OVERLAY: + return of_path_platform_device_exists(path); + case I2C_OVERLAY: + return of_path_i2c_client_exists(path); + } + return 0; +} + static const char *overlay_path(int nr) { static char buf[256]; @@ -1093,16 +1130,15 @@ out: /* 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 before, int after, enum overlay_type ovtype) { int ret; /* selftest device must not be in before state */ - if (of_path_platform_device_exists(selftest_path(selftest_nr)) - != before) { + if (of_selftest_device_exists(selftest_nr, ovtype) != before) { selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n", overlay_path(overlay_nr), - selftest_path(selftest_nr), + selftest_path(selftest_nr, ovtype), !before ? "enabled" : "disabled"); return -EINVAL; } @@ -1114,11 +1150,10 @@ static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr, } /* selftest device must be to set to after state */ - if (of_path_platform_device_exists(selftest_path(selftest_nr)) - != after) { + if (of_selftest_device_exists(selftest_nr, ovtype) != after) { selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", overlay_path(overlay_nr), - selftest_path(selftest_nr), + selftest_path(selftest_nr, ovtype), !after ? "enabled" : "disabled"); return -EINVAL; } @@ -1128,16 +1163,16 @@ static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr, /* 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 selftest_nr, int before, int after, + enum overlay_type ovtype) { int ret, ov_id; /* selftest device must be in before state */ - if (of_path_platform_device_exists(selftest_path(selftest_nr)) - != before) { + if (of_selftest_device_exists(selftest_nr, ovtype) != before) { selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n", overlay_path(overlay_nr), - selftest_path(selftest_nr), + selftest_path(selftest_nr, ovtype), !before ? "enabled" : "disabled"); return -EINVAL; } @@ -1150,11 +1185,10 @@ static int of_selftest_apply_revert_overlay_check(int overlay_nr, } /* selftest device must be in after state */ - if (of_path_platform_device_exists(selftest_path(selftest_nr)) - != after) { + if (of_selftest_device_exists(selftest_nr, ovtype) != after) { selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", overlay_path(overlay_nr), - selftest_path(selftest_nr), + selftest_path(selftest_nr, ovtype), !after ? "enabled" : "disabled"); return -EINVAL; } @@ -1163,16 +1197,15 @@ static int of_selftest_apply_revert_overlay_check(int overlay_nr, if (ret != 0) { selftest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n", overlay_path(overlay_nr), - selftest_path(selftest_nr)); + selftest_path(selftest_nr, ovtype)); return ret; } /* selftest device must be again in before state */ - if (of_path_platform_device_exists(selftest_path(selftest_nr)) - != before) { + if (of_selftest_device_exists(selftest_nr, PDEV_OVERLAY) != before) { selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n", overlay_path(overlay_nr), - selftest_path(selftest_nr), + selftest_path(selftest_nr, ovtype), !before ? "enabled" : "disabled"); return -EINVAL; } @@ -1186,7 +1219,7 @@ static void of_selftest_overlay_0(void) int ret; /* device should enable */ - ret = of_selftest_apply_overlay_check(0, 0, 0, 1); + ret = of_selftest_apply_overlay_check(0, 0, 0, 1, PDEV_OVERLAY); if (ret != 0) return; @@ -1199,7 +1232,7 @@ static void of_selftest_overlay_1(void) int ret; /* device should disable */ - ret = of_selftest_apply_overlay_check(1, 1, 1, 0); + ret = of_selftest_apply_overlay_check(1, 1, 1, 0, PDEV_OVERLAY); if (ret != 0) return; @@ -1212,7 +1245,7 @@ static void of_selftest_overlay_2(void) int ret; /* device should enable */ - ret = of_selftest_apply_overlay_check(2, 2, 0, 1); + ret = of_selftest_apply_overlay_check(2, 2, 0, 1, PDEV_OVERLAY); if (ret != 0) return; @@ -1225,7 +1258,7 @@ static void of_selftest_overlay_3(void) int ret; /* device should disable */ - ret = of_selftest_apply_overlay_check(3, 3, 1, 0); + ret = of_selftest_apply_overlay_check(3, 3, 1, 0, PDEV_OVERLAY); if (ret != 0) return; @@ -1238,7 +1271,7 @@ static void of_selftest_overlay_4(void) int ret; /* device should disable */ - ret = of_selftest_apply_overlay_check(4, 4, 0, 1); + ret = of_selftest_apply_overlay_check(4, 4, 0, 1, PDEV_OVERLAY); if (ret != 0) return; @@ -1251,7 +1284,7 @@ static void of_selftest_overlay_5(void) int ret; /* device should disable */ - ret = of_selftest_apply_revert_overlay_check(5, 5, 0, 1); + ret = of_selftest_apply_revert_overlay_check(5, 5, 0, 1, PDEV_OVERLAY); if (ret != 0) return; @@ -1268,12 +1301,12 @@ static void of_selftest_overlay_6(void) /* selftest device must be in before state */ for (i = 0; i < 2; i++) { - if (of_path_platform_device_exists( - selftest_path(selftest_nr + i)) + if (of_selftest_device_exists(selftest_nr + i, PDEV_OVERLAY) != before) { selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n", overlay_path(overlay_nr + i), - selftest_path(selftest_nr + i), + selftest_path(selftest_nr + i, + PDEV_OVERLAY), !before ? "enabled" : "disabled"); return; } @@ -1300,12 +1333,12 @@ static void of_selftest_overlay_6(void) for (i = 0; i < 2; i++) { /* selftest device must be in after state */ - if (of_path_platform_device_exists( - selftest_path(selftest_nr + i)) + if (of_selftest_device_exists(selftest_nr + i, PDEV_OVERLAY) != after) { selftest(0, "overlay @\"%s\" failed @\"%s\" %s\n", overlay_path(overlay_nr + i), - selftest_path(selftest_nr + i), + selftest_path(selftest_nr + i, + PDEV_OVERLAY), !after ? "enabled" : "disabled"); return; } @@ -1316,19 +1349,20 @@ static void of_selftest_overlay_6(void) if (ret != 0) { selftest(0, "overlay @\"%s\" failed destroy @\"%s\"\n", overlay_path(overlay_nr + i), - selftest_path(selftest_nr + i)); + selftest_path(selftest_nr + i, + PDEV_OVERLAY)); 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)) + if (of_selftest_device_exists(selftest_nr + i, PDEV_OVERLAY) != before) { selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n", overlay_path(overlay_nr + i), - selftest_path(selftest_nr + i), + selftest_path(selftest_nr + i, + PDEV_OVERLAY), !before ? "enabled" : "disabled"); return; } @@ -1370,7 +1404,8 @@ static void of_selftest_overlay_8(void) if (ret == 0) { selftest(0, "overlay @\"%s\" was destroyed @\"%s\"\n", overlay_path(overlay_nr + 0), - selftest_path(selftest_nr)); + selftest_path(selftest_nr, + PDEV_OVERLAY)); return; } @@ -1380,7 +1415,8 @@ static void of_selftest_overlay_8(void) if (ret != 0) { selftest(0, "overlay @\"%s\" not destroyed @\"%s\"\n", overlay_path(overlay_nr + i), - selftest_path(selftest_nr)); + selftest_path(selftest_nr, + PDEV_OVERLAY)); return; } } @@ -1395,16 +1431,17 @@ static void of_selftest_overlay_10(void) char *child_path; /* device should disable */ - ret = of_selftest_apply_overlay_check(10, 10, 0, 1); - if (selftest(ret == 0, "overlay test %d failed; overlay application\n", 10)) + ret = of_selftest_apply_overlay_check(10, 10, 0, 1, PDEV_OVERLAY); + if (selftest(ret == 0, + "overlay test %d failed; overlay application\n", 10)) return; child_path = kasprintf(GFP_KERNEL, "%s/test-selftest101", - selftest_path(10)); + selftest_path(10, PDEV_OVERLAY)); if (selftest(child_path, "overlay test %d failed; kasprintf\n", 10)) return; - ret = of_path_platform_device_exists(child_path); + ret = of_path_device_type_exists(child_path, PDEV_OVERLAY); kfree(child_path); if (selftest(ret, "overlay test %d failed; no child device\n", 10)) return; @@ -1416,11 +1453,331 @@ static void of_selftest_overlay_11(void) int ret; /* device should disable */ - ret = of_selftest_apply_revert_overlay_check(11, 11, 0, 1); - if (selftest(ret == 0, "overlay test %d failed; overlay application\n", 11)) + ret = of_selftest_apply_revert_overlay_check(11, 11, 0, 1, + PDEV_OVERLAY); + if (selftest(ret == 0, + "overlay test %d failed; overlay application\n", 11)) return; } +#if IS_BUILTIN(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY) + +struct selftest_i2c_bus_data { + struct platform_device *pdev; + struct i2c_adapter adap; +}; + +static int selftest_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct selftest_i2c_bus_data *std = i2c_get_adapdata(adap); + + (void)std; + + return num; +} + +static u32 selftest_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm selftest_i2c_algo = { + .master_xfer = selftest_i2c_master_xfer, + .functionality = selftest_i2c_functionality, +}; + +static int selftest_i2c_bus_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct selftest_i2c_bus_data *std; + struct i2c_adapter *adap; + int ret; + + 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); + + std = devm_kzalloc(dev, sizeof(*std), GFP_KERNEL); + if (!std) { + dev_err(dev, "Failed to allocate selftest i2c data\n"); + return -ENOMEM; + } + + /* link them together */ + std->pdev = pdev; + platform_set_drvdata(pdev, std); + + adap = &std->adap; + i2c_set_adapdata(adap, std); + adap->nr = -1; + strlcpy(adap->name, pdev->name, sizeof(adap->name)); + adap->class = I2C_CLASS_DEPRECATED; + adap->algo = &selftest_i2c_algo; + adap->dev.parent = dev; + adap->dev.of_node = dev->of_node; + adap->timeout = 5 * HZ; + adap->retries = 3; + + ret = i2c_add_numbered_adapter(adap); + if (ret != 0) { + dev_err(dev, "Failed to add I2C adapter\n"); + return ret; + } + + return 0; +} + +static int selftest_i2c_bus_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct selftest_i2c_bus_data *std = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); + i2c_del_adapter(&std->adap); + + return 0; +} + +static struct of_device_id selftest_i2c_bus_match[] = { + { .compatible = "selftest-i2c-bus", }, + {}, +}; + +static struct platform_driver selftest_i2c_bus_driver = { + .probe = selftest_i2c_bus_probe, + .remove = selftest_i2c_bus_remove, + .driver = { + .name = "selftest-i2c-bus", + .of_match_table = of_match_ptr(selftest_i2c_bus_match), + }, +}; + +static int selftest_i2c_dev_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = client->dev.of_node; + + if (!np) { + dev_err(dev, "No OF node\n"); + return -EINVAL; + } + + dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); + + return 0; +}; + +static int selftest_i2c_dev_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *np = client->dev.of_node; + + dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); + return 0; +} + +static const struct i2c_device_id selftest_i2c_dev_id[] = { + { .name = "selftest-i2c-dev" }, + { } +}; + +static struct i2c_driver selftest_i2c_dev_driver = { + .driver = { + .name = "selftest-i2c-dev", + .owner = THIS_MODULE, + }, + .probe = selftest_i2c_dev_probe, + .remove = selftest_i2c_dev_remove, + .id_table = selftest_i2c_dev_id, +}; + +#if IS_BUILTIN(CONFIG_I2C_MUX) + +struct selftest_i2c_mux_data { + int nchans; + struct i2c_adapter *adap[]; +}; + +static int selftest_i2c_mux_select_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + return 0; +} + +static int selftest_i2c_mux_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret, i, nchans, size; + struct device *dev = &client->dev; + struct i2c_adapter *adap = to_i2c_adapter(dev->parent); + struct device_node *np = client->dev.of_node, *child; + struct selftest_i2c_mux_data *stm; + u32 reg, max_reg; + + dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); + + if (!np) { + dev_err(dev, "No OF node\n"); + return -EINVAL; + } + + max_reg = (u32)-1; + for_each_child_of_node(np, child) { + ret = of_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (max_reg == (u32)-1 || reg > max_reg) + max_reg = reg; + } + nchans = max_reg == (u32)-1 ? 0 : max_reg + 1; + if (nchans == 0) { + dev_err(dev, "No channels\n"); + return -EINVAL; + } + + size = offsetof(struct selftest_i2c_mux_data, adap[nchans]); + stm = devm_kzalloc(dev, size, GFP_KERNEL); + if (!stm) { + dev_err(dev, "Out of memory\n"); + return -ENOMEM; + } + stm->nchans = nchans; + for (i = 0; i < nchans; i++) { + stm->adap[i] = i2c_add_mux_adapter(adap, dev, client, + 0, i, 0, selftest_i2c_mux_select_chan, NULL); + if (!stm->adap[i]) { + dev_err(dev, "Failed to register mux #%d\n", i); + for (i--; i >= 0; i--) + i2c_del_mux_adapter(stm->adap[i]); + return -ENODEV; + } + } + + i2c_set_clientdata(client, stm); + + return 0; +}; + +static int selftest_i2c_mux_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *np = client->dev.of_node; + struct selftest_i2c_mux_data *stm = i2c_get_clientdata(client); + int i; + + dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); + for (i = stm->nchans - 1; i >= 0; i--) + i2c_del_mux_adapter(stm->adap[i]); + return 0; +} + +static const struct i2c_device_id selftest_i2c_mux_id[] = { + { .name = "selftest-i2c-mux" }, + { } +}; + +static struct i2c_driver selftest_i2c_mux_driver = { + .driver = { + .name = "selftest-i2c-mux", + .owner = THIS_MODULE, + }, + .probe = selftest_i2c_mux_probe, + .remove = selftest_i2c_mux_remove, + .id_table = selftest_i2c_mux_id, +}; + +#endif + +static int of_selftest_overlay_i2c_init(void) +{ + int ret; + + ret = i2c_add_driver(&selftest_i2c_dev_driver); + if (selftest(ret == 0, + "could not register selftest i2c device driver\n")) + return ret; + + ret = platform_driver_register(&selftest_i2c_bus_driver); + if (selftest(ret == 0, + "could not register selftest i2c bus driver\n")) + return ret; + +#if IS_BUILTIN(CONFIG_I2C_MUX) + ret = i2c_add_driver(&selftest_i2c_mux_driver); + if (selftest(ret == 0, + "could not register selftest i2c mux driver\n")) + return ret; +#endif + + return 0; +} + +static void of_selftest_overlay_i2c_cleanup(void) +{ +#if IS_BUILTIN(CONFIG_I2C_MUX) + i2c_del_driver(&selftest_i2c_mux_driver); +#endif + platform_driver_unregister(&selftest_i2c_bus_driver); + i2c_del_driver(&selftest_i2c_dev_driver); +} + +static void of_selftest_overlay_i2c_12(void) +{ + int ret; + + /* device should enable */ + ret = of_selftest_apply_overlay_check(12, 12, 0, 1, I2C_OVERLAY); + if (ret != 0) + return; + + selftest(1, "overlay test %d passed\n", 12); +} + +/* test deactivation of device */ +static void of_selftest_overlay_i2c_13(void) +{ + int ret; + + /* device should disable */ + ret = of_selftest_apply_overlay_check(13, 13, 1, 0, I2C_OVERLAY); + if (ret != 0) + return; + + selftest(1, "overlay test %d passed\n", 13); +} + +/* just check for i2c mux existence */ +static void of_selftest_overlay_i2c_14(void) +{ +} + +static void of_selftest_overlay_i2c_15(void) +{ + int ret; + + /* device should enable */ + ret = of_selftest_apply_overlay_check(16, 15, 0, 1, I2C_OVERLAY); + if (ret != 0) + return; + + selftest(1, "overlay test %d passed\n", 15); +} + +#else + +static inline void of_selftest_overlay_i2c_14(void) { } +static inline void of_selftest_overlay_i2c_15(void) { } + +#endif + static void __init of_selftest_overlay(void) { struct device_node *bus_np = NULL; @@ -1445,15 +1802,15 @@ static void __init of_selftest_overlay(void) goto out; } - if (!of_path_platform_device_exists(selftest_path(100))) { + if (!of_selftest_device_exists(100, PDEV_OVERLAY)) { selftest(0, "could not find selftest0 @ \"%s\"\n", - selftest_path(100)); + selftest_path(100, PDEV_OVERLAY)); goto out; } - if (of_path_platform_device_exists(selftest_path(101))) { + if (of_selftest_device_exists(101, PDEV_OVERLAY)) { selftest(0, "selftest1 @ \"%s\" should not exist\n", - selftest_path(101)); + selftest_path(101, PDEV_OVERLAY)); goto out; } @@ -1472,6 +1829,18 @@ static void __init of_selftest_overlay(void) of_selftest_overlay_10(); of_selftest_overlay_11(); +#if IS_BUILTIN(CONFIG_I2C) + if (selftest(of_selftest_overlay_i2c_init() == 0, "i2c init failed\n")) + goto out; + + of_selftest_overlay_i2c_12(); + of_selftest_overlay_i2c_13(); + of_selftest_overlay_i2c_14(); + of_selftest_overlay_i2c_15(); + + of_selftest_overlay_i2c_cleanup(); +#endif + out: of_node_put(bus_np); } @@ -1514,9 +1883,6 @@ static int __init of_selftest(void) of_selftest_platform_populate(); of_selftest_overlay(); - /* removing selftest data from live tree */ - selftest_data_remove(); - /* Double check linkage after removing testcase data */ of_selftest_check_tree_linkage(); |