| /* 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, -ENOENT on device not found, otherwise -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: Bootdev 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, -EPFNOSUPPORT 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_unhunt() - Mark a device as needing to be hunted again |
| * |
| * @id: uclass ID to update |
| * Return: 0 if done, -EALREADY if already in this state, -ENOENT if no hunter |
| * found for that uclass |
| */ |
| int bootdev_unhunt(enum uclass_id id); |
| |
| /** |
| * 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_for_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_sibling_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_for_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_for_sibling_blk(struct udevice *blk, |
| const char *drv_name) |
| { |
| return 0; |
| } |
| |
| static inline int bootdev_unbind_dev(struct udevice *parent) |
| { |
| return 0; |
| } |
| #endif |
| |
| #endif |