| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 2014 Google, Inc |
| */ |
| |
| #include <dm.h> |
| #include <dm/test.h> |
| #include <asm/global_data.h> |
| |
| /* Records the last testbus device that was removed */ |
| static struct udevice *testbus_removed; |
| |
| struct udevice *testbus_get_clear_removed(void) |
| { |
| struct udevice *removed = testbus_removed; |
| |
| testbus_removed = NULL; |
| |
| return removed; |
| } |
| |
| static int testbus_drv_probe(struct udevice *dev) |
| { |
| if (!CONFIG_IS_ENABLED(OF_PLATDATA)) { |
| int ret; |
| |
| ret = dm_scan_fdt_dev(dev); |
| if (ret) |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int testbus_child_post_bind(struct udevice *dev) |
| { |
| struct dm_test_parent_plat *plat; |
| |
| plat = dev_get_parent_plat(dev); |
| plat->bind_flag = 1; |
| plat->uclass_bind_flag = 2; |
| |
| return 0; |
| } |
| |
| static int testbus_child_pre_probe(struct udevice *dev) |
| { |
| struct dm_test_parent_data *parent_data = dev_get_parent_priv(dev); |
| |
| parent_data->flag += TEST_FLAG_CHILD_PROBED; |
| |
| return 0; |
| } |
| |
| static int testbus_child_pre_probe_uclass(struct udevice *dev) |
| { |
| struct dm_test_priv *priv = dev_get_priv(dev); |
| |
| priv->uclass_flag++; |
| |
| return 0; |
| } |
| |
| static int testbus_child_post_probe_uclass(struct udevice *dev) |
| { |
| struct dm_test_priv *priv = dev_get_priv(dev); |
| |
| priv->uclass_postp++; |
| |
| return 0; |
| } |
| |
| static int testbus_child_post_remove(struct udevice *dev) |
| { |
| struct dm_test_parent_data *parent_data = dev_get_parent_priv(dev); |
| |
| parent_data->flag += TEST_FLAG_CHILD_REMOVED; |
| testbus_removed = dev; |
| |
| return 0; |
| } |
| |
| static const struct udevice_id testbus_ids[] = { |
| { .compatible = "denx,u-boot-test-bus", .data = DM_TEST_TYPE_FIRST }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(denx_u_boot_test_bus) = { |
| .name = "testbus_drv", |
| .of_match = testbus_ids, |
| .id = UCLASS_TEST_BUS, |
| .probe = testbus_drv_probe, |
| .child_post_bind = testbus_child_post_bind, |
| .priv_auto = sizeof(struct dm_test_priv), |
| .plat_auto = sizeof(struct dm_test_pdata), |
| .per_child_auto = sizeof(struct dm_test_parent_data), |
| .per_child_plat_auto = sizeof(struct dm_test_parent_plat), |
| .child_pre_probe = testbus_child_pre_probe, |
| .child_post_remove = testbus_child_post_remove, |
| DM_HEADER(<test.h>) |
| }; |
| |
| UCLASS_DRIVER(testbus) = { |
| .name = "testbus", |
| .id = UCLASS_TEST_BUS, |
| .flags = DM_UC_FLAG_SEQ_ALIAS, |
| .child_pre_probe = testbus_child_pre_probe_uclass, |
| .child_post_probe = testbus_child_post_probe_uclass, |
| |
| .per_device_auto = sizeof(struct dm_test_uclass_priv), |
| |
| /* Note: this is for dtoc testing as well as tags*/ |
| .per_device_plat_auto = sizeof(struct dm_test_uclass_plat), |
| }; |
| |
| static int testfdt_drv_ping(struct udevice *dev, int pingval, int *pingret) |
| { |
| const struct dm_test_pdata *pdata = dev_get_plat(dev); |
| struct dm_test_priv *priv = dev_get_priv(dev); |
| |
| *pingret = pingval + pdata->ping_add; |
| priv->ping_total += *pingret; |
| |
| return 0; |
| } |
| |
| static const struct test_ops test_ops = { |
| .ping = testfdt_drv_ping, |
| }; |
| |
| static int testfdt_of_to_plat(struct udevice *dev) |
| { |
| struct dm_test_pdata *pdata = dev_get_plat(dev); |
| |
| pdata->ping_add = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), |
| "ping-add", -1); |
| pdata->base = fdtdec_get_addr(gd->fdt_blob, dev_of_offset(dev), |
| "ping-expect"); |
| |
| return 0; |
| } |
| |
| static int testfdt_drv_probe(struct udevice *dev) |
| { |
| struct dm_test_priv *priv = dev_get_priv(dev); |
| |
| priv->ping_total += DM_TEST_START_TOTAL; |
| |
| /* |
| * If this device is on a bus, the uclass_flag will be set before |
| * calling this function. In the meantime the uclass_postp is |
| * initlized to a value -1. These are used respectively by |
| * dm_test_bus_child_pre_probe_uclass() and |
| * dm_test_bus_child_post_probe_uclass(). |
| */ |
| priv->uclass_total += priv->uclass_flag; |
| priv->uclass_postp = -1; |
| |
| return 0; |
| } |
| |
| static const struct udevice_id testfdt_ids[] = { |
| { .compatible = "denx,u-boot-fdt-test", .data = DM_TEST_TYPE_FIRST }, |
| { .compatible = "google,another-fdt-test", .data = DM_TEST_TYPE_SECOND }, |
| { } |
| }; |
| |
| DM_DRIVER_ALIAS(denx_u_boot_fdt_test, google_another_fdt_test) |
| |
| U_BOOT_DRIVER(denx_u_boot_fdt_test) = { |
| .name = "testfdt_drv", |
| .of_match = testfdt_ids, |
| .id = UCLASS_TEST_FDT, |
| .of_to_plat = testfdt_of_to_plat, |
| .probe = testfdt_drv_probe, |
| .ops = &test_ops, |
| .priv_auto = sizeof(struct dm_test_priv), |
| .plat_auto = sizeof(struct dm_test_pdata), |
| }; |
| |
| static const struct udevice_id testfdt1_ids[] = { |
| { .compatible = "denx,u-boot-fdt-test1", .data = DM_TEST_TYPE_FIRST }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(testfdt1_drv) = { |
| .name = "testfdt1_drv", |
| .of_match = testfdt1_ids, |
| .id = UCLASS_TEST_FDT, |
| .of_to_plat = testfdt_of_to_plat, |
| .probe = testfdt_drv_probe, |
| .ops = &test_ops, |
| .priv_auto = sizeof(struct dm_test_priv), |
| .plat_auto = sizeof(struct dm_test_pdata), |
| .flags = DM_FLAG_PRE_RELOC, |
| }; |
| |
| /* From here is the testfdt uclass code */ |
| int testfdt_ping(struct udevice *dev, int pingval, int *pingret) |
| { |
| const struct test_ops *ops = device_get_ops(dev); |
| |
| if (!ops->ping) |
| return -ENOSYS; |
| |
| return ops->ping(dev, pingval, pingret); |
| } |
| |
| UCLASS_DRIVER(testfdt) = { |
| .name = "testfdt", |
| .id = UCLASS_TEST_FDT, |
| .flags = DM_UC_FLAG_SEQ_ALIAS, |
| .priv_auto = sizeof(struct dm_test_uc_priv), |
| }; |
| |
| static const struct udevice_id testfdtm_ids[] = { |
| { .compatible = "denx,u-boot-fdtm-test" }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(testfdtm_drv) = { |
| .name = "testfdtm_drv", |
| .of_match = testfdtm_ids, |
| .id = UCLASS_TEST_FDT_MANUAL, |
| }; |
| |
| UCLASS_DRIVER(testfdtm) = { |
| .name = "testfdtm", |
| .id = UCLASS_TEST_FDT_MANUAL, |
| .flags = DM_UC_FLAG_SEQ_ALIAS | DM_UC_FLAG_NO_AUTO_SEQ, |
| }; |