162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef __FIRMWARE_LOADER_H 362306a36Sopenharmony_ci#define __FIRMWARE_LOADER_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/bitops.h> 662306a36Sopenharmony_ci#include <linux/firmware.h> 762306a36Sopenharmony_ci#include <linux/types.h> 862306a36Sopenharmony_ci#include <linux/kref.h> 962306a36Sopenharmony_ci#include <linux/list.h> 1062306a36Sopenharmony_ci#include <linux/completion.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/** 1362306a36Sopenharmony_ci * enum fw_opt - options to control firmware loading behaviour 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * @FW_OPT_UEVENT: Enables the fallback mechanism to send a kobject uevent 1662306a36Sopenharmony_ci * when the firmware is not found. Userspace is in charge to load the 1762306a36Sopenharmony_ci * firmware using the sysfs loading facility. 1862306a36Sopenharmony_ci * @FW_OPT_NOWAIT: Used to describe the firmware request is asynchronous. 1962306a36Sopenharmony_ci * @FW_OPT_USERHELPER: Enable the fallback mechanism, in case the direct 2062306a36Sopenharmony_ci * filesystem lookup fails at finding the firmware. For details refer to 2162306a36Sopenharmony_ci * firmware_fallback_sysfs(). 2262306a36Sopenharmony_ci * @FW_OPT_NO_WARN: Quiet, avoid printing warning messages. 2362306a36Sopenharmony_ci * @FW_OPT_NOCACHE: Disables firmware caching. Firmware caching is used to 2462306a36Sopenharmony_ci * cache the firmware upon suspend, so that upon resume races against the 2562306a36Sopenharmony_ci * firmware file lookup on storage is avoided. Used for calls where the 2662306a36Sopenharmony_ci * file may be too big, or where the driver takes charge of its own 2762306a36Sopenharmony_ci * firmware caching mechanism. 2862306a36Sopenharmony_ci * @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes 2962306a36Sopenharmony_ci * precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER. 3062306a36Sopenharmony_ci * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in 3162306a36Sopenharmony_ci * the platform's main firmware. If both this fallback and the sysfs 3262306a36Sopenharmony_ci * fallback are enabled, then this fallback will be tried first. 3362306a36Sopenharmony_ci * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read 3462306a36Sopenharmony_ci * entire file. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cienum fw_opt { 3762306a36Sopenharmony_ci FW_OPT_UEVENT = BIT(0), 3862306a36Sopenharmony_ci FW_OPT_NOWAIT = BIT(1), 3962306a36Sopenharmony_ci FW_OPT_USERHELPER = BIT(2), 4062306a36Sopenharmony_ci FW_OPT_NO_WARN = BIT(3), 4162306a36Sopenharmony_ci FW_OPT_NOCACHE = BIT(4), 4262306a36Sopenharmony_ci FW_OPT_NOFALLBACK_SYSFS = BIT(5), 4362306a36Sopenharmony_ci FW_OPT_FALLBACK_PLATFORM = BIT(6), 4462306a36Sopenharmony_ci FW_OPT_PARTIAL = BIT(7), 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cienum fw_status { 4862306a36Sopenharmony_ci FW_STATUS_UNKNOWN, 4962306a36Sopenharmony_ci FW_STATUS_LOADING, 5062306a36Sopenharmony_ci FW_STATUS_DONE, 5162306a36Sopenharmony_ci FW_STATUS_ABORTED, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Concurrent request_firmware() for the same firmware need to be 5662306a36Sopenharmony_ci * serialized. struct fw_state is simple state machine which hold the 5762306a36Sopenharmony_ci * state of the firmware loading. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistruct fw_state { 6062306a36Sopenharmony_ci struct completion completion; 6162306a36Sopenharmony_ci enum fw_status status; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct fw_priv { 6562306a36Sopenharmony_ci struct kref ref; 6662306a36Sopenharmony_ci struct list_head list; 6762306a36Sopenharmony_ci struct firmware_cache *fwc; 6862306a36Sopenharmony_ci struct fw_state fw_st; 6962306a36Sopenharmony_ci void *data; 7062306a36Sopenharmony_ci size_t size; 7162306a36Sopenharmony_ci size_t allocated_size; 7262306a36Sopenharmony_ci size_t offset; 7362306a36Sopenharmony_ci u32 opt_flags; 7462306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_PAGED_BUF 7562306a36Sopenharmony_ci bool is_paged_buf; 7662306a36Sopenharmony_ci struct page **pages; 7762306a36Sopenharmony_ci int nr_pages; 7862306a36Sopenharmony_ci int page_array_size; 7962306a36Sopenharmony_ci#endif 8062306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_USER_HELPER 8162306a36Sopenharmony_ci bool need_uevent; 8262306a36Sopenharmony_ci struct list_head pending_list; 8362306a36Sopenharmony_ci#endif 8462306a36Sopenharmony_ci const char *fw_name; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciextern struct mutex fw_lock; 8862306a36Sopenharmony_ciextern struct firmware_cache fw_cache; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic inline bool __fw_state_check(struct fw_priv *fw_priv, 9162306a36Sopenharmony_ci enum fw_status status) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct fw_state *fw_st = &fw_priv->fw_st; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return fw_st->status == status; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct fw_state *fw_st = &fw_priv->fw_st; 10162306a36Sopenharmony_ci long ret; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout); 10462306a36Sopenharmony_ci if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) 10562306a36Sopenharmony_ci return -ENOENT; 10662306a36Sopenharmony_ci if (!ret) 10762306a36Sopenharmony_ci return -ETIMEDOUT; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return ret < 0 ? ret : 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline void __fw_state_set(struct fw_priv *fw_priv, 11362306a36Sopenharmony_ci enum fw_status status) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct fw_state *fw_st = &fw_priv->fw_st; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci WRITE_ONCE(fw_st->status, status); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) { 12062306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_USER_HELPER 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * Doing this here ensures that the fw_priv is deleted from 12362306a36Sopenharmony_ci * the pending list in all abort/done paths. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci list_del_init(&fw_priv->pending_list); 12662306a36Sopenharmony_ci#endif 12762306a36Sopenharmony_ci complete_all(&fw_st->completion); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic inline void fw_state_aborted(struct fw_priv *fw_priv) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci __fw_state_set(fw_priv, FW_STATUS_ABORTED); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline bool fw_state_is_aborted(struct fw_priv *fw_priv) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci return __fw_state_check(fw_priv, FW_STATUS_ABORTED); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic inline void fw_state_start(struct fw_priv *fw_priv) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci __fw_state_set(fw_priv, FW_STATUS_LOADING); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic inline void fw_state_done(struct fw_priv *fw_priv) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci __fw_state_set(fw_priv, FW_STATUS_DONE); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic inline bool fw_state_is_done(struct fw_priv *fw_priv) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci return __fw_state_check(fw_priv, FW_STATUS_DONE); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic inline bool fw_state_is_loading(struct fw_priv *fw_priv) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return __fw_state_check(fw_priv, FW_STATUS_LOADING); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciint alloc_lookup_fw_priv(const char *fw_name, struct firmware_cache *fwc, 16262306a36Sopenharmony_ci struct fw_priv **fw_priv, void *dbuf, size_t size, 16362306a36Sopenharmony_ci size_t offset, u32 opt_flags); 16462306a36Sopenharmony_ciint assign_fw(struct firmware *fw, struct device *device); 16562306a36Sopenharmony_civoid free_fw_priv(struct fw_priv *fw_priv); 16662306a36Sopenharmony_civoid fw_state_init(struct fw_priv *fw_priv); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER 16962306a36Sopenharmony_cibool firmware_is_builtin(const struct firmware *fw); 17062306a36Sopenharmony_cibool firmware_request_builtin_buf(struct firmware *fw, const char *name, 17162306a36Sopenharmony_ci void *buf, size_t size); 17262306a36Sopenharmony_ci#else /* module case */ 17362306a36Sopenharmony_cistatic inline bool firmware_is_builtin(const struct firmware *fw) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return false; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_cistatic inline bool firmware_request_builtin_buf(struct firmware *fw, 17862306a36Sopenharmony_ci const char *name, 17962306a36Sopenharmony_ci void *buf, size_t size) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci return false; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci#endif 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_PAGED_BUF 18662306a36Sopenharmony_civoid fw_free_paged_buf(struct fw_priv *fw_priv); 18762306a36Sopenharmony_ciint fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed); 18862306a36Sopenharmony_ciint fw_map_paged_buf(struct fw_priv *fw_priv); 18962306a36Sopenharmony_cibool fw_is_paged_buf(struct fw_priv *fw_priv); 19062306a36Sopenharmony_ci#else 19162306a36Sopenharmony_cistatic inline void fw_free_paged_buf(struct fw_priv *fw_priv) {} 19262306a36Sopenharmony_cistatic inline int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; } 19362306a36Sopenharmony_cistatic inline int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; } 19462306a36Sopenharmony_cistatic inline bool fw_is_paged_buf(struct fw_priv *fw_priv) { return false; } 19562306a36Sopenharmony_ci#endif 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#endif /* __FIRMWARE_LOADER_H */ 198