18c2ecf20Sopenharmony_ci 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Atheros Communications Inc. 48c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 78c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 88c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 118c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 128c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 138c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 148c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 158c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 168c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 228c2ecf20Sopenharmony_ci#include <linux/errno.h> 238c2ecf20Sopenharmony_ci#include <linux/export.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h> 268c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "core.h" 298c2ecf20Sopenharmony_ci#include "cfg80211.h" 308c2ecf20Sopenharmony_ci#include "target.h" 318c2ecf20Sopenharmony_ci#include "debug.h" 328c2ecf20Sopenharmony_ci#include "hif-ops.h" 338c2ecf20Sopenharmony_ci#include "htc-ops.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct ath6kl_hw hw_list[] = { 368c2ecf20Sopenharmony_ci { 378c2ecf20Sopenharmony_ci .id = AR6003_HW_2_0_VERSION, 388c2ecf20Sopenharmony_ci .name = "ar6003 hw 2.0", 398c2ecf20Sopenharmony_ci .dataset_patch_addr = 0x57e884, 408c2ecf20Sopenharmony_ci .app_load_addr = 0x543180, 418c2ecf20Sopenharmony_ci .board_ext_data_addr = 0x57e500, 428c2ecf20Sopenharmony_ci .reserved_ram_size = 6912, 438c2ecf20Sopenharmony_ci .refclk_hz = 26000000, 448c2ecf20Sopenharmony_ci .uarttx_pin = 8, 458c2ecf20Sopenharmony_ci .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR, 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* hw2.0 needs override address hardcoded */ 488c2ecf20Sopenharmony_ci .app_start_override_addr = 0x944C00, 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci .fw = { 518c2ecf20Sopenharmony_ci .dir = AR6003_HW_2_0_FW_DIR, 528c2ecf20Sopenharmony_ci .otp = AR6003_HW_2_0_OTP_FILE, 538c2ecf20Sopenharmony_ci .fw = AR6003_HW_2_0_FIRMWARE_FILE, 548c2ecf20Sopenharmony_ci .tcmd = AR6003_HW_2_0_TCMD_FIRMWARE_FILE, 558c2ecf20Sopenharmony_ci .patch = AR6003_HW_2_0_PATCH_FILE, 568c2ecf20Sopenharmony_ci }, 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci .fw_board = AR6003_HW_2_0_BOARD_DATA_FILE, 598c2ecf20Sopenharmony_ci .fw_default_board = AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE, 608c2ecf20Sopenharmony_ci }, 618c2ecf20Sopenharmony_ci { 628c2ecf20Sopenharmony_ci .id = AR6003_HW_2_1_1_VERSION, 638c2ecf20Sopenharmony_ci .name = "ar6003 hw 2.1.1", 648c2ecf20Sopenharmony_ci .dataset_patch_addr = 0x57ff74, 658c2ecf20Sopenharmony_ci .app_load_addr = 0x1234, 668c2ecf20Sopenharmony_ci .board_ext_data_addr = 0x542330, 678c2ecf20Sopenharmony_ci .reserved_ram_size = 512, 688c2ecf20Sopenharmony_ci .refclk_hz = 26000000, 698c2ecf20Sopenharmony_ci .uarttx_pin = 8, 708c2ecf20Sopenharmony_ci .testscript_addr = 0x57ef74, 718c2ecf20Sopenharmony_ci .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR, 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci .fw = { 748c2ecf20Sopenharmony_ci .dir = AR6003_HW_2_1_1_FW_DIR, 758c2ecf20Sopenharmony_ci .otp = AR6003_HW_2_1_1_OTP_FILE, 768c2ecf20Sopenharmony_ci .fw = AR6003_HW_2_1_1_FIRMWARE_FILE, 778c2ecf20Sopenharmony_ci .tcmd = AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE, 788c2ecf20Sopenharmony_ci .patch = AR6003_HW_2_1_1_PATCH_FILE, 798c2ecf20Sopenharmony_ci .utf = AR6003_HW_2_1_1_UTF_FIRMWARE_FILE, 808c2ecf20Sopenharmony_ci .testscript = AR6003_HW_2_1_1_TESTSCRIPT_FILE, 818c2ecf20Sopenharmony_ci }, 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci .fw_board = AR6003_HW_2_1_1_BOARD_DATA_FILE, 848c2ecf20Sopenharmony_ci .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, 858c2ecf20Sopenharmony_ci }, 868c2ecf20Sopenharmony_ci { 878c2ecf20Sopenharmony_ci .id = AR6004_HW_1_0_VERSION, 888c2ecf20Sopenharmony_ci .name = "ar6004 hw 1.0", 898c2ecf20Sopenharmony_ci .dataset_patch_addr = 0x57e884, 908c2ecf20Sopenharmony_ci .app_load_addr = 0x1234, 918c2ecf20Sopenharmony_ci .board_ext_data_addr = 0x437000, 928c2ecf20Sopenharmony_ci .reserved_ram_size = 19456, 938c2ecf20Sopenharmony_ci .board_addr = 0x433900, 948c2ecf20Sopenharmony_ci .refclk_hz = 26000000, 958c2ecf20Sopenharmony_ci .uarttx_pin = 11, 968c2ecf20Sopenharmony_ci .flags = 0, 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci .fw = { 998c2ecf20Sopenharmony_ci .dir = AR6004_HW_1_0_FW_DIR, 1008c2ecf20Sopenharmony_ci .fw = AR6004_HW_1_0_FIRMWARE_FILE, 1018c2ecf20Sopenharmony_ci }, 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci .fw_board = AR6004_HW_1_0_BOARD_DATA_FILE, 1048c2ecf20Sopenharmony_ci .fw_default_board = AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE, 1058c2ecf20Sopenharmony_ci }, 1068c2ecf20Sopenharmony_ci { 1078c2ecf20Sopenharmony_ci .id = AR6004_HW_1_1_VERSION, 1088c2ecf20Sopenharmony_ci .name = "ar6004 hw 1.1", 1098c2ecf20Sopenharmony_ci .dataset_patch_addr = 0x57e884, 1108c2ecf20Sopenharmony_ci .app_load_addr = 0x1234, 1118c2ecf20Sopenharmony_ci .board_ext_data_addr = 0x437000, 1128c2ecf20Sopenharmony_ci .reserved_ram_size = 11264, 1138c2ecf20Sopenharmony_ci .board_addr = 0x43d400, 1148c2ecf20Sopenharmony_ci .refclk_hz = 40000000, 1158c2ecf20Sopenharmony_ci .uarttx_pin = 11, 1168c2ecf20Sopenharmony_ci .flags = 0, 1178c2ecf20Sopenharmony_ci .fw = { 1188c2ecf20Sopenharmony_ci .dir = AR6004_HW_1_1_FW_DIR, 1198c2ecf20Sopenharmony_ci .fw = AR6004_HW_1_1_FIRMWARE_FILE, 1208c2ecf20Sopenharmony_ci }, 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci .fw_board = AR6004_HW_1_1_BOARD_DATA_FILE, 1238c2ecf20Sopenharmony_ci .fw_default_board = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE, 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci { 1268c2ecf20Sopenharmony_ci .id = AR6004_HW_1_2_VERSION, 1278c2ecf20Sopenharmony_ci .name = "ar6004 hw 1.2", 1288c2ecf20Sopenharmony_ci .dataset_patch_addr = 0x436ecc, 1298c2ecf20Sopenharmony_ci .app_load_addr = 0x1234, 1308c2ecf20Sopenharmony_ci .board_ext_data_addr = 0x437000, 1318c2ecf20Sopenharmony_ci .reserved_ram_size = 9216, 1328c2ecf20Sopenharmony_ci .board_addr = 0x435c00, 1338c2ecf20Sopenharmony_ci .refclk_hz = 40000000, 1348c2ecf20Sopenharmony_ci .uarttx_pin = 11, 1358c2ecf20Sopenharmony_ci .flags = 0, 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci .fw = { 1388c2ecf20Sopenharmony_ci .dir = AR6004_HW_1_2_FW_DIR, 1398c2ecf20Sopenharmony_ci .fw = AR6004_HW_1_2_FIRMWARE_FILE, 1408c2ecf20Sopenharmony_ci }, 1418c2ecf20Sopenharmony_ci .fw_board = AR6004_HW_1_2_BOARD_DATA_FILE, 1428c2ecf20Sopenharmony_ci .fw_default_board = AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE, 1438c2ecf20Sopenharmony_ci }, 1448c2ecf20Sopenharmony_ci { 1458c2ecf20Sopenharmony_ci .id = AR6004_HW_1_3_VERSION, 1468c2ecf20Sopenharmony_ci .name = "ar6004 hw 1.3", 1478c2ecf20Sopenharmony_ci .dataset_patch_addr = 0x437860, 1488c2ecf20Sopenharmony_ci .app_load_addr = 0x1234, 1498c2ecf20Sopenharmony_ci .board_ext_data_addr = 0x437000, 1508c2ecf20Sopenharmony_ci .reserved_ram_size = 7168, 1518c2ecf20Sopenharmony_ci .board_addr = 0x436400, 1528c2ecf20Sopenharmony_ci .refclk_hz = 0, 1538c2ecf20Sopenharmony_ci .uarttx_pin = 11, 1548c2ecf20Sopenharmony_ci .flags = 0, 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci .fw = { 1578c2ecf20Sopenharmony_ci .dir = AR6004_HW_1_3_FW_DIR, 1588c2ecf20Sopenharmony_ci .fw = AR6004_HW_1_3_FIRMWARE_FILE, 1598c2ecf20Sopenharmony_ci .tcmd = AR6004_HW_1_3_TCMD_FIRMWARE_FILE, 1608c2ecf20Sopenharmony_ci .utf = AR6004_HW_1_3_UTF_FIRMWARE_FILE, 1618c2ecf20Sopenharmony_ci .testscript = AR6004_HW_1_3_TESTSCRIPT_FILE, 1628c2ecf20Sopenharmony_ci }, 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci .fw_board = AR6004_HW_1_3_BOARD_DATA_FILE, 1658c2ecf20Sopenharmony_ci .fw_default_board = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE, 1668c2ecf20Sopenharmony_ci }, 1678c2ecf20Sopenharmony_ci { 1688c2ecf20Sopenharmony_ci .id = AR6004_HW_3_0_VERSION, 1698c2ecf20Sopenharmony_ci .name = "ar6004 hw 3.0", 1708c2ecf20Sopenharmony_ci .dataset_patch_addr = 0, 1718c2ecf20Sopenharmony_ci .app_load_addr = 0x1234, 1728c2ecf20Sopenharmony_ci .board_ext_data_addr = 0, 1738c2ecf20Sopenharmony_ci .reserved_ram_size = 7168, 1748c2ecf20Sopenharmony_ci .board_addr = 0x436400, 1758c2ecf20Sopenharmony_ci .testscript_addr = 0, 1768c2ecf20Sopenharmony_ci .uarttx_pin = 11, 1778c2ecf20Sopenharmony_ci .flags = 0, 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci .fw = { 1808c2ecf20Sopenharmony_ci .dir = AR6004_HW_3_0_FW_DIR, 1818c2ecf20Sopenharmony_ci .fw = AR6004_HW_3_0_FIRMWARE_FILE, 1828c2ecf20Sopenharmony_ci .tcmd = AR6004_HW_3_0_TCMD_FIRMWARE_FILE, 1838c2ecf20Sopenharmony_ci .utf = AR6004_HW_3_0_UTF_FIRMWARE_FILE, 1848c2ecf20Sopenharmony_ci .testscript = AR6004_HW_3_0_TESTSCRIPT_FILE, 1858c2ecf20Sopenharmony_ci }, 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci .fw_board = AR6004_HW_3_0_BOARD_DATA_FILE, 1888c2ecf20Sopenharmony_ci .fw_default_board = AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE, 1898c2ecf20Sopenharmony_ci }, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* 1938c2ecf20Sopenharmony_ci * Include definitions here that can be used to tune the WLAN module 1948c2ecf20Sopenharmony_ci * behavior. Different customers can tune the behavior as per their needs, 1958c2ecf20Sopenharmony_ci * here. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * This configuration item enable/disable keepalive support. 2008c2ecf20Sopenharmony_ci * Keepalive support: In the absence of any data traffic to AP, null 2018c2ecf20Sopenharmony_ci * frames will be sent to the AP at periodic interval, to keep the association 2028c2ecf20Sopenharmony_ci * active. This configuration item defines the periodic interval. 2038c2ecf20Sopenharmony_ci * Use value of zero to disable keepalive support 2048c2ecf20Sopenharmony_ci * Default: 60 seconds 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci#define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* 2098c2ecf20Sopenharmony_ci * This configuration item sets the value of disconnect timeout 2108c2ecf20Sopenharmony_ci * Firmware delays sending the disconnec event to the host for this 2118c2ecf20Sopenharmony_ci * timeout after is gets disconnected from the current AP. 2128c2ecf20Sopenharmony_ci * If the firmware successly roams within the disconnect timeout 2138c2ecf20Sopenharmony_ci * it sends a new connect event 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci#define WLAN_CONFIG_DISCONNECT_TIMEOUT 10 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci#define ATH6KL_DATA_OFFSET 64 2198c2ecf20Sopenharmony_cistruct sk_buff *ath6kl_buf_alloc(int size) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct sk_buff *skb; 2228c2ecf20Sopenharmony_ci u16 reserved; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Add chacheline space at front and back of buffer */ 2258c2ecf20Sopenharmony_ci reserved = roundup((2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + 2268c2ecf20Sopenharmony_ci sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES, 4); 2278c2ecf20Sopenharmony_ci skb = dev_alloc_skb(size + reserved); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (skb) 2308c2ecf20Sopenharmony_ci skb_reserve(skb, reserved - L1_CACHE_BYTES); 2318c2ecf20Sopenharmony_ci return skb; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_civoid ath6kl_init_profile_info(struct ath6kl_vif *vif) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci vif->ssid_len = 0; 2378c2ecf20Sopenharmony_ci memset(vif->ssid, 0, sizeof(vif->ssid)); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci vif->dot11_auth_mode = OPEN_AUTH; 2408c2ecf20Sopenharmony_ci vif->auth_mode = NONE_AUTH; 2418c2ecf20Sopenharmony_ci vif->prwise_crypto = NONE_CRYPT; 2428c2ecf20Sopenharmony_ci vif->prwise_crypto_len = 0; 2438c2ecf20Sopenharmony_ci vif->grp_crypto = NONE_CRYPT; 2448c2ecf20Sopenharmony_ci vif->grp_crypto_len = 0; 2458c2ecf20Sopenharmony_ci memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); 2468c2ecf20Sopenharmony_ci memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); 2478c2ecf20Sopenharmony_ci memset(vif->bssid, 0, sizeof(vif->bssid)); 2488c2ecf20Sopenharmony_ci vif->bss_ch = 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int ath6kl_set_host_app_area(struct ath6kl *ar) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci u32 address, data; 2548c2ecf20Sopenharmony_ci struct host_app_area host_app_area; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Fetch the address of the host_app_area_s 2578c2ecf20Sopenharmony_ci * instance in the host interest area */ 2588c2ecf20Sopenharmony_ci address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); 2598c2ecf20Sopenharmony_ci address = TARG_VTOP(ar->target_type, address); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (ath6kl_diag_read32(ar, address, &data)) 2628c2ecf20Sopenharmony_ci return -EIO; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci address = TARG_VTOP(ar->target_type, data); 2658c2ecf20Sopenharmony_ci host_app_area.wmi_protocol_ver = cpu_to_le32(WMI_PROTOCOL_VERSION); 2668c2ecf20Sopenharmony_ci if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area, 2678c2ecf20Sopenharmony_ci sizeof(struct host_app_area))) 2688c2ecf20Sopenharmony_ci return -EIO; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic inline void set_ac2_ep_map(struct ath6kl *ar, 2748c2ecf20Sopenharmony_ci u8 ac, 2758c2ecf20Sopenharmony_ci enum htc_endpoint_id ep) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci ar->ac2ep_map[ac] = ep; 2788c2ecf20Sopenharmony_ci ar->ep2ac_map[ep] = ac; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* connect to a service */ 2828c2ecf20Sopenharmony_cistatic int ath6kl_connectservice(struct ath6kl *ar, 2838c2ecf20Sopenharmony_ci struct htc_service_connect_req *con_req, 2848c2ecf20Sopenharmony_ci char *desc) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int status; 2878c2ecf20Sopenharmony_ci struct htc_service_connect_resp response; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci memset(&response, 0, sizeof(response)); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response); 2928c2ecf20Sopenharmony_ci if (status) { 2938c2ecf20Sopenharmony_ci ath6kl_err("failed to connect to %s service status:%d\n", 2948c2ecf20Sopenharmony_ci desc, status); 2958c2ecf20Sopenharmony_ci return status; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci switch (con_req->svc_id) { 2998c2ecf20Sopenharmony_ci case WMI_CONTROL_SVC: 3008c2ecf20Sopenharmony_ci if (test_bit(WMI_ENABLED, &ar->flag)) 3018c2ecf20Sopenharmony_ci ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint); 3028c2ecf20Sopenharmony_ci ar->ctrl_ep = response.endpoint; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case WMI_DATA_BE_SVC: 3058c2ecf20Sopenharmony_ci set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint); 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case WMI_DATA_BK_SVC: 3088c2ecf20Sopenharmony_ci set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint); 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case WMI_DATA_VI_SVC: 3118c2ecf20Sopenharmony_ci set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint); 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci case WMI_DATA_VO_SVC: 3148c2ecf20Sopenharmony_ci set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint); 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci default: 3178c2ecf20Sopenharmony_ci ath6kl_err("service id is not mapped %d\n", con_req->svc_id); 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int ath6kl_init_service_ep(struct ath6kl *ar) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct htc_service_connect_req connect; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci memset(&connect, 0, sizeof(connect)); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* these fields are the same for all service endpoints */ 3318c2ecf20Sopenharmony_ci connect.ep_cb.tx_comp_multi = ath6kl_tx_complete; 3328c2ecf20Sopenharmony_ci connect.ep_cb.rx = ath6kl_rx; 3338c2ecf20Sopenharmony_ci connect.ep_cb.rx_refill = ath6kl_rx_refill; 3348c2ecf20Sopenharmony_ci connect.ep_cb.tx_full = ath6kl_tx_queue_full; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* 3378c2ecf20Sopenharmony_ci * Set the max queue depth so that our ath6kl_tx_queue_full handler 3388c2ecf20Sopenharmony_ci * gets called. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH; 3418c2ecf20Sopenharmony_ci connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4; 3428c2ecf20Sopenharmony_ci if (!connect.ep_cb.rx_refill_thresh) 3438c2ecf20Sopenharmony_ci connect.ep_cb.rx_refill_thresh++; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* connect to control service */ 3468c2ecf20Sopenharmony_ci connect.svc_id = WMI_CONTROL_SVC; 3478c2ecf20Sopenharmony_ci if (ath6kl_connectservice(ar, &connect, "WMI CONTROL")) 3488c2ecf20Sopenharmony_ci return -EIO; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * Limit the HTC message size on the send path, although e can 3548c2ecf20Sopenharmony_ci * receive A-MSDU frames of 4K, we will only send ethernet-sized 3558c2ecf20Sopenharmony_ci * (802.3) frames on the send path. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * To reduce the amount of committed memory for larger A_MSDU 3618c2ecf20Sopenharmony_ci * frames, use the recv-alloc threshold mechanism for larger 3628c2ecf20Sopenharmony_ci * packets. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE; 3658c2ecf20Sopenharmony_ci connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * For the remaining data services set the connection flag to 3698c2ecf20Sopenharmony_ci * reduce dribbling, if configured to do so. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB; 3728c2ecf20Sopenharmony_ci connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK; 3738c2ecf20Sopenharmony_ci connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci connect.svc_id = WMI_DATA_BE_SVC; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (ath6kl_connectservice(ar, &connect, "WMI DATA BE")) 3788c2ecf20Sopenharmony_ci return -EIO; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* connect to back-ground map this to WMI LOW_PRI */ 3818c2ecf20Sopenharmony_ci connect.svc_id = WMI_DATA_BK_SVC; 3828c2ecf20Sopenharmony_ci if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) 3838c2ecf20Sopenharmony_ci return -EIO; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* connect to Video service, map this to HI PRI */ 3868c2ecf20Sopenharmony_ci connect.svc_id = WMI_DATA_VI_SVC; 3878c2ecf20Sopenharmony_ci if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) 3888c2ecf20Sopenharmony_ci return -EIO; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * Connect to VO service, this is currently not mapped to a WMI 3928c2ecf20Sopenharmony_ci * priority stream due to historical reasons. WMI originally 3938c2ecf20Sopenharmony_ci * defined 3 priorities over 3 mailboxes We can change this when 3948c2ecf20Sopenharmony_ci * WMI is reworked so that priorities are not dependent on 3958c2ecf20Sopenharmony_ci * mailboxes. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci connect.svc_id = WMI_DATA_VO_SVC; 3988c2ecf20Sopenharmony_ci if (ath6kl_connectservice(ar, &connect, "WMI DATA VO")) 3998c2ecf20Sopenharmony_ci return -EIO; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_civoid ath6kl_init_control_info(struct ath6kl_vif *vif) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci ath6kl_init_profile_info(vif); 4078c2ecf20Sopenharmony_ci vif->def_txkey_index = 0; 4088c2ecf20Sopenharmony_ci memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); 4098c2ecf20Sopenharmony_ci vif->ch_hint = 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/* 4138c2ecf20Sopenharmony_ci * Set HTC/Mbox operational parameters, this can only be called when the 4148c2ecf20Sopenharmony_ci * target is in the BMI phase. 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_cistatic int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, 4178c2ecf20Sopenharmony_ci u8 htc_ctrl_buf) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci int status; 4208c2ecf20Sopenharmony_ci u32 blk_size; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci blk_size = ar->mbox_info.block_size; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (htc_ctrl_buf) 4258c2ecf20Sopenharmony_ci blk_size |= ((u32)htc_ctrl_buf) << 16; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* set the host interest area for the block size */ 4288c2ecf20Sopenharmony_ci status = ath6kl_bmi_write_hi32(ar, hi_mbox_io_block_sz, blk_size); 4298c2ecf20Sopenharmony_ci if (status) { 4308c2ecf20Sopenharmony_ci ath6kl_err("bmi_write_memory for IO block size failed\n"); 4318c2ecf20Sopenharmony_ci goto out; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n", 4358c2ecf20Sopenharmony_ci blk_size, 4368c2ecf20Sopenharmony_ci ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz))); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (mbox_isr_yield_val) { 4398c2ecf20Sopenharmony_ci /* set the host interest area for the mbox ISR yield limit */ 4408c2ecf20Sopenharmony_ci status = ath6kl_bmi_write_hi32(ar, hi_mbox_isr_yield_limit, 4418c2ecf20Sopenharmony_ci mbox_isr_yield_val); 4428c2ecf20Sopenharmony_ci if (status) { 4438c2ecf20Sopenharmony_ci ath6kl_err("bmi_write_memory for yield limit failed\n"); 4448c2ecf20Sopenharmony_ci goto out; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ciout: 4498c2ecf20Sopenharmony_ci return status; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci int ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * Configure the device for rx dot11 header rules. "0,0" are the 4588c2ecf20Sopenharmony_ci * default values. Required if checksum offload is needed. Set 4598c2ecf20Sopenharmony_ci * RxMetaVersion to 2. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci ret = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, 4628c2ecf20Sopenharmony_ci ar->rx_meta_ver, 0, 0); 4638c2ecf20Sopenharmony_ci if (ret) { 4648c2ecf20Sopenharmony_ci ath6kl_err("unable to set the rx frame format: %d\n", ret); 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) { 4698c2ecf20Sopenharmony_ci ret = ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, 4708c2ecf20Sopenharmony_ci IGNORE_PS_FAIL_DURING_SCAN); 4718c2ecf20Sopenharmony_ci if (ret) { 4728c2ecf20Sopenharmony_ci ath6kl_err("unable to set power save fail event policy: %d\n", 4738c2ecf20Sopenharmony_ci ret); 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) { 4798c2ecf20Sopenharmony_ci ret = ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, 4808c2ecf20Sopenharmony_ci WMI_FOLLOW_BARKER_IN_ERP); 4818c2ecf20Sopenharmony_ci if (ret) { 4828c2ecf20Sopenharmony_ci ath6kl_err("unable to set barker preamble policy: %d\n", 4838c2ecf20Sopenharmony_ci ret); 4848c2ecf20Sopenharmony_ci return ret; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, 4898c2ecf20Sopenharmony_ci WLAN_CONFIG_KEEP_ALIVE_INTERVAL); 4908c2ecf20Sopenharmony_ci if (ret) { 4918c2ecf20Sopenharmony_ci ath6kl_err("unable to set keep alive interval: %d\n", ret); 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, 4968c2ecf20Sopenharmony_ci WLAN_CONFIG_DISCONNECT_TIMEOUT); 4978c2ecf20Sopenharmony_ci if (ret) { 4988c2ecf20Sopenharmony_ci ath6kl_err("unable to set disconnect timeout: %d\n", ret); 4998c2ecf20Sopenharmony_ci return ret; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) { 5038c2ecf20Sopenharmony_ci ret = ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED); 5048c2ecf20Sopenharmony_ci if (ret) { 5058c2ecf20Sopenharmony_ci ath6kl_err("unable to set txop bursting: %d\n", ret); 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (ar->p2p && (ar->vif_max == 1 || idx)) { 5118c2ecf20Sopenharmony_ci ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx, 5128c2ecf20Sopenharmony_ci P2P_FLAG_CAPABILITIES_REQ | 5138c2ecf20Sopenharmony_ci P2P_FLAG_MACADDR_REQ | 5148c2ecf20Sopenharmony_ci P2P_FLAG_HMODEL_REQ); 5158c2ecf20Sopenharmony_ci if (ret) { 5168c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_TRC, 5178c2ecf20Sopenharmony_ci "failed to request P2P capabilities (%d) - assuming P2P not supported\n", 5188c2ecf20Sopenharmony_ci ret); 5198c2ecf20Sopenharmony_ci ar->p2p = false; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (ar->p2p && (ar->vif_max == 1 || idx)) { 5248c2ecf20Sopenharmony_ci /* Enable Probe Request reporting for P2P */ 5258c2ecf20Sopenharmony_ci ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true); 5268c2ecf20Sopenharmony_ci if (ret) { 5278c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_TRC, 5288c2ecf20Sopenharmony_ci "failed to enable Probe Request reporting (%d)\n", 5298c2ecf20Sopenharmony_ci ret); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return ret; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ciint ath6kl_configure_target(struct ath6kl *ar) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci u32 param, ram_reserved_size; 5398c2ecf20Sopenharmony_ci u8 fw_iftype, fw_mode = 0, fw_submode = 0; 5408c2ecf20Sopenharmony_ci int i, status; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci param = !!(ar->conf_flags & ATH6KL_CONF_UART_DEBUG); 5438c2ecf20Sopenharmony_ci if (ath6kl_bmi_write_hi32(ar, hi_serial_enable, param)) { 5448c2ecf20Sopenharmony_ci ath6kl_err("bmi_write_memory for uart debug failed\n"); 5458c2ecf20Sopenharmony_ci return -EIO; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * Note: Even though the firmware interface type is 5508c2ecf20Sopenharmony_ci * chosen as BSS_STA for all three interfaces, can 5518c2ecf20Sopenharmony_ci * be configured to IBSS/AP as long as the fw submode 5528c2ecf20Sopenharmony_ci * remains normal mode (0 - AP, STA and IBSS). But 5538c2ecf20Sopenharmony_ci * due to an target assert in firmware only one interface is 5548c2ecf20Sopenharmony_ci * configured for now. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci fw_iftype = HI_OPTION_FW_MODE_BSS_STA; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci for (i = 0; i < ar->vif_max; i++) 5598c2ecf20Sopenharmony_ci fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * Submodes when fw does not support dynamic interface 5638c2ecf20Sopenharmony_ci * switching: 5648c2ecf20Sopenharmony_ci * vif[0] - AP/STA/IBSS 5658c2ecf20Sopenharmony_ci * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" 5668c2ecf20Sopenharmony_ci * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" 5678c2ecf20Sopenharmony_ci * Otherwise, All the interface are initialized to p2p dev. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, 5718c2ecf20Sopenharmony_ci ar->fw_capabilities)) { 5728c2ecf20Sopenharmony_ci for (i = 0; i < ar->vif_max; i++) 5738c2ecf20Sopenharmony_ci fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << 5748c2ecf20Sopenharmony_ci (i * HI_OPTION_FW_SUBMODE_BITS); 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci for (i = 0; i < ar->max_norm_iface; i++) 5778c2ecf20Sopenharmony_ci fw_submode |= HI_OPTION_FW_SUBMODE_NONE << 5788c2ecf20Sopenharmony_ci (i * HI_OPTION_FW_SUBMODE_BITS); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci for (i = ar->max_norm_iface; i < ar->vif_max; i++) 5818c2ecf20Sopenharmony_ci fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << 5828c2ecf20Sopenharmony_ci (i * HI_OPTION_FW_SUBMODE_BITS); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (ar->p2p && ar->vif_max == 1) 5858c2ecf20Sopenharmony_ci fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest, 5898c2ecf20Sopenharmony_ci HTC_PROTOCOL_VERSION) != 0) { 5908c2ecf20Sopenharmony_ci ath6kl_err("bmi_write_memory for htc version failed\n"); 5918c2ecf20Sopenharmony_ci return -EIO; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* set the firmware mode to STA/IBSS/AP */ 5958c2ecf20Sopenharmony_ci param = 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (ath6kl_bmi_read_hi32(ar, hi_option_flag, ¶m) != 0) { 5988c2ecf20Sopenharmony_ci ath6kl_err("bmi_read_memory for setting fwmode failed\n"); 5998c2ecf20Sopenharmony_ci return -EIO; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT); 6038c2ecf20Sopenharmony_ci param |= fw_mode << HI_OPTION_FW_MODE_SHIFT; 6048c2ecf20Sopenharmony_ci param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); 6078c2ecf20Sopenharmony_ci param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (ath6kl_bmi_write_hi32(ar, hi_option_flag, param) != 0) { 6108c2ecf20Sopenharmony_ci ath6kl_err("bmi_write_memory for setting fwmode failed\n"); 6118c2ecf20Sopenharmony_ci return -EIO; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n"); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* 6178c2ecf20Sopenharmony_ci * Hardcode the address use for the extended board data 6188c2ecf20Sopenharmony_ci * Ideally this should be pre-allocate by the OS at boot time 6198c2ecf20Sopenharmony_ci * But since it is a new feature and board data is loaded 6208c2ecf20Sopenharmony_ci * at init time, we have to workaround this from host. 6218c2ecf20Sopenharmony_ci * It is difficult to patch the firmware boot code, 6228c2ecf20Sopenharmony_ci * but possible in theory. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if ((ar->target_type == TARGET_TYPE_AR6003) || 6268c2ecf20Sopenharmony_ci (ar->version.target_ver == AR6004_HW_1_3_VERSION) || 6278c2ecf20Sopenharmony_ci (ar->version.target_ver == AR6004_HW_3_0_VERSION)) { 6288c2ecf20Sopenharmony_ci param = ar->hw.board_ext_data_addr; 6298c2ecf20Sopenharmony_ci ram_reserved_size = ar->hw.reserved_ram_size; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { 6328c2ecf20Sopenharmony_ci ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); 6338c2ecf20Sopenharmony_ci return -EIO; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 6378c2ecf20Sopenharmony_ci ram_reserved_size) != 0) { 6388c2ecf20Sopenharmony_ci ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); 6398c2ecf20Sopenharmony_ci return -EIO; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* set the block size for the target */ 6448c2ecf20Sopenharmony_ci if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0)) 6458c2ecf20Sopenharmony_ci /* use default number of control buffers */ 6468c2ecf20Sopenharmony_ci return -EIO; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* Configure GPIO AR600x UART */ 6498c2ecf20Sopenharmony_ci status = ath6kl_bmi_write_hi32(ar, hi_dbg_uart_txpin, 6508c2ecf20Sopenharmony_ci ar->hw.uarttx_pin); 6518c2ecf20Sopenharmony_ci if (status) 6528c2ecf20Sopenharmony_ci return status; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* Only set the baud rate if we're actually doing debug */ 6558c2ecf20Sopenharmony_ci if (ar->conf_flags & ATH6KL_CONF_UART_DEBUG) { 6568c2ecf20Sopenharmony_ci status = ath6kl_bmi_write_hi32(ar, hi_desired_baud_rate, 6578c2ecf20Sopenharmony_ci ar->hw.uarttx_rate); 6588c2ecf20Sopenharmony_ci if (status) 6598c2ecf20Sopenharmony_ci return status; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* Configure target refclk_hz */ 6638c2ecf20Sopenharmony_ci if (ar->hw.refclk_hz != 0) { 6648c2ecf20Sopenharmony_ci status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, 6658c2ecf20Sopenharmony_ci ar->hw.refclk_hz); 6668c2ecf20Sopenharmony_ci if (status) 6678c2ecf20Sopenharmony_ci return status; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci return 0; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/* firmware upload */ 6748c2ecf20Sopenharmony_cistatic int ath6kl_get_fw(struct ath6kl *ar, const char *filename, 6758c2ecf20Sopenharmony_ci u8 **fw, size_t *fw_len) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci const struct firmware *fw_entry; 6788c2ecf20Sopenharmony_ci int ret; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci ret = request_firmware(&fw_entry, filename, ar->dev); 6818c2ecf20Sopenharmony_ci if (ret) 6828c2ecf20Sopenharmony_ci return ret; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci *fw_len = fw_entry->size; 6858c2ecf20Sopenharmony_ci *fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (*fw == NULL) 6888c2ecf20Sopenharmony_ci ret = -ENOMEM; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci release_firmware(fw_entry); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return ret; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6968c2ecf20Sopenharmony_ci/* 6978c2ecf20Sopenharmony_ci * Check the device tree for a board-id and use it to construct 6988c2ecf20Sopenharmony_ci * the pathname to the firmware file. Used (for now) to find a 6998c2ecf20Sopenharmony_ci * fallback to the "bdata.bin" file--typically a symlink to the 7008c2ecf20Sopenharmony_ci * appropriate board-specific file. 7018c2ecf20Sopenharmony_ci */ 7028c2ecf20Sopenharmony_cistatic bool check_device_tree(struct ath6kl *ar) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci static const char *board_id_prop = "atheros,board-id"; 7058c2ecf20Sopenharmony_ci struct device_node *node; 7068c2ecf20Sopenharmony_ci char board_filename[64]; 7078c2ecf20Sopenharmony_ci const char *board_id; 7088c2ecf20Sopenharmony_ci int ret; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci for_each_compatible_node(node, NULL, "atheros,ath6kl") { 7118c2ecf20Sopenharmony_ci board_id = of_get_property(node, board_id_prop, NULL); 7128c2ecf20Sopenharmony_ci if (board_id == NULL) { 7138c2ecf20Sopenharmony_ci ath6kl_warn("No \"%s\" property on %pOFn node.\n", 7148c2ecf20Sopenharmony_ci board_id_prop, node); 7158c2ecf20Sopenharmony_ci continue; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci snprintf(board_filename, sizeof(board_filename), 7188c2ecf20Sopenharmony_ci "%s/bdata.%s.bin", ar->hw.fw.dir, board_id); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board, 7218c2ecf20Sopenharmony_ci &ar->fw_board_len); 7228c2ecf20Sopenharmony_ci if (ret) { 7238c2ecf20Sopenharmony_ci ath6kl_err("Failed to get DT board file %s: %d\n", 7248c2ecf20Sopenharmony_ci board_filename, ret); 7258c2ecf20Sopenharmony_ci continue; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci of_node_put(node); 7288c2ecf20Sopenharmony_ci return true; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci return false; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci#else 7338c2ecf20Sopenharmony_cistatic bool check_device_tree(struct ath6kl *ar) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci return false; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic int ath6kl_fetch_board_file(struct ath6kl *ar) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci const char *filename; 7428c2ecf20Sopenharmony_ci int ret; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (ar->fw_board != NULL) 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (WARN_ON(ar->hw.fw_board == NULL)) 7488c2ecf20Sopenharmony_ci return -EINVAL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci filename = ar->hw.fw_board; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, filename, &ar->fw_board, 7538c2ecf20Sopenharmony_ci &ar->fw_board_len); 7548c2ecf20Sopenharmony_ci if (ret == 0) { 7558c2ecf20Sopenharmony_ci /* managed to get proper board file */ 7568c2ecf20Sopenharmony_ci return 0; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (check_device_tree(ar)) { 7608c2ecf20Sopenharmony_ci /* got board file from device tree */ 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* there was no proper board file, try to use default instead */ 7658c2ecf20Sopenharmony_ci ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", 7668c2ecf20Sopenharmony_ci filename, ret); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci filename = ar->hw.fw_default_board; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, filename, &ar->fw_board, 7718c2ecf20Sopenharmony_ci &ar->fw_board_len); 7728c2ecf20Sopenharmony_ci if (ret) { 7738c2ecf20Sopenharmony_ci ath6kl_err("Failed to get default board file %s: %d\n", 7748c2ecf20Sopenharmony_ci filename, ret); 7758c2ecf20Sopenharmony_ci return ret; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n"); 7798c2ecf20Sopenharmony_ci ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n"); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic int ath6kl_fetch_otp_file(struct ath6kl *ar) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci char filename[100]; 7878c2ecf20Sopenharmony_ci int ret; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (ar->fw_otp != NULL) 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (ar->hw.fw.otp == NULL) { 7938c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 7948c2ecf20Sopenharmony_ci "no OTP file configured for this hw\n"); 7958c2ecf20Sopenharmony_ci return 0; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", 7998c2ecf20Sopenharmony_ci ar->hw.fw.dir, ar->hw.fw.otp); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, 8028c2ecf20Sopenharmony_ci &ar->fw_otp_len); 8038c2ecf20Sopenharmony_ci if (ret) { 8048c2ecf20Sopenharmony_ci ath6kl_err("Failed to get OTP file %s: %d\n", 8058c2ecf20Sopenharmony_ci filename, ret); 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic int ath6kl_fetch_testmode_file(struct ath6kl *ar) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci char filename[100]; 8158c2ecf20Sopenharmony_ci int ret; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (ar->testmode == 0) 8188c2ecf20Sopenharmony_ci return 0; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "testmode %d\n", ar->testmode); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (ar->testmode == 2) { 8238c2ecf20Sopenharmony_ci if (ar->hw.fw.utf == NULL) { 8248c2ecf20Sopenharmony_ci ath6kl_warn("testmode 2 not supported\n"); 8258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", 8298c2ecf20Sopenharmony_ci ar->hw.fw.dir, ar->hw.fw.utf); 8308c2ecf20Sopenharmony_ci } else { 8318c2ecf20Sopenharmony_ci if (ar->hw.fw.tcmd == NULL) { 8328c2ecf20Sopenharmony_ci ath6kl_warn("testmode 1 not supported\n"); 8338c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", 8378c2ecf20Sopenharmony_ci ar->hw.fw.dir, ar->hw.fw.tcmd); 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci set_bit(TESTMODE, &ar->flag); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); 8438c2ecf20Sopenharmony_ci if (ret) { 8448c2ecf20Sopenharmony_ci ath6kl_err("Failed to get testmode %d firmware file %s: %d\n", 8458c2ecf20Sopenharmony_ci ar->testmode, filename, ret); 8468c2ecf20Sopenharmony_ci return ret; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic int ath6kl_fetch_fw_file(struct ath6kl *ar) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci char filename[100]; 8558c2ecf20Sopenharmony_ci int ret; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (ar->fw != NULL) 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* FIXME: remove WARN_ON() as we won't support FW API 1 for long */ 8618c2ecf20Sopenharmony_ci if (WARN_ON(ar->hw.fw.fw == NULL)) 8628c2ecf20Sopenharmony_ci return -EINVAL; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", 8658c2ecf20Sopenharmony_ci ar->hw.fw.dir, ar->hw.fw.fw); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); 8688c2ecf20Sopenharmony_ci if (ret) { 8698c2ecf20Sopenharmony_ci ath6kl_err("Failed to get firmware file %s: %d\n", 8708c2ecf20Sopenharmony_ci filename, ret); 8718c2ecf20Sopenharmony_ci return ret; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic int ath6kl_fetch_patch_file(struct ath6kl *ar) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci char filename[100]; 8808c2ecf20Sopenharmony_ci int ret; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (ar->fw_patch != NULL) 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (ar->hw.fw.patch == NULL) 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", 8898c2ecf20Sopenharmony_ci ar->hw.fw.dir, ar->hw.fw.patch); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, 8928c2ecf20Sopenharmony_ci &ar->fw_patch_len); 8938c2ecf20Sopenharmony_ci if (ret) { 8948c2ecf20Sopenharmony_ci ath6kl_err("Failed to get patch file %s: %d\n", 8958c2ecf20Sopenharmony_ci filename, ret); 8968c2ecf20Sopenharmony_ci return ret; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int ath6kl_fetch_testscript_file(struct ath6kl *ar) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci char filename[100]; 9058c2ecf20Sopenharmony_ci int ret; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (ar->testmode != 2) 9088c2ecf20Sopenharmony_ci return 0; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (ar->fw_testscript != NULL) 9118c2ecf20Sopenharmony_ci return 0; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (ar->hw.fw.testscript == NULL) 9148c2ecf20Sopenharmony_ci return 0; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", 9178c2ecf20Sopenharmony_ci ar->hw.fw.dir, ar->hw.fw.testscript); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = ath6kl_get_fw(ar, filename, &ar->fw_testscript, 9208c2ecf20Sopenharmony_ci &ar->fw_testscript_len); 9218c2ecf20Sopenharmony_ci if (ret) { 9228c2ecf20Sopenharmony_ci ath6kl_err("Failed to get testscript file %s: %d\n", 9238c2ecf20Sopenharmony_ci filename, ret); 9248c2ecf20Sopenharmony_ci return ret; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return 0; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic int ath6kl_fetch_fw_api1(struct ath6kl *ar) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci int ret; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ret = ath6kl_fetch_otp_file(ar); 9358c2ecf20Sopenharmony_ci if (ret) 9368c2ecf20Sopenharmony_ci return ret; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci ret = ath6kl_fetch_fw_file(ar); 9398c2ecf20Sopenharmony_ci if (ret) 9408c2ecf20Sopenharmony_ci return ret; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci ret = ath6kl_fetch_patch_file(ar); 9438c2ecf20Sopenharmony_ci if (ret) 9448c2ecf20Sopenharmony_ci return ret; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ret = ath6kl_fetch_testscript_file(ar); 9478c2ecf20Sopenharmony_ci if (ret) 9488c2ecf20Sopenharmony_ci return ret; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci return 0; 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_cistatic int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci size_t magic_len, len, ie_len; 9568c2ecf20Sopenharmony_ci const struct firmware *fw; 9578c2ecf20Sopenharmony_ci struct ath6kl_fw_ie *hdr; 9588c2ecf20Sopenharmony_ci char filename[100]; 9598c2ecf20Sopenharmony_ci const u8 *data; 9608c2ecf20Sopenharmony_ci int ret, ie_id, i, index, bit; 9618c2ecf20Sopenharmony_ci __le32 *val; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci ret = request_firmware(&fw, filename, ar->dev); 9668c2ecf20Sopenharmony_ci if (ret) { 9678c2ecf20Sopenharmony_ci ath6kl_err("Failed request firmware, rv: %d\n", ret); 9688c2ecf20Sopenharmony_ci return ret; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci data = fw->data; 9728c2ecf20Sopenharmony_ci len = fw->size; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* magic also includes the null byte, check that as well */ 9758c2ecf20Sopenharmony_ci magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (len < magic_len) { 9788c2ecf20Sopenharmony_ci ath6kl_err("Magic length is invalid, len: %zd magic_len: %zd\n", 9798c2ecf20Sopenharmony_ci len, magic_len); 9808c2ecf20Sopenharmony_ci ret = -EINVAL; 9818c2ecf20Sopenharmony_ci goto out; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { 9858c2ecf20Sopenharmony_ci ath6kl_err("Magic is invalid, magic_len: %zd\n", 9868c2ecf20Sopenharmony_ci magic_len); 9878c2ecf20Sopenharmony_ci ret = -EINVAL; 9888c2ecf20Sopenharmony_ci goto out; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci len -= magic_len; 9928c2ecf20Sopenharmony_ci data += magic_len; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci /* loop elements */ 9958c2ecf20Sopenharmony_ci while (len > sizeof(struct ath6kl_fw_ie)) { 9968c2ecf20Sopenharmony_ci /* hdr is unaligned! */ 9978c2ecf20Sopenharmony_ci hdr = (struct ath6kl_fw_ie *) data; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci ie_id = le32_to_cpup(&hdr->id); 10008c2ecf20Sopenharmony_ci ie_len = le32_to_cpup(&hdr->len); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci len -= sizeof(*hdr); 10038c2ecf20Sopenharmony_ci data += sizeof(*hdr); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "ie-id: %d len: %zd (0x%zx)\n", 10068c2ecf20Sopenharmony_ci ie_id, ie_len, ie_len); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (len < ie_len) { 10098c2ecf20Sopenharmony_ci ath6kl_err("IE len is invalid, len: %zd ie_len: %zd ie-id: %d\n", 10108c2ecf20Sopenharmony_ci len, ie_len, ie_id); 10118c2ecf20Sopenharmony_ci ret = -EINVAL; 10128c2ecf20Sopenharmony_ci goto out; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci switch (ie_id) { 10168c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_FW_VERSION: 10178c2ecf20Sopenharmony_ci strlcpy(ar->wiphy->fw_version, data, 10188c2ecf20Sopenharmony_ci min(sizeof(ar->wiphy->fw_version), ie_len+1)); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 10218c2ecf20Sopenharmony_ci "found fw version %s\n", 10228c2ecf20Sopenharmony_ci ar->wiphy->fw_version); 10238c2ecf20Sopenharmony_ci break; 10248c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_OTP_IMAGE: 10258c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", 10268c2ecf20Sopenharmony_ci ie_len); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (ar->fw_otp == NULL) { 10318c2ecf20Sopenharmony_ci ath6kl_err("fw_otp cannot be allocated\n"); 10328c2ecf20Sopenharmony_ci ret = -ENOMEM; 10338c2ecf20Sopenharmony_ci goto out; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci ar->fw_otp_len = ie_len; 10378c2ecf20Sopenharmony_ci break; 10388c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_FW_IMAGE: 10398c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n", 10408c2ecf20Sopenharmony_ci ie_len); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* in testmode we already might have a fw file */ 10438c2ecf20Sopenharmony_ci if (ar->fw != NULL) 10448c2ecf20Sopenharmony_ci break; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci ar->fw = vmalloc(ie_len); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (ar->fw == NULL) { 10498c2ecf20Sopenharmony_ci ath6kl_err("fw storage cannot be allocated, len: %zd\n", ie_len); 10508c2ecf20Sopenharmony_ci ret = -ENOMEM; 10518c2ecf20Sopenharmony_ci goto out; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci memcpy(ar->fw, data, ie_len); 10558c2ecf20Sopenharmony_ci ar->fw_len = ie_len; 10568c2ecf20Sopenharmony_ci break; 10578c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_PATCH_IMAGE: 10588c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n", 10598c2ecf20Sopenharmony_ci ie_len); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (ar->fw_patch == NULL) { 10648c2ecf20Sopenharmony_ci ath6kl_err("fw_patch storage cannot be allocated, len: %zd\n", ie_len); 10658c2ecf20Sopenharmony_ci ret = -ENOMEM; 10668c2ecf20Sopenharmony_ci goto out; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ar->fw_patch_len = ie_len; 10708c2ecf20Sopenharmony_ci break; 10718c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_RESERVED_RAM_SIZE: 10728c2ecf20Sopenharmony_ci val = (__le32 *) data; 10738c2ecf20Sopenharmony_ci ar->hw.reserved_ram_size = le32_to_cpup(val); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 10768c2ecf20Sopenharmony_ci "found reserved ram size ie %d\n", 10778c2ecf20Sopenharmony_ci ar->hw.reserved_ram_size); 10788c2ecf20Sopenharmony_ci break; 10798c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_CAPABILITIES: 10808c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 10818c2ecf20Sopenharmony_ci "found firmware capabilities ie (%zd B)\n", 10828c2ecf20Sopenharmony_ci ie_len); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { 10858c2ecf20Sopenharmony_ci index = i / 8; 10868c2ecf20Sopenharmony_ci bit = i % 8; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (index == ie_len) 10898c2ecf20Sopenharmony_ci break; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (data[index] & (1 << bit)) 10928c2ecf20Sopenharmony_ci __set_bit(i, ar->fw_capabilities); 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "", 10968c2ecf20Sopenharmony_ci ar->fw_capabilities, 10978c2ecf20Sopenharmony_ci sizeof(ar->fw_capabilities)); 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_PATCH_ADDR: 11008c2ecf20Sopenharmony_ci if (ie_len != sizeof(*val)) 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci val = (__le32 *) data; 11048c2ecf20Sopenharmony_ci ar->hw.dataset_patch_addr = le32_to_cpup(val); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 11078c2ecf20Sopenharmony_ci "found patch address ie 0x%x\n", 11088c2ecf20Sopenharmony_ci ar->hw.dataset_patch_addr); 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_BOARD_ADDR: 11118c2ecf20Sopenharmony_ci if (ie_len != sizeof(*val)) 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci val = (__le32 *) data; 11158c2ecf20Sopenharmony_ci ar->hw.board_addr = le32_to_cpup(val); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 11188c2ecf20Sopenharmony_ci "found board address ie 0x%x\n", 11198c2ecf20Sopenharmony_ci ar->hw.board_addr); 11208c2ecf20Sopenharmony_ci break; 11218c2ecf20Sopenharmony_ci case ATH6KL_FW_IE_VIF_MAX: 11228c2ecf20Sopenharmony_ci if (ie_len != sizeof(*val)) 11238c2ecf20Sopenharmony_ci break; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci val = (__le32 *) data; 11268c2ecf20Sopenharmony_ci ar->vif_max = min_t(unsigned int, le32_to_cpup(val), 11278c2ecf20Sopenharmony_ci ATH6KL_VIF_MAX); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (ar->vif_max > 1 && !ar->p2p) 11308c2ecf20Sopenharmony_ci ar->max_norm_iface = 2; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 11338c2ecf20Sopenharmony_ci "found vif max ie %d\n", ar->vif_max); 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci default: 11368c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n", 11378c2ecf20Sopenharmony_ci le32_to_cpup(&hdr->id)); 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci len -= ie_len; 11428c2ecf20Sopenharmony_ci data += ie_len; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci ret = 0; 11468c2ecf20Sopenharmony_ciout: 11478c2ecf20Sopenharmony_ci release_firmware(fw); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci return ret; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ciint ath6kl_init_fetch_firmwares(struct ath6kl *ar) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci int ret; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci ret = ath6kl_fetch_board_file(ar); 11578c2ecf20Sopenharmony_ci if (ret) 11588c2ecf20Sopenharmony_ci return ret; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci ret = ath6kl_fetch_testmode_file(ar); 11618c2ecf20Sopenharmony_ci if (ret) 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API5_FILE); 11658c2ecf20Sopenharmony_ci if (ret == 0) { 11668c2ecf20Sopenharmony_ci ar->fw_api = 5; 11678c2ecf20Sopenharmony_ci goto out; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE); 11718c2ecf20Sopenharmony_ci if (ret == 0) { 11728c2ecf20Sopenharmony_ci ar->fw_api = 4; 11738c2ecf20Sopenharmony_ci goto out; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API3_FILE); 11778c2ecf20Sopenharmony_ci if (ret == 0) { 11788c2ecf20Sopenharmony_ci ar->fw_api = 3; 11798c2ecf20Sopenharmony_ci goto out; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API2_FILE); 11838c2ecf20Sopenharmony_ci if (ret == 0) { 11848c2ecf20Sopenharmony_ci ar->fw_api = 2; 11858c2ecf20Sopenharmony_ci goto out; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci ret = ath6kl_fetch_fw_api1(ar); 11898c2ecf20Sopenharmony_ci if (ret) 11908c2ecf20Sopenharmony_ci return ret; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ar->fw_api = 1; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ciout: 11958c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api %d\n", ar->fw_api); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci return 0; 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic int ath6kl_upload_board_file(struct ath6kl *ar) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci u32 board_address, board_ext_address, param; 12038c2ecf20Sopenharmony_ci u32 board_data_size, board_ext_data_size; 12048c2ecf20Sopenharmony_ci int ret; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (WARN_ON(ar->fw_board == NULL)) 12078c2ecf20Sopenharmony_ci return -ENOENT; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* 12108c2ecf20Sopenharmony_ci * Determine where in Target RAM to write Board Data. 12118c2ecf20Sopenharmony_ci * For AR6004, host determine Target RAM address for 12128c2ecf20Sopenharmony_ci * writing board data. 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_ci if (ar->hw.board_addr != 0) { 12158c2ecf20Sopenharmony_ci board_address = ar->hw.board_addr; 12168c2ecf20Sopenharmony_ci ath6kl_bmi_write_hi32(ar, hi_board_data, 12178c2ecf20Sopenharmony_ci board_address); 12188c2ecf20Sopenharmony_ci } else { 12198c2ecf20Sopenharmony_ci ret = ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address); 12208c2ecf20Sopenharmony_ci if (ret) { 12218c2ecf20Sopenharmony_ci ath6kl_err("Failed to get board file target address.\n"); 12228c2ecf20Sopenharmony_ci return ret; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* determine where in target ram to write extended board data */ 12278c2ecf20Sopenharmony_ci ret = ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address); 12288c2ecf20Sopenharmony_ci if (ret) { 12298c2ecf20Sopenharmony_ci ath6kl_err("Failed to get extended board file target address.\n"); 12308c2ecf20Sopenharmony_ci return ret; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if (ar->target_type == TARGET_TYPE_AR6003 && 12348c2ecf20Sopenharmony_ci board_ext_address == 0) { 12358c2ecf20Sopenharmony_ci ath6kl_err("Failed to get board file target address.\n"); 12368c2ecf20Sopenharmony_ci return -EINVAL; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci switch (ar->target_type) { 12408c2ecf20Sopenharmony_ci case TARGET_TYPE_AR6003: 12418c2ecf20Sopenharmony_ci board_data_size = AR6003_BOARD_DATA_SZ; 12428c2ecf20Sopenharmony_ci board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ; 12438c2ecf20Sopenharmony_ci if (ar->fw_board_len > (board_data_size + board_ext_data_size)) 12448c2ecf20Sopenharmony_ci board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ_V2; 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci case TARGET_TYPE_AR6004: 12478c2ecf20Sopenharmony_ci board_data_size = AR6004_BOARD_DATA_SZ; 12488c2ecf20Sopenharmony_ci board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ; 12498c2ecf20Sopenharmony_ci break; 12508c2ecf20Sopenharmony_ci default: 12518c2ecf20Sopenharmony_ci WARN_ON(1); 12528c2ecf20Sopenharmony_ci return -EINVAL; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci if (board_ext_address && 12568c2ecf20Sopenharmony_ci ar->fw_board_len == (board_data_size + board_ext_data_size)) { 12578c2ecf20Sopenharmony_ci /* write extended board data */ 12588c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 12598c2ecf20Sopenharmony_ci "writing extended board data to 0x%x (%d B)\n", 12608c2ecf20Sopenharmony_ci board_ext_address, board_ext_data_size); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci ret = ath6kl_bmi_write(ar, board_ext_address, 12638c2ecf20Sopenharmony_ci ar->fw_board + board_data_size, 12648c2ecf20Sopenharmony_ci board_ext_data_size); 12658c2ecf20Sopenharmony_ci if (ret) { 12668c2ecf20Sopenharmony_ci ath6kl_err("Failed to write extended board data: %d\n", 12678c2ecf20Sopenharmony_ci ret); 12688c2ecf20Sopenharmony_ci return ret; 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci /* record that extended board data is initialized */ 12728c2ecf20Sopenharmony_ci param = (board_ext_data_size << 16) | 1; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci ath6kl_bmi_write_hi32(ar, hi_board_ext_data_config, param); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (ar->fw_board_len < board_data_size) { 12788c2ecf20Sopenharmony_ci ath6kl_err("Too small board file: %zu\n", ar->fw_board_len); 12798c2ecf20Sopenharmony_ci ret = -EINVAL; 12808c2ecf20Sopenharmony_ci return ret; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n", 12848c2ecf20Sopenharmony_ci board_address, board_data_size); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, 12878c2ecf20Sopenharmony_ci board_data_size); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (ret) { 12908c2ecf20Sopenharmony_ci ath6kl_err("Board file bmi write failed: %d\n", ret); 12918c2ecf20Sopenharmony_ci return ret; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* record the fact that Board Data IS initialized */ 12958c2ecf20Sopenharmony_ci if ((ar->version.target_ver == AR6004_HW_1_3_VERSION) || 12968c2ecf20Sopenharmony_ci (ar->version.target_ver == AR6004_HW_3_0_VERSION)) 12978c2ecf20Sopenharmony_ci param = board_data_size; 12988c2ecf20Sopenharmony_ci else 12998c2ecf20Sopenharmony_ci param = 1; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, param); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci return ret; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic int ath6kl_upload_otp(struct ath6kl *ar) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci u32 address, param; 13098c2ecf20Sopenharmony_ci bool from_hw = false; 13108c2ecf20Sopenharmony_ci int ret; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (ar->fw_otp == NULL) 13138c2ecf20Sopenharmony_ci return 0; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci address = ar->hw.app_load_addr; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address, 13188c2ecf20Sopenharmony_ci ar->fw_otp_len); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, 13218c2ecf20Sopenharmony_ci ar->fw_otp_len); 13228c2ecf20Sopenharmony_ci if (ret) { 13238c2ecf20Sopenharmony_ci ath6kl_err("Failed to upload OTP file: %d\n", ret); 13248c2ecf20Sopenharmony_ci return ret; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* read firmware start address */ 13288c2ecf20Sopenharmony_ci ret = ath6kl_bmi_read_hi32(ar, hi_app_start, &address); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (ret) { 13318c2ecf20Sopenharmony_ci ath6kl_err("Failed to read hi_app_start: %d\n", ret); 13328c2ecf20Sopenharmony_ci return ret; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (ar->hw.app_start_override_addr == 0) { 13368c2ecf20Sopenharmony_ci ar->hw.app_start_override_addr = address; 13378c2ecf20Sopenharmony_ci from_hw = true; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr%s 0x%x\n", 13418c2ecf20Sopenharmony_ci from_hw ? " (from hw)" : "", 13428c2ecf20Sopenharmony_ci ar->hw.app_start_override_addr); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci /* execute the OTP code */ 13458c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", 13468c2ecf20Sopenharmony_ci ar->hw.app_start_override_addr); 13478c2ecf20Sopenharmony_ci param = 0; 13488c2ecf20Sopenharmony_ci ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, ¶m); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci return ret; 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic int ath6kl_upload_firmware(struct ath6kl *ar) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci u32 address; 13568c2ecf20Sopenharmony_ci int ret; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if (WARN_ON(ar->fw == NULL)) 13598c2ecf20Sopenharmony_ci return 0; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci address = ar->hw.app_load_addr; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n", 13648c2ecf20Sopenharmony_ci address, ar->fw_len); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (ret) { 13698c2ecf20Sopenharmony_ci ath6kl_err("Failed to write firmware: %d\n", ret); 13708c2ecf20Sopenharmony_ci return ret; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci /* 13748c2ecf20Sopenharmony_ci * Set starting address for firmware 13758c2ecf20Sopenharmony_ci * Don't need to setup app_start override addr on AR6004 13768c2ecf20Sopenharmony_ci */ 13778c2ecf20Sopenharmony_ci if (ar->target_type != TARGET_TYPE_AR6004) { 13788c2ecf20Sopenharmony_ci address = ar->hw.app_start_override_addr; 13798c2ecf20Sopenharmony_ci ath6kl_bmi_set_app_start(ar, address); 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci return ret; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic int ath6kl_upload_patch(struct ath6kl *ar) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci u32 address; 13878c2ecf20Sopenharmony_ci int ret; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (ar->fw_patch == NULL) 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci address = ar->hw.dataset_patch_addr; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n", 13958c2ecf20Sopenharmony_ci address, ar->fw_patch_len); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); 13988c2ecf20Sopenharmony_ci if (ret) { 13998c2ecf20Sopenharmony_ci ath6kl_err("Failed to write patch file: %d\n", ret); 14008c2ecf20Sopenharmony_ci return ret; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci ath6kl_bmi_write_hi32(ar, hi_dset_list_head, address); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci return 0; 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_cistatic int ath6kl_upload_testscript(struct ath6kl *ar) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci u32 address; 14118c2ecf20Sopenharmony_ci int ret; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci if (ar->testmode != 2) 14148c2ecf20Sopenharmony_ci return 0; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (ar->fw_testscript == NULL) 14178c2ecf20Sopenharmony_ci return 0; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci address = ar->hw.testscript_addr; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "writing testscript to 0x%x (%zd B)\n", 14228c2ecf20Sopenharmony_ci address, ar->fw_testscript_len); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci ret = ath6kl_bmi_write(ar, address, ar->fw_testscript, 14258c2ecf20Sopenharmony_ci ar->fw_testscript_len); 14268c2ecf20Sopenharmony_ci if (ret) { 14278c2ecf20Sopenharmony_ci ath6kl_err("Failed to write testscript file: %d\n", ret); 14288c2ecf20Sopenharmony_ci return ret; 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if ((ar->version.target_ver != AR6004_HW_1_3_VERSION) && 14348c2ecf20Sopenharmony_ci (ar->version.target_ver != AR6004_HW_3_0_VERSION)) 14358c2ecf20Sopenharmony_ci ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci return 0; 14408c2ecf20Sopenharmony_ci} 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_cistatic int ath6kl_init_upload(struct ath6kl *ar) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci u32 param, options, sleep, address; 14458c2ecf20Sopenharmony_ci int status = 0; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (ar->target_type != TARGET_TYPE_AR6003 && 14488c2ecf20Sopenharmony_ci ar->target_type != TARGET_TYPE_AR6004) 14498c2ecf20Sopenharmony_ci return -EINVAL; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* temporarily disable system sleep */ 14528c2ecf20Sopenharmony_ci address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; 14538c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_read(ar, address, ¶m); 14548c2ecf20Sopenharmony_ci if (status) 14558c2ecf20Sopenharmony_ci return status; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci options = param; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci param |= ATH6KL_OPTION_SLEEP_DISABLE; 14608c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 14618c2ecf20Sopenharmony_ci if (status) 14628c2ecf20Sopenharmony_ci return status; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; 14658c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_read(ar, address, ¶m); 14668c2ecf20Sopenharmony_ci if (status) 14678c2ecf20Sopenharmony_ci return status; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci sleep = param; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci param |= SM(SYSTEM_SLEEP_DISABLE, 1); 14728c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 14738c2ecf20Sopenharmony_ci if (status) 14748c2ecf20Sopenharmony_ci return status; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n", 14778c2ecf20Sopenharmony_ci options, sleep); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci /* program analog PLL register */ 14808c2ecf20Sopenharmony_ci /* no need to control 40/44MHz clock on AR6004 */ 14818c2ecf20Sopenharmony_ci if (ar->target_type != TARGET_TYPE_AR6004) { 14828c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, 14838c2ecf20Sopenharmony_ci 0xF9104001); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci if (status) 14868c2ecf20Sopenharmony_ci return status; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* Run at 80/88MHz by default */ 14898c2ecf20Sopenharmony_ci param = SM(CPU_CLOCK_STANDARD, 1); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; 14928c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 14938c2ecf20Sopenharmony_ci if (status) 14948c2ecf20Sopenharmony_ci return status; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci param = 0; 14988c2ecf20Sopenharmony_ci address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS; 14998c2ecf20Sopenharmony_ci param = SM(LPO_CAL_ENABLE, 1); 15008c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 15018c2ecf20Sopenharmony_ci if (status) 15028c2ecf20Sopenharmony_ci return status; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* WAR to avoid SDIO CRC err */ 15058c2ecf20Sopenharmony_ci if (ar->hw.flags & ATH6KL_HW_SDIO_CRC_ERROR_WAR) { 15068c2ecf20Sopenharmony_ci ath6kl_err("temporary war to avoid sdio crc error\n"); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci param = 0x28; 15098c2ecf20Sopenharmony_ci address = GPIO_BASE_ADDRESS + GPIO_PIN9_ADDRESS; 15108c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 15118c2ecf20Sopenharmony_ci if (status) 15128c2ecf20Sopenharmony_ci return status; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci param = 0x20; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; 15178c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 15188c2ecf20Sopenharmony_ci if (status) 15198c2ecf20Sopenharmony_ci return status; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS; 15228c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 15238c2ecf20Sopenharmony_ci if (status) 15248c2ecf20Sopenharmony_ci return status; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS; 15278c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 15288c2ecf20Sopenharmony_ci if (status) 15298c2ecf20Sopenharmony_ci return status; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS; 15328c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 15338c2ecf20Sopenharmony_ci if (status) 15348c2ecf20Sopenharmony_ci return status; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci /* write EEPROM data to Target RAM */ 15388c2ecf20Sopenharmony_ci status = ath6kl_upload_board_file(ar); 15398c2ecf20Sopenharmony_ci if (status) 15408c2ecf20Sopenharmony_ci return status; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci /* transfer One time Programmable data */ 15438c2ecf20Sopenharmony_ci status = ath6kl_upload_otp(ar); 15448c2ecf20Sopenharmony_ci if (status) 15458c2ecf20Sopenharmony_ci return status; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* Download Target firmware */ 15488c2ecf20Sopenharmony_ci status = ath6kl_upload_firmware(ar); 15498c2ecf20Sopenharmony_ci if (status) 15508c2ecf20Sopenharmony_ci return status; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci status = ath6kl_upload_patch(ar); 15538c2ecf20Sopenharmony_ci if (status) 15548c2ecf20Sopenharmony_ci return status; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* Download the test script */ 15578c2ecf20Sopenharmony_ci status = ath6kl_upload_testscript(ar); 15588c2ecf20Sopenharmony_ci if (status) 15598c2ecf20Sopenharmony_ci return status; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci /* Restore system sleep */ 15628c2ecf20Sopenharmony_ci address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; 15638c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, sleep); 15648c2ecf20Sopenharmony_ci if (status) 15658c2ecf20Sopenharmony_ci return status; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; 15688c2ecf20Sopenharmony_ci param = options | 0x20; 15698c2ecf20Sopenharmony_ci status = ath6kl_bmi_reg_write(ar, address, param); 15708c2ecf20Sopenharmony_ci if (status) 15718c2ecf20Sopenharmony_ci return status; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci return status; 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ciint ath6kl_init_hw_params(struct ath6kl *ar) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci const struct ath6kl_hw *hw; 15798c2ecf20Sopenharmony_ci int i; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hw_list); i++) { 15828c2ecf20Sopenharmony_ci hw = &hw_list[i]; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci if (hw->id == ar->version.target_ver) 15858c2ecf20Sopenharmony_ci break; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(hw_list)) { 15898c2ecf20Sopenharmony_ci ath6kl_err("Unsupported hardware version: 0x%x\n", 15908c2ecf20Sopenharmony_ci ar->version.target_ver); 15918c2ecf20Sopenharmony_ci return -EINVAL; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci ar->hw = *hw; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 15978c2ecf20Sopenharmony_ci "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n", 15988c2ecf20Sopenharmony_ci ar->version.target_ver, ar->target_type, 15998c2ecf20Sopenharmony_ci ar->hw.dataset_patch_addr, ar->hw.app_load_addr); 16008c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 16018c2ecf20Sopenharmony_ci "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x", 16028c2ecf20Sopenharmony_ci ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr, 16038c2ecf20Sopenharmony_ci ar->hw.reserved_ram_size); 16048c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 16058c2ecf20Sopenharmony_ci "refclk_hz %d uarttx_pin %d", 16068c2ecf20Sopenharmony_ci ar->hw.refclk_hz, ar->hw.uarttx_pin); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci return 0; 16098c2ecf20Sopenharmony_ci} 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cistatic const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci switch (type) { 16148c2ecf20Sopenharmony_ci case ATH6KL_HIF_TYPE_SDIO: 16158c2ecf20Sopenharmony_ci return "sdio"; 16168c2ecf20Sopenharmony_ci case ATH6KL_HIF_TYPE_USB: 16178c2ecf20Sopenharmony_ci return "usb"; 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci return NULL; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic const struct fw_capa_str_map { 16258c2ecf20Sopenharmony_ci int id; 16268c2ecf20Sopenharmony_ci const char *name; 16278c2ecf20Sopenharmony_ci} fw_capa_map[] = { 16288c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_HOST_P2P, "host-p2p" }, 16298c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_SCHED_SCAN, "sched-scan" }, 16308c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, "sta-p2pdev-duplex" }, 16318c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, "inactivity-timeout" }, 16328c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, "rsn-cap-override" }, 16338c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, "wow-mc-filter" }, 16348c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, "bmiss-enhance" }, 16358c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, "sscan-match-list" }, 16368c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, "rssi-scan-thold" }, 16378c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, "custom-mac-addr" }, 16388c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, "tx-err-notify" }, 16398c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_REGDOMAIN, "regdomain" }, 16408c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, "sched-scan-v2" }, 16418c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, "hb-poll" }, 16428c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_64BIT_RATES, "64bit-rates" }, 16438c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, "ap-inactivity-mins" }, 16448c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, "map-lp-endpoint" }, 16458c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, "ratetable-mcs15" }, 16468c2ecf20Sopenharmony_ci { ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, "no-ip-checksum" }, 16478c2ecf20Sopenharmony_ci}; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_cistatic const char *ath6kl_init_get_fw_capa_name(unsigned int id) 16508c2ecf20Sopenharmony_ci{ 16518c2ecf20Sopenharmony_ci int i; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_capa_map); i++) { 16548c2ecf20Sopenharmony_ci if (fw_capa_map[i].id == id) 16558c2ecf20Sopenharmony_ci return fw_capa_map[i].name; 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci return "<unknown>"; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic void ath6kl_init_get_fwcaps(struct ath6kl *ar, char *buf, size_t buf_len) 16628c2ecf20Sopenharmony_ci{ 16638c2ecf20Sopenharmony_ci u8 *data = (u8 *) ar->fw_capabilities; 16648c2ecf20Sopenharmony_ci size_t trunc_len, len = 0; 16658c2ecf20Sopenharmony_ci int i, index, bit; 16668c2ecf20Sopenharmony_ci char *trunc = "..."; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { 16698c2ecf20Sopenharmony_ci index = i / 8; 16708c2ecf20Sopenharmony_ci bit = i % 8; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (index >= sizeof(ar->fw_capabilities) * 4) 16738c2ecf20Sopenharmony_ci break; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (buf_len - len < 4) { 16768c2ecf20Sopenharmony_ci ath6kl_warn("firmware capability buffer too small!\n"); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci /* add "..." to the end of string */ 16798c2ecf20Sopenharmony_ci trunc_len = strlen(trunc) + 1; 16808c2ecf20Sopenharmony_ci strncpy(buf + buf_len - trunc_len, trunc, trunc_len); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci return; 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci if (data[index] & (1 << bit)) { 16868c2ecf20Sopenharmony_ci len += scnprintf(buf + len, buf_len - len, "%s,", 16878c2ecf20Sopenharmony_ci ath6kl_init_get_fw_capa_name(i)); 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci /* overwrite the last comma */ 16928c2ecf20Sopenharmony_ci if (len > 0) 16938c2ecf20Sopenharmony_ci len--; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci buf[len] = '\0'; 16968c2ecf20Sopenharmony_ci} 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_cistatic int ath6kl_init_hw_reset(struct ath6kl *ar) 16998c2ecf20Sopenharmony_ci{ 17008c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "cold resetting the device"); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci return ath6kl_diag_write32(ar, RESET_CONTROL_ADDRESS, 17038c2ecf20Sopenharmony_ci cpu_to_le32(RESET_CONTROL_COLD_RST)); 17048c2ecf20Sopenharmony_ci} 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_cistatic int __ath6kl_init_hw_start(struct ath6kl *ar) 17078c2ecf20Sopenharmony_ci{ 17088c2ecf20Sopenharmony_ci long timeleft; 17098c2ecf20Sopenharmony_ci int ret, i; 17108c2ecf20Sopenharmony_ci char buf[200]; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "hw start\n"); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci ret = ath6kl_hif_power_on(ar); 17158c2ecf20Sopenharmony_ci if (ret) 17168c2ecf20Sopenharmony_ci return ret; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci ret = ath6kl_configure_target(ar); 17198c2ecf20Sopenharmony_ci if (ret) 17208c2ecf20Sopenharmony_ci goto err_power_off; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci ret = ath6kl_init_upload(ar); 17238c2ecf20Sopenharmony_ci if (ret) 17248c2ecf20Sopenharmony_ci goto err_power_off; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci /* Do we need to finish the BMI phase */ 17278c2ecf20Sopenharmony_ci ret = ath6kl_bmi_done(ar); 17288c2ecf20Sopenharmony_ci if (ret) 17298c2ecf20Sopenharmony_ci goto err_power_off; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci /* 17328c2ecf20Sopenharmony_ci * The reason we have to wait for the target here is that the 17338c2ecf20Sopenharmony_ci * driver layer has to init BMI in order to set the host block 17348c2ecf20Sopenharmony_ci * size. 17358c2ecf20Sopenharmony_ci */ 17368c2ecf20Sopenharmony_ci ret = ath6kl_htc_wait_target(ar->htc_target); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) { 17398c2ecf20Sopenharmony_ci /* 17408c2ecf20Sopenharmony_ci * Most likely USB target is in odd state after reboot and 17418c2ecf20Sopenharmony_ci * needs a reset. A cold reset makes the whole device 17428c2ecf20Sopenharmony_ci * disappear from USB bus and initialisation starts from 17438c2ecf20Sopenharmony_ci * beginning. 17448c2ecf20Sopenharmony_ci */ 17458c2ecf20Sopenharmony_ci ath6kl_warn("htc wait target timed out, resetting device\n"); 17468c2ecf20Sopenharmony_ci ath6kl_init_hw_reset(ar); 17478c2ecf20Sopenharmony_ci goto err_power_off; 17488c2ecf20Sopenharmony_ci } else if (ret) { 17498c2ecf20Sopenharmony_ci ath6kl_err("htc wait target failed: %d\n", ret); 17508c2ecf20Sopenharmony_ci goto err_power_off; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci ret = ath6kl_init_service_ep(ar); 17548c2ecf20Sopenharmony_ci if (ret) { 17558c2ecf20Sopenharmony_ci ath6kl_err("Endpoint service initialization failed: %d\n", ret); 17568c2ecf20Sopenharmony_ci goto err_cleanup_scatter; 17578c2ecf20Sopenharmony_ci } 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci /* setup credit distribution */ 17608c2ecf20Sopenharmony_ci ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci /* start HTC */ 17638c2ecf20Sopenharmony_ci ret = ath6kl_htc_start(ar->htc_target); 17648c2ecf20Sopenharmony_ci if (ret) { 17658c2ecf20Sopenharmony_ci /* FIXME: call this */ 17668c2ecf20Sopenharmony_ci ath6kl_cookie_cleanup(ar); 17678c2ecf20Sopenharmony_ci goto err_cleanup_scatter; 17688c2ecf20Sopenharmony_ci } 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci /* Wait for Wmi event to be ready */ 17718c2ecf20Sopenharmony_ci timeleft = wait_event_interruptible_timeout(ar->event_wq, 17728c2ecf20Sopenharmony_ci test_bit(WMI_READY, 17738c2ecf20Sopenharmony_ci &ar->flag), 17748c2ecf20Sopenharmony_ci WMI_TIMEOUT); 17758c2ecf20Sopenharmony_ci if (timeleft <= 0) { 17768c2ecf20Sopenharmony_ci clear_bit(WMI_READY, &ar->flag); 17778c2ecf20Sopenharmony_ci ath6kl_err("wmi is not ready or wait was interrupted: %ld\n", 17788c2ecf20Sopenharmony_ci timeleft); 17798c2ecf20Sopenharmony_ci ret = -EIO; 17808c2ecf20Sopenharmony_ci goto err_htc_stop; 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { 17868c2ecf20Sopenharmony_ci ath6kl_info("%s %s fw %s api %d%s\n", 17878c2ecf20Sopenharmony_ci ar->hw.name, 17888c2ecf20Sopenharmony_ci ath6kl_init_get_hif_name(ar->hif_type), 17898c2ecf20Sopenharmony_ci ar->wiphy->fw_version, 17908c2ecf20Sopenharmony_ci ar->fw_api, 17918c2ecf20Sopenharmony_ci test_bit(TESTMODE, &ar->flag) ? " testmode" : ""); 17928c2ecf20Sopenharmony_ci ath6kl_init_get_fwcaps(ar, buf, sizeof(buf)); 17938c2ecf20Sopenharmony_ci ath6kl_info("firmware supports: %s\n", buf); 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { 17978c2ecf20Sopenharmony_ci ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", 17988c2ecf20Sopenharmony_ci ATH6KL_ABI_VERSION, ar->version.abi_ver); 17998c2ecf20Sopenharmony_ci ret = -EIO; 18008c2ecf20Sopenharmony_ci goto err_htc_stop; 18018c2ecf20Sopenharmony_ci } 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci /* communicate the wmi protocol verision to the target */ 18068c2ecf20Sopenharmony_ci /* FIXME: return error */ 18078c2ecf20Sopenharmony_ci if ((ath6kl_set_host_app_area(ar)) != 0) 18088c2ecf20Sopenharmony_ci ath6kl_err("unable to set the host app area\n"); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci for (i = 0; i < ar->vif_max; i++) { 18118c2ecf20Sopenharmony_ci ret = ath6kl_target_config_wlan_params(ar, i); 18128c2ecf20Sopenharmony_ci if (ret) 18138c2ecf20Sopenharmony_ci goto err_htc_stop; 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci return 0; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_cierr_htc_stop: 18198c2ecf20Sopenharmony_ci ath6kl_htc_stop(ar->htc_target); 18208c2ecf20Sopenharmony_cierr_cleanup_scatter: 18218c2ecf20Sopenharmony_ci ath6kl_hif_cleanup_scatter(ar); 18228c2ecf20Sopenharmony_cierr_power_off: 18238c2ecf20Sopenharmony_ci ath6kl_hif_power_off(ar); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci return ret; 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ciint ath6kl_init_hw_start(struct ath6kl *ar) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci int err; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci err = __ath6kl_init_hw_start(ar); 18338c2ecf20Sopenharmony_ci if (err) 18348c2ecf20Sopenharmony_ci return err; 18358c2ecf20Sopenharmony_ci ar->state = ATH6KL_STATE_ON; 18368c2ecf20Sopenharmony_ci return 0; 18378c2ecf20Sopenharmony_ci} 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_cistatic int __ath6kl_init_hw_stop(struct ath6kl *ar) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci int ret; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "hw stop\n"); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci ath6kl_htc_stop(ar->htc_target); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci ath6kl_hif_stop(ar); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci ath6kl_bmi_reset(ar); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci ret = ath6kl_hif_power_off(ar); 18528c2ecf20Sopenharmony_ci if (ret) 18538c2ecf20Sopenharmony_ci ath6kl_warn("failed to power off hif: %d\n", ret); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci return 0; 18568c2ecf20Sopenharmony_ci} 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ciint ath6kl_init_hw_stop(struct ath6kl *ar) 18598c2ecf20Sopenharmony_ci{ 18608c2ecf20Sopenharmony_ci int err; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci err = __ath6kl_init_hw_stop(ar); 18638c2ecf20Sopenharmony_ci if (err) 18648c2ecf20Sopenharmony_ci return err; 18658c2ecf20Sopenharmony_ci ar->state = ATH6KL_STATE_OFF; 18668c2ecf20Sopenharmony_ci return 0; 18678c2ecf20Sopenharmony_ci} 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_civoid ath6kl_init_hw_restart(struct ath6kl *ar) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci clear_bit(WMI_READY, &ar->flag); 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci ath6kl_cfg80211_stop_all(ar); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci if (__ath6kl_init_hw_stop(ar)) { 18768c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to stop during fw error recovery\n"); 18778c2ecf20Sopenharmony_ci return; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci if (__ath6kl_init_hw_start(ar)) { 18818c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); 18828c2ecf20Sopenharmony_ci return; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci} 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_civoid ath6kl_stop_txrx(struct ath6kl *ar) 18878c2ecf20Sopenharmony_ci{ 18888c2ecf20Sopenharmony_ci struct ath6kl_vif *vif, *tmp_vif; 18898c2ecf20Sopenharmony_ci int i; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci set_bit(DESTROY_IN_PROGRESS, &ar->flag); 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci if (down_interruptible(&ar->sem)) { 18948c2ecf20Sopenharmony_ci ath6kl_err("down_interruptible failed\n"); 18958c2ecf20Sopenharmony_ci return; 18968c2ecf20Sopenharmony_ci } 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci for (i = 0; i < AP_MAX_NUM_STA; i++) 18998c2ecf20Sopenharmony_ci aggr_reset_state(ar->sta_list[i].aggr_conn); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci spin_lock_bh(&ar->list_lock); 19028c2ecf20Sopenharmony_ci list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) { 19038c2ecf20Sopenharmony_ci list_del(&vif->list); 19048c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->list_lock); 19058c2ecf20Sopenharmony_ci ath6kl_cfg80211_vif_stop(vif, test_bit(WMI_READY, &ar->flag)); 19068c2ecf20Sopenharmony_ci rtnl_lock(); 19078c2ecf20Sopenharmony_ci ath6kl_cfg80211_vif_cleanup(vif); 19088c2ecf20Sopenharmony_ci rtnl_unlock(); 19098c2ecf20Sopenharmony_ci spin_lock_bh(&ar->list_lock); 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->list_lock); 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci clear_bit(WMI_READY, &ar->flag); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci if (ar->fw_recovery.enable) 19168c2ecf20Sopenharmony_ci del_timer_sync(&ar->fw_recovery.hb_timer); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci /* 19198c2ecf20Sopenharmony_ci * After wmi_shudown all WMI events will be dropped. We 19208c2ecf20Sopenharmony_ci * need to cleanup the buffers allocated in AP mode and 19218c2ecf20Sopenharmony_ci * give disconnect notification to stack, which usually 19228c2ecf20Sopenharmony_ci * happens in the disconnect_event. Simulate the disconnect 19238c2ecf20Sopenharmony_ci * event by calling the function directly. Sometimes 19248c2ecf20Sopenharmony_ci * disconnect_event will be received when the debug logs 19258c2ecf20Sopenharmony_ci * are collected. 19268c2ecf20Sopenharmony_ci */ 19278c2ecf20Sopenharmony_ci ath6kl_wmi_shutdown(ar->wmi); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci clear_bit(WMI_ENABLED, &ar->flag); 19308c2ecf20Sopenharmony_ci if (ar->htc_target) { 19318c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__); 19328c2ecf20Sopenharmony_ci ath6kl_htc_stop(ar->htc_target); 19338c2ecf20Sopenharmony_ci } 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci /* 19368c2ecf20Sopenharmony_ci * Try to reset the device if we can. The driver may have been 19378c2ecf20Sopenharmony_ci * configure NOT to reset the target during a debug session. 19388c2ecf20Sopenharmony_ci */ 19398c2ecf20Sopenharmony_ci ath6kl_init_hw_reset(ar); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci up(&ar->sem); 19428c2ecf20Sopenharmony_ci} 19438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath6kl_stop_txrx); 1944