mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-11 13:56:30 +00:00
91943ff703
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>
443 lines
15 KiB
C
443 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
|