18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file is part of wl18xx 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "../wlcore/cmd.h" 98c2ecf20Sopenharmony_ci#include "../wlcore/debug.h" 108c2ecf20Sopenharmony_ci#include "../wlcore/acx.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "acx.h" 138c2ecf20Sopenharmony_ci#include "wl18xx.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciint wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, 168c2ecf20Sopenharmony_ci u32 sdio_blk_size, u32 extra_mem_blks, 178c2ecf20Sopenharmony_ci u32 len_field_size) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci struct wl18xx_acx_host_config_bitmap *bitmap_conf; 208c2ecf20Sopenharmony_ci int ret; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d", 238c2ecf20Sopenharmony_ci host_cfg_bitmap, sdio_blk_size, extra_mem_blks, 248c2ecf20Sopenharmony_ci len_field_size); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); 278c2ecf20Sopenharmony_ci if (!bitmap_conf) { 288c2ecf20Sopenharmony_ci ret = -ENOMEM; 298c2ecf20Sopenharmony_ci goto out; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); 338c2ecf20Sopenharmony_ci bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size); 348c2ecf20Sopenharmony_ci bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks); 358c2ecf20Sopenharmony_ci bitmap_conf->length_field_size = cpu_to_le32(len_field_size); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, 388c2ecf20Sopenharmony_ci bitmap_conf, sizeof(*bitmap_conf)); 398c2ecf20Sopenharmony_ci if (ret < 0) { 408c2ecf20Sopenharmony_ci wl1271_warning("wl1271 bitmap config opt failed: %d", ret); 418c2ecf20Sopenharmony_ci goto out; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciout: 458c2ecf20Sopenharmony_ci kfree(bitmap_conf); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciint wl18xx_acx_set_checksum_state(struct wl1271 *wl) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct wl18xx_acx_checksum_state *acx; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx checksum state"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 588c2ecf20Sopenharmony_ci if (!acx) { 598c2ecf20Sopenharmony_ci ret = -ENOMEM; 608c2ecf20Sopenharmony_ci goto out; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx)); 668c2ecf20Sopenharmony_ci if (ret < 0) { 678c2ecf20Sopenharmony_ci wl1271_warning("failed to set Tx checksum state: %d", ret); 688c2ecf20Sopenharmony_ci goto out; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ciout: 728c2ecf20Sopenharmony_ci kfree(acx); 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciint wl18xx_acx_clear_statistics(struct wl1271 *wl) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct wl18xx_acx_clear_statistics *acx; 798c2ecf20Sopenharmony_ci int ret = 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx clear statistics"); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 848c2ecf20Sopenharmony_ci if (!acx) { 858c2ecf20Sopenharmony_ci ret = -ENOMEM; 868c2ecf20Sopenharmony_ci goto out; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx)); 908c2ecf20Sopenharmony_ci if (ret < 0) { 918c2ecf20Sopenharmony_ci wl1271_warning("failed to clear firmware statistics: %d", ret); 928c2ecf20Sopenharmony_ci goto out; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciout: 968c2ecf20Sopenharmony_ci kfree(acx); 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciint wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct wlcore_peer_ht_operation_mode *acx; 1038c2ecf20Sopenharmony_ci int ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d", 1068c2ecf20Sopenharmony_ci hlid, wide); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 1098c2ecf20Sopenharmony_ci if (!acx) { 1108c2ecf20Sopenharmony_ci ret = -ENOMEM; 1118c2ecf20Sopenharmony_ci goto out; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci acx->hlid = hlid; 1158c2ecf20Sopenharmony_ci acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx, 1188c2ecf20Sopenharmony_ci sizeof(*acx)); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (ret < 0) { 1218c2ecf20Sopenharmony_ci wl1271_warning("acx peer ht operation mode failed: %d", ret); 1228c2ecf20Sopenharmony_ci goto out; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciout: 1268c2ecf20Sopenharmony_ci kfree(acx); 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * this command is basically the same as wl1271_acx_ht_capabilities, 1338c2ecf20Sopenharmony_ci * with the addition of supported rates. they should be unified in 1348c2ecf20Sopenharmony_ci * the next fw api change 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ciint wl18xx_acx_set_peer_cap(struct wl1271 *wl, 1378c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap, 1388c2ecf20Sopenharmony_ci bool allow_ht_operation, 1398c2ecf20Sopenharmony_ci u32 rate_set, u8 hlid) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct wlcore_acx_peer_cap *acx; 1428c2ecf20Sopenharmony_ci int ret = 0; 1438c2ecf20Sopenharmony_ci u32 ht_capabilites = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, 1468c2ecf20Sopenharmony_ci "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x", 1478c2ecf20Sopenharmony_ci ht_cap->ht_supported, ht_cap->cap, rate_set); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 1508c2ecf20Sopenharmony_ci if (!acx) { 1518c2ecf20Sopenharmony_ci ret = -ENOMEM; 1528c2ecf20Sopenharmony_ci goto out; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (allow_ht_operation && ht_cap->ht_supported) { 1568c2ecf20Sopenharmony_ci /* no need to translate capabilities - use the spec values */ 1578c2ecf20Sopenharmony_ci ht_capabilites = ht_cap->cap; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * this bit is not employed by the spec but only by FW to 1618c2ecf20Sopenharmony_ci * indicate peer HT support 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* get data from A-MPDU parameters field */ 1668c2ecf20Sopenharmony_ci acx->ampdu_max_length = ht_cap->ampdu_factor; 1678c2ecf20Sopenharmony_ci acx->ampdu_min_spacing = ht_cap->ampdu_density; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci acx->hlid = hlid; 1718c2ecf20Sopenharmony_ci acx->ht_capabilites = cpu_to_le32(ht_capabilites); 1728c2ecf20Sopenharmony_ci acx->supported_rates = cpu_to_le32(rate_set); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_PEER_CAP, acx, sizeof(*acx)); 1758c2ecf20Sopenharmony_ci if (ret < 0) { 1768c2ecf20Sopenharmony_ci wl1271_warning("acx ht capabilities setting failed: %d", ret); 1778c2ecf20Sopenharmony_ci goto out; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciout: 1818c2ecf20Sopenharmony_ci kfree(acx); 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * When the host is suspended, we don't want to get any fast-link/PSM 1878c2ecf20Sopenharmony_ci * notifications 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ciint wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, 1908c2ecf20Sopenharmony_ci bool action) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct wl18xx_acx_interrupt_notify *acx; 1938c2ecf20Sopenharmony_ci int ret = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!acx) { 1978c2ecf20Sopenharmony_ci ret = -ENOMEM; 1988c2ecf20Sopenharmony_ci goto out; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci acx->enable = action; 2028c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx)); 2038c2ecf20Sopenharmony_ci if (ret < 0) { 2048c2ecf20Sopenharmony_ci wl1271_warning("acx interrupt notify setting failed: %d", ret); 2058c2ecf20Sopenharmony_ci goto out; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciout: 2098c2ecf20Sopenharmony_ci kfree(acx); 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * When the host is suspended, we can configure the FW to disable RX BA 2158c2ecf20Sopenharmony_ci * notifications. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ciint wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct wl18xx_acx_rx_ba_filter *acx; 2208c2ecf20Sopenharmony_ci int ret = 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 2238c2ecf20Sopenharmony_ci if (!acx) { 2248c2ecf20Sopenharmony_ci ret = -ENOMEM; 2258c2ecf20Sopenharmony_ci goto out; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci acx->enable = (u32)action; 2298c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_RX_BA_FILTER, acx, sizeof(*acx)); 2308c2ecf20Sopenharmony_ci if (ret < 0) { 2318c2ecf20Sopenharmony_ci wl1271_warning("acx rx ba activity filter setting failed: %d", 2328c2ecf20Sopenharmony_ci ret); 2338c2ecf20Sopenharmony_ci goto out; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciout: 2378c2ecf20Sopenharmony_ci kfree(acx); 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ciint wl18xx_acx_ap_sleep(struct wl1271 *wl) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct wl18xx_priv *priv = wl->priv; 2448c2ecf20Sopenharmony_ci struct acx_ap_sleep_cfg *acx; 2458c2ecf20Sopenharmony_ci struct conf_ap_sleep_settings *conf = &priv->conf.ap_sleep; 2468c2ecf20Sopenharmony_ci int ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx config ap sleep"); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 2518c2ecf20Sopenharmony_ci if (!acx) { 2528c2ecf20Sopenharmony_ci ret = -ENOMEM; 2538c2ecf20Sopenharmony_ci goto out; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci acx->idle_duty_cycle = conf->idle_duty_cycle; 2578c2ecf20Sopenharmony_ci acx->connected_duty_cycle = conf->connected_duty_cycle; 2588c2ecf20Sopenharmony_ci acx->max_stations_thresh = conf->max_stations_thresh; 2598c2ecf20Sopenharmony_ci acx->idle_conn_thresh = conf->idle_conn_thresh; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_AP_SLEEP_CFG, acx, sizeof(*acx)); 2628c2ecf20Sopenharmony_ci if (ret < 0) { 2638c2ecf20Sopenharmony_ci wl1271_warning("acx config ap-sleep failed: %d", ret); 2648c2ecf20Sopenharmony_ci goto out; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ciout: 2688c2ecf20Sopenharmony_ci kfree(acx); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ciint wl18xx_acx_dynamic_fw_traces(struct wl1271 *wl) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct acx_dynamic_fw_traces_cfg *acx; 2758c2ecf20Sopenharmony_ci int ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx dynamic fw traces config %d", 2788c2ecf20Sopenharmony_ci wl->dynamic_fw_traces); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 2818c2ecf20Sopenharmony_ci if (!acx) { 2828c2ecf20Sopenharmony_ci ret = -ENOMEM; 2838c2ecf20Sopenharmony_ci goto out; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci acx->dynamic_fw_traces = cpu_to_le32(wl->dynamic_fw_traces); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_DYNAMIC_TRACES_CFG, 2898c2ecf20Sopenharmony_ci acx, sizeof(*acx)); 2908c2ecf20Sopenharmony_ci if (ret < 0) { 2918c2ecf20Sopenharmony_ci wl1271_warning("acx config dynamic fw traces failed: %d", ret); 2928c2ecf20Sopenharmony_ci goto out; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ciout: 2958c2ecf20Sopenharmony_ci kfree(acx); 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ciint wl18xx_acx_time_sync_cfg(struct wl1271 *wl) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct acx_time_sync_cfg *acx; 3028c2ecf20Sopenharmony_ci int ret; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx time sync cfg: mode %d, addr: %pM", 3058c2ecf20Sopenharmony_ci wl->conf.sg.params[WL18XX_CONF_SG_TIME_SYNC], 3068c2ecf20Sopenharmony_ci wl->zone_master_mac_addr); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 3098c2ecf20Sopenharmony_ci if (!acx) { 3108c2ecf20Sopenharmony_ci ret = -ENOMEM; 3118c2ecf20Sopenharmony_ci goto out; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci acx->sync_mode = wl->conf.sg.params[WL18XX_CONF_SG_TIME_SYNC]; 3158c2ecf20Sopenharmony_ci memcpy(acx->zone_mac_addr, wl->zone_master_mac_addr, ETH_ALEN); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_TIME_SYNC_CFG, 3188c2ecf20Sopenharmony_ci acx, sizeof(*acx)); 3198c2ecf20Sopenharmony_ci if (ret < 0) { 3208c2ecf20Sopenharmony_ci wl1271_warning("acx time sync cfg failed: %d", ret); 3218c2ecf20Sopenharmony_ci goto out; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ciout: 3248c2ecf20Sopenharmony_ci kfree(acx); 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci} 327