15d6d1dddSLuis R. Rodriguez /* SPDX-License-Identifier: GPL-2.0 */ 25d6d1dddSLuis R. Rodriguez #ifndef __FIRMWARE_LOADER_H 35d6d1dddSLuis R. Rodriguez #define __FIRMWARE_LOADER_H 45d6d1dddSLuis R. Rodriguez 5*eb33eb04SAndres Rodriguez #include <linux/bitops.h> 65d6d1dddSLuis R. Rodriguez #include <linux/firmware.h> 75d6d1dddSLuis R. Rodriguez #include <linux/types.h> 85d6d1dddSLuis R. Rodriguez #include <linux/kref.h> 95d6d1dddSLuis R. Rodriguez #include <linux/list.h> 105d6d1dddSLuis R. Rodriguez #include <linux/completion.h> 115d6d1dddSLuis R. Rodriguez 125d6d1dddSLuis R. Rodriguez #include <generated/utsrelease.h> 135d6d1dddSLuis R. Rodriguez 14*eb33eb04SAndres Rodriguez /** 15*eb33eb04SAndres Rodriguez * enum fw_opt - options to control firmware loading behaviour 16*eb33eb04SAndres Rodriguez * 17*eb33eb04SAndres Rodriguez * @FW_OPT_UEVENT: Enables the fallback mechanism to send a kobject uevent 18*eb33eb04SAndres Rodriguez * when the firmware is not found. Userspace is in charge to load the 19*eb33eb04SAndres Rodriguez * firmware using the sysfs loading facility. 20*eb33eb04SAndres Rodriguez * @FW_OPT_NOWAIT: Used to describe the firmware request is asynchronous. 21*eb33eb04SAndres Rodriguez * @FW_OPT_USERHELPER: Enable the fallback mechanism, in case the direct 22*eb33eb04SAndres Rodriguez * filesystem lookup fails at finding the firmware. For details refer to 23*eb33eb04SAndres Rodriguez * fw_sysfs_fallback(). 24*eb33eb04SAndres Rodriguez * @FW_OPT_NO_WARN: Quiet, avoid printing warning messages. 25*eb33eb04SAndres Rodriguez * @FW_OPT_NOCACHE: Disables firmware caching. Firmware caching is used to 26*eb33eb04SAndres Rodriguez * cache the firmware upon suspend, so that upon resume races against the 27*eb33eb04SAndres Rodriguez * firmware file lookup on storage is avoided. Used for calls where the 28*eb33eb04SAndres Rodriguez * file may be too big, or where the driver takes charge of its own 29*eb33eb04SAndres Rodriguez * firmware caching mechanism. 30*eb33eb04SAndres Rodriguez * @FW_OPT_NOFALLBACK: Disable the fallback mechanism. Takes precedence over 31*eb33eb04SAndres Rodriguez * &FW_OPT_UEVENT and &FW_OPT_USERHELPER. 32*eb33eb04SAndres Rodriguez */ 33*eb33eb04SAndres Rodriguez enum fw_opt { 34*eb33eb04SAndres Rodriguez FW_OPT_UEVENT = BIT(0), 35*eb33eb04SAndres Rodriguez FW_OPT_NOWAIT = BIT(1), 36*eb33eb04SAndres Rodriguez FW_OPT_USERHELPER = BIT(2), 37*eb33eb04SAndres Rodriguez FW_OPT_NO_WARN = BIT(3), 38*eb33eb04SAndres Rodriguez FW_OPT_NOCACHE = BIT(4), 39*eb33eb04SAndres Rodriguez FW_OPT_NOFALLBACK = BIT(5), 40*eb33eb04SAndres Rodriguez }; 415d6d1dddSLuis R. Rodriguez 425d6d1dddSLuis R. Rodriguez enum fw_status { 435d6d1dddSLuis R. Rodriguez FW_STATUS_UNKNOWN, 445d6d1dddSLuis R. Rodriguez FW_STATUS_LOADING, 455d6d1dddSLuis R. Rodriguez FW_STATUS_DONE, 465d6d1dddSLuis R. Rodriguez FW_STATUS_ABORTED, 475d6d1dddSLuis R. Rodriguez }; 485d6d1dddSLuis R. Rodriguez 495d6d1dddSLuis R. Rodriguez /* 505d6d1dddSLuis R. Rodriguez * Concurrent request_firmware() for the same firmware need to be 515d6d1dddSLuis R. Rodriguez * serialized. struct fw_state is simple state machine which hold the 525d6d1dddSLuis R. Rodriguez * state of the firmware loading. 535d6d1dddSLuis R. Rodriguez */ 545d6d1dddSLuis R. Rodriguez struct fw_state { 555d6d1dddSLuis R. Rodriguez struct completion completion; 565d6d1dddSLuis R. Rodriguez enum fw_status status; 575d6d1dddSLuis R. Rodriguez }; 585d6d1dddSLuis R. Rodriguez 595d6d1dddSLuis R. Rodriguez struct fw_priv { 605d6d1dddSLuis R. Rodriguez struct kref ref; 615d6d1dddSLuis R. Rodriguez struct list_head list; 625d6d1dddSLuis R. Rodriguez struct firmware_cache *fwc; 635d6d1dddSLuis R. Rodriguez struct fw_state fw_st; 645d6d1dddSLuis R. Rodriguez void *data; 655d6d1dddSLuis R. Rodriguez size_t size; 665d6d1dddSLuis R. Rodriguez size_t allocated_size; 675d6d1dddSLuis R. Rodriguez #ifdef CONFIG_FW_LOADER_USER_HELPER 685d6d1dddSLuis R. Rodriguez bool is_paged_buf; 695d6d1dddSLuis R. Rodriguez bool need_uevent; 705d6d1dddSLuis R. Rodriguez struct page **pages; 715d6d1dddSLuis R. Rodriguez int nr_pages; 725d6d1dddSLuis R. Rodriguez int page_array_size; 735d6d1dddSLuis R. Rodriguez struct list_head pending_list; 745d6d1dddSLuis R. Rodriguez #endif 755d6d1dddSLuis R. Rodriguez const char *fw_name; 765d6d1dddSLuis R. Rodriguez }; 775d6d1dddSLuis R. Rodriguez 785d6d1dddSLuis R. Rodriguez extern struct mutex fw_lock; 795d6d1dddSLuis R. Rodriguez 805d6d1dddSLuis R. Rodriguez static inline bool __fw_state_check(struct fw_priv *fw_priv, 815d6d1dddSLuis R. Rodriguez enum fw_status status) 825d6d1dddSLuis R. Rodriguez { 835d6d1dddSLuis R. Rodriguez struct fw_state *fw_st = &fw_priv->fw_st; 845d6d1dddSLuis R. Rodriguez 855d6d1dddSLuis R. Rodriguez return fw_st->status == status; 865d6d1dddSLuis R. Rodriguez } 875d6d1dddSLuis R. Rodriguez 885d6d1dddSLuis R. Rodriguez static inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout) 895d6d1dddSLuis R. Rodriguez { 905d6d1dddSLuis R. Rodriguez struct fw_state *fw_st = &fw_priv->fw_st; 915d6d1dddSLuis R. Rodriguez long ret; 925d6d1dddSLuis R. Rodriguez 935d6d1dddSLuis R. Rodriguez ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout); 945d6d1dddSLuis R. Rodriguez if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) 955d6d1dddSLuis R. Rodriguez return -ENOENT; 965d6d1dddSLuis R. Rodriguez if (!ret) 975d6d1dddSLuis R. Rodriguez return -ETIMEDOUT; 985d6d1dddSLuis R. Rodriguez 995d6d1dddSLuis R. Rodriguez return ret < 0 ? ret : 0; 1005d6d1dddSLuis R. Rodriguez } 1015d6d1dddSLuis R. Rodriguez 1025d6d1dddSLuis R. Rodriguez static inline void __fw_state_set(struct fw_priv *fw_priv, 1035d6d1dddSLuis R. Rodriguez enum fw_status status) 1045d6d1dddSLuis R. Rodriguez { 1055d6d1dddSLuis R. Rodriguez struct fw_state *fw_st = &fw_priv->fw_st; 1065d6d1dddSLuis R. Rodriguez 1075d6d1dddSLuis R. Rodriguez WRITE_ONCE(fw_st->status, status); 1085d6d1dddSLuis R. Rodriguez 1095d6d1dddSLuis R. Rodriguez if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) 1105d6d1dddSLuis R. Rodriguez complete_all(&fw_st->completion); 1115d6d1dddSLuis R. Rodriguez } 1125d6d1dddSLuis R. Rodriguez 1135d6d1dddSLuis R. Rodriguez static inline void fw_state_aborted(struct fw_priv *fw_priv) 1145d6d1dddSLuis R. Rodriguez { 1155d6d1dddSLuis R. Rodriguez __fw_state_set(fw_priv, FW_STATUS_ABORTED); 1165d6d1dddSLuis R. Rodriguez } 1175d6d1dddSLuis R. Rodriguez 1185d6d1dddSLuis R. Rodriguez static inline bool fw_state_is_aborted(struct fw_priv *fw_priv) 1195d6d1dddSLuis R. Rodriguez { 1205d6d1dddSLuis R. Rodriguez return __fw_state_check(fw_priv, FW_STATUS_ABORTED); 1215d6d1dddSLuis R. Rodriguez } 1225d6d1dddSLuis R. Rodriguez 1235d6d1dddSLuis R. Rodriguez static inline void fw_state_start(struct fw_priv *fw_priv) 1245d6d1dddSLuis R. Rodriguez { 1255d6d1dddSLuis R. Rodriguez __fw_state_set(fw_priv, FW_STATUS_LOADING); 1265d6d1dddSLuis R. Rodriguez } 1275d6d1dddSLuis R. Rodriguez 1285d6d1dddSLuis R. Rodriguez static inline void fw_state_done(struct fw_priv *fw_priv) 1295d6d1dddSLuis R. Rodriguez { 1305d6d1dddSLuis R. Rodriguez __fw_state_set(fw_priv, FW_STATUS_DONE); 1315d6d1dddSLuis R. Rodriguez } 1325d6d1dddSLuis R. Rodriguez 1335d6d1dddSLuis R. Rodriguez int assign_fw(struct firmware *fw, struct device *device, 134*eb33eb04SAndres Rodriguez enum fw_opt opt_flags); 1355d6d1dddSLuis R. Rodriguez 1365d6d1dddSLuis R. Rodriguez #endif /* __FIRMWARE_LOADER_H */ 137