18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef __FIRMWARE_LOADER_H
38c2ecf20Sopenharmony_ci#define __FIRMWARE_LOADER_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/bitops.h>
68c2ecf20Sopenharmony_ci#include <linux/firmware.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/kref.h>
98c2ecf20Sopenharmony_ci#include <linux/list.h>
108c2ecf20Sopenharmony_ci#include <linux/completion.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <generated/utsrelease.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/**
158c2ecf20Sopenharmony_ci * enum fw_opt - options to control firmware loading behaviour
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * @FW_OPT_UEVENT: Enables the fallback mechanism to send a kobject uevent
188c2ecf20Sopenharmony_ci *	when the firmware is not found. Userspace is in charge to load the
198c2ecf20Sopenharmony_ci *	firmware using the sysfs loading facility.
208c2ecf20Sopenharmony_ci * @FW_OPT_NOWAIT: Used to describe the firmware request is asynchronous.
218c2ecf20Sopenharmony_ci * @FW_OPT_USERHELPER: Enable the fallback mechanism, in case the direct
228c2ecf20Sopenharmony_ci *	filesystem lookup fails at finding the firmware.  For details refer to
238c2ecf20Sopenharmony_ci *	firmware_fallback_sysfs().
248c2ecf20Sopenharmony_ci * @FW_OPT_NO_WARN: Quiet, avoid printing warning messages.
258c2ecf20Sopenharmony_ci * @FW_OPT_NOCACHE: Disables firmware caching. Firmware caching is used to
268c2ecf20Sopenharmony_ci *	cache the firmware upon suspend, so that upon resume races against the
278c2ecf20Sopenharmony_ci *	firmware file lookup on storage is avoided. Used for calls where the
288c2ecf20Sopenharmony_ci *	file may be too big, or where the driver takes charge of its own
298c2ecf20Sopenharmony_ci *	firmware caching mechanism.
308c2ecf20Sopenharmony_ci * @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes
318c2ecf20Sopenharmony_ci *	precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER.
328c2ecf20Sopenharmony_ci * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in
338c2ecf20Sopenharmony_ci *	the platform's main firmware. If both this fallback and the sysfs
348c2ecf20Sopenharmony_ci *      fallback are enabled, then this fallback will be tried first.
358c2ecf20Sopenharmony_ci * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read
368c2ecf20Sopenharmony_ci *	entire file.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cienum fw_opt {
398c2ecf20Sopenharmony_ci	FW_OPT_UEVENT			= BIT(0),
408c2ecf20Sopenharmony_ci	FW_OPT_NOWAIT			= BIT(1),
418c2ecf20Sopenharmony_ci	FW_OPT_USERHELPER		= BIT(2),
428c2ecf20Sopenharmony_ci	FW_OPT_NO_WARN			= BIT(3),
438c2ecf20Sopenharmony_ci	FW_OPT_NOCACHE			= BIT(4),
448c2ecf20Sopenharmony_ci	FW_OPT_NOFALLBACK_SYSFS		= BIT(5),
458c2ecf20Sopenharmony_ci	FW_OPT_FALLBACK_PLATFORM	= BIT(6),
468c2ecf20Sopenharmony_ci	FW_OPT_PARTIAL			= BIT(7),
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cienum fw_status {
508c2ecf20Sopenharmony_ci	FW_STATUS_UNKNOWN,
518c2ecf20Sopenharmony_ci	FW_STATUS_LOADING,
528c2ecf20Sopenharmony_ci	FW_STATUS_DONE,
538c2ecf20Sopenharmony_ci	FW_STATUS_ABORTED,
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Concurrent request_firmware() for the same firmware need to be
588c2ecf20Sopenharmony_ci * serialized.  struct fw_state is simple state machine which hold the
598c2ecf20Sopenharmony_ci * state of the firmware loading.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistruct fw_state {
628c2ecf20Sopenharmony_ci	struct completion completion;
638c2ecf20Sopenharmony_ci	enum fw_status status;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistruct fw_priv {
678c2ecf20Sopenharmony_ci	struct kref ref;
688c2ecf20Sopenharmony_ci	struct list_head list;
698c2ecf20Sopenharmony_ci	struct firmware_cache *fwc;
708c2ecf20Sopenharmony_ci	struct fw_state fw_st;
718c2ecf20Sopenharmony_ci	void *data;
728c2ecf20Sopenharmony_ci	size_t size;
738c2ecf20Sopenharmony_ci	size_t allocated_size;
748c2ecf20Sopenharmony_ci	size_t offset;
758c2ecf20Sopenharmony_ci	u32 opt_flags;
768c2ecf20Sopenharmony_ci#ifdef CONFIG_FW_LOADER_PAGED_BUF
778c2ecf20Sopenharmony_ci	bool is_paged_buf;
788c2ecf20Sopenharmony_ci	struct page **pages;
798c2ecf20Sopenharmony_ci	int nr_pages;
808c2ecf20Sopenharmony_ci	int page_array_size;
818c2ecf20Sopenharmony_ci#endif
828c2ecf20Sopenharmony_ci#ifdef CONFIG_FW_LOADER_USER_HELPER
838c2ecf20Sopenharmony_ci	bool need_uevent;
848c2ecf20Sopenharmony_ci	struct list_head pending_list;
858c2ecf20Sopenharmony_ci#endif
868c2ecf20Sopenharmony_ci	const char *fw_name;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciextern struct mutex fw_lock;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic inline bool __fw_state_check(struct fw_priv *fw_priv,
928c2ecf20Sopenharmony_ci				    enum fw_status status)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct fw_state *fw_st = &fw_priv->fw_st;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return fw_st->status == status;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct fw_state *fw_st = &fw_priv->fw_st;
1028c2ecf20Sopenharmony_ci	long ret;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
1058c2ecf20Sopenharmony_ci	if (ret != 0 && fw_st->status == FW_STATUS_ABORTED)
1068c2ecf20Sopenharmony_ci		return -ENOENT;
1078c2ecf20Sopenharmony_ci	if (!ret)
1088c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return ret < 0 ? ret : 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic inline void __fw_state_set(struct fw_priv *fw_priv,
1148c2ecf20Sopenharmony_ci				  enum fw_status status)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct fw_state *fw_st = &fw_priv->fw_st;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	WRITE_ONCE(fw_st->status, status);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) {
1218c2ecf20Sopenharmony_ci#ifdef CONFIG_FW_LOADER_USER_HELPER
1228c2ecf20Sopenharmony_ci		/*
1238c2ecf20Sopenharmony_ci		 * Doing this here ensures that the fw_priv is deleted from
1248c2ecf20Sopenharmony_ci		 * the pending list in all abort/done paths.
1258c2ecf20Sopenharmony_ci		 */
1268c2ecf20Sopenharmony_ci		list_del_init(&fw_priv->pending_list);
1278c2ecf20Sopenharmony_ci#endif
1288c2ecf20Sopenharmony_ci		complete_all(&fw_st->completion);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic inline void fw_state_aborted(struct fw_priv *fw_priv)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	__fw_state_set(fw_priv, FW_STATUS_ABORTED);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic inline bool fw_state_is_aborted(struct fw_priv *fw_priv)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	return __fw_state_check(fw_priv, FW_STATUS_ABORTED);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic inline void fw_state_start(struct fw_priv *fw_priv)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	__fw_state_set(fw_priv, FW_STATUS_LOADING);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic inline void fw_state_done(struct fw_priv *fw_priv)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	__fw_state_set(fw_priv, FW_STATUS_DONE);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciint assign_fw(struct firmware *fw, struct device *device);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#ifdef CONFIG_FW_LOADER_PAGED_BUF
1558c2ecf20Sopenharmony_civoid fw_free_paged_buf(struct fw_priv *fw_priv);
1568c2ecf20Sopenharmony_ciint fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed);
1578c2ecf20Sopenharmony_ciint fw_map_paged_buf(struct fw_priv *fw_priv);
1588c2ecf20Sopenharmony_cibool fw_is_paged_buf(struct fw_priv *fw_priv);
1598c2ecf20Sopenharmony_ci#else
1608c2ecf20Sopenharmony_cistatic inline void fw_free_paged_buf(struct fw_priv *fw_priv) {}
1618c2ecf20Sopenharmony_cistatic inline int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; }
1628c2ecf20Sopenharmony_cistatic inline int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; }
1638c2ecf20Sopenharmony_cistatic inline bool fw_is_paged_buf(struct fw_priv *fw_priv) { return false; }
1648c2ecf20Sopenharmony_ci#endif
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#endif /* __FIRMWARE_LOADER_H */
167