mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	We want to support scanning a single label, like 'mmc' or 'usb0'. Add this feature by plumbing the label through to the iterator, setting a flag to indicate that only siblings of the initial device should be used. This means that scanning a bootdev by its name is not supported anymore. That feature doesn't seem very useful in practice, so it is no great loss. Add a test for bootdev_find_by_any() while we are here. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			444 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			444 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0+ */
 | |
| /*
 | |
|  * Copyright 2021 Google LLC
 | |
|  * Written by Simon Glass <sjg@chromium.org>
 | |
|  */
 | |
| 
 | |
| #ifndef __bootdev_h
 | |
| #define __bootdev_h
 | |
| 
 | |
| #include <linux/list.h>
 | |
| 
 | |
| struct bootflow;
 | |
| struct bootflow_iter;
 | |
| struct bootstd_priv;
 | |
| struct udevice;
 | |
| 
 | |
| /**
 | |
|  * enum bootdev_prio_t - priority of each bootdev
 | |
|  *
 | |
|  * These values are associated with each bootdev and set up by the driver.
 | |
|  *
 | |
|  * Smallest value is the highest priority. By default, bootdevs are scanned from
 | |
|  * highest to lowest priority
 | |
|  *
 | |
|  * BOOTDEVP_0_NONE: Invalid value, do not use
 | |
|  * @BOOTDEVP_6_PRE_SCAN: Scan bootdevs with this priority always, before
 | |
|  * starting any bootflow scan
 | |
|  * @BOOTDEVP_2_INTERNAL_FAST: Internal devices which don't need scanning and
 | |
|  * generally very quick to access, e.g. less than 100ms
 | |
|  * @BOOTDEVP_3_INTERNAL_SLOW: Internal devices which don't need scanning but
 | |
|  * take a significant fraction of a second to access
 | |
|  * @BOOTDEVP_4_SCAN_FAST: Extenal devices which need scanning or bus
 | |
|  * enumeration to find, but this enumeration happens quickly, typically under
 | |
|  * 100ms
 | |
|  * @BOOTDEVP_5_SCAN_SLOW: Extenal devices which need scanning or bus
 | |
|  * enumeration to find. The enumeration takes significant fraction of a second
 | |
|  * to complete
 | |
|  * @BOOTDEVP_6_NET_BASE: Basic network devices which are quickly and easily
 | |
|  * available. Typically used for an internal Ethernet device
 | |
|  * @BOOTDEVP_7_NET_FALLBACK: Secondary network devices which require extra time
 | |
|  * to start up, or are less desirable. Typically used for secondary Ethernet
 | |
|  * devices. Note that USB ethernet devices are found during USB enumeration,
 | |
|  * so do not use this priority
 | |
|  */
 | |
| enum bootdev_prio_t {
 | |
| 	BOOTDEVP_0_NONE,
 | |
| 	BOOTDEVP_1_PRE_SCAN,
 | |
| 	BOOTDEVP_2_INTERNAL_FAST,
 | |
| 	BOOTDEVP_3_INTERNAL_SLOW,
 | |
| 	BOOTDEVP_4_SCAN_FAST,
 | |
| 	BOOTDEVP_5_SCAN_SLOW,
 | |
| 	BOOTDEVP_6_NET_BASE,
 | |
| 	BOOTDEVP_7_NET_FALLBACK,
 | |
| 
 | |
| 	BOOTDEVP_COUNT,
 | |
| };
 | |
| 
 | |
| struct bootdev_hunter;
 | |
| 
 | |
| /**
 | |
|  * bootdev_hunter_func - function to probe for bootdevs of a given type
 | |
|  *
 | |
|  * This should hunt around for bootdevs of the given type, binding them as it
 | |
|  * finds them. This may involve bus enumeration, etc.
 | |
|  *
 | |
|  * @info: Info structure describing this hunter
 | |
|  * @show: true to show information from the hunter
 | |
|  * Returns: 0 if OK, -ve on error
 | |
|  */
 | |
| typedef int (*bootdev_hunter_func)(struct bootdev_hunter *info, bool show);
 | |
| 
 | |
| /**
 | |
|  * struct bootdev_hunter - information about how to hunt for bootdevs
 | |
|  *
 | |
|  * @prio: Scanning priority of this hunter
 | |
|  * @uclass: Uclass ID for the media associated with this bootdev
 | |
|  * @drv: bootdev driver for the things found by this hunter
 | |
|  * @hunt: Function to call to hunt for bootdevs of this type (NULL if none)
 | |
|  *
 | |
|  * Some bootdevs are not visible until other devices are enumerated. For
 | |
|  * example, USB bootdevs only appear when the USB bus is enumerated.
 | |
|  *
 | |
|  * On the other hand, we don't always want to enumerate all the buses just to
 | |
|  * find the first valid bootdev. Ideally we want to work through them in
 | |
|  * priority order, so that the fastest bootdevs are discovered first.
 | |
|  *
 | |
|  * This struct holds information about the bootdev so we can determine the probe
 | |
|  * order and how to hunt for bootdevs of this type
 | |
|  */
 | |
| struct bootdev_hunter {
 | |
| 	enum bootdev_prio_t prio;
 | |
| 	enum uclass_id uclass;
 | |
| 	struct driver *drv;
 | |
| 	bootdev_hunter_func hunt;
 | |
| };
 | |
| 
 | |
| /* declare a new bootdev hunter */
 | |
| #define BOOTDEV_HUNTER(__name)						\
 | |
| 	ll_entry_declare(struct bootdev_hunter, __name, bootdev_hunter)
 | |
| 
 | |
| /* access a bootdev hunter by name */
 | |
| #define BOOTDEV_HUNTER_GET(__name)						\
 | |
| 	ll_entry_get(struct bootdev_hunter, __name, bootdev_hunter)
 | |
| 
 | |
| /**
 | |
|  * struct bootdev_uc_plat - uclass information about a bootdev
 | |
|  *
 | |
|  * This is attached to each device in the bootdev uclass and accessible via
 | |
|  * dev_get_uclass_plat(dev)
 | |
|  *
 | |
|  * @bootflows: List of available bootflows for this bootdev
 | |
|  * @piro: Priority of this bootdev
 | |
|  */
 | |
| struct bootdev_uc_plat {
 | |
| 	struct list_head bootflow_head;
 | |
| 	enum bootdev_prio_t prio;
 | |
| };
 | |
| 
 | |
| /** struct bootdev_ops - Operations for the bootdev uclass */
 | |
| struct bootdev_ops {
 | |
| 	/**
 | |
| 	 * get_bootflow() - get a bootflow (optional)
 | |
| 	 *
 | |
| 	 * If this is NULL then the default implementaton is used, which is
 | |
| 	 * default_get_bootflow()
 | |
| 	 *
 | |
| 	 * @dev:	Bootflow device to check
 | |
| 	 * @iter:	Provides current dev, part, method to get. Should update
 | |
| 	 *	max_part if there is a partition table. Should update state,
 | |
| 	 *	subdir, fname, buf, size according to progress
 | |
| 	 * @bflow:	Updated bootflow if found
 | |
| 	 * Return: 0 if OK, -ESHUTDOWN if there are no more bootflows on this
 | |
| 	 *	device, -ENOSYS if this device doesn't support bootflows,
 | |
| 	 *	other -ve value on other error
 | |
| 	 */
 | |
| 	int (*get_bootflow)(struct udevice *dev, struct bootflow_iter *iter,
 | |
| 			    struct bootflow *bflow);
 | |
| };
 | |
| 
 | |
| #define bootdev_get_ops(dev)  ((struct bootdev_ops *)(dev)->driver->ops)
 | |
| 
 | |
| /**
 | |
|  * bootdev_get_bootflow() - get a bootflow
 | |
|  *
 | |
|  * @dev:	Bootflow device to check
 | |
|  * @iter:	Provides current  part, method to get
 | |
|  * @bflow:	Returns bootflow if found
 | |
|  * Return: 0 if OK, -ESHUTDOWN if there are no more bootflows on this device,
 | |
|  *	-ENOSYS if this device doesn't support bootflows, other -ve value on
 | |
|  *	other error
 | |
|  */
 | |
| int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
 | |
| 			 struct bootflow *bflow);
 | |
| 
 | |
| /**
 | |
|  * bootdev_bind() - Bind a new named bootdev device
 | |
|  *
 | |
|  * @parent:	Parent of the new device
 | |
|  * @drv_name:	Driver name to use for the bootdev device
 | |
|  * @name:	Name for the device (parent name is prepended)
 | |
|  * @devp:	the new device (which has not been probed)
 | |
|  */
 | |
| int bootdev_bind(struct udevice *parent, const char *drv_name, const char *name,
 | |
| 		 struct udevice **devp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_find_in_blk() - Find a bootdev in a block device
 | |
|  *
 | |
|  * @dev: Bootflow device associated with this block device
 | |
|  * @blk: Block device to search
 | |
|  * @iter:	Provides current dev, part, method to get. Should update
 | |
|  *	max_part if there is a partition table
 | |
|  * @bflow: On entry, provides information about the partition and device to
 | |
|  *	check. On exit, returns bootflow if found
 | |
|  * Return: 0 if found, -ESHUTDOWN if no more bootflows, other -ve on error
 | |
|  */
 | |
| int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
 | |
| 			struct bootflow_iter *iter, struct bootflow *bflow);
 | |
| 
 | |
| /**
 | |
|  * bootdev_list() - List all available bootdevs
 | |
|  *
 | |
|  * @probe: true to probe devices, false to leave them as is
 | |
|  */
 | |
| void bootdev_list(bool probe);
 | |
| 
 | |
| /**
 | |
|  * bootdev_clear_bootflows() - Clear bootflows from a bootdev
 | |
|  *
 | |
|  * Each bootdev maintains a list of discovered bootflows. This provides a
 | |
|  * way to clear it. These bootflows are removed from the global list too.
 | |
|  *
 | |
|  * @dev: bootdev device to update
 | |
|  */
 | |
| void bootdev_clear_bootflows(struct udevice *dev);
 | |
| 
 | |
| /**
 | |
|  * bootdev_add_bootflow() - Add a bootflow to the bootdev's list
 | |
|  *
 | |
|  * All fields in @bflow must be set up. Note that @bflow->dev is used to add the
 | |
|  * bootflow to that device.
 | |
|  *
 | |
|  * @dev: Bootdevice device to add to
 | |
|  * @bflow: Bootflow to add. Note that fields within bflow must be allocated
 | |
|  *	since this function takes over ownership of these. This functions makes
 | |
|  *	a copy of @bflow itself (without allocating its fields again), so the
 | |
|  *	caller must dispose of the memory used by the @bflow pointer itself
 | |
|  * Return: 0 if OK, -ENOMEM if out of memory
 | |
|  */
 | |
| int bootdev_add_bootflow(struct bootflow *bflow);
 | |
| 
 | |
| /**
 | |
|  * bootdev_first_bootflow() - Get the first bootflow from a bootdev
 | |
|  *
 | |
|  * Returns the first bootflow attached to a bootdev
 | |
|  *
 | |
|  * @dev: bootdev device
 | |
|  * @bflowp: Returns a pointer to the bootflow
 | |
|  * Return: 0 if found, -ENOENT if there are no bootflows
 | |
|  */
 | |
| int bootdev_first_bootflow(struct udevice *dev, struct bootflow **bflowp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_next_bootflow() - Get the next bootflow from a bootdev
 | |
|  *
 | |
|  * Returns the next bootflow attached to a bootdev
 | |
|  *
 | |
|  * @bflowp: On entry, the last bootflow returned , e.g. from
 | |
|  *	bootdev_first_bootflow()
 | |
|  * Return: 0 if found, -ENOENT if there are no more bootflows
 | |
|  */
 | |
| int bootdev_next_bootflow(struct bootflow **bflowp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_find_by_label() - Look up a bootdev by label
 | |
|  *
 | |
|  * Each bootdev has a label which contains the media-uclass name and a number,
 | |
|  * e.g. 'mmc2'. This looks up the label and returns the associated bootdev
 | |
|  *
 | |
|  * The lookup is performed based on the media device's sequence number. So for
 | |
|  * 'mmc2' this looks for a device in UCLASS_MMC with a dev_seq() of 2.
 | |
|  *
 | |
|  * @label: Label to look up (e.g. "mmc1" or "mmc0")
 | |
|  * @devp: Returns the bootdev device found, or NULL if none (note it does not
 | |
|  *	return the media device, but its bootdev child)
 | |
|  * @method_flagsp: If non-NULL, returns any flags implied by the label
 | |
|  * (enum bootflow_meth_flags_t), 0 if none. Unset if function fails
 | |
|  * Return: 0 if OK, -EINVAL if the uclass is not supported by this board,
 | |
|  * -ENOENT if there is no device with that number
 | |
|  */
 | |
| int bootdev_find_by_label(const char *label, struct udevice **devp,
 | |
| 			  int *method_flagsp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_find_by_any() - Find a bootdev by name, label or sequence
 | |
|  *
 | |
|  * @name: name (e.g. "mmc2.bootdev"), label ("mmc2"), or sequence ("2") to find
 | |
|  * @devp: returns the device found, on success
 | |
|  * @method_flagsp: If non-NULL, returns any flags implied by the label
 | |
|  * (enum bootflow_meth_flags_t), 0 if none. Unset if function fails
 | |
|  * Return: 0 if OK, -EINVAL if the uclass is not supported by this board,
 | |
|  * -ENOENT if there is no device with that number
 | |
|  */
 | |
| int bootdev_find_by_any(const char *name, struct udevice **devp,
 | |
| 			int *method_flagsp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_setup_iter() - Set up iteration through bootdevs
 | |
|  *
 | |
|  * This sets up the an interation, based on the provided device or label. If
 | |
|  * neither is provided, the iteration is based on the priority of each bootdev,
 | |
|  * the * bootdev-order property in the bootstd node (or the boot_targets env
 | |
|  * var).
 | |
|  *
 | |
|  * @iter: Iterator to update with the order
 | |
|  * @label: label to scan, or NULL to scan all
 | |
|  * @devp: On entry, *devp is NULL to scan all, otherwise this is the (single)
 | |
|  *	device to scan. Returns the first device to use, which is the passed-in
 | |
|  *	@devp if it was non-NULL
 | |
|  * @method_flagsp: If non-NULL, returns any flags implied by the label
 | |
|  * (enum bootflow_meth_flags_t), 0 if none
 | |
|  * Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve
 | |
|  *	on other error
 | |
|  */
 | |
| int bootdev_setup_iter(struct bootflow_iter *iter, const char *label,
 | |
| 		       struct udevice **devp, int *method_flagsp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_list_hunters() - List the available bootdev hunters
 | |
|  *
 | |
|  * These provide a way to find new bootdevs by enumerating buses, etc. This
 | |
|  * function lists the available hunters
 | |
|  *
 | |
|  * @std: Pointer to bootstd private info
 | |
|  */
 | |
| void bootdev_list_hunters(struct bootstd_priv *std);
 | |
| 
 | |
| /**
 | |
|  * bootdev_hunt() - Hunt for bootdevs matching a particular spec
 | |
|  *
 | |
|  * This runs the selected hunter (or all if @spec is NULL) to try to find new
 | |
|  * bootdevs.
 | |
|  *
 | |
|  * @spec: Spec to match, e.g. "mmc0", or NULL for any. If provided, this must
 | |
|  * match a uclass name so that the hunter can be determined. Any trailing number
 | |
|  * is ignored
 | |
|  * @show: true to show each hunter before using it
 | |
|  * Returns: 0 if OK, -ve on error
 | |
|  */
 | |
| int bootdev_hunt(const char *spec, bool show);
 | |
| 
 | |
| /**
 | |
|  * bootdev_hunt_prio() - Hunt for bootdevs of a particular priority
 | |
|  *
 | |
|  * This runs all hunters which can find bootdevs of the given priority.
 | |
|  *
 | |
|  * @prio: Priority to use
 | |
|  * @show: true to show each hunter as it is used
 | |
|  * Returns: 0 if OK, -ve on error
 | |
|  */
 | |
| int bootdev_hunt_prio(enum bootdev_prio_t prio, bool show);
 | |
| 
 | |
| /**
 | |
|  * bootdev_hunt_and_find_by_label() - Hunt for bootdevs by label
 | |
|  *
 | |
|  * Runs the hunter for the label, then tries to find the bootdev, possible
 | |
|  * created by the hunter
 | |
|  *
 | |
|  * @label: Label to look up (e.g. "mmc1" or "mmc0")
 | |
|  * @devp: Returns the bootdev device found, or NULL if none (note it does not
 | |
|  *	return the media device, but its bootdev child)
 | |
|  * @method_flagsp: If non-NULL, returns any flags implied by the label
 | |
|  * (enum bootflow_meth_flags_t), 0 if none. Unset if function fails
 | |
|  * Return: 0 if OK, -EINVAL if the uclass is not supported by this board,
 | |
|  * -ENOENT if there is no device with that number
 | |
|  */
 | |
| int bootdev_hunt_and_find_by_label(const char *label, struct udevice **devp,
 | |
| 				   int *method_flagsp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_next_label() - Move to the next bootdev in the label sequence
 | |
|  *
 | |
|  * Looks through the remaining labels until it finds one that matches a bootdev.
 | |
|  * Bootdev scanners are used as needed. For example a label "mmc1" results in
 | |
|  * running the "mmc" bootdrv.
 | |
|  *
 | |
|  * @iter: Interation info, containing iter->cur_label
 | |
|  * @devp: New bootdev found, if any was found
 | |
|  * @method_flagsp: If non-NULL, returns any flags implied by the label
 | |
|  * (enum bootflow_meth_flags_t), 0 if none
 | |
|  * Returns 0 if OK, -ENODEV if no bootdev was found
 | |
|  */
 | |
| int bootdev_next_label(struct bootflow_iter *iter, struct udevice **devp,
 | |
| 		       int *method_flagsp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_next_prio() - Find the next bootdev in priority order
 | |
|  *
 | |
|  * This moves @devp to the next bootdev with the current priority. If there is
 | |
|  * none, then it moves to the next priority and scans for new bootdevs there.
 | |
|  *
 | |
|  * @iter: Interation info, containing iter->cur_prio
 | |
|  * @devp: On entry this is the previous bootdev that was considered. On exit
 | |
|  *	this is the new bootdev, if any was found
 | |
|  * Returns 0 on success (*devp is updated), -ENODEV if there are no more
 | |
|  * bootdevs at any priority
 | |
|  */
 | |
| int bootdev_next_prio(struct bootflow_iter *iter, struct udevice **devp);
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(BOOTSTD)
 | |
| /**
 | |
|  * bootdev_setup_for_dev() - Bind a new bootdev device (deprecated)
 | |
|  *
 | |
|  * Please use bootdev_setup_sibling_blk() instead since it supports multiple
 | |
|  * (child) block devices for each media device.
 | |
|  *
 | |
|  * Creates a bootdev device as a child of @parent. This should be called from
 | |
|  * the driver's bind() method or its uclass' post_bind() method.
 | |
|  *
 | |
|  * If a child bootdev already exists, this function does nothing
 | |
|  *
 | |
|  * @parent: Parent device (e.g. MMC or Ethernet)
 | |
|  * @drv_name: Name of bootdev driver to bind
 | |
|  * Return: 0 if OK, -ve on error
 | |
|  */
 | |
| int bootdev_setup_for_dev(struct udevice *parent, const char *drv_name);
 | |
| 
 | |
| /**
 | |
|  * bootdev_setup_for_blk() - Bind a new bootdev device for a blk device
 | |
|  *
 | |
|  * Creates a bootdev device as a sibling of @blk. This should be called from
 | |
|  * the driver's bind() method or its uclass' post_bind() method, at the same
 | |
|  * time as the bould device is bound
 | |
|  *
 | |
|  * If a device of the same name already exists, this function does nothing
 | |
|  *
 | |
|  * @parent: Parent device (e.g. MMC or Ethernet)
 | |
|  * @drv_name: Name of bootdev driver to bind
 | |
|  * Return: 0 if OK, -ve on error
 | |
|  */
 | |
| int bootdev_setup_sibling_blk(struct udevice *blk, const char *drv_name);
 | |
| 
 | |
| /**
 | |
|  * bootdev_get_sibling_blk() - Locate the block device for a bootdev
 | |
|  *
 | |
|  * @dev: bootdev to check
 | |
|  * @blkp: returns associated block device
 | |
|  * Return: 0 if OK, -EINVAL if @dev is not a bootdev device, other -ve on other
 | |
|  *	error
 | |
|  */
 | |
| int bootdev_get_sibling_blk(struct udevice *dev, struct udevice **blkp);
 | |
| 
 | |
| /**
 | |
|  * bootdev_unbind_dev() - Unbind a bootdev device
 | |
|  *
 | |
|  * Remove and unbind a bootdev device which is a child of @parent. This should
 | |
|  * be called from the driver's unbind() method or its uclass' post_bind()
 | |
|  * method.
 | |
|  *
 | |
|  * @parent: Parent device (e.g. MMC or Ethernet)
 | |
|  * Return: 0 if OK, -ve on error
 | |
|  */
 | |
| int bootdev_unbind_dev(struct udevice *parent);
 | |
| #else
 | |
| static inline int bootdev_setup_for_dev(struct udevice *parent,
 | |
| 					const char *drv_name)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static inline int bootdev_setup_sibling_blk(struct udevice *blk,
 | |
| 					    const char *drv_name)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static inline int bootdev_unbind_dev(struct udevice *parent)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #endif
 |