162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Firmware loading and handling functions. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/sched.h> 762306a36Sopenharmony_ci#include <linux/firmware.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "dev.h" 1162306a36Sopenharmony_ci#include "decl.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic void load_next_firmware_from_table(struct lbs_private *private); 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic void lbs_fw_loaded(struct lbs_private *priv, int ret, 1662306a36Sopenharmony_ci const struct firmware *helper, const struct firmware *mainfw) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci unsigned long flags; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci lbs_deb_fw("firmware load complete, code %d\n", ret); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci /* User must free helper/mainfw */ 2362306a36Sopenharmony_ci priv->fw_callback(priv, ret, helper, mainfw); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 2662306a36Sopenharmony_ci priv->fw_callback = NULL; 2762306a36Sopenharmony_ci wake_up(&priv->fw_waitq); 2862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void do_load_firmware(struct lbs_private *priv, const char *name, 3262306a36Sopenharmony_ci void (*cb)(const struct firmware *fw, void *context)) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int ret; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci lbs_deb_fw("Requesting %s\n", name); 3762306a36Sopenharmony_ci ret = request_firmware_nowait(THIS_MODULE, true, name, 3862306a36Sopenharmony_ci priv->fw_device, GFP_KERNEL, priv, cb); 3962306a36Sopenharmony_ci if (ret) { 4062306a36Sopenharmony_ci lbs_deb_fw("request_firmware_nowait error %d\n", ret); 4162306a36Sopenharmony_ci lbs_fw_loaded(priv, ret, NULL, NULL); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void main_firmware_cb(const struct firmware *firmware, void *context) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct lbs_private *priv = context; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (!firmware) { 5062306a36Sopenharmony_ci /* Failed to find firmware: try next table entry */ 5162306a36Sopenharmony_ci load_next_firmware_from_table(priv); 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Firmware found! */ 5662306a36Sopenharmony_ci lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); 5762306a36Sopenharmony_ci if (priv->helper_fw) { 5862306a36Sopenharmony_ci release_firmware (priv->helper_fw); 5962306a36Sopenharmony_ci priv->helper_fw = NULL; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci release_firmware (firmware); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void helper_firmware_cb(const struct firmware *firmware, void *context) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct lbs_private *priv = context; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!firmware) { 6962306a36Sopenharmony_ci /* Failed to find firmware: try next table entry */ 7062306a36Sopenharmony_ci load_next_firmware_from_table(priv); 7162306a36Sopenharmony_ci return; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Firmware found! */ 7562306a36Sopenharmony_ci if (priv->fw_iter->fwname) { 7662306a36Sopenharmony_ci priv->helper_fw = firmware; 7762306a36Sopenharmony_ci do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); 7862306a36Sopenharmony_ci } else { 7962306a36Sopenharmony_ci /* No main firmware needed for this helper --> success! */ 8062306a36Sopenharmony_ci lbs_fw_loaded(priv, 0, firmware, NULL); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void load_next_firmware_from_table(struct lbs_private *priv) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci const struct lbs_fw_table *iter; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!priv->fw_iter) 8962306a36Sopenharmony_ci iter = priv->fw_table; 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci iter = ++priv->fw_iter; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (priv->helper_fw) { 9462306a36Sopenharmony_ci release_firmware(priv->helper_fw); 9562306a36Sopenharmony_ci priv->helper_fw = NULL; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cinext: 9962306a36Sopenharmony_ci if (!iter->helper) { 10062306a36Sopenharmony_ci /* End of table hit. */ 10162306a36Sopenharmony_ci lbs_fw_loaded(priv, -ENOENT, NULL, NULL); 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (iter->model != priv->fw_model) { 10662306a36Sopenharmony_ci iter++; 10762306a36Sopenharmony_ci goto next; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci priv->fw_iter = iter; 11162306a36Sopenharmony_ci do_load_firmware(priv, iter->helper, helper_firmware_cb); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_civoid lbs_wait_for_firmware_load(struct lbs_private *priv) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci wait_event(priv->fw_waitq, priv->fw_callback == NULL); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load 12162306a36Sopenharmony_ci * either a helper firmware and a main firmware (2-stage), or just the helper. 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * @priv: Pointer to lbs_private instance 12462306a36Sopenharmony_ci * @device: A pointer to &device structure 12562306a36Sopenharmony_ci * @card_model: Bus-specific card model ID used to filter firmware table 12662306a36Sopenharmony_ci * elements 12762306a36Sopenharmony_ci * @fw_table: Table of firmware file names and device model numbers 12862306a36Sopenharmony_ci * terminated by an entry with a NULL helper name 12962306a36Sopenharmony_ci * @callback: User callback to invoke when firmware load succeeds or fails. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ciint lbs_get_firmware_async(struct lbs_private *priv, struct device *device, 13262306a36Sopenharmony_ci u32 card_model, const struct lbs_fw_table *fw_table, 13362306a36Sopenharmony_ci lbs_fw_cb callback) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned long flags; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 13862306a36Sopenharmony_ci if (priv->fw_callback) { 13962306a36Sopenharmony_ci lbs_deb_fw("firmware load already in progress\n"); 14062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 14162306a36Sopenharmony_ci return -EBUSY; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci priv->fw_device = device; 14562306a36Sopenharmony_ci priv->fw_callback = callback; 14662306a36Sopenharmony_ci priv->fw_table = fw_table; 14762306a36Sopenharmony_ci priv->fw_iter = NULL; 14862306a36Sopenharmony_ci priv->fw_model = card_model; 14962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci lbs_deb_fw("Starting async firmware load\n"); 15262306a36Sopenharmony_ci load_next_firmware_from_table(priv); 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_get_firmware_async); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/** 15862306a36Sopenharmony_ci * lbs_get_firmware - Retrieves two-stage firmware 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * @dev: A pointer to &device structure 16162306a36Sopenharmony_ci * @card_model: Bus-specific card model ID used to filter firmware table 16262306a36Sopenharmony_ci * elements 16362306a36Sopenharmony_ci * @fw_table: Table of firmware file names and device model numbers 16462306a36Sopenharmony_ci * terminated by an entry with a NULL helper name 16562306a36Sopenharmony_ci * @helper: On success, the helper firmware; caller must free 16662306a36Sopenharmony_ci * @mainfw: On success, the main firmware; caller must free 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * Deprecated: use lbs_get_firmware_async() instead. 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * returns: 0 on success, non-zero on failure 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ciint lbs_get_firmware(struct device *dev, u32 card_model, 17362306a36Sopenharmony_ci const struct lbs_fw_table *fw_table, 17462306a36Sopenharmony_ci const struct firmware **helper, 17562306a36Sopenharmony_ci const struct firmware **mainfw) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci const struct lbs_fw_table *iter; 17862306a36Sopenharmony_ci int ret; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci BUG_ON(helper == NULL); 18162306a36Sopenharmony_ci BUG_ON(mainfw == NULL); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Search for firmware to use from the table. */ 18462306a36Sopenharmony_ci iter = fw_table; 18562306a36Sopenharmony_ci while (iter && iter->helper) { 18662306a36Sopenharmony_ci if (iter->model != card_model) 18762306a36Sopenharmony_ci goto next; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (*helper == NULL) { 19062306a36Sopenharmony_ci ret = request_firmware(helper, iter->helper, dev); 19162306a36Sopenharmony_ci if (ret) 19262306a36Sopenharmony_ci goto next; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* If the device has one-stage firmware (ie cf8305) and 19562306a36Sopenharmony_ci * we've got it then we don't need to bother with the 19662306a36Sopenharmony_ci * main firmware. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci if (iter->fwname == NULL) 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (*mainfw == NULL) { 20362306a36Sopenharmony_ci ret = request_firmware(mainfw, iter->fwname, dev); 20462306a36Sopenharmony_ci if (ret) { 20562306a36Sopenharmony_ci /* Clear the helper to ensure we don't have 20662306a36Sopenharmony_ci * mismatched firmware pairs. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci release_firmware(*helper); 20962306a36Sopenharmony_ci *helper = NULL; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (*helper && *mainfw) 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci next: 21762306a36Sopenharmony_ci iter++; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Failed */ 22162306a36Sopenharmony_ci release_firmware(*helper); 22262306a36Sopenharmony_ci *helper = NULL; 22362306a36Sopenharmony_ci release_firmware(*mainfw); 22462306a36Sopenharmony_ci *mainfw = NULL; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return -ENOENT; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_get_firmware); 229