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