Loading Documentation/devicetree/bindings/unittest.txt +58 −1 Original line number Diff line number Diff line * OF selftest platform device 1) OF selftest platform device ** selftest Loading @@ -12,3 +12,60 @@ Example: compatible = "selftest"; status = "okay"; }; 2) OF selftest i2c adapter platform device ** platform device unittest adapter Required properties: - compatible: must be selftest-i2c-bus Children nodes contain selftest i2c devices. Example: selftest-i2c-bus { compatible = "selftest-i2c-bus"; status = "okay"; }; 3) OF selftest i2c device ** I2C selftest device Required properties: - compatible: must be selftest-i2c-dev All other properties are optional Example: selftest-i2c-dev { compatible = "selftest-i2c-dev"; status = "okay"; }; 4) OF selftest i2c mux device ** I2C selftest mux Required properties: - compatible: must be selftest-i2c-mux Children nodes contain selftest i2c bus nodes per channel. Example: selftest-i2c-mux { compatible = "selftest-i2c-mux"; status = "okay"; #address-cells = <1>; #size-cells = <0>; channel-0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; i2c-dev { reg = <8>; compatible = "selftest-i2c-dev"; status = "okay"; }; }; }; drivers/of/unittest-data/tests-overlay.dtsi +94 −0 Original line number Diff line number Diff line Loading @@ -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"; }; }; }; }; }; }; Loading Loading @@ -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"; }; }; }; }; }; }; }; }; drivers/of/unittest.c +462 −50 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -991,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_ENABLED(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]; Loading Loading @@ -1050,16 +1130,15 @@ static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr, /* 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; } Loading @@ -1071,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; } Loading @@ -1085,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; } Loading @@ -1107,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; } Loading @@ -1120,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; } Loading @@ -1143,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; Loading @@ -1156,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; Loading @@ -1169,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; Loading @@ -1182,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; Loading @@ -1195,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; Loading @@ -1208,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; Loading @@ -1225,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; } Loading @@ -1257,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; } Loading @@ -1273,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; } Loading Loading @@ -1327,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; } Loading @@ -1337,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; } } Loading @@ -1352,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; Loading @@ -1373,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_ENABLED(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_ENABLED(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_ENABLED(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_ENABLED(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; Loading @@ -1402,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; } Loading @@ -1429,6 +1829,18 @@ static void __init of_selftest_overlay(void) of_selftest_overlay_10(); of_selftest_overlay_11(); #if IS_ENABLED(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); } Loading Loading
Documentation/devicetree/bindings/unittest.txt +58 −1 Original line number Diff line number Diff line * OF selftest platform device 1) OF selftest platform device ** selftest Loading @@ -12,3 +12,60 @@ Example: compatible = "selftest"; status = "okay"; }; 2) OF selftest i2c adapter platform device ** platform device unittest adapter Required properties: - compatible: must be selftest-i2c-bus Children nodes contain selftest i2c devices. Example: selftest-i2c-bus { compatible = "selftest-i2c-bus"; status = "okay"; }; 3) OF selftest i2c device ** I2C selftest device Required properties: - compatible: must be selftest-i2c-dev All other properties are optional Example: selftest-i2c-dev { compatible = "selftest-i2c-dev"; status = "okay"; }; 4) OF selftest i2c mux device ** I2C selftest mux Required properties: - compatible: must be selftest-i2c-mux Children nodes contain selftest i2c bus nodes per channel. Example: selftest-i2c-mux { compatible = "selftest-i2c-mux"; status = "okay"; #address-cells = <1>; #size-cells = <0>; channel-0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; i2c-dev { reg = <8>; compatible = "selftest-i2c-dev"; status = "okay"; }; }; };
drivers/of/unittest-data/tests-overlay.dtsi +94 −0 Original line number Diff line number Diff line Loading @@ -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"; }; }; }; }; }; }; Loading Loading @@ -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"; }; }; }; }; }; }; }; };
drivers/of/unittest.c +462 −50 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -991,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_ENABLED(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]; Loading Loading @@ -1050,16 +1130,15 @@ static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr, /* 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; } Loading @@ -1071,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; } Loading @@ -1085,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; } Loading @@ -1107,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; } Loading @@ -1120,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; } Loading @@ -1143,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; Loading @@ -1156,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; Loading @@ -1169,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; Loading @@ -1182,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; Loading @@ -1195,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; Loading @@ -1208,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; Loading @@ -1225,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; } Loading @@ -1257,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; } Loading @@ -1273,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; } Loading Loading @@ -1327,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; } Loading @@ -1337,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; } } Loading @@ -1352,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; Loading @@ -1373,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_ENABLED(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_ENABLED(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_ENABLED(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_ENABLED(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; Loading @@ -1402,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; } Loading @@ -1429,6 +1829,18 @@ static void __init of_selftest_overlay(void) of_selftest_overlay_10(); of_selftest_overlay_11(); #if IS_ENABLED(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); } Loading