diff options
-rw-r--r-- | boot/bootflow.c | 29 | ||||
-rw-r--r-- | boot/bootmeth_script.c | 16 | ||||
-rw-r--r-- | drivers/ata/Makefile | 2 | ||||
-rw-r--r-- | drivers/ata/sata.c | 38 | ||||
-rw-r--r-- | drivers/ata/sata_bootdev.c | 62 | ||||
-rw-r--r-- | include/sata.h | 6 | ||||
-rw-r--r-- | test/boot/bootdev.c | 54 |
7 files changed, 192 insertions, 15 deletions
diff --git a/boot/bootflow.c b/boot/bootflow.c index e03932e65a..be543c8588 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -156,6 +156,27 @@ static void bootflow_iter_set_dev(struct bootflow_iter *iter, } /** + * scan_next_in_uclass() - Scan for the next bootdev in the same media uclass + * + * Move through the following bootdevs until we find another in this media + * uclass, or run out + * + * @devp: On entry, the device to check, on exit the new device, or NULL if + * there is none + */ +static void scan_next_in_uclass(struct udevice **devp) +{ + struct udevice *dev = *devp; + enum uclass_id cur_id = device_get_uclass_id(dev->parent); + + do { + uclass_find_next_device(&dev); + } while (dev && cur_id != device_get_uclass_id(dev->parent)); + + *devp = dev; +} + +/** * iter_incr() - Move to the next item (method, part, bootdev) * * Return: 0 if OK, BF_NO_MORE_DEVICES if there are no more bootdevs @@ -230,8 +251,7 @@ static int iter_incr(struct bootflow_iter *iter) &method_flags); } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && (iter->flags & BOOTFLOWIF_SINGLE_UCLASS)) { - /* Move to the next bootdev in this uclass */ - uclass_find_next_device(&dev); + scan_next_in_uclass(&dev); if (!dev) { log_debug("finished uclass %s\n", dev_get_uclass_name(dev)); @@ -266,8 +286,9 @@ static int iter_incr(struct bootflow_iter *iter) * bootdev_find_by_label() where this flag is * set up */ - if (iter->method_flags & BOOTFLOW_METHF_SINGLE_UCLASS) { - uclass_next_device(&dev); + if (iter->method_flags & + BOOTFLOW_METHF_SINGLE_UCLASS) { + scan_next_in_uclass(&dev); log_debug("looking for next device %s: %s\n", iter->dev->name, dev ? dev->name : "<none>"); diff --git a/boot/bootmeth_script.c b/boot/bootmeth_script.c index 345114dabf..06340e43d2 100644 --- a/boot/bootmeth_script.c +++ b/boot/bootmeth_script.c @@ -188,12 +188,20 @@ static int script_boot(struct udevice *dev, struct bootflow *bflow) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); ulong addr; - int ret; + int ret = 0; - if (desc->uclass_id == UCLASS_USB) + if (desc->uclass_id == UCLASS_USB) { ret = env_set("devtype", "usb"); - else - ret = env_set("devtype", blk_get_devtype(bflow->blk)); + } else { + /* If the uclass is AHCI, but the driver is ATA + * (not scsi), set devtype to sata + */ + if (IS_ENABLED(CONFIG_SATA) && + desc->uclass_id == UCLASS_AHCI) + ret = env_set("devtype", "sata"); + else + ret = env_set("devtype", blk_get_devtype(bflow->blk)); + } if (!ret) ret = env_set_hex("devnum", desc->devnum); if (!ret) diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 6e30180b8b..0b6f91098a 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_SCSI_AHCI) += ahci.o obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o obj-$(CONFIG_FSL_SATA) += fsl_sata.o obj-$(CONFIG_LIBATA) += libata.o -obj-$(CONFIG_SATA) += sata.o +obj-$(CONFIG_SATA) += sata.o sata_bootdev.o obj-$(CONFIG_SATA_CEVA) += sata_ceva.o obj-$(CONFIG_SATA_MV) += sata_mv.o obj-$(CONFIG_SATA_SIL) += sata_sil.o diff --git a/drivers/ata/sata.c b/drivers/ata/sata.c index ce3e9b5a40..dcb5fcf476 100644 --- a/drivers/ata/sata.c +++ b/drivers/ata/sata.c @@ -15,6 +15,8 @@ #include <dm.h> #include <part.h> #include <sata.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> #ifndef CONFIG_AHCI struct blk_desc sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE]; @@ -50,6 +52,42 @@ int sata_scan(struct udevice *dev) return ops->scan(dev); } +int sata_rescan(bool verbose) +{ + int ret; + struct udevice *dev; + + if (verbose) + printf("Removing devices on SATA bus...\n"); + + blk_unbind_all(UCLASS_AHCI); + + ret = uclass_find_first_device(UCLASS_AHCI, &dev); + if (ret || !dev) { + printf("Cannot find SATA device (err=%d)\n", ret); + return -ENOSYS; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + printf("Cannot remove SATA device '%s' (err=%d)\n", dev->name, ret); + return -ENOSYS; + } + + if (verbose) + printf("Rescanning SATA bus for devices...\n"); + + ret = uclass_probe_all(UCLASS_AHCI); + + if (ret == -ENODEV) { + if (verbose) + printf("No SATA block device found\n"); + return 0; + } + + return ret; +} + #ifndef CONFIG_AHCI #ifdef CONFIG_PARTITIONS struct blk_desc *sata_get_dev(int dev) diff --git a/drivers/ata/sata_bootdev.c b/drivers/ata/sata_bootdev.c new file mode 100644 index 0000000000..f638493ce0 --- /dev/null +++ b/drivers/ata/sata_bootdev.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Bootdev for sata + * + * Copyright 2023 Tony Dinh <mibodhi@gmail.com> + */ + +#include <common.h> +#include <ahci.h> +#include <bootdev.h> +#include <dm.h> +#include <init.h> +#include <sata.h> + +static int sata_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_4_SCAN_FAST; + + return 0; +} + +static int sata_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + int ret; + + if (IS_ENABLED(CONFIG_PCI)) { + ret = pci_init(); + if (ret) + return ret; + } + + ret = sata_rescan(true); + if (ret) + return ret; + + return 0; +} + +struct bootdev_ops sata_bootdev_ops = { +}; + +static const struct udevice_id sata_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-sata" }, + { } +}; + +U_BOOT_DRIVER(sata_bootdev) = { + .name = "sata_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &sata_bootdev_ops, + .bind = sata_bootdev_bind, + .of_match = sata_bootdev_ids, +}; + +BOOTDEV_HUNTER(sata_bootdev_hunter) = { + .prio = BOOTDEVP_4_SCAN_FAST, + .uclass = UCLASS_AHCI, + .hunt = sata_bootdev_hunt, + .drv = DM_DRIVER_REF(sata_bootdev), +}; diff --git a/include/sata.h b/include/sata.h index d89f7a8a29..6111cf65d9 100644 --- a/include/sata.h +++ b/include/sata.h @@ -21,4 +21,10 @@ extern struct blk_desc sata_dev_desc[]; int sata_probe(int devnum); int sata_remove(int devnum); +/* + * Remove existing AHCI SATA device uclass and all of its children, + * if any, and probe it again. + */ +int sata_rescan(bool verbose); + #endif diff --git a/test/boot/bootdev.c b/test/boot/bootdev.c index c5f14a7a13..0702fccdae 100644 --- a/test/boot/bootdev.c +++ b/test/boot/bootdev.c @@ -190,12 +190,21 @@ static int bootdev_test_any(struct unit_test_state *uts) BOOTSTD_TEST(bootdev_test_any, UT_TESTF_DM | UT_TESTF_SCAN_FDT | UT_TESTF_ETH_BOOTDEV); -/* Check bootdev ordering with the bootdev-order property */ +/* + * Check bootdev ordering with the bootdev-order property and boot_targets + * environment variable + */ static int bootdev_test_order(struct unit_test_state *uts) { struct bootflow_iter iter; struct bootflow bflow; + test_set_skip_delays(true); + + /* Start up USB which gives us three additional bootdevs */ + usb_started = false; + ut_assertok(run_command("usb start", 0)); + /* * First try the order set by the bootdev-order property * Like all sandbox unit tests this relies on the devicetree setting up @@ -213,17 +222,22 @@ static int bootdev_test_order(struct unit_test_state *uts) bootflow_iter_uninit(&iter); /* Use the environment variable to override it */ - ut_assertok(env_set("boot_targets", "mmc1 mmc2")); + ut_assertok(env_set("boot_targets", "mmc1 mmc2 usb")); ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); - ut_asserteq(2, iter.num_devs); + ut_asserteq(5, iter.num_devs); ut_asserteq_str("mmc1.bootdev", iter.dev_used[0]->name); ut_asserteq_str("mmc2.bootdev", iter.dev_used[1]->name); + ut_asserteq_str("usb_mass_storage.lun0.bootdev", + iter.dev_used[2]->name); bootflow_iter_uninit(&iter); - /* Make sure it scans a bootdevs in each target */ - ut_assertok(env_set("boot_targets", "mmc spi")); - ut_asserteq(0, bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); + /* Try a single uclass */ + ut_assertok(env_set("boot_targets", NULL)); + ut_assertok(bootflow_scan_first(NULL, "mmc", &iter, 0, &bflow)); + ut_asserteq(2, iter.num_devs); + + /* Now scan past mmc1 and make sure that only mmc0 shows up */ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); ut_asserteq(3, iter.num_devs); ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name); @@ -231,6 +245,34 @@ static int bootdev_test_order(struct unit_test_state *uts) ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name); bootflow_iter_uninit(&iter); + /* Try a single uclass with boot_targets */ + ut_assertok(env_set("boot_targets", "mmc")); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); + ut_asserteq(2, iter.num_devs); + + /* Now scan past mmc1 and make sure that only mmc0 shows up */ + ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); + ut_asserteq(3, iter.num_devs); + ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name); + ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name); + ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name); + bootflow_iter_uninit(&iter); + + /* Try a single uclass with boot_targets */ + ut_assertok(env_set("boot_targets", "mmc usb")); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); + ut_asserteq(2, iter.num_devs); + + /* Now scan past mmc1 and make sure that the 3 USB devices show up */ + ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); + ut_asserteq(6, iter.num_devs); + ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name); + ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name); + ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name); + ut_asserteq_str("usb_mass_storage.lun0.bootdev", + iter.dev_used[3]->name); + bootflow_iter_uninit(&iter); + return 0; } BOOTSTD_TEST(bootdev_test_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT); |