18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Firmware loading and handling functions. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/sched.h> 78c2ecf20Sopenharmony_ci#include <linux/firmware.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "dev.h" 118c2ecf20Sopenharmony_ci#include "decl.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic void load_next_firmware_from_table(struct lbs_private *private); 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic void lbs_fw_loaded(struct lbs_private *priv, int ret, 168c2ecf20Sopenharmony_ci const struct firmware *helper, const struct firmware *mainfw) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci unsigned long flags; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci lbs_deb_fw("firmware load complete, code %d\n", ret); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci /* User must free helper/mainfw */ 238c2ecf20Sopenharmony_ci priv->fw_callback(priv, ret, helper, mainfw); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 268c2ecf20Sopenharmony_ci priv->fw_callback = NULL; 278c2ecf20Sopenharmony_ci wake_up(&priv->fw_waitq); 288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void do_load_firmware(struct lbs_private *priv, const char *name, 328c2ecf20Sopenharmony_ci void (*cb)(const struct firmware *fw, void *context)) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci int ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci lbs_deb_fw("Requesting %s\n", name); 378c2ecf20Sopenharmony_ci ret = request_firmware_nowait(THIS_MODULE, true, name, 388c2ecf20Sopenharmony_ci priv->fw_device, GFP_KERNEL, priv, cb); 398c2ecf20Sopenharmony_ci if (ret) { 408c2ecf20Sopenharmony_ci lbs_deb_fw("request_firmware_nowait error %d\n", ret); 418c2ecf20Sopenharmony_ci lbs_fw_loaded(priv, ret, NULL, NULL); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void main_firmware_cb(const struct firmware *firmware, void *context) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct lbs_private *priv = context; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!firmware) { 508c2ecf20Sopenharmony_ci /* Failed to find firmware: try next table entry */ 518c2ecf20Sopenharmony_ci load_next_firmware_from_table(priv); 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* Firmware found! */ 568c2ecf20Sopenharmony_ci lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); 578c2ecf20Sopenharmony_ci if (priv->helper_fw) { 588c2ecf20Sopenharmony_ci release_firmware (priv->helper_fw); 598c2ecf20Sopenharmony_ci priv->helper_fw = NULL; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci release_firmware (firmware); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void helper_firmware_cb(const struct firmware *firmware, void *context) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct lbs_private *priv = context; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!firmware) { 698c2ecf20Sopenharmony_ci /* Failed to find firmware: try next table entry */ 708c2ecf20Sopenharmony_ci load_next_firmware_from_table(priv); 718c2ecf20Sopenharmony_ci return; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Firmware found! */ 758c2ecf20Sopenharmony_ci if (priv->fw_iter->fwname) { 768c2ecf20Sopenharmony_ci priv->helper_fw = firmware; 778c2ecf20Sopenharmony_ci do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); 788c2ecf20Sopenharmony_ci } else { 798c2ecf20Sopenharmony_ci /* No main firmware needed for this helper --> success! */ 808c2ecf20Sopenharmony_ci lbs_fw_loaded(priv, 0, firmware, NULL); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void load_next_firmware_from_table(struct lbs_private *priv) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci const struct lbs_fw_table *iter; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!priv->fw_iter) 898c2ecf20Sopenharmony_ci iter = priv->fw_table; 908c2ecf20Sopenharmony_ci else 918c2ecf20Sopenharmony_ci iter = ++priv->fw_iter; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (priv->helper_fw) { 948c2ecf20Sopenharmony_ci release_firmware(priv->helper_fw); 958c2ecf20Sopenharmony_ci priv->helper_fw = NULL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cinext: 998c2ecf20Sopenharmony_ci if (!iter->helper) { 1008c2ecf20Sopenharmony_ci /* End of table hit. */ 1018c2ecf20Sopenharmony_ci lbs_fw_loaded(priv, -ENOENT, NULL, NULL); 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (iter->model != priv->fw_model) { 1068c2ecf20Sopenharmony_ci iter++; 1078c2ecf20Sopenharmony_ci goto next; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci priv->fw_iter = iter; 1118c2ecf20Sopenharmony_ci do_load_firmware(priv, iter->helper, helper_firmware_cb); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_civoid lbs_wait_for_firmware_load(struct lbs_private *priv) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci wait_event(priv->fw_waitq, priv->fw_callback == NULL); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/** 1208c2ecf20Sopenharmony_ci * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load 1218c2ecf20Sopenharmony_ci * either a helper firmware and a main firmware (2-stage), or just the helper. 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * @priv: Pointer to lbs_private instance 1248c2ecf20Sopenharmony_ci * @device: A pointer to &device structure 1258c2ecf20Sopenharmony_ci * @card_model: Bus-specific card model ID used to filter firmware table 1268c2ecf20Sopenharmony_ci * elements 1278c2ecf20Sopenharmony_ci * @fw_table: Table of firmware file names and device model numbers 1288c2ecf20Sopenharmony_ci * terminated by an entry with a NULL helper name 1298c2ecf20Sopenharmony_ci * @callback: User callback to invoke when firmware load succeeds or fails. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ciint lbs_get_firmware_async(struct lbs_private *priv, struct device *device, 1328c2ecf20Sopenharmony_ci u32 card_model, const struct lbs_fw_table *fw_table, 1338c2ecf20Sopenharmony_ci lbs_fw_cb callback) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 1388c2ecf20Sopenharmony_ci if (priv->fw_callback) { 1398c2ecf20Sopenharmony_ci lbs_deb_fw("firmware load already in progress\n"); 1408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 1418c2ecf20Sopenharmony_ci return -EBUSY; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci priv->fw_device = device; 1458c2ecf20Sopenharmony_ci priv->fw_callback = callback; 1468c2ecf20Sopenharmony_ci priv->fw_table = fw_table; 1478c2ecf20Sopenharmony_ci priv->fw_iter = NULL; 1488c2ecf20Sopenharmony_ci priv->fw_model = card_model; 1498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci lbs_deb_fw("Starting async firmware load\n"); 1528c2ecf20Sopenharmony_ci load_next_firmware_from_table(priv); 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_get_firmware_async); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/** 1588c2ecf20Sopenharmony_ci * lbs_get_firmware - Retrieves two-stage firmware 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * @dev: A pointer to &device structure 1618c2ecf20Sopenharmony_ci * @card_model: Bus-specific card model ID used to filter firmware table 1628c2ecf20Sopenharmony_ci * elements 1638c2ecf20Sopenharmony_ci * @fw_table: Table of firmware file names and device model numbers 1648c2ecf20Sopenharmony_ci * terminated by an entry with a NULL helper name 1658c2ecf20Sopenharmony_ci * @helper: On success, the helper firmware; caller must free 1668c2ecf20Sopenharmony_ci * @mainfw: On success, the main firmware; caller must free 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Deprecated: use lbs_get_firmware_async() instead. 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * returns: 0 on success, non-zero on failure 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ciint lbs_get_firmware(struct device *dev, u32 card_model, 1738c2ecf20Sopenharmony_ci const struct lbs_fw_table *fw_table, 1748c2ecf20Sopenharmony_ci const struct firmware **helper, 1758c2ecf20Sopenharmony_ci const struct firmware **mainfw) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci const struct lbs_fw_table *iter; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci BUG_ON(helper == NULL); 1818c2ecf20Sopenharmony_ci BUG_ON(mainfw == NULL); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Search for firmware to use from the table. */ 1848c2ecf20Sopenharmony_ci iter = fw_table; 1858c2ecf20Sopenharmony_ci while (iter && iter->helper) { 1868c2ecf20Sopenharmony_ci if (iter->model != card_model) 1878c2ecf20Sopenharmony_ci goto next; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (*helper == NULL) { 1908c2ecf20Sopenharmony_ci ret = request_firmware(helper, iter->helper, dev); 1918c2ecf20Sopenharmony_ci if (ret) 1928c2ecf20Sopenharmony_ci goto next; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* If the device has one-stage firmware (ie cf8305) and 1958c2ecf20Sopenharmony_ci * we've got it then we don't need to bother with the 1968c2ecf20Sopenharmony_ci * main firmware. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci if (iter->fwname == NULL) 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (*mainfw == NULL) { 2038c2ecf20Sopenharmony_ci ret = request_firmware(mainfw, iter->fwname, dev); 2048c2ecf20Sopenharmony_ci if (ret) { 2058c2ecf20Sopenharmony_ci /* Clear the helper to ensure we don't have 2068c2ecf20Sopenharmony_ci * mismatched firmware pairs. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci release_firmware(*helper); 2098c2ecf20Sopenharmony_ci *helper = NULL; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (*helper && *mainfw) 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci next: 2178c2ecf20Sopenharmony_ci iter++; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* Failed */ 2218c2ecf20Sopenharmony_ci release_firmware(*helper); 2228c2ecf20Sopenharmony_ci *helper = NULL; 2238c2ecf20Sopenharmony_ci release_firmware(*mainfw); 2248c2ecf20Sopenharmony_ci *mainfw = NULL; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return -ENOENT; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_get_firmware); 229