162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file is part of wl18xx 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "../wlcore/cmd.h" 962306a36Sopenharmony_ci#include "../wlcore/debug.h" 1062306a36Sopenharmony_ci#include "../wlcore/acx.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "acx.h" 1362306a36Sopenharmony_ci#include "wl18xx.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciint wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, 1662306a36Sopenharmony_ci u32 sdio_blk_size, u32 extra_mem_blks, 1762306a36Sopenharmony_ci u32 len_field_size) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct wl18xx_acx_host_config_bitmap *bitmap_conf; 2062306a36Sopenharmony_ci int ret; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d", 2362306a36Sopenharmony_ci host_cfg_bitmap, sdio_blk_size, extra_mem_blks, 2462306a36Sopenharmony_ci len_field_size); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); 2762306a36Sopenharmony_ci if (!bitmap_conf) { 2862306a36Sopenharmony_ci ret = -ENOMEM; 2962306a36Sopenharmony_ci goto out; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); 3362306a36Sopenharmony_ci bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size); 3462306a36Sopenharmony_ci bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks); 3562306a36Sopenharmony_ci bitmap_conf->length_field_size = cpu_to_le32(len_field_size); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, 3862306a36Sopenharmony_ci bitmap_conf, sizeof(*bitmap_conf)); 3962306a36Sopenharmony_ci if (ret < 0) { 4062306a36Sopenharmony_ci wl1271_warning("wl1271 bitmap config opt failed: %d", ret); 4162306a36Sopenharmony_ci goto out; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciout: 4562306a36Sopenharmony_ci kfree(bitmap_conf); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return ret; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciint wl18xx_acx_set_checksum_state(struct wl1271 *wl) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct wl18xx_acx_checksum_state *acx; 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx checksum state"); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 5862306a36Sopenharmony_ci if (!acx) { 5962306a36Sopenharmony_ci ret = -ENOMEM; 6062306a36Sopenharmony_ci goto out; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx)); 6662306a36Sopenharmony_ci if (ret < 0) { 6762306a36Sopenharmony_ci wl1271_warning("failed to set Tx checksum state: %d", ret); 6862306a36Sopenharmony_ci goto out; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciout: 7262306a36Sopenharmony_ci kfree(acx); 7362306a36Sopenharmony_ci return ret; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciint wl18xx_acx_clear_statistics(struct wl1271 *wl) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct wl18xx_acx_clear_statistics *acx; 7962306a36Sopenharmony_ci int ret = 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx clear statistics"); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 8462306a36Sopenharmony_ci if (!acx) { 8562306a36Sopenharmony_ci ret = -ENOMEM; 8662306a36Sopenharmony_ci goto out; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx)); 9062306a36Sopenharmony_ci if (ret < 0) { 9162306a36Sopenharmony_ci wl1271_warning("failed to clear firmware statistics: %d", ret); 9262306a36Sopenharmony_ci goto out; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciout: 9662306a36Sopenharmony_ci kfree(acx); 9762306a36Sopenharmony_ci return ret; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciint wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct wlcore_peer_ht_operation_mode *acx; 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d", 10662306a36Sopenharmony_ci hlid, wide); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 10962306a36Sopenharmony_ci if (!acx) { 11062306a36Sopenharmony_ci ret = -ENOMEM; 11162306a36Sopenharmony_ci goto out; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci acx->hlid = hlid; 11562306a36Sopenharmony_ci acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx, 11862306a36Sopenharmony_ci sizeof(*acx)); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (ret < 0) { 12162306a36Sopenharmony_ci wl1271_warning("acx peer ht operation mode failed: %d", ret); 12262306a36Sopenharmony_ci goto out; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciout: 12662306a36Sopenharmony_ci kfree(acx); 12762306a36Sopenharmony_ci return ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * this command is basically the same as wl1271_acx_ht_capabilities, 13362306a36Sopenharmony_ci * with the addition of supported rates. they should be unified in 13462306a36Sopenharmony_ci * the next fw api change 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ciint wl18xx_acx_set_peer_cap(struct wl1271 *wl, 13762306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap, 13862306a36Sopenharmony_ci bool allow_ht_operation, 13962306a36Sopenharmony_ci u32 rate_set, u8 hlid) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct wlcore_acx_peer_cap *acx; 14262306a36Sopenharmony_ci int ret = 0; 14362306a36Sopenharmony_ci u32 ht_capabilites = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, 14662306a36Sopenharmony_ci "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x", 14762306a36Sopenharmony_ci ht_cap->ht_supported, ht_cap->cap, rate_set); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 15062306a36Sopenharmony_ci if (!acx) { 15162306a36Sopenharmony_ci ret = -ENOMEM; 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (allow_ht_operation && ht_cap->ht_supported) { 15662306a36Sopenharmony_ci /* no need to translate capabilities - use the spec values */ 15762306a36Sopenharmony_ci ht_capabilites = ht_cap->cap; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * this bit is not employed by the spec but only by FW to 16162306a36Sopenharmony_ci * indicate peer HT support 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* get data from A-MPDU parameters field */ 16662306a36Sopenharmony_ci acx->ampdu_max_length = ht_cap->ampdu_factor; 16762306a36Sopenharmony_ci acx->ampdu_min_spacing = ht_cap->ampdu_density; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci acx->hlid = hlid; 17162306a36Sopenharmony_ci acx->ht_capabilites = cpu_to_le32(ht_capabilites); 17262306a36Sopenharmony_ci acx->supported_rates = cpu_to_le32(rate_set); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_PEER_CAP, acx, sizeof(*acx)); 17562306a36Sopenharmony_ci if (ret < 0) { 17662306a36Sopenharmony_ci wl1271_warning("acx ht capabilities setting failed: %d", ret); 17762306a36Sopenharmony_ci goto out; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciout: 18162306a36Sopenharmony_ci kfree(acx); 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * When the host is suspended, we don't want to get any fast-link/PSM 18762306a36Sopenharmony_ci * notifications 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ciint wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, 19062306a36Sopenharmony_ci bool action) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct wl18xx_acx_interrupt_notify *acx; 19362306a36Sopenharmony_ci int ret = 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 19662306a36Sopenharmony_ci if (!acx) { 19762306a36Sopenharmony_ci ret = -ENOMEM; 19862306a36Sopenharmony_ci goto out; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci acx->enable = action; 20262306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx)); 20362306a36Sopenharmony_ci if (ret < 0) { 20462306a36Sopenharmony_ci wl1271_warning("acx interrupt notify setting failed: %d", ret); 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciout: 20962306a36Sopenharmony_ci kfree(acx); 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * When the host is suspended, we can configure the FW to disable RX BA 21562306a36Sopenharmony_ci * notifications. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ciint wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct wl18xx_acx_rx_ba_filter *acx; 22062306a36Sopenharmony_ci int ret = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 22362306a36Sopenharmony_ci if (!acx) { 22462306a36Sopenharmony_ci ret = -ENOMEM; 22562306a36Sopenharmony_ci goto out; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci acx->enable = (u32)action; 22962306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_RX_BA_FILTER, acx, sizeof(*acx)); 23062306a36Sopenharmony_ci if (ret < 0) { 23162306a36Sopenharmony_ci wl1271_warning("acx rx ba activity filter setting failed: %d", 23262306a36Sopenharmony_ci ret); 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ciout: 23762306a36Sopenharmony_ci kfree(acx); 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciint wl18xx_acx_ap_sleep(struct wl1271 *wl) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct wl18xx_priv *priv = wl->priv; 24462306a36Sopenharmony_ci struct acx_ap_sleep_cfg *acx; 24562306a36Sopenharmony_ci struct conf_ap_sleep_settings *conf = &priv->conf.ap_sleep; 24662306a36Sopenharmony_ci int ret; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx config ap sleep"); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 25162306a36Sopenharmony_ci if (!acx) { 25262306a36Sopenharmony_ci ret = -ENOMEM; 25362306a36Sopenharmony_ci goto out; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci acx->idle_duty_cycle = conf->idle_duty_cycle; 25762306a36Sopenharmony_ci acx->connected_duty_cycle = conf->connected_duty_cycle; 25862306a36Sopenharmony_ci acx->max_stations_thresh = conf->max_stations_thresh; 25962306a36Sopenharmony_ci acx->idle_conn_thresh = conf->idle_conn_thresh; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_AP_SLEEP_CFG, acx, sizeof(*acx)); 26262306a36Sopenharmony_ci if (ret < 0) { 26362306a36Sopenharmony_ci wl1271_warning("acx config ap-sleep failed: %d", ret); 26462306a36Sopenharmony_ci goto out; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciout: 26862306a36Sopenharmony_ci kfree(acx); 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciint wl18xx_acx_dynamic_fw_traces(struct wl1271 *wl) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct acx_dynamic_fw_traces_cfg *acx; 27562306a36Sopenharmony_ci int ret; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx dynamic fw traces config %d", 27862306a36Sopenharmony_ci wl->dynamic_fw_traces); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 28162306a36Sopenharmony_ci if (!acx) { 28262306a36Sopenharmony_ci ret = -ENOMEM; 28362306a36Sopenharmony_ci goto out; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci acx->dynamic_fw_traces = cpu_to_le32(wl->dynamic_fw_traces); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_DYNAMIC_TRACES_CFG, 28962306a36Sopenharmony_ci acx, sizeof(*acx)); 29062306a36Sopenharmony_ci if (ret < 0) { 29162306a36Sopenharmony_ci wl1271_warning("acx config dynamic fw traces failed: %d", ret); 29262306a36Sopenharmony_ci goto out; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ciout: 29562306a36Sopenharmony_ci kfree(acx); 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ciint wl18xx_acx_time_sync_cfg(struct wl1271 *wl) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct acx_time_sync_cfg *acx; 30262306a36Sopenharmony_ci int ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci wl1271_debug(DEBUG_ACX, "acx time sync cfg: mode %d, addr: %pM", 30562306a36Sopenharmony_ci wl->conf.sg.params[WL18XX_CONF_SG_TIME_SYNC], 30662306a36Sopenharmony_ci wl->zone_master_mac_addr); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci acx = kzalloc(sizeof(*acx), GFP_KERNEL); 30962306a36Sopenharmony_ci if (!acx) { 31062306a36Sopenharmony_ci ret = -ENOMEM; 31162306a36Sopenharmony_ci goto out; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci acx->sync_mode = wl->conf.sg.params[WL18XX_CONF_SG_TIME_SYNC]; 31562306a36Sopenharmony_ci memcpy(acx->zone_mac_addr, wl->zone_master_mac_addr, ETH_ALEN); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ret = wl1271_cmd_configure(wl, ACX_TIME_SYNC_CFG, 31862306a36Sopenharmony_ci acx, sizeof(*acx)); 31962306a36Sopenharmony_ci if (ret < 0) { 32062306a36Sopenharmony_ci wl1271_warning("acx time sync cfg failed: %d", ret); 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ciout: 32462306a36Sopenharmony_ci kfree(acx); 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci} 327