18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Intel Wireless WiMAX Connection 2400m 38c2ecf20Sopenharmony_ci * Firmware uploader 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 98c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 108c2ecf20Sopenharmony_ci * are met: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 138c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 148c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 158c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 168c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 178c2ecf20Sopenharmony_ci * distribution. 188c2ecf20Sopenharmony_ci * * Neither the name of Intel Corporation nor the names of its 198c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 208c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 238c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 248c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 258c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 268c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 278c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 288c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 298c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 308c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 318c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 328c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Intel Corporation <linux-wimax@intel.com> 368c2ecf20Sopenharmony_ci * Yanir Lubetkin <yanirx.lubetkin@intel.com> 378c2ecf20Sopenharmony_ci * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 388c2ecf20Sopenharmony_ci * - Initial implementation 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * THE PROCEDURE 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * The 2400m and derived devices work in two modes: boot-mode or 448c2ecf20Sopenharmony_ci * normal mode. In boot mode we can execute only a handful of commands 458c2ecf20Sopenharmony_ci * targeted at uploading the firmware and launching it. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * The 2400m enters boot mode when it is first connected to the 488c2ecf20Sopenharmony_ci * system, when it crashes and when you ask it to reboot. There are 498c2ecf20Sopenharmony_ci * two submodes of the boot mode: signed and non-signed. Signed takes 508c2ecf20Sopenharmony_ci * firmwares signed with a certain private key, non-signed takes any 518c2ecf20Sopenharmony_ci * firmware. Normal hardware takes only signed firmware. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * On boot mode, in USB, we write to the device using the bulk out 548c2ecf20Sopenharmony_ci * endpoint and read from it in the notification endpoint. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * Upon entrance to boot mode, the device sends (preceded with a few 578c2ecf20Sopenharmony_ci * zero length packets (ZLPs) on the notification endpoint in USB) a 588c2ecf20Sopenharmony_ci * reboot barker (4 le32 words with the same value). We ack it by 598c2ecf20Sopenharmony_ci * sending the same barker to the device. The device acks with a 608c2ecf20Sopenharmony_ci * reboot ack barker (4 le32 words with value I2400M_ACK_BARKER) and 618c2ecf20Sopenharmony_ci * then is fully booted. At this point we can upload the firmware. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Note that different iterations of the device and EEPROM 648c2ecf20Sopenharmony_ci * configurations will send different [re]boot barkers; these are 658c2ecf20Sopenharmony_ci * collected in i2400m_barker_db along with the firmware 668c2ecf20Sopenharmony_ci * characteristics they require. 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * This process is accomplished by the i2400m_bootrom_init() 698c2ecf20Sopenharmony_ci * function. All the device interaction happens through the 708c2ecf20Sopenharmony_ci * i2400m_bm_cmd() [boot mode command]. Special return values will 718c2ecf20Sopenharmony_ci * indicate if the device did reset during the process. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * After this, we read the MAC address and then (if needed) 748c2ecf20Sopenharmony_ci * reinitialize the device. We need to read it ahead of time because 758c2ecf20Sopenharmony_ci * in the future, we might not upload the firmware until userspace 768c2ecf20Sopenharmony_ci * 'ifconfig up's the device. 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * We can then upload the firmware file. The file is composed of a BCF 798c2ecf20Sopenharmony_ci * header (basic data, keys and signatures) and a list of write 808c2ecf20Sopenharmony_ci * commands and payloads. Optionally more BCF headers might follow the 818c2ecf20Sopenharmony_ci * main payload. We first upload the header [i2400m_dnload_init()] and 828c2ecf20Sopenharmony_ci * then pass the commands and payloads verbatim to the i2400m_bm_cmd() 838c2ecf20Sopenharmony_ci * function [i2400m_dnload_bcf()]. Then we tell the device to jump to 848c2ecf20Sopenharmony_ci * the new firmware [i2400m_dnload_finalize()]. 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * Once firmware is uploaded, we are good to go :) 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * When we don't know in which mode we are, we first try by sending a 898c2ecf20Sopenharmony_ci * warm reset request that will take us to boot-mode. If we time out 908c2ecf20Sopenharmony_ci * waiting for a reboot barker, that means maybe we are already in 918c2ecf20Sopenharmony_ci * boot mode, so we send a reboot barker. 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * COMMAND EXECUTION 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * This code (and process) is single threaded; for executing commands, 968c2ecf20Sopenharmony_ci * we post a URB to the notification endpoint, post the command, wait 978c2ecf20Sopenharmony_ci * for data on the notification buffer. We don't need to worry about 988c2ecf20Sopenharmony_ci * others as we know we are the only ones in there. 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * BACKEND IMPLEMENTATION 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * This code is bus-generic; the bus-specific driver provides back end 1038c2ecf20Sopenharmony_ci * implementations to send a boot mode command to the device and to 1048c2ecf20Sopenharmony_ci * read an acknolwedgement from it (or an asynchronous notification) 1058c2ecf20Sopenharmony_ci * from it. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * FIRMWARE LOADING 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * Note that in some cases, we can't just load a firmware file (for 1108c2ecf20Sopenharmony_ci * example, when resuming). For that, we might cache the firmware 1118c2ecf20Sopenharmony_ci * file. Thus, when doing the bootstrap, if there is a cache firmware 1128c2ecf20Sopenharmony_ci * file, it is used; if not, loading from disk is attempted. 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * ROADMAP 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * i2400m_barker_db_init Called by i2400m_driver_init() 1178c2ecf20Sopenharmony_ci * i2400m_barker_db_add 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * i2400m_barker_db_exit Called by i2400m_driver_exit() 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * i2400m_dev_bootstrap Called by __i2400m_dev_start() 1228c2ecf20Sopenharmony_ci * request_firmware 1238c2ecf20Sopenharmony_ci * i2400m_fw_bootstrap 1248c2ecf20Sopenharmony_ci * i2400m_fw_check 1258c2ecf20Sopenharmony_ci * i2400m_fw_hdr_check 1268c2ecf20Sopenharmony_ci * i2400m_fw_dnload 1278c2ecf20Sopenharmony_ci * release_firmware 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * i2400m_fw_dnload 1308c2ecf20Sopenharmony_ci * i2400m_bootrom_init 1318c2ecf20Sopenharmony_ci * i2400m_bm_cmd 1328c2ecf20Sopenharmony_ci * i2400m_reset 1338c2ecf20Sopenharmony_ci * i2400m_dnload_init 1348c2ecf20Sopenharmony_ci * i2400m_dnload_init_signed 1358c2ecf20Sopenharmony_ci * i2400m_dnload_init_nonsigned 1368c2ecf20Sopenharmony_ci * i2400m_download_chunk 1378c2ecf20Sopenharmony_ci * i2400m_bm_cmd 1388c2ecf20Sopenharmony_ci * i2400m_dnload_bcf 1398c2ecf20Sopenharmony_ci * i2400m_bm_cmd 1408c2ecf20Sopenharmony_ci * i2400m_dnload_finalize 1418c2ecf20Sopenharmony_ci * i2400m_bm_cmd 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * i2400m_bm_cmd 1448c2ecf20Sopenharmony_ci * i2400m->bus_bm_cmd_send() 1458c2ecf20Sopenharmony_ci * i2400m->bus_bm_wait_for_ack 1468c2ecf20Sopenharmony_ci * __i2400m_bm_ack_verify 1478c2ecf20Sopenharmony_ci * i2400m_is_boot_barker 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * i2400m_bm_cmd_prepare Used by bus-drivers to prep 1508c2ecf20Sopenharmony_ci * commands before sending 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * i2400m_pm_notifier Called on Power Management events 1538c2ecf20Sopenharmony_ci * i2400m_fw_cache 1548c2ecf20Sopenharmony_ci * i2400m_fw_uncache 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci#include <linux/firmware.h> 1578c2ecf20Sopenharmony_ci#include <linux/sched.h> 1588c2ecf20Sopenharmony_ci#include <linux/slab.h> 1598c2ecf20Sopenharmony_ci#include <linux/usb.h> 1608c2ecf20Sopenharmony_ci#include <linux/export.h> 1618c2ecf20Sopenharmony_ci#include "i2400m.h" 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define D_SUBMODULE fw 1658c2ecf20Sopenharmony_ci#include "debug-levels.h" 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const __le32 i2400m_ACK_BARKER[4] = { 1698c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_ACK_BARKER), 1708c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_ACK_BARKER), 1718c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_ACK_BARKER), 1728c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_ACK_BARKER) 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/** 1778c2ecf20Sopenharmony_ci * Prepare a boot-mode command for delivery 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * @cmd: pointer to bootrom header to prepare 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * Computes checksum if so needed. After calling this function, DO NOT 1828c2ecf20Sopenharmony_ci * modify the command or header as the checksum won't work anymore. 1838c2ecf20Sopenharmony_ci * 1848c2ecf20Sopenharmony_ci * We do it from here because some times we cannot do it in the 1858c2ecf20Sopenharmony_ci * original context the command was sent (it is a const), so when we 1868c2ecf20Sopenharmony_ci * copy it to our staging buffer, we add the checksum there. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_civoid i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *cmd) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci if (i2400m_brh_get_use_checksum(cmd)) { 1918c2ecf20Sopenharmony_ci int i; 1928c2ecf20Sopenharmony_ci u32 checksum = 0; 1938c2ecf20Sopenharmony_ci const u32 *checksum_ptr = (void *) cmd->payload; 1948c2ecf20Sopenharmony_ci for (i = 0; i < cmd->data_size / 4; i++) 1958c2ecf20Sopenharmony_ci checksum += cpu_to_le32(*checksum_ptr++); 1968c2ecf20Sopenharmony_ci checksum += cmd->command + cmd->target_addr + cmd->data_size; 1978c2ecf20Sopenharmony_ci cmd->block_checksum = cpu_to_le32(checksum); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * Database of known barkers. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * A barker is what the device sends indicating he is ready to be 2078c2ecf20Sopenharmony_ci * bootloaded. Different versions of the device will send different 2088c2ecf20Sopenharmony_ci * barkers. Depending on the barker, it might mean the device wants 2098c2ecf20Sopenharmony_ci * some kind of firmware or the other. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_cistatic struct i2400m_barker_db { 2128c2ecf20Sopenharmony_ci __le32 data[4]; 2138c2ecf20Sopenharmony_ci} *i2400m_barker_db; 2148c2ecf20Sopenharmony_cistatic size_t i2400m_barker_db_used, i2400m_barker_db_size; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic 2188c2ecf20Sopenharmony_ciint i2400m_zrealloc_2x(void **ptr, size_t *_count, size_t el_size, 2198c2ecf20Sopenharmony_ci gfp_t gfp_flags) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci size_t old_count = *_count, 2228c2ecf20Sopenharmony_ci new_count = old_count ? 2 * old_count : 2, 2238c2ecf20Sopenharmony_ci old_size = el_size * old_count, 2248c2ecf20Sopenharmony_ci new_size = el_size * new_count; 2258c2ecf20Sopenharmony_ci void *nptr = krealloc(*ptr, new_size, gfp_flags); 2268c2ecf20Sopenharmony_ci if (nptr) { 2278c2ecf20Sopenharmony_ci /* zero the other half or the whole thing if old_count 2288c2ecf20Sopenharmony_ci * was zero */ 2298c2ecf20Sopenharmony_ci if (old_size == 0) 2308c2ecf20Sopenharmony_ci memset(nptr, 0, new_size); 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci memset(nptr + old_size, 0, old_size); 2338c2ecf20Sopenharmony_ci *_count = new_count; 2348c2ecf20Sopenharmony_ci *ptr = nptr; 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci } else 2378c2ecf20Sopenharmony_ci return -ENOMEM; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * Add a barker to the database 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci * This cannot used outside of this module and only at at module_init 2458c2ecf20Sopenharmony_ci * time. This is to avoid the need to do locking. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic 2488c2ecf20Sopenharmony_ciint i2400m_barker_db_add(u32 barker_id) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int result; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci struct i2400m_barker_db *barker; 2538c2ecf20Sopenharmony_ci if (i2400m_barker_db_used >= i2400m_barker_db_size) { 2548c2ecf20Sopenharmony_ci result = i2400m_zrealloc_2x( 2558c2ecf20Sopenharmony_ci (void **) &i2400m_barker_db, &i2400m_barker_db_size, 2568c2ecf20Sopenharmony_ci sizeof(i2400m_barker_db[0]), GFP_KERNEL); 2578c2ecf20Sopenharmony_ci if (result < 0) 2588c2ecf20Sopenharmony_ci return result; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci barker = i2400m_barker_db + i2400m_barker_db_used++; 2618c2ecf20Sopenharmony_ci barker->data[0] = le32_to_cpu(barker_id); 2628c2ecf20Sopenharmony_ci barker->data[1] = le32_to_cpu(barker_id); 2638c2ecf20Sopenharmony_ci barker->data[2] = le32_to_cpu(barker_id); 2648c2ecf20Sopenharmony_ci barker->data[3] = le32_to_cpu(barker_id); 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid i2400m_barker_db_exit(void) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci kfree(i2400m_barker_db); 2728c2ecf20Sopenharmony_ci i2400m_barker_db = NULL; 2738c2ecf20Sopenharmony_ci i2400m_barker_db_size = 0; 2748c2ecf20Sopenharmony_ci i2400m_barker_db_used = 0; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* 2798c2ecf20Sopenharmony_ci * Helper function to add all the known stable barkers to the barker 2808c2ecf20Sopenharmony_ci * database. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistatic 2838c2ecf20Sopenharmony_ciint i2400m_barker_db_known_barkers(void) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int result; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci result = i2400m_barker_db_add(I2400M_NBOOT_BARKER); 2888c2ecf20Sopenharmony_ci if (result < 0) 2898c2ecf20Sopenharmony_ci goto error_add; 2908c2ecf20Sopenharmony_ci result = i2400m_barker_db_add(I2400M_SBOOT_BARKER); 2918c2ecf20Sopenharmony_ci if (result < 0) 2928c2ecf20Sopenharmony_ci goto error_add; 2938c2ecf20Sopenharmony_ci result = i2400m_barker_db_add(I2400M_SBOOT_BARKER_6050); 2948c2ecf20Sopenharmony_ci if (result < 0) 2958c2ecf20Sopenharmony_ci goto error_add; 2968c2ecf20Sopenharmony_cierror_add: 2978c2ecf20Sopenharmony_ci return result; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* 3028c2ecf20Sopenharmony_ci * Initialize the barker database 3038c2ecf20Sopenharmony_ci * 3048c2ecf20Sopenharmony_ci * This can only be used from the module_init function for this 3058c2ecf20Sopenharmony_ci * module; this is to avoid the need to do locking. 3068c2ecf20Sopenharmony_ci * 3078c2ecf20Sopenharmony_ci * @options: command line argument with extra barkers to 3088c2ecf20Sopenharmony_ci * recognize. This is a comma-separated list of 32-bit hex 3098c2ecf20Sopenharmony_ci * numbers. They are appended to the existing list. Setting 0 3108c2ecf20Sopenharmony_ci * cleans the existing list and starts a new one. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ciint i2400m_barker_db_init(const char *_options) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int result; 3158c2ecf20Sopenharmony_ci char *options = NULL, *options_orig, *token; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci i2400m_barker_db = NULL; 3188c2ecf20Sopenharmony_ci i2400m_barker_db_size = 0; 3198c2ecf20Sopenharmony_ci i2400m_barker_db_used = 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci result = i2400m_barker_db_known_barkers(); 3228c2ecf20Sopenharmony_ci if (result < 0) 3238c2ecf20Sopenharmony_ci goto error_add; 3248c2ecf20Sopenharmony_ci /* parse command line options from i2400m.barkers */ 3258c2ecf20Sopenharmony_ci if (_options != NULL) { 3268c2ecf20Sopenharmony_ci unsigned barker; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci options_orig = kstrdup(_options, GFP_KERNEL); 3298c2ecf20Sopenharmony_ci if (options_orig == NULL) { 3308c2ecf20Sopenharmony_ci result = -ENOMEM; 3318c2ecf20Sopenharmony_ci goto error_parse; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci options = options_orig; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci while ((token = strsep(&options, ",")) != NULL) { 3368c2ecf20Sopenharmony_ci if (*token == '\0') /* eat joint commas */ 3378c2ecf20Sopenharmony_ci continue; 3388c2ecf20Sopenharmony_ci if (sscanf(token, "%x", &barker) != 1 3398c2ecf20Sopenharmony_ci || barker > 0xffffffff) { 3408c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: can't recognize " 3418c2ecf20Sopenharmony_ci "i2400m.barkers value '%s' as " 3428c2ecf20Sopenharmony_ci "a 32-bit number\n", 3438c2ecf20Sopenharmony_ci __func__, token); 3448c2ecf20Sopenharmony_ci result = -EINVAL; 3458c2ecf20Sopenharmony_ci goto error_parse; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci if (barker == 0) { 3488c2ecf20Sopenharmony_ci /* clean list and start new */ 3498c2ecf20Sopenharmony_ci i2400m_barker_db_exit(); 3508c2ecf20Sopenharmony_ci continue; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci result = i2400m_barker_db_add(barker); 3538c2ecf20Sopenharmony_ci if (result < 0) 3548c2ecf20Sopenharmony_ci goto error_parse_add; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci kfree(options_orig); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cierror_parse_add: 3618c2ecf20Sopenharmony_cierror_parse: 3628c2ecf20Sopenharmony_ci kfree(options_orig); 3638c2ecf20Sopenharmony_cierror_add: 3648c2ecf20Sopenharmony_ci kfree(i2400m_barker_db); 3658c2ecf20Sopenharmony_ci return result; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* 3708c2ecf20Sopenharmony_ci * Recognize a boot barker 3718c2ecf20Sopenharmony_ci * 3728c2ecf20Sopenharmony_ci * @buf: buffer where the boot barker. 3738c2ecf20Sopenharmony_ci * @buf_size: size of the buffer (has to be 16 bytes). It is passed 3748c2ecf20Sopenharmony_ci * here so the function can check it for the caller. 3758c2ecf20Sopenharmony_ci * 3768c2ecf20Sopenharmony_ci * Note that as a side effect, upon identifying the obtained boot 3778c2ecf20Sopenharmony_ci * barker, this function will set i2400m->barker to point to the right 3788c2ecf20Sopenharmony_ci * barker database entry. Subsequent calls to the function will result 3798c2ecf20Sopenharmony_ci * in verifying that the same type of boot barker is returned when the 3808c2ecf20Sopenharmony_ci * device [re]boots (as long as the same device instance is used). 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * Return: 0 if @buf matches a known boot barker. -ENOENT if the 3838c2ecf20Sopenharmony_ci * buffer in @buf doesn't match any boot barker in the database or 3848c2ecf20Sopenharmony_ci * -EILSEQ if the buffer doesn't have the right size. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ciint i2400m_is_boot_barker(struct i2400m *i2400m, 3878c2ecf20Sopenharmony_ci const void *buf, size_t buf_size) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci int result; 3908c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 3918c2ecf20Sopenharmony_ci struct i2400m_barker_db *barker; 3928c2ecf20Sopenharmony_ci int i; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci result = -ENOENT; 3958c2ecf20Sopenharmony_ci if (buf_size != sizeof(i2400m_barker_db[i].data)) 3968c2ecf20Sopenharmony_ci return result; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Short circuit if we have already discovered the barker 3998c2ecf20Sopenharmony_ci * associated with the device. */ 4008c2ecf20Sopenharmony_ci if (i2400m->barker && 4018c2ecf20Sopenharmony_ci !memcmp(buf, i2400m->barker, sizeof(i2400m->barker->data))) 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci for (i = 0; i < i2400m_barker_db_used; i++) { 4058c2ecf20Sopenharmony_ci barker = &i2400m_barker_db[i]; 4068c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(barker->data) != 16); 4078c2ecf20Sopenharmony_ci if (memcmp(buf, barker->data, sizeof(barker->data))) 4088c2ecf20Sopenharmony_ci continue; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (i2400m->barker == NULL) { 4118c2ecf20Sopenharmony_ci i2400m->barker = barker; 4128c2ecf20Sopenharmony_ci d_printf(1, dev, "boot barker set to #%u/%08x\n", 4138c2ecf20Sopenharmony_ci i, le32_to_cpu(barker->data[0])); 4148c2ecf20Sopenharmony_ci if (barker->data[0] == le32_to_cpu(I2400M_NBOOT_BARKER)) 4158c2ecf20Sopenharmony_ci i2400m->sboot = 0; 4168c2ecf20Sopenharmony_ci else 4178c2ecf20Sopenharmony_ci i2400m->sboot = 1; 4188c2ecf20Sopenharmony_ci } else if (i2400m->barker != barker) { 4198c2ecf20Sopenharmony_ci dev_err(dev, "HW inconsistency: device " 4208c2ecf20Sopenharmony_ci "reports a different boot barker " 4218c2ecf20Sopenharmony_ci "than set (from %08x to %08x)\n", 4228c2ecf20Sopenharmony_ci le32_to_cpu(i2400m->barker->data[0]), 4238c2ecf20Sopenharmony_ci le32_to_cpu(barker->data[0])); 4248c2ecf20Sopenharmony_ci result = -EIO; 4258c2ecf20Sopenharmony_ci } else 4268c2ecf20Sopenharmony_ci d_printf(2, dev, "boot barker confirmed #%u/%08x\n", 4278c2ecf20Sopenharmony_ci i, le32_to_cpu(barker->data[0])); 4288c2ecf20Sopenharmony_ci result = 0; 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci return result; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2400m_is_boot_barker); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * Verify the ack data received 4388c2ecf20Sopenharmony_ci * 4398c2ecf20Sopenharmony_ci * Given a reply to a boot mode command, chew it and verify everything 4408c2ecf20Sopenharmony_ci * is ok. 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * @opcode: opcode which generated this ack. For error messages. 4438c2ecf20Sopenharmony_ci * @ack: pointer to ack data we received 4448c2ecf20Sopenharmony_ci * @ack_size: size of that data buffer 4458c2ecf20Sopenharmony_ci * @flags: I2400M_BM_CMD_* flags we called the command with. 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * Way too long function -- maybe it should be further split 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_cistatic 4508c2ecf20Sopenharmony_cissize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode, 4518c2ecf20Sopenharmony_ci struct i2400m_bootrom_header *ack, 4528c2ecf20Sopenharmony_ci size_t ack_size, int flags) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci ssize_t result = -ENOMEM; 4558c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci d_fnstart(8, dev, "(i2400m %p opcode %d ack %p size %zu)\n", 4588c2ecf20Sopenharmony_ci i2400m, opcode, ack, ack_size); 4598c2ecf20Sopenharmony_ci if (ack_size < sizeof(*ack)) { 4608c2ecf20Sopenharmony_ci result = -EIO; 4618c2ecf20Sopenharmony_ci dev_err(dev, "boot-mode cmd %d: HW BUG? notification didn't " 4628c2ecf20Sopenharmony_ci "return enough data (%zu bytes vs %zu expected)\n", 4638c2ecf20Sopenharmony_ci opcode, ack_size, sizeof(*ack)); 4648c2ecf20Sopenharmony_ci goto error_ack_short; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci result = i2400m_is_boot_barker(i2400m, ack, ack_size); 4678c2ecf20Sopenharmony_ci if (result >= 0) { 4688c2ecf20Sopenharmony_ci result = -ERESTARTSYS; 4698c2ecf20Sopenharmony_ci d_printf(6, dev, "boot-mode cmd %d: HW boot barker\n", opcode); 4708c2ecf20Sopenharmony_ci goto error_reboot; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci if (ack_size == sizeof(i2400m_ACK_BARKER) 4738c2ecf20Sopenharmony_ci && memcmp(ack, i2400m_ACK_BARKER, sizeof(*ack)) == 0) { 4748c2ecf20Sopenharmony_ci result = -EISCONN; 4758c2ecf20Sopenharmony_ci d_printf(3, dev, "boot-mode cmd %d: HW reboot ack barker\n", 4768c2ecf20Sopenharmony_ci opcode); 4778c2ecf20Sopenharmony_ci goto error_reboot_ack; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci result = 0; 4808c2ecf20Sopenharmony_ci if (flags & I2400M_BM_CMD_RAW) 4818c2ecf20Sopenharmony_ci goto out_raw; 4828c2ecf20Sopenharmony_ci ack->data_size = le32_to_cpu(ack->data_size); 4838c2ecf20Sopenharmony_ci ack->target_addr = le32_to_cpu(ack->target_addr); 4848c2ecf20Sopenharmony_ci ack->block_checksum = le32_to_cpu(ack->block_checksum); 4858c2ecf20Sopenharmony_ci d_printf(5, dev, "boot-mode cmd %d: notification for opcode %u " 4868c2ecf20Sopenharmony_ci "response %u csum %u rr %u da %u\n", 4878c2ecf20Sopenharmony_ci opcode, i2400m_brh_get_opcode(ack), 4888c2ecf20Sopenharmony_ci i2400m_brh_get_response(ack), 4898c2ecf20Sopenharmony_ci i2400m_brh_get_use_checksum(ack), 4908c2ecf20Sopenharmony_ci i2400m_brh_get_response_required(ack), 4918c2ecf20Sopenharmony_ci i2400m_brh_get_direct_access(ack)); 4928c2ecf20Sopenharmony_ci result = -EIO; 4938c2ecf20Sopenharmony_ci if (i2400m_brh_get_signature(ack) != 0xcbbc) { 4948c2ecf20Sopenharmony_ci dev_err(dev, "boot-mode cmd %d: HW BUG? wrong signature " 4958c2ecf20Sopenharmony_ci "0x%04x\n", opcode, i2400m_brh_get_signature(ack)); 4968c2ecf20Sopenharmony_ci goto error_ack_signature; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci if (opcode != -1 && opcode != i2400m_brh_get_opcode(ack)) { 4998c2ecf20Sopenharmony_ci dev_err(dev, "boot-mode cmd %d: HW BUG? " 5008c2ecf20Sopenharmony_ci "received response for opcode %u, expected %u\n", 5018c2ecf20Sopenharmony_ci opcode, i2400m_brh_get_opcode(ack), opcode); 5028c2ecf20Sopenharmony_ci goto error_ack_opcode; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci if (i2400m_brh_get_response(ack) != 0) { /* failed? */ 5058c2ecf20Sopenharmony_ci dev_err(dev, "boot-mode cmd %d: error; hw response %u\n", 5068c2ecf20Sopenharmony_ci opcode, i2400m_brh_get_response(ack)); 5078c2ecf20Sopenharmony_ci goto error_ack_failed; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci if (ack_size < ack->data_size + sizeof(*ack)) { 5108c2ecf20Sopenharmony_ci dev_err(dev, "boot-mode cmd %d: SW BUG " 5118c2ecf20Sopenharmony_ci "driver provided only %zu bytes for %zu bytes " 5128c2ecf20Sopenharmony_ci "of data\n", opcode, ack_size, 5138c2ecf20Sopenharmony_ci (size_t) le32_to_cpu(ack->data_size) + sizeof(*ack)); 5148c2ecf20Sopenharmony_ci goto error_ack_short_buffer; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci result = ack_size; 5178c2ecf20Sopenharmony_ci /* Don't you love this stack of empty targets? Well, I don't 5188c2ecf20Sopenharmony_ci * either, but it helps track exactly who comes in here and 5198c2ecf20Sopenharmony_ci * why :) */ 5208c2ecf20Sopenharmony_cierror_ack_short_buffer: 5218c2ecf20Sopenharmony_cierror_ack_failed: 5228c2ecf20Sopenharmony_cierror_ack_opcode: 5238c2ecf20Sopenharmony_cierror_ack_signature: 5248c2ecf20Sopenharmony_ciout_raw: 5258c2ecf20Sopenharmony_cierror_reboot_ack: 5268c2ecf20Sopenharmony_cierror_reboot: 5278c2ecf20Sopenharmony_cierror_ack_short: 5288c2ecf20Sopenharmony_ci d_fnend(8, dev, "(i2400m %p opcode %d ack %p size %zu) = %d\n", 5298c2ecf20Sopenharmony_ci i2400m, opcode, ack, ack_size, (int) result); 5308c2ecf20Sopenharmony_ci return result; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/** 5358c2ecf20Sopenharmony_ci * i2400m_bm_cmd - Execute a boot mode command 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * @cmd: buffer containing the command data (pointing at the header). 5388c2ecf20Sopenharmony_ci * This data can be ANYWHERE (for USB, we will copy it to an 5398c2ecf20Sopenharmony_ci * specific buffer). Make sure everything is in proper little 5408c2ecf20Sopenharmony_ci * endian. 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * A raw buffer can be also sent, just cast it and set flags to 5438c2ecf20Sopenharmony_ci * I2400M_BM_CMD_RAW. 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * This function will generate a checksum for you if the 5468c2ecf20Sopenharmony_ci * checksum bit in the command is set (unless I2400M_BM_CMD_RAW 5478c2ecf20Sopenharmony_ci * is set). 5488c2ecf20Sopenharmony_ci * 5498c2ecf20Sopenharmony_ci * You can use the i2400m->bm_cmd_buf to stage your commands and 5508c2ecf20Sopenharmony_ci * send them. 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * If NULL, no command is sent (we just wait for an ack). 5538c2ecf20Sopenharmony_ci * 5548c2ecf20Sopenharmony_ci * @cmd_size: size of the command. Will be auto padded to the 5558c2ecf20Sopenharmony_ci * bus-specific drivers padding requirements. 5568c2ecf20Sopenharmony_ci * 5578c2ecf20Sopenharmony_ci * @ack: buffer where to place the acknowledgement. If it is a regular 5588c2ecf20Sopenharmony_ci * command response, all fields will be returned with the right, 5598c2ecf20Sopenharmony_ci * native endianess. 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * You *cannot* use i2400m->bm_ack_buf for this buffer. 5628c2ecf20Sopenharmony_ci * 5638c2ecf20Sopenharmony_ci * @ack_size: size of @ack, 16 aligned; you need to provide at least 5648c2ecf20Sopenharmony_ci * sizeof(*ack) bytes and then enough to contain the return data 5658c2ecf20Sopenharmony_ci * from the command 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * @flags: see I2400M_BM_CMD_* above. 5688c2ecf20Sopenharmony_ci * 5698c2ecf20Sopenharmony_ci * @returns: bytes received by the notification; if < 0, an errno code 5708c2ecf20Sopenharmony_ci * denoting an error or: 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * -ERESTARTSYS The device has rebooted 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci * Executes a boot-mode command and waits for a response, doing basic 5758c2ecf20Sopenharmony_ci * validation on it; if a zero length response is received, it retries 5768c2ecf20Sopenharmony_ci * waiting for a response until a non-zero one is received (timing out 5778c2ecf20Sopenharmony_ci * after %I2400M_BOOT_RETRIES retries). 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_cistatic 5808c2ecf20Sopenharmony_cissize_t i2400m_bm_cmd(struct i2400m *i2400m, 5818c2ecf20Sopenharmony_ci const struct i2400m_bootrom_header *cmd, size_t cmd_size, 5828c2ecf20Sopenharmony_ci struct i2400m_bootrom_header *ack, size_t ack_size, 5838c2ecf20Sopenharmony_ci int flags) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci ssize_t result = -ENOMEM, rx_bytes; 5868c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 5878c2ecf20Sopenharmony_ci int opcode = cmd == NULL ? -1 : i2400m_brh_get_opcode(cmd); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci d_fnstart(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu)\n", 5908c2ecf20Sopenharmony_ci i2400m, cmd, cmd_size, ack, ack_size); 5918c2ecf20Sopenharmony_ci BUG_ON(ack_size < sizeof(*ack)); 5928c2ecf20Sopenharmony_ci BUG_ON(i2400m->boot_mode == 0); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (cmd != NULL) { /* send the command */ 5958c2ecf20Sopenharmony_ci result = i2400m->bus_bm_cmd_send(i2400m, cmd, cmd_size, flags); 5968c2ecf20Sopenharmony_ci if (result < 0) 5978c2ecf20Sopenharmony_ci goto error_cmd_send; 5988c2ecf20Sopenharmony_ci if ((flags & I2400M_BM_CMD_RAW) == 0) 5998c2ecf20Sopenharmony_ci d_printf(5, dev, 6008c2ecf20Sopenharmony_ci "boot-mode cmd %d csum %u rr %u da %u: " 6018c2ecf20Sopenharmony_ci "addr 0x%04x size %u block csum 0x%04x\n", 6028c2ecf20Sopenharmony_ci opcode, i2400m_brh_get_use_checksum(cmd), 6038c2ecf20Sopenharmony_ci i2400m_brh_get_response_required(cmd), 6048c2ecf20Sopenharmony_ci i2400m_brh_get_direct_access(cmd), 6058c2ecf20Sopenharmony_ci cmd->target_addr, cmd->data_size, 6068c2ecf20Sopenharmony_ci cmd->block_checksum); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci result = i2400m->bus_bm_wait_for_ack(i2400m, ack, ack_size); 6098c2ecf20Sopenharmony_ci if (result < 0) { 6108c2ecf20Sopenharmony_ci dev_err(dev, "boot-mode cmd %d: error waiting for an ack: %d\n", 6118c2ecf20Sopenharmony_ci opcode, (int) result); /* bah, %zd doesn't work */ 6128c2ecf20Sopenharmony_ci goto error_wait_for_ack; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci rx_bytes = result; 6158c2ecf20Sopenharmony_ci /* verify the ack and read more if necessary [result is the 6168c2ecf20Sopenharmony_ci * final amount of bytes we get in the ack] */ 6178c2ecf20Sopenharmony_ci result = __i2400m_bm_ack_verify(i2400m, opcode, ack, ack_size, flags); 6188c2ecf20Sopenharmony_ci if (result < 0) 6198c2ecf20Sopenharmony_ci goto error_bad_ack; 6208c2ecf20Sopenharmony_ci /* Don't you love this stack of empty targets? Well, I don't 6218c2ecf20Sopenharmony_ci * either, but it helps track exactly who comes in here and 6228c2ecf20Sopenharmony_ci * why :) */ 6238c2ecf20Sopenharmony_ci result = rx_bytes; 6248c2ecf20Sopenharmony_cierror_bad_ack: 6258c2ecf20Sopenharmony_cierror_wait_for_ack: 6268c2ecf20Sopenharmony_cierror_cmd_send: 6278c2ecf20Sopenharmony_ci d_fnend(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu) = %d\n", 6288c2ecf20Sopenharmony_ci i2400m, cmd, cmd_size, ack, ack_size, (int) result); 6298c2ecf20Sopenharmony_ci return result; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci/** 6348c2ecf20Sopenharmony_ci * i2400m_download_chunk - write a single chunk of data to the device's memory 6358c2ecf20Sopenharmony_ci * 6368c2ecf20Sopenharmony_ci * @i2400m: device descriptor 6378c2ecf20Sopenharmony_ci * @buf: the buffer to write 6388c2ecf20Sopenharmony_ci * @buf_len: length of the buffer to write 6398c2ecf20Sopenharmony_ci * @addr: address in the device memory space 6408c2ecf20Sopenharmony_ci * @direct: bootrom write mode 6418c2ecf20Sopenharmony_ci * @do_csum: should a checksum validation be performed 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_cistatic int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk, 6448c2ecf20Sopenharmony_ci size_t __chunk_len, unsigned long addr, 6458c2ecf20Sopenharmony_ci unsigned int direct, unsigned int do_csum) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci int ret; 6488c2ecf20Sopenharmony_ci size_t chunk_len = ALIGN(__chunk_len, I2400M_PL_ALIGN); 6498c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 6508c2ecf20Sopenharmony_ci struct { 6518c2ecf20Sopenharmony_ci struct i2400m_bootrom_header cmd; 6528c2ecf20Sopenharmony_ci u8 cmd_payload[]; 6538c2ecf20Sopenharmony_ci } __packed *buf; 6548c2ecf20Sopenharmony_ci struct i2400m_bootrom_header ack; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci d_fnstart(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx " 6578c2ecf20Sopenharmony_ci "direct %u do_csum %u)\n", i2400m, chunk, __chunk_len, 6588c2ecf20Sopenharmony_ci addr, direct, do_csum); 6598c2ecf20Sopenharmony_ci buf = i2400m->bm_cmd_buf; 6608c2ecf20Sopenharmony_ci memcpy(buf->cmd_payload, chunk, __chunk_len); 6618c2ecf20Sopenharmony_ci memset(buf->cmd_payload + __chunk_len, 0xad, chunk_len - __chunk_len); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci buf->cmd.command = i2400m_brh_command(I2400M_BRH_WRITE, 6648c2ecf20Sopenharmony_ci __chunk_len & 0x3 ? 0 : do_csum, 6658c2ecf20Sopenharmony_ci __chunk_len & 0xf ? 0 : direct); 6668c2ecf20Sopenharmony_ci buf->cmd.target_addr = cpu_to_le32(addr); 6678c2ecf20Sopenharmony_ci buf->cmd.data_size = cpu_to_le32(__chunk_len); 6688c2ecf20Sopenharmony_ci ret = i2400m_bm_cmd(i2400m, &buf->cmd, sizeof(buf->cmd) + chunk_len, 6698c2ecf20Sopenharmony_ci &ack, sizeof(ack), 0); 6708c2ecf20Sopenharmony_ci if (ret >= 0) 6718c2ecf20Sopenharmony_ci ret = 0; 6728c2ecf20Sopenharmony_ci d_fnend(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx " 6738c2ecf20Sopenharmony_ci "direct %u do_csum %u) = %d\n", i2400m, chunk, __chunk_len, 6748c2ecf20Sopenharmony_ci addr, direct, do_csum, ret); 6758c2ecf20Sopenharmony_ci return ret; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* 6808c2ecf20Sopenharmony_ci * Download a BCF file's sections to the device 6818c2ecf20Sopenharmony_ci * 6828c2ecf20Sopenharmony_ci * @i2400m: device descriptor 6838c2ecf20Sopenharmony_ci * @bcf: pointer to firmware data (first header followed by the 6848c2ecf20Sopenharmony_ci * payloads). Assumed verified and consistent. 6858c2ecf20Sopenharmony_ci * @bcf_len: length (in bytes) of the @bcf buffer. 6868c2ecf20Sopenharmony_ci * 6878c2ecf20Sopenharmony_ci * Returns: < 0 errno code on error or the offset to the jump instruction. 6888c2ecf20Sopenharmony_ci * 6898c2ecf20Sopenharmony_ci * Given a BCF file, downloads each section (a command and a payload) 6908c2ecf20Sopenharmony_ci * to the device's address space. Actually, it just executes each 6918c2ecf20Sopenharmony_ci * command i the BCF file. 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * The section size has to be aligned to 4 bytes AND the padding has 6948c2ecf20Sopenharmony_ci * to be taken from the firmware file, as the signature takes it into 6958c2ecf20Sopenharmony_ci * account. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_cistatic 6988c2ecf20Sopenharmony_cissize_t i2400m_dnload_bcf(struct i2400m *i2400m, 6998c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf, size_t bcf_len) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci ssize_t ret; 7028c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 7038c2ecf20Sopenharmony_ci size_t offset, /* iterator offset */ 7048c2ecf20Sopenharmony_ci data_size, /* Size of the data payload */ 7058c2ecf20Sopenharmony_ci section_size, /* Size of the whole section (cmd + payload) */ 7068c2ecf20Sopenharmony_ci section = 1; 7078c2ecf20Sopenharmony_ci const struct i2400m_bootrom_header *bh; 7088c2ecf20Sopenharmony_ci struct i2400m_bootrom_header ack; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(i2400m %p bcf %p bcf_len %zu)\n", 7118c2ecf20Sopenharmony_ci i2400m, bcf, bcf_len); 7128c2ecf20Sopenharmony_ci /* Iterate over the command blocks in the BCF file that start 7138c2ecf20Sopenharmony_ci * after the header */ 7148c2ecf20Sopenharmony_ci offset = le32_to_cpu(bcf->header_len) * sizeof(u32); 7158c2ecf20Sopenharmony_ci while (1) { /* start sending the file */ 7168c2ecf20Sopenharmony_ci bh = (void *) bcf + offset; 7178c2ecf20Sopenharmony_ci data_size = le32_to_cpu(bh->data_size); 7188c2ecf20Sopenharmony_ci section_size = ALIGN(sizeof(*bh) + data_size, 4); 7198c2ecf20Sopenharmony_ci d_printf(7, dev, 7208c2ecf20Sopenharmony_ci "downloading section #%zu (@%zu %zu B) to 0x%08x\n", 7218c2ecf20Sopenharmony_ci section, offset, sizeof(*bh) + data_size, 7228c2ecf20Sopenharmony_ci le32_to_cpu(bh->target_addr)); 7238c2ecf20Sopenharmony_ci /* 7248c2ecf20Sopenharmony_ci * We look for JUMP cmd from the bootmode header, 7258c2ecf20Sopenharmony_ci * either I2400M_BRH_SIGNED_JUMP for secure boot 7268c2ecf20Sopenharmony_ci * or I2400M_BRH_JUMP for unsecure boot, the last chunk 7278c2ecf20Sopenharmony_ci * should be the bootmode header with JUMP cmd. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP || 7308c2ecf20Sopenharmony_ci i2400m_brh_get_opcode(bh) == I2400M_BRH_JUMP) { 7318c2ecf20Sopenharmony_ci d_printf(5, dev, "jump found @%zu\n", offset); 7328c2ecf20Sopenharmony_ci break; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci if (offset + section_size > bcf_len) { 7358c2ecf20Sopenharmony_ci dev_err(dev, "fw %s: bad section #%zu, " 7368c2ecf20Sopenharmony_ci "end (@%zu) beyond EOF (@%zu)\n", 7378c2ecf20Sopenharmony_ci i2400m->fw_name, section, 7388c2ecf20Sopenharmony_ci offset + section_size, bcf_len); 7398c2ecf20Sopenharmony_ci ret = -EINVAL; 7408c2ecf20Sopenharmony_ci goto error_section_beyond_eof; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci __i2400m_msleep(20); 7438c2ecf20Sopenharmony_ci ret = i2400m_bm_cmd(i2400m, bh, section_size, 7448c2ecf20Sopenharmony_ci &ack, sizeof(ack), I2400M_BM_CMD_RAW); 7458c2ecf20Sopenharmony_ci if (ret < 0) { 7468c2ecf20Sopenharmony_ci dev_err(dev, "fw %s: section #%zu (@%zu %zu B) " 7478c2ecf20Sopenharmony_ci "failed %d\n", i2400m->fw_name, section, 7488c2ecf20Sopenharmony_ci offset, sizeof(*bh) + data_size, (int) ret); 7498c2ecf20Sopenharmony_ci goto error_send; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci offset += section_size; 7528c2ecf20Sopenharmony_ci section++; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci ret = offset; 7558c2ecf20Sopenharmony_cierror_section_beyond_eof: 7568c2ecf20Sopenharmony_cierror_send: 7578c2ecf20Sopenharmony_ci d_fnend(3, dev, "(i2400m %p bcf %p bcf_len %zu) = %d\n", 7588c2ecf20Sopenharmony_ci i2400m, bcf, bcf_len, (int) ret); 7598c2ecf20Sopenharmony_ci return ret; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci/* 7648c2ecf20Sopenharmony_ci * Indicate if the device emitted a reboot barker that indicates 7658c2ecf20Sopenharmony_ci * "signed boot" 7668c2ecf20Sopenharmony_ci */ 7678c2ecf20Sopenharmony_cistatic 7688c2ecf20Sopenharmony_ciunsigned i2400m_boot_is_signed(struct i2400m *i2400m) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci return likely(i2400m->sboot); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/* 7758c2ecf20Sopenharmony_ci * Do the final steps of uploading firmware 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * @bcf_hdr: BCF header we are actually using 7788c2ecf20Sopenharmony_ci * @bcf: pointer to the firmware image (which matches the first header 7798c2ecf20Sopenharmony_ci * that is followed by the actual payloads). 7808c2ecf20Sopenharmony_ci * @offset: [byte] offset into @bcf for the command we need to send. 7818c2ecf20Sopenharmony_ci * 7828c2ecf20Sopenharmony_ci * Depending on the boot mode (signed vs non-signed), different 7838c2ecf20Sopenharmony_ci * actions need to be taken. 7848c2ecf20Sopenharmony_ci */ 7858c2ecf20Sopenharmony_cistatic 7868c2ecf20Sopenharmony_ciint i2400m_dnload_finalize(struct i2400m *i2400m, 7878c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf_hdr, 7888c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf, size_t offset) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci int ret = 0; 7918c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 7928c2ecf20Sopenharmony_ci struct i2400m_bootrom_header *cmd, ack; 7938c2ecf20Sopenharmony_ci struct { 7948c2ecf20Sopenharmony_ci struct i2400m_bootrom_header cmd; 7958c2ecf20Sopenharmony_ci u8 cmd_pl[0]; 7968c2ecf20Sopenharmony_ci } __packed *cmd_buf; 7978c2ecf20Sopenharmony_ci size_t signature_block_offset, signature_block_size; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci d_fnstart(3, dev, "offset %zu\n", offset); 8008c2ecf20Sopenharmony_ci cmd = (void *) bcf + offset; 8018c2ecf20Sopenharmony_ci if (i2400m_boot_is_signed(i2400m) == 0) { 8028c2ecf20Sopenharmony_ci struct i2400m_bootrom_header jump_ack; 8038c2ecf20Sopenharmony_ci d_printf(1, dev, "unsecure boot, jumping to 0x%08x\n", 8048c2ecf20Sopenharmony_ci le32_to_cpu(cmd->target_addr)); 8058c2ecf20Sopenharmony_ci cmd_buf = i2400m->bm_cmd_buf; 8068c2ecf20Sopenharmony_ci memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); 8078c2ecf20Sopenharmony_ci cmd = &cmd_buf->cmd; 8088c2ecf20Sopenharmony_ci /* now cmd points to the actual bootrom_header in cmd_buf */ 8098c2ecf20Sopenharmony_ci i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP); 8108c2ecf20Sopenharmony_ci cmd->data_size = 0; 8118c2ecf20Sopenharmony_ci ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), 8128c2ecf20Sopenharmony_ci &jump_ack, sizeof(jump_ack), 0); 8138c2ecf20Sopenharmony_ci } else { 8148c2ecf20Sopenharmony_ci d_printf(1, dev, "secure boot, jumping to 0x%08x\n", 8158c2ecf20Sopenharmony_ci le32_to_cpu(cmd->target_addr)); 8168c2ecf20Sopenharmony_ci cmd_buf = i2400m->bm_cmd_buf; 8178c2ecf20Sopenharmony_ci memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); 8188c2ecf20Sopenharmony_ci signature_block_offset = 8198c2ecf20Sopenharmony_ci sizeof(*bcf_hdr) 8208c2ecf20Sopenharmony_ci + le32_to_cpu(bcf_hdr->key_size) * sizeof(u32) 8218c2ecf20Sopenharmony_ci + le32_to_cpu(bcf_hdr->exponent_size) * sizeof(u32); 8228c2ecf20Sopenharmony_ci signature_block_size = 8238c2ecf20Sopenharmony_ci le32_to_cpu(bcf_hdr->modulus_size) * sizeof(u32); 8248c2ecf20Sopenharmony_ci memcpy(cmd_buf->cmd_pl, 8258c2ecf20Sopenharmony_ci (void *) bcf_hdr + signature_block_offset, 8268c2ecf20Sopenharmony_ci signature_block_size); 8278c2ecf20Sopenharmony_ci ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, 8288c2ecf20Sopenharmony_ci sizeof(cmd_buf->cmd) + signature_block_size, 8298c2ecf20Sopenharmony_ci &ack, sizeof(ack), I2400M_BM_CMD_RAW); 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci d_fnend(3, dev, "returning %d\n", ret); 8328c2ecf20Sopenharmony_ci return ret; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/** 8378c2ecf20Sopenharmony_ci * i2400m_bootrom_init - Reboots a powered device into boot mode 8388c2ecf20Sopenharmony_ci * 8398c2ecf20Sopenharmony_ci * @i2400m: device descriptor 8408c2ecf20Sopenharmony_ci * @flags: 8418c2ecf20Sopenharmony_ci * I2400M_BRI_SOFT: a reboot barker has been seen 8428c2ecf20Sopenharmony_ci * already, so don't wait for it. 8438c2ecf20Sopenharmony_ci * 8448c2ecf20Sopenharmony_ci * I2400M_BRI_NO_REBOOT: Don't send a reboot command, but wait 8458c2ecf20Sopenharmony_ci * for a reboot barker notification. This is a one shot; if 8468c2ecf20Sopenharmony_ci * the state machine needs to send a reboot command it will. 8478c2ecf20Sopenharmony_ci * 8488c2ecf20Sopenharmony_ci * Returns: 8498c2ecf20Sopenharmony_ci * 8508c2ecf20Sopenharmony_ci * < 0 errno code on error, 0 if ok. 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * Description: 8538c2ecf20Sopenharmony_ci * 8548c2ecf20Sopenharmony_ci * Tries hard enough to put the device in boot-mode. There are two 8558c2ecf20Sopenharmony_ci * main phases to this: 8568c2ecf20Sopenharmony_ci * 8578c2ecf20Sopenharmony_ci * a. (1) send a reboot command and (2) get a reboot barker 8588c2ecf20Sopenharmony_ci * 8598c2ecf20Sopenharmony_ci * b. (1) echo/ack the reboot sending the reboot barker back and (2) 8608c2ecf20Sopenharmony_ci * getting an ack barker in return 8618c2ecf20Sopenharmony_ci * 8628c2ecf20Sopenharmony_ci * We want to skip (a) in some cases [soft]. The state machine is 8638c2ecf20Sopenharmony_ci * horrible, but it is basically: on each phase, send what has to be 8648c2ecf20Sopenharmony_ci * sent (if any), wait for the answer and act on the answer. We might 8658c2ecf20Sopenharmony_ci * have to backtrack and retry, so we keep a max tries counter for 8668c2ecf20Sopenharmony_ci * that. 8678c2ecf20Sopenharmony_ci * 8688c2ecf20Sopenharmony_ci * It sucks because we don't know ahead of time which is going to be 8698c2ecf20Sopenharmony_ci * the reboot barker (the device might send different ones depending 8708c2ecf20Sopenharmony_ci * on its EEPROM config) and once the device reboots and waits for the 8718c2ecf20Sopenharmony_ci * echo/ack reboot barker being sent back, it doesn't understand 8728c2ecf20Sopenharmony_ci * anything else. So we can be left at the point where we don't know 8738c2ecf20Sopenharmony_ci * what to send to it -- cold reset and bus reset seem to have little 8748c2ecf20Sopenharmony_ci * effect. So the function iterates (in this case) through all the 8758c2ecf20Sopenharmony_ci * known barkers and tries them all until an ACK is 8768c2ecf20Sopenharmony_ci * received. Otherwise, it gives up. 8778c2ecf20Sopenharmony_ci * 8788c2ecf20Sopenharmony_ci * If we get a timeout after sending a warm reset, we do it again. 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_ciint i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci int result; 8838c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 8848c2ecf20Sopenharmony_ci struct i2400m_bootrom_header *cmd; 8858c2ecf20Sopenharmony_ci struct i2400m_bootrom_header ack; 8868c2ecf20Sopenharmony_ci int count = i2400m->bus_bm_retries; 8878c2ecf20Sopenharmony_ci int ack_timeout_cnt = 1; 8888c2ecf20Sopenharmony_ci unsigned i; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_barker_db[0].data)); 8918c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER)); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags); 8948c2ecf20Sopenharmony_ci result = -ENOMEM; 8958c2ecf20Sopenharmony_ci cmd = i2400m->bm_cmd_buf; 8968c2ecf20Sopenharmony_ci if (flags & I2400M_BRI_SOFT) 8978c2ecf20Sopenharmony_ci goto do_reboot_ack; 8988c2ecf20Sopenharmony_cido_reboot: 8998c2ecf20Sopenharmony_ci ack_timeout_cnt = 1; 9008c2ecf20Sopenharmony_ci if (--count < 0) 9018c2ecf20Sopenharmony_ci goto error_timeout; 9028c2ecf20Sopenharmony_ci d_printf(4, dev, "device reboot: reboot command [%d # left]\n", 9038c2ecf20Sopenharmony_ci count); 9048c2ecf20Sopenharmony_ci if ((flags & I2400M_BRI_NO_REBOOT) == 0) 9058c2ecf20Sopenharmony_ci i2400m_reset(i2400m, I2400M_RT_WARM); 9068c2ecf20Sopenharmony_ci result = i2400m_bm_cmd(i2400m, NULL, 0, &ack, sizeof(ack), 9078c2ecf20Sopenharmony_ci I2400M_BM_CMD_RAW); 9088c2ecf20Sopenharmony_ci flags &= ~I2400M_BRI_NO_REBOOT; 9098c2ecf20Sopenharmony_ci switch (result) { 9108c2ecf20Sopenharmony_ci case -ERESTARTSYS: 9118c2ecf20Sopenharmony_ci /* 9128c2ecf20Sopenharmony_ci * at this point, i2400m_bm_cmd(), through 9138c2ecf20Sopenharmony_ci * __i2400m_bm_ack_process(), has updated 9148c2ecf20Sopenharmony_ci * i2400m->barker and we are good to go. 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ci d_printf(4, dev, "device reboot: got reboot barker\n"); 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci case -EISCONN: /* we don't know how it got here...but we follow it */ 9198c2ecf20Sopenharmony_ci d_printf(4, dev, "device reboot: got ack barker - whatever\n"); 9208c2ecf20Sopenharmony_ci goto do_reboot; 9218c2ecf20Sopenharmony_ci case -ETIMEDOUT: 9228c2ecf20Sopenharmony_ci /* 9238c2ecf20Sopenharmony_ci * Device has timed out, we might be in boot mode 9248c2ecf20Sopenharmony_ci * already and expecting an ack; if we don't know what 9258c2ecf20Sopenharmony_ci * the barker is, we just send them all. Cold reset 9268c2ecf20Sopenharmony_ci * and bus reset don't work. Beats me. 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_ci if (i2400m->barker != NULL) { 9298c2ecf20Sopenharmony_ci dev_err(dev, "device boot: reboot barker timed out, " 9308c2ecf20Sopenharmony_ci "trying (set) %08x echo/ack\n", 9318c2ecf20Sopenharmony_ci le32_to_cpu(i2400m->barker->data[0])); 9328c2ecf20Sopenharmony_ci goto do_reboot_ack; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci for (i = 0; i < i2400m_barker_db_used; i++) { 9358c2ecf20Sopenharmony_ci struct i2400m_barker_db *barker = &i2400m_barker_db[i]; 9368c2ecf20Sopenharmony_ci memcpy(cmd, barker->data, sizeof(barker->data)); 9378c2ecf20Sopenharmony_ci result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), 9388c2ecf20Sopenharmony_ci &ack, sizeof(ack), 9398c2ecf20Sopenharmony_ci I2400M_BM_CMD_RAW); 9408c2ecf20Sopenharmony_ci if (result == -EISCONN) { 9418c2ecf20Sopenharmony_ci dev_warn(dev, "device boot: got ack barker " 9428c2ecf20Sopenharmony_ci "after sending echo/ack barker " 9438c2ecf20Sopenharmony_ci "#%d/%08x; rebooting j.i.c.\n", 9448c2ecf20Sopenharmony_ci i, le32_to_cpu(barker->data[0])); 9458c2ecf20Sopenharmony_ci flags &= ~I2400M_BRI_NO_REBOOT; 9468c2ecf20Sopenharmony_ci goto do_reboot; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci dev_err(dev, "device boot: tried all the echo/acks, could " 9508c2ecf20Sopenharmony_ci "not get device to respond; giving up"); 9518c2ecf20Sopenharmony_ci result = -ESHUTDOWN; 9528c2ecf20Sopenharmony_ci case -EPROTO: 9538c2ecf20Sopenharmony_ci case -ESHUTDOWN: /* dev is gone */ 9548c2ecf20Sopenharmony_ci case -EINTR: /* user cancelled */ 9558c2ecf20Sopenharmony_ci goto error_dev_gone; 9568c2ecf20Sopenharmony_ci default: 9578c2ecf20Sopenharmony_ci dev_err(dev, "device reboot: error %d while waiting " 9588c2ecf20Sopenharmony_ci "for reboot barker - rebooting\n", result); 9598c2ecf20Sopenharmony_ci d_dump(1, dev, &ack, result); 9608c2ecf20Sopenharmony_ci goto do_reboot; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci /* At this point we ack back with 4 REBOOT barkers and expect 9638c2ecf20Sopenharmony_ci * 4 ACK barkers. This is ugly, as we send a raw command -- 9648c2ecf20Sopenharmony_ci * hence the cast. _bm_cmd() will catch the reboot ack 9658c2ecf20Sopenharmony_ci * notification and report it as -EISCONN. */ 9668c2ecf20Sopenharmony_cido_reboot_ack: 9678c2ecf20Sopenharmony_ci d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count); 9688c2ecf20Sopenharmony_ci memcpy(cmd, i2400m->barker->data, sizeof(i2400m->barker->data)); 9698c2ecf20Sopenharmony_ci result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), 9708c2ecf20Sopenharmony_ci &ack, sizeof(ack), I2400M_BM_CMD_RAW); 9718c2ecf20Sopenharmony_ci switch (result) { 9728c2ecf20Sopenharmony_ci case -ERESTARTSYS: 9738c2ecf20Sopenharmony_ci d_printf(4, dev, "reboot ack: got reboot barker - retrying\n"); 9748c2ecf20Sopenharmony_ci if (--count < 0) 9758c2ecf20Sopenharmony_ci goto error_timeout; 9768c2ecf20Sopenharmony_ci goto do_reboot_ack; 9778c2ecf20Sopenharmony_ci case -EISCONN: 9788c2ecf20Sopenharmony_ci d_printf(4, dev, "reboot ack: got ack barker - good\n"); 9798c2ecf20Sopenharmony_ci break; 9808c2ecf20Sopenharmony_ci case -ETIMEDOUT: /* no response, maybe it is the other type? */ 9818c2ecf20Sopenharmony_ci if (ack_timeout_cnt-- < 0) { 9828c2ecf20Sopenharmony_ci d_printf(4, dev, "reboot ack timedout: retrying\n"); 9838c2ecf20Sopenharmony_ci goto do_reboot_ack; 9848c2ecf20Sopenharmony_ci } else { 9858c2ecf20Sopenharmony_ci dev_err(dev, "reboot ack timedout too long: " 9868c2ecf20Sopenharmony_ci "trying reboot\n"); 9878c2ecf20Sopenharmony_ci goto do_reboot; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci case -EPROTO: 9918c2ecf20Sopenharmony_ci case -ESHUTDOWN: /* dev is gone */ 9928c2ecf20Sopenharmony_ci goto error_dev_gone; 9938c2ecf20Sopenharmony_ci default: 9948c2ecf20Sopenharmony_ci dev_err(dev, "device reboot ack: error %d while waiting for " 9958c2ecf20Sopenharmony_ci "reboot ack barker - rebooting\n", result); 9968c2ecf20Sopenharmony_ci goto do_reboot; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci d_printf(2, dev, "device reboot ack: got ack barker - boot done\n"); 9998c2ecf20Sopenharmony_ci result = 0; 10008c2ecf20Sopenharmony_ciexit_timeout: 10018c2ecf20Sopenharmony_cierror_dev_gone: 10028c2ecf20Sopenharmony_ci d_fnend(4, dev, "(i2400m %p flags 0x%08x) = %d\n", 10038c2ecf20Sopenharmony_ci i2400m, flags, result); 10048c2ecf20Sopenharmony_ci return result; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cierror_timeout: 10078c2ecf20Sopenharmony_ci dev_err(dev, "Timed out waiting for reboot ack\n"); 10088c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 10098c2ecf20Sopenharmony_ci goto exit_timeout; 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci/* 10148c2ecf20Sopenharmony_ci * Read the MAC addr 10158c2ecf20Sopenharmony_ci * 10168c2ecf20Sopenharmony_ci * The position this function reads is fixed in device memory and 10178c2ecf20Sopenharmony_ci * always available, even without firmware. 10188c2ecf20Sopenharmony_ci * 10198c2ecf20Sopenharmony_ci * Note we specify we want to read only six bytes, but provide space 10208c2ecf20Sopenharmony_ci * for 16, as we always get it rounded up. 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ciint i2400m_read_mac_addr(struct i2400m *i2400m) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci int result; 10258c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 10268c2ecf20Sopenharmony_ci struct net_device *net_dev = i2400m->wimax_dev.net_dev; 10278c2ecf20Sopenharmony_ci struct i2400m_bootrom_header *cmd; 10288c2ecf20Sopenharmony_ci struct { 10298c2ecf20Sopenharmony_ci struct i2400m_bootrom_header ack; 10308c2ecf20Sopenharmony_ci u8 ack_pl[16]; 10318c2ecf20Sopenharmony_ci } __packed ack_buf; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci d_fnstart(5, dev, "(i2400m %p)\n", i2400m); 10348c2ecf20Sopenharmony_ci cmd = i2400m->bm_cmd_buf; 10358c2ecf20Sopenharmony_ci cmd->command = i2400m_brh_command(I2400M_BRH_READ, 0, 1); 10368c2ecf20Sopenharmony_ci cmd->target_addr = cpu_to_le32(0x00203fe8); 10378c2ecf20Sopenharmony_ci cmd->data_size = cpu_to_le32(6); 10388c2ecf20Sopenharmony_ci result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), 10398c2ecf20Sopenharmony_ci &ack_buf.ack, sizeof(ack_buf), 0); 10408c2ecf20Sopenharmony_ci if (result < 0) { 10418c2ecf20Sopenharmony_ci dev_err(dev, "BM: read mac addr failed: %d\n", result); 10428c2ecf20Sopenharmony_ci goto error_read_mac; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci d_printf(2, dev, "mac addr is %pM\n", ack_buf.ack_pl); 10458c2ecf20Sopenharmony_ci if (i2400m->bus_bm_mac_addr_impaired == 1) { 10468c2ecf20Sopenharmony_ci ack_buf.ack_pl[0] = 0x00; 10478c2ecf20Sopenharmony_ci ack_buf.ack_pl[1] = 0x16; 10488c2ecf20Sopenharmony_ci ack_buf.ack_pl[2] = 0xd3; 10498c2ecf20Sopenharmony_ci get_random_bytes(&ack_buf.ack_pl[3], 3); 10508c2ecf20Sopenharmony_ci dev_err(dev, "BM is MAC addr impaired, faking MAC addr to " 10518c2ecf20Sopenharmony_ci "mac addr is %pM\n", ack_buf.ack_pl); 10528c2ecf20Sopenharmony_ci result = 0; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci net_dev->addr_len = ETH_ALEN; 10558c2ecf20Sopenharmony_ci memcpy(net_dev->dev_addr, ack_buf.ack_pl, ETH_ALEN); 10568c2ecf20Sopenharmony_cierror_read_mac: 10578c2ecf20Sopenharmony_ci d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, result); 10588c2ecf20Sopenharmony_ci return result; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci/* 10638c2ecf20Sopenharmony_ci * Initialize a non signed boot 10648c2ecf20Sopenharmony_ci * 10658c2ecf20Sopenharmony_ci * This implies sending some magic values to the device's memory. Note 10668c2ecf20Sopenharmony_ci * we convert the values to little endian in the same array 10678c2ecf20Sopenharmony_ci * declaration. 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_cistatic 10708c2ecf20Sopenharmony_ciint i2400m_dnload_init_nonsigned(struct i2400m *i2400m) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci unsigned i = 0; 10738c2ecf20Sopenharmony_ci int ret = 0; 10748c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 10758c2ecf20Sopenharmony_ci d_fnstart(5, dev, "(i2400m %p)\n", i2400m); 10768c2ecf20Sopenharmony_ci if (i2400m->bus_bm_pokes_table) { 10778c2ecf20Sopenharmony_ci while (i2400m->bus_bm_pokes_table[i].address) { 10788c2ecf20Sopenharmony_ci ret = i2400m_download_chunk( 10798c2ecf20Sopenharmony_ci i2400m, 10808c2ecf20Sopenharmony_ci &i2400m->bus_bm_pokes_table[i].data, 10818c2ecf20Sopenharmony_ci sizeof(i2400m->bus_bm_pokes_table[i].data), 10828c2ecf20Sopenharmony_ci i2400m->bus_bm_pokes_table[i].address, 1, 1); 10838c2ecf20Sopenharmony_ci if (ret < 0) 10848c2ecf20Sopenharmony_ci break; 10858c2ecf20Sopenharmony_ci i++; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); 10898c2ecf20Sopenharmony_ci return ret; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci/* 10948c2ecf20Sopenharmony_ci * Initialize the signed boot process 10958c2ecf20Sopenharmony_ci * 10968c2ecf20Sopenharmony_ci * @i2400m: device descriptor 10978c2ecf20Sopenharmony_ci * 10988c2ecf20Sopenharmony_ci * @bcf_hdr: pointer to the firmware header; assumes it is fully in 10998c2ecf20Sopenharmony_ci * memory (it has gone through basic validation). 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * Returns: 0 if ok, < 0 errno code on error, -ERESTARTSYS if the hw 11028c2ecf20Sopenharmony_ci * rebooted. 11038c2ecf20Sopenharmony_ci * 11048c2ecf20Sopenharmony_ci * This writes the firmware BCF header to the device using the 11058c2ecf20Sopenharmony_ci * HASH_PAYLOAD_ONLY command. 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_cistatic 11088c2ecf20Sopenharmony_ciint i2400m_dnload_init_signed(struct i2400m *i2400m, 11098c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf_hdr) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci int ret; 11128c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 11138c2ecf20Sopenharmony_ci struct { 11148c2ecf20Sopenharmony_ci struct i2400m_bootrom_header cmd; 11158c2ecf20Sopenharmony_ci struct i2400m_bcf_hdr cmd_pl; 11168c2ecf20Sopenharmony_ci } __packed *cmd_buf; 11178c2ecf20Sopenharmony_ci struct i2400m_bootrom_header ack; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci d_fnstart(5, dev, "(i2400m %p bcf_hdr %p)\n", i2400m, bcf_hdr); 11208c2ecf20Sopenharmony_ci cmd_buf = i2400m->bm_cmd_buf; 11218c2ecf20Sopenharmony_ci cmd_buf->cmd.command = 11228c2ecf20Sopenharmony_ci i2400m_brh_command(I2400M_BRH_HASH_PAYLOAD_ONLY, 0, 0); 11238c2ecf20Sopenharmony_ci cmd_buf->cmd.target_addr = 0; 11248c2ecf20Sopenharmony_ci cmd_buf->cmd.data_size = cpu_to_le32(sizeof(cmd_buf->cmd_pl)); 11258c2ecf20Sopenharmony_ci memcpy(&cmd_buf->cmd_pl, bcf_hdr, sizeof(*bcf_hdr)); 11268c2ecf20Sopenharmony_ci ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, sizeof(*cmd_buf), 11278c2ecf20Sopenharmony_ci &ack, sizeof(ack), 0); 11288c2ecf20Sopenharmony_ci if (ret >= 0) 11298c2ecf20Sopenharmony_ci ret = 0; 11308c2ecf20Sopenharmony_ci d_fnend(5, dev, "(i2400m %p bcf_hdr %p) = %d\n", i2400m, bcf_hdr, ret); 11318c2ecf20Sopenharmony_ci return ret; 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/* 11368c2ecf20Sopenharmony_ci * Initialize the firmware download at the device size 11378c2ecf20Sopenharmony_ci * 11388c2ecf20Sopenharmony_ci * Multiplex to the one that matters based on the device's mode 11398c2ecf20Sopenharmony_ci * (signed or non-signed). 11408c2ecf20Sopenharmony_ci */ 11418c2ecf20Sopenharmony_cistatic 11428c2ecf20Sopenharmony_ciint i2400m_dnload_init(struct i2400m *i2400m, 11438c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf_hdr) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci int result; 11468c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (i2400m_boot_is_signed(i2400m)) { 11498c2ecf20Sopenharmony_ci d_printf(1, dev, "signed boot\n"); 11508c2ecf20Sopenharmony_ci result = i2400m_dnload_init_signed(i2400m, bcf_hdr); 11518c2ecf20Sopenharmony_ci if (result == -ERESTARTSYS) 11528c2ecf20Sopenharmony_ci return result; 11538c2ecf20Sopenharmony_ci if (result < 0) 11548c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s: signed boot download " 11558c2ecf20Sopenharmony_ci "initialization failed: %d\n", 11568c2ecf20Sopenharmony_ci i2400m->fw_name, result); 11578c2ecf20Sopenharmony_ci } else { 11588c2ecf20Sopenharmony_ci /* non-signed boot process without pokes */ 11598c2ecf20Sopenharmony_ci d_printf(1, dev, "non-signed boot\n"); 11608c2ecf20Sopenharmony_ci result = i2400m_dnload_init_nonsigned(i2400m); 11618c2ecf20Sopenharmony_ci if (result == -ERESTARTSYS) 11628c2ecf20Sopenharmony_ci return result; 11638c2ecf20Sopenharmony_ci if (result < 0) 11648c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s: non-signed download " 11658c2ecf20Sopenharmony_ci "initialization failed: %d\n", 11668c2ecf20Sopenharmony_ci i2400m->fw_name, result); 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci return result; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci/* 11738c2ecf20Sopenharmony_ci * Run consistency tests on the firmware file and load up headers 11748c2ecf20Sopenharmony_ci * 11758c2ecf20Sopenharmony_ci * Check for the firmware being made for the i2400m device, 11768c2ecf20Sopenharmony_ci * etc...These checks are mostly informative, as the device will make 11778c2ecf20Sopenharmony_ci * them too; but the driver's response is more informative on what 11788c2ecf20Sopenharmony_ci * went wrong. 11798c2ecf20Sopenharmony_ci * 11808c2ecf20Sopenharmony_ci * This will also look at all the headers present on the firmware 11818c2ecf20Sopenharmony_ci * file, and update i2400m->fw_bcf_hdr to point to them. 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_cistatic 11848c2ecf20Sopenharmony_ciint i2400m_fw_hdr_check(struct i2400m *i2400m, 11858c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf_hdr, 11868c2ecf20Sopenharmony_ci size_t index, size_t offset) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci unsigned module_type, header_len, major_version, minor_version, 11918c2ecf20Sopenharmony_ci module_id, module_vendor, date, size; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci module_type = le32_to_cpu(bcf_hdr->module_type); 11948c2ecf20Sopenharmony_ci header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); 11958c2ecf20Sopenharmony_ci major_version = (le32_to_cpu(bcf_hdr->header_version) & 0xffff0000) 11968c2ecf20Sopenharmony_ci >> 16; 11978c2ecf20Sopenharmony_ci minor_version = le32_to_cpu(bcf_hdr->header_version) & 0x0000ffff; 11988c2ecf20Sopenharmony_ci module_id = le32_to_cpu(bcf_hdr->module_id); 11998c2ecf20Sopenharmony_ci module_vendor = le32_to_cpu(bcf_hdr->module_vendor); 12008c2ecf20Sopenharmony_ci date = le32_to_cpu(bcf_hdr->date); 12018c2ecf20Sopenharmony_ci size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci d_printf(1, dev, "firmware %s #%zd@%08zx: BCF header " 12048c2ecf20Sopenharmony_ci "type:vendor:id 0x%x:%x:%x v%u.%u (%u/%u B) built %08x\n", 12058c2ecf20Sopenharmony_ci i2400m->fw_name, index, offset, 12068c2ecf20Sopenharmony_ci module_type, module_vendor, module_id, 12078c2ecf20Sopenharmony_ci major_version, minor_version, header_len, size, date); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* Hard errors */ 12108c2ecf20Sopenharmony_ci if (major_version != 1) { 12118c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s #%zd@%08zx: major header version " 12128c2ecf20Sopenharmony_ci "v%u.%u not supported\n", 12138c2ecf20Sopenharmony_ci i2400m->fw_name, index, offset, 12148c2ecf20Sopenharmony_ci major_version, minor_version); 12158c2ecf20Sopenharmony_ci return -EBADF; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (module_type != 6) { /* built for the right hardware? */ 12198c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s #%zd@%08zx: unexpected module " 12208c2ecf20Sopenharmony_ci "type 0x%x; aborting\n", 12218c2ecf20Sopenharmony_ci i2400m->fw_name, index, offset, 12228c2ecf20Sopenharmony_ci module_type); 12238c2ecf20Sopenharmony_ci return -EBADF; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (module_vendor != 0x8086) { 12278c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s #%zd@%08zx: unexpected module " 12288c2ecf20Sopenharmony_ci "vendor 0x%x; aborting\n", 12298c2ecf20Sopenharmony_ci i2400m->fw_name, index, offset, module_vendor); 12308c2ecf20Sopenharmony_ci return -EBADF; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if (date < 0x20080300) 12348c2ecf20Sopenharmony_ci dev_warn(dev, "firmware %s #%zd@%08zx: build date %08x " 12358c2ecf20Sopenharmony_ci "too old; unsupported\n", 12368c2ecf20Sopenharmony_ci i2400m->fw_name, index, offset, date); 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci/* 12428c2ecf20Sopenharmony_ci * Run consistency tests on the firmware file and load up headers 12438c2ecf20Sopenharmony_ci * 12448c2ecf20Sopenharmony_ci * Check for the firmware being made for the i2400m device, 12458c2ecf20Sopenharmony_ci * etc...These checks are mostly informative, as the device will make 12468c2ecf20Sopenharmony_ci * them too; but the driver's response is more informative on what 12478c2ecf20Sopenharmony_ci * went wrong. 12488c2ecf20Sopenharmony_ci * 12498c2ecf20Sopenharmony_ci * This will also look at all the headers present on the firmware 12508c2ecf20Sopenharmony_ci * file, and update i2400m->fw_hdrs to point to them. 12518c2ecf20Sopenharmony_ci */ 12528c2ecf20Sopenharmony_cistatic 12538c2ecf20Sopenharmony_ciint i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci int result; 12568c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 12578c2ecf20Sopenharmony_ci size_t headers = 0; 12588c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf_hdr; 12598c2ecf20Sopenharmony_ci const void *itr, *next, *top; 12608c2ecf20Sopenharmony_ci size_t slots = 0, used_slots = 0; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci for (itr = bcf, top = itr + bcf_size; 12638c2ecf20Sopenharmony_ci itr < top; 12648c2ecf20Sopenharmony_ci headers++, itr = next) { 12658c2ecf20Sopenharmony_ci size_t leftover, offset, header_len, size; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci leftover = top - itr; 12688c2ecf20Sopenharmony_ci offset = itr - bcf; 12698c2ecf20Sopenharmony_ci if (leftover <= sizeof(*bcf_hdr)) { 12708c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s: %zu B left at @%zx, " 12718c2ecf20Sopenharmony_ci "not enough for BCF header\n", 12728c2ecf20Sopenharmony_ci i2400m->fw_name, leftover, offset); 12738c2ecf20Sopenharmony_ci break; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci bcf_hdr = itr; 12768c2ecf20Sopenharmony_ci /* Only the first header is supposed to be followed by 12778c2ecf20Sopenharmony_ci * payload */ 12788c2ecf20Sopenharmony_ci header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); 12798c2ecf20Sopenharmony_ci size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); 12808c2ecf20Sopenharmony_ci if (headers == 0) 12818c2ecf20Sopenharmony_ci next = itr + size; 12828c2ecf20Sopenharmony_ci else 12838c2ecf20Sopenharmony_ci next = itr + header_len; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci result = i2400m_fw_hdr_check(i2400m, bcf_hdr, headers, offset); 12868c2ecf20Sopenharmony_ci if (result < 0) 12878c2ecf20Sopenharmony_ci continue; 12888c2ecf20Sopenharmony_ci if (used_slots + 1 >= slots) { 12898c2ecf20Sopenharmony_ci /* +1 -> we need to account for the one we'll 12908c2ecf20Sopenharmony_ci * occupy and at least an extra one for 12918c2ecf20Sopenharmony_ci * always being NULL */ 12928c2ecf20Sopenharmony_ci result = i2400m_zrealloc_2x( 12938c2ecf20Sopenharmony_ci (void **) &i2400m->fw_hdrs, &slots, 12948c2ecf20Sopenharmony_ci sizeof(i2400m->fw_hdrs[0]), 12958c2ecf20Sopenharmony_ci GFP_KERNEL); 12968c2ecf20Sopenharmony_ci if (result < 0) 12978c2ecf20Sopenharmony_ci goto error_zrealloc; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci i2400m->fw_hdrs[used_slots] = bcf_hdr; 13008c2ecf20Sopenharmony_ci used_slots++; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci if (headers == 0) { 13038c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s: no usable headers found\n", 13048c2ecf20Sopenharmony_ci i2400m->fw_name); 13058c2ecf20Sopenharmony_ci result = -EBADF; 13068c2ecf20Sopenharmony_ci } else 13078c2ecf20Sopenharmony_ci result = 0; 13088c2ecf20Sopenharmony_cierror_zrealloc: 13098c2ecf20Sopenharmony_ci return result; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci/* 13148c2ecf20Sopenharmony_ci * Match a barker to a BCF header module ID 13158c2ecf20Sopenharmony_ci * 13168c2ecf20Sopenharmony_ci * The device sends a barker which tells the firmware loader which 13178c2ecf20Sopenharmony_ci * header in the BCF file has to be used. This does the matching. 13188c2ecf20Sopenharmony_ci */ 13198c2ecf20Sopenharmony_cistatic 13208c2ecf20Sopenharmony_ciunsigned i2400m_bcf_hdr_match(struct i2400m *i2400m, 13218c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf_hdr) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci u32 barker = le32_to_cpu(i2400m->barker->data[0]) 13248c2ecf20Sopenharmony_ci & 0x7fffffff; 13258c2ecf20Sopenharmony_ci u32 module_id = le32_to_cpu(bcf_hdr->module_id) 13268c2ecf20Sopenharmony_ci & 0x7fffffff; /* high bit used for something else */ 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* special case for 5x50 */ 13298c2ecf20Sopenharmony_ci if (barker == I2400M_SBOOT_BARKER && module_id == 0) 13308c2ecf20Sopenharmony_ci return 1; 13318c2ecf20Sopenharmony_ci if (module_id == barker) 13328c2ecf20Sopenharmony_ci return 1; 13338c2ecf20Sopenharmony_ci return 0; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic 13378c2ecf20Sopenharmony_ciconst struct i2400m_bcf_hdr *i2400m_bcf_hdr_find(struct i2400m *i2400m) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 13408c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr **bcf_itr, *bcf_hdr; 13418c2ecf20Sopenharmony_ci unsigned i = 0; 13428c2ecf20Sopenharmony_ci u32 barker = le32_to_cpu(i2400m->barker->data[0]); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci d_printf(2, dev, "finding BCF header for barker %08x\n", barker); 13458c2ecf20Sopenharmony_ci if (barker == I2400M_NBOOT_BARKER) { 13468c2ecf20Sopenharmony_ci bcf_hdr = i2400m->fw_hdrs[0]; 13478c2ecf20Sopenharmony_ci d_printf(1, dev, "using BCF header #%u/%08x for non-signed " 13488c2ecf20Sopenharmony_ci "barker\n", 0, le32_to_cpu(bcf_hdr->module_id)); 13498c2ecf20Sopenharmony_ci return bcf_hdr; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci for (bcf_itr = i2400m->fw_hdrs; *bcf_itr != NULL; bcf_itr++, i++) { 13528c2ecf20Sopenharmony_ci bcf_hdr = *bcf_itr; 13538c2ecf20Sopenharmony_ci if (i2400m_bcf_hdr_match(i2400m, bcf_hdr)) { 13548c2ecf20Sopenharmony_ci d_printf(1, dev, "hit on BCF hdr #%u/%08x\n", 13558c2ecf20Sopenharmony_ci i, le32_to_cpu(bcf_hdr->module_id)); 13568c2ecf20Sopenharmony_ci return bcf_hdr; 13578c2ecf20Sopenharmony_ci } else 13588c2ecf20Sopenharmony_ci d_printf(1, dev, "miss on BCF hdr #%u/%08x\n", 13598c2ecf20Sopenharmony_ci i, le32_to_cpu(bcf_hdr->module_id)); 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci dev_err(dev, "cannot find a matching BCF header for barker %08x\n", 13628c2ecf20Sopenharmony_ci barker); 13638c2ecf20Sopenharmony_ci return NULL; 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci/* 13688c2ecf20Sopenharmony_ci * Download the firmware to the device 13698c2ecf20Sopenharmony_ci * 13708c2ecf20Sopenharmony_ci * @i2400m: device descriptor 13718c2ecf20Sopenharmony_ci * @bcf: pointer to loaded (and minimally verified for consistency) 13728c2ecf20Sopenharmony_ci * firmware 13738c2ecf20Sopenharmony_ci * @bcf_size: size of the @bcf buffer (header plus payloads) 13748c2ecf20Sopenharmony_ci * 13758c2ecf20Sopenharmony_ci * The process for doing this is described in this file's header. 13768c2ecf20Sopenharmony_ci * 13778c2ecf20Sopenharmony_ci * Note we only reinitialize boot-mode if the flags say so. Some hw 13788c2ecf20Sopenharmony_ci * iterations need it, some don't. In any case, if we loop, we always 13798c2ecf20Sopenharmony_ci * need to reinitialize the boot room, hence the flags modification. 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_cistatic 13828c2ecf20Sopenharmony_ciint i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf, 13838c2ecf20Sopenharmony_ci size_t fw_size, enum i2400m_bri flags) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci int ret = 0; 13868c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 13878c2ecf20Sopenharmony_ci int count = i2400m->bus_bm_retries; 13888c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf_hdr; 13898c2ecf20Sopenharmony_ci size_t bcf_size; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci d_fnstart(5, dev, "(i2400m %p bcf %p fw size %zu)\n", 13928c2ecf20Sopenharmony_ci i2400m, bcf, fw_size); 13938c2ecf20Sopenharmony_ci i2400m->boot_mode = 1; 13948c2ecf20Sopenharmony_ci wmb(); /* Make sure other readers see it */ 13958c2ecf20Sopenharmony_cihw_reboot: 13968c2ecf20Sopenharmony_ci if (count-- == 0) { 13978c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 13988c2ecf20Sopenharmony_ci dev_err(dev, "device rebooted too many times, aborting\n"); 13998c2ecf20Sopenharmony_ci goto error_too_many_reboots; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci if (flags & I2400M_BRI_MAC_REINIT) { 14028c2ecf20Sopenharmony_ci ret = i2400m_bootrom_init(i2400m, flags); 14038c2ecf20Sopenharmony_ci if (ret < 0) { 14048c2ecf20Sopenharmony_ci dev_err(dev, "bootrom init failed: %d\n", ret); 14058c2ecf20Sopenharmony_ci goto error_bootrom_init; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci flags |= I2400M_BRI_MAC_REINIT; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* 14118c2ecf20Sopenharmony_ci * Initialize the download, push the bytes to the device and 14128c2ecf20Sopenharmony_ci * then jump to the new firmware. Note @ret is passed with the 14138c2ecf20Sopenharmony_ci * offset of the jump instruction to _dnload_finalize() 14148c2ecf20Sopenharmony_ci * 14158c2ecf20Sopenharmony_ci * Note we need to use the BCF header in the firmware image 14168c2ecf20Sopenharmony_ci * that matches the barker that the device sent when it 14178c2ecf20Sopenharmony_ci * rebooted, so it has to be passed along. 14188c2ecf20Sopenharmony_ci */ 14198c2ecf20Sopenharmony_ci ret = -EBADF; 14208c2ecf20Sopenharmony_ci bcf_hdr = i2400m_bcf_hdr_find(i2400m); 14218c2ecf20Sopenharmony_ci if (bcf_hdr == NULL) 14228c2ecf20Sopenharmony_ci goto error_bcf_hdr_find; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci ret = i2400m_dnload_init(i2400m, bcf_hdr); 14258c2ecf20Sopenharmony_ci if (ret == -ERESTARTSYS) 14268c2ecf20Sopenharmony_ci goto error_dev_rebooted; 14278c2ecf20Sopenharmony_ci if (ret < 0) 14288c2ecf20Sopenharmony_ci goto error_dnload_init; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci /* 14318c2ecf20Sopenharmony_ci * bcf_size refers to one header size plus the fw sections size 14328c2ecf20Sopenharmony_ci * indicated by the header,ie. if there are other extended headers 14338c2ecf20Sopenharmony_ci * at the tail, they are not counted 14348c2ecf20Sopenharmony_ci */ 14358c2ecf20Sopenharmony_ci bcf_size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); 14368c2ecf20Sopenharmony_ci ret = i2400m_dnload_bcf(i2400m, bcf, bcf_size); 14378c2ecf20Sopenharmony_ci if (ret == -ERESTARTSYS) 14388c2ecf20Sopenharmony_ci goto error_dev_rebooted; 14398c2ecf20Sopenharmony_ci if (ret < 0) { 14408c2ecf20Sopenharmony_ci dev_err(dev, "fw %s: download failed: %d\n", 14418c2ecf20Sopenharmony_ci i2400m->fw_name, ret); 14428c2ecf20Sopenharmony_ci goto error_dnload_bcf; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci ret = i2400m_dnload_finalize(i2400m, bcf_hdr, bcf, ret); 14468c2ecf20Sopenharmony_ci if (ret == -ERESTARTSYS) 14478c2ecf20Sopenharmony_ci goto error_dev_rebooted; 14488c2ecf20Sopenharmony_ci if (ret < 0) { 14498c2ecf20Sopenharmony_ci dev_err(dev, "fw %s: " 14508c2ecf20Sopenharmony_ci "download finalization failed: %d\n", 14518c2ecf20Sopenharmony_ci i2400m->fw_name, ret); 14528c2ecf20Sopenharmony_ci goto error_dnload_finalize; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci d_printf(2, dev, "fw %s successfully uploaded\n", 14568c2ecf20Sopenharmony_ci i2400m->fw_name); 14578c2ecf20Sopenharmony_ci i2400m->boot_mode = 0; 14588c2ecf20Sopenharmony_ci wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */ 14598c2ecf20Sopenharmony_cierror_dnload_finalize: 14608c2ecf20Sopenharmony_cierror_dnload_bcf: 14618c2ecf20Sopenharmony_cierror_dnload_init: 14628c2ecf20Sopenharmony_cierror_bcf_hdr_find: 14638c2ecf20Sopenharmony_cierror_bootrom_init: 14648c2ecf20Sopenharmony_cierror_too_many_reboots: 14658c2ecf20Sopenharmony_ci d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n", 14668c2ecf20Sopenharmony_ci i2400m, bcf, fw_size, ret); 14678c2ecf20Sopenharmony_ci return ret; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cierror_dev_rebooted: 14708c2ecf20Sopenharmony_ci dev_err(dev, "device rebooted, %d tries left\n", count); 14718c2ecf20Sopenharmony_ci /* we got the notification already, no need to wait for it again */ 14728c2ecf20Sopenharmony_ci flags |= I2400M_BRI_SOFT; 14738c2ecf20Sopenharmony_ci goto hw_reboot; 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_cistatic 14778c2ecf20Sopenharmony_ciint i2400m_fw_bootstrap(struct i2400m *i2400m, const struct firmware *fw, 14788c2ecf20Sopenharmony_ci enum i2400m_bri flags) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci int ret; 14818c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 14828c2ecf20Sopenharmony_ci const struct i2400m_bcf_hdr *bcf; /* Firmware data */ 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci d_fnstart(5, dev, "(i2400m %p)\n", i2400m); 14858c2ecf20Sopenharmony_ci bcf = (void *) fw->data; 14868c2ecf20Sopenharmony_ci ret = i2400m_fw_check(i2400m, bcf, fw->size); 14878c2ecf20Sopenharmony_ci if (ret >= 0) 14888c2ecf20Sopenharmony_ci ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); 14898c2ecf20Sopenharmony_ci if (ret < 0) 14908c2ecf20Sopenharmony_ci dev_err(dev, "%s: cannot use: %d, skipping\n", 14918c2ecf20Sopenharmony_ci i2400m->fw_name, ret); 14928c2ecf20Sopenharmony_ci kfree(i2400m->fw_hdrs); 14938c2ecf20Sopenharmony_ci i2400m->fw_hdrs = NULL; 14948c2ecf20Sopenharmony_ci d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); 14958c2ecf20Sopenharmony_ci return ret; 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci/* Refcounted container for firmware data */ 15008c2ecf20Sopenharmony_cistruct i2400m_fw { 15018c2ecf20Sopenharmony_ci struct kref kref; 15028c2ecf20Sopenharmony_ci const struct firmware *fw; 15038c2ecf20Sopenharmony_ci}; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_cistatic 15078c2ecf20Sopenharmony_civoid i2400m_fw_destroy(struct kref *kref) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci struct i2400m_fw *i2400m_fw = 15108c2ecf20Sopenharmony_ci container_of(kref, struct i2400m_fw, kref); 15118c2ecf20Sopenharmony_ci release_firmware(i2400m_fw->fw); 15128c2ecf20Sopenharmony_ci kfree(i2400m_fw); 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic 15178c2ecf20Sopenharmony_cistruct i2400m_fw *i2400m_fw_get(struct i2400m_fw *i2400m_fw) 15188c2ecf20Sopenharmony_ci{ 15198c2ecf20Sopenharmony_ci if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) 15208c2ecf20Sopenharmony_ci kref_get(&i2400m_fw->kref); 15218c2ecf20Sopenharmony_ci return i2400m_fw; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_cistatic 15268c2ecf20Sopenharmony_civoid i2400m_fw_put(struct i2400m_fw *i2400m_fw) 15278c2ecf20Sopenharmony_ci{ 15288c2ecf20Sopenharmony_ci kref_put(&i2400m_fw->kref, i2400m_fw_destroy); 15298c2ecf20Sopenharmony_ci} 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci/** 15338c2ecf20Sopenharmony_ci * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware 15348c2ecf20Sopenharmony_ci * 15358c2ecf20Sopenharmony_ci * @i2400m: device descriptor 15368c2ecf20Sopenharmony_ci * 15378c2ecf20Sopenharmony_ci * Returns: >= 0 if ok, < 0 errno code on error. 15388c2ecf20Sopenharmony_ci * 15398c2ecf20Sopenharmony_ci * This sets up the firmware upload environment, loads the firmware 15408c2ecf20Sopenharmony_ci * file from disk, verifies and then calls the firmware upload process 15418c2ecf20Sopenharmony_ci * per se. 15428c2ecf20Sopenharmony_ci * 15438c2ecf20Sopenharmony_ci * Can be called either from probe, or after a warm reset. Can not be 15448c2ecf20Sopenharmony_ci * called from within an interrupt. All the flow in this code is 15458c2ecf20Sopenharmony_ci * single-threade; all I/Os are synchronous. 15468c2ecf20Sopenharmony_ci */ 15478c2ecf20Sopenharmony_ciint i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) 15488c2ecf20Sopenharmony_ci{ 15498c2ecf20Sopenharmony_ci int ret, itr; 15508c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 15518c2ecf20Sopenharmony_ci struct i2400m_fw *i2400m_fw; 15528c2ecf20Sopenharmony_ci const struct firmware *fw; 15538c2ecf20Sopenharmony_ci const char *fw_name; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci d_fnstart(5, dev, "(i2400m %p)\n", i2400m); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci ret = -ENODEV; 15588c2ecf20Sopenharmony_ci spin_lock(&i2400m->rx_lock); 15598c2ecf20Sopenharmony_ci i2400m_fw = i2400m_fw_get(i2400m->fw_cached); 15608c2ecf20Sopenharmony_ci spin_unlock(&i2400m->rx_lock); 15618c2ecf20Sopenharmony_ci if (i2400m_fw == (void *) ~0) { 15628c2ecf20Sopenharmony_ci dev_err(dev, "can't load firmware now!"); 15638c2ecf20Sopenharmony_ci goto out; 15648c2ecf20Sopenharmony_ci } else if (i2400m_fw != NULL) { 15658c2ecf20Sopenharmony_ci dev_info(dev, "firmware %s: loading from cache\n", 15668c2ecf20Sopenharmony_ci i2400m->fw_name); 15678c2ecf20Sopenharmony_ci ret = i2400m_fw_bootstrap(i2400m, i2400m_fw->fw, flags); 15688c2ecf20Sopenharmony_ci i2400m_fw_put(i2400m_fw); 15698c2ecf20Sopenharmony_ci goto out; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci /* Load firmware files to memory. */ 15738c2ecf20Sopenharmony_ci for (itr = 0, ret = -ENOENT; ; itr++) { 15748c2ecf20Sopenharmony_ci fw_name = i2400m->bus_fw_names[itr]; 15758c2ecf20Sopenharmony_ci if (fw_name == NULL) { 15768c2ecf20Sopenharmony_ci dev_err(dev, "Could not find a usable firmware image\n"); 15778c2ecf20Sopenharmony_ci break; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci d_printf(1, dev, "trying firmware %s (%d)\n", fw_name, itr); 15808c2ecf20Sopenharmony_ci ret = request_firmware(&fw, fw_name, dev); 15818c2ecf20Sopenharmony_ci if (ret < 0) { 15828c2ecf20Sopenharmony_ci dev_err(dev, "fw %s: cannot load file: %d\n", 15838c2ecf20Sopenharmony_ci fw_name, ret); 15848c2ecf20Sopenharmony_ci continue; 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci i2400m->fw_name = fw_name; 15878c2ecf20Sopenharmony_ci ret = i2400m_fw_bootstrap(i2400m, fw, flags); 15888c2ecf20Sopenharmony_ci release_firmware(fw); 15898c2ecf20Sopenharmony_ci if (ret >= 0) /* firmware loaded successfully */ 15908c2ecf20Sopenharmony_ci break; 15918c2ecf20Sopenharmony_ci i2400m->fw_name = NULL; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ciout: 15948c2ecf20Sopenharmony_ci d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); 15958c2ecf20Sopenharmony_ci return ret; 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2400m_dev_bootstrap); 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_civoid i2400m_fw_cache(struct i2400m *i2400m) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci int result; 16038c2ecf20Sopenharmony_ci struct i2400m_fw *i2400m_fw; 16048c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* if there is anything there, free it -- now, this'd be weird */ 16078c2ecf20Sopenharmony_ci spin_lock(&i2400m->rx_lock); 16088c2ecf20Sopenharmony_ci i2400m_fw = i2400m->fw_cached; 16098c2ecf20Sopenharmony_ci spin_unlock(&i2400m->rx_lock); 16108c2ecf20Sopenharmony_ci if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) { 16118c2ecf20Sopenharmony_ci i2400m_fw_put(i2400m_fw); 16128c2ecf20Sopenharmony_ci WARN(1, "%s:%u: still cached fw still present?\n", 16138c2ecf20Sopenharmony_ci __func__, __LINE__); 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci if (i2400m->fw_name == NULL) { 16178c2ecf20Sopenharmony_ci dev_err(dev, "firmware n/a: can't cache\n"); 16188c2ecf20Sopenharmony_ci i2400m_fw = (void *) ~0; 16198c2ecf20Sopenharmony_ci goto out; 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci i2400m_fw = kzalloc(sizeof(*i2400m_fw), GFP_ATOMIC); 16238c2ecf20Sopenharmony_ci if (i2400m_fw == NULL) 16248c2ecf20Sopenharmony_ci goto out; 16258c2ecf20Sopenharmony_ci kref_init(&i2400m_fw->kref); 16268c2ecf20Sopenharmony_ci result = request_firmware(&i2400m_fw->fw, i2400m->fw_name, dev); 16278c2ecf20Sopenharmony_ci if (result < 0) { 16288c2ecf20Sopenharmony_ci dev_err(dev, "firmware %s: failed to cache: %d\n", 16298c2ecf20Sopenharmony_ci i2400m->fw_name, result); 16308c2ecf20Sopenharmony_ci kfree(i2400m_fw); 16318c2ecf20Sopenharmony_ci i2400m_fw = (void *) ~0; 16328c2ecf20Sopenharmony_ci } else 16338c2ecf20Sopenharmony_ci dev_info(dev, "firmware %s: cached\n", i2400m->fw_name); 16348c2ecf20Sopenharmony_ciout: 16358c2ecf20Sopenharmony_ci spin_lock(&i2400m->rx_lock); 16368c2ecf20Sopenharmony_ci i2400m->fw_cached = i2400m_fw; 16378c2ecf20Sopenharmony_ci spin_unlock(&i2400m->rx_lock); 16388c2ecf20Sopenharmony_ci} 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_civoid i2400m_fw_uncache(struct i2400m *i2400m) 16428c2ecf20Sopenharmony_ci{ 16438c2ecf20Sopenharmony_ci struct i2400m_fw *i2400m_fw; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci spin_lock(&i2400m->rx_lock); 16468c2ecf20Sopenharmony_ci i2400m_fw = i2400m->fw_cached; 16478c2ecf20Sopenharmony_ci i2400m->fw_cached = NULL; 16488c2ecf20Sopenharmony_ci spin_unlock(&i2400m->rx_lock); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) 16518c2ecf20Sopenharmony_ci i2400m_fw_put(i2400m_fw); 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 1654