162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2014 Redpine Signals Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/firmware.h> 1862306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 1962306a36Sopenharmony_ci#include "rsi_mgmt.h" 2062306a36Sopenharmony_ci#include "rsi_hal.h" 2162306a36Sopenharmony_ci#include "rsi_sdio.h" 2262306a36Sopenharmony_ci#include "rsi_common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* FLASH Firmware */ 2562306a36Sopenharmony_cistatic struct ta_metadata metadata_flash_content[] = { 2662306a36Sopenharmony_ci {"flash_content", 0x00010000}, 2762306a36Sopenharmony_ci {"rsi/rs9113_wlan_qspi.rps", 0x00010000}, 2862306a36Sopenharmony_ci {"rsi/rs9113_wlan_bt_dual_mode.rps", 0x00010000}, 2962306a36Sopenharmony_ci {"flash_content", 0x00010000}, 3062306a36Sopenharmony_ci {"rsi/rs9113_ap_bt_dual_mode.rps", 0x00010000}, 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic struct ta_metadata metadata[] = {{"pmemdata_dummy", 0x00000000}, 3562306a36Sopenharmony_ci {"rsi/rs9116_wlan.rps", 0x00000000}, 3662306a36Sopenharmony_ci {"rsi/rs9116_wlan_bt_classic.rps", 0x00000000}, 3762306a36Sopenharmony_ci {"rsi/pmemdata_dummy", 0x00000000}, 3862306a36Sopenharmony_ci {"rsi/rs9116_wlan_bt_classic.rps", 0x00000000} 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciint rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct rsi_hw *adapter = common->priv; 4462306a36Sopenharmony_ci int status; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (common->coex_mode > 1) 4762306a36Sopenharmony_ci mutex_lock(&common->tx_bus_mutex); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci status = adapter->host_intf_ops->write_pkt(common->priv, 5062306a36Sopenharmony_ci skb->data, skb->len); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (common->coex_mode > 1) 5362306a36Sopenharmony_ci mutex_unlock(&common->tx_bus_mutex); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return status; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciint rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct rsi_hw *adapter = common->priv; 6162306a36Sopenharmony_ci struct ieee80211_hdr *wh = NULL; 6262306a36Sopenharmony_ci struct ieee80211_tx_info *info; 6362306a36Sopenharmony_ci struct ieee80211_conf *conf = &adapter->hw->conf; 6462306a36Sopenharmony_ci struct ieee80211_vif *vif; 6562306a36Sopenharmony_ci struct rsi_mgmt_desc *mgmt_desc; 6662306a36Sopenharmony_ci struct skb_info *tx_params; 6762306a36Sopenharmony_ci struct rsi_xtended_desc *xtend_desc = NULL; 6862306a36Sopenharmony_ci u8 header_size; 6962306a36Sopenharmony_ci u32 dword_align_bytes = 0; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (skb->len > MAX_MGMT_PKT_SIZE) { 7262306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 7762306a36Sopenharmony_ci tx_params = (struct skb_info *)info->driver_data; 7862306a36Sopenharmony_ci vif = tx_params->vif; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* Update header size */ 8162306a36Sopenharmony_ci header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc); 8262306a36Sopenharmony_ci if (header_size > skb_headroom(skb)) { 8362306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 8462306a36Sopenharmony_ci "%s: Failed to add extended descriptor\n", 8562306a36Sopenharmony_ci __func__); 8662306a36Sopenharmony_ci return -ENOSPC; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci skb_push(skb, header_size); 8962306a36Sopenharmony_ci dword_align_bytes = ((unsigned long)skb->data & 0x3f); 9062306a36Sopenharmony_ci if (dword_align_bytes > skb_headroom(skb)) { 9162306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 9262306a36Sopenharmony_ci "%s: Failed to add dword align\n", __func__); 9362306a36Sopenharmony_ci return -ENOSPC; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci skb_push(skb, dword_align_bytes); 9662306a36Sopenharmony_ci header_size += dword_align_bytes; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci tx_params->internal_hdr_size = header_size; 9962306a36Sopenharmony_ci memset(&skb->data[0], 0, header_size); 10062306a36Sopenharmony_ci wh = (struct ieee80211_hdr *)&skb->data[header_size]; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci mgmt_desc = (struct rsi_mgmt_desc *)skb->data; 10362306a36Sopenharmony_ci xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ), 10662306a36Sopenharmony_ci RSI_WIFI_MGMT_Q); 10762306a36Sopenharmony_ci mgmt_desc->frame_type = TX_DOT11_MGMT; 10862306a36Sopenharmony_ci mgmt_desc->header_len = MIN_802_11_HDR_LEN; 10962306a36Sopenharmony_ci mgmt_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (ieee80211_is_probe_req(wh->frame_control)) 11262306a36Sopenharmony_ci mgmt_desc->frame_info = cpu_to_le16(RSI_INSERT_SEQ_IN_FW); 11362306a36Sopenharmony_ci mgmt_desc->frame_info |= cpu_to_le16(RATE_INFO_ENABLE); 11462306a36Sopenharmony_ci if (is_broadcast_ether_addr(wh->addr1)) 11562306a36Sopenharmony_ci mgmt_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci mgmt_desc->seq_ctrl = 11862306a36Sopenharmony_ci cpu_to_le16(IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl))); 11962306a36Sopenharmony_ci if ((common->band == NL80211_BAND_2GHZ) && !common->p2p_enabled) 12062306a36Sopenharmony_ci mgmt_desc->rate_info = cpu_to_le16(RSI_RATE_1); 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci mgmt_desc->rate_info = cpu_to_le16(RSI_RATE_6); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (conf_is_ht40(conf)) 12562306a36Sopenharmony_ci mgmt_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (ieee80211_is_probe_resp(wh->frame_control)) { 12862306a36Sopenharmony_ci mgmt_desc->misc_flags |= (RSI_ADD_DELTA_TSF_VAP_ID | 12962306a36Sopenharmony_ci RSI_FETCH_RETRY_CNT_FRM_HST); 13062306a36Sopenharmony_ci#define PROBE_RESP_RETRY_CNT 3 13162306a36Sopenharmony_ci xtend_desc->retry_cnt = PROBE_RESP_RETRY_CNT; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (((vif->type == NL80211_IFTYPE_AP) || 13562306a36Sopenharmony_ci (vif->type == NL80211_IFTYPE_P2P_GO)) && 13662306a36Sopenharmony_ci (ieee80211_is_action(wh->frame_control))) { 13762306a36Sopenharmony_ci struct rsi_sta *rsta = rsi_find_sta(common, wh->addr1); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (rsta) 14062306a36Sopenharmony_ci mgmt_desc->sta_id = tx_params->sta_id; 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci mgmt_desc->rate_info |= 14562306a36Sopenharmony_ci cpu_to_le16((tx_params->vap_id << RSI_DESC_VAP_ID_OFST) & 14662306a36Sopenharmony_ci RSI_DESC_VAP_ID_MASK); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* This function prepares descriptor for given data packet */ 15262306a36Sopenharmony_ciint rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct rsi_hw *adapter = common->priv; 15562306a36Sopenharmony_ci struct ieee80211_vif *vif; 15662306a36Sopenharmony_ci struct ieee80211_hdr *wh = NULL; 15762306a36Sopenharmony_ci struct ieee80211_tx_info *info; 15862306a36Sopenharmony_ci struct skb_info *tx_params; 15962306a36Sopenharmony_ci struct rsi_data_desc *data_desc; 16062306a36Sopenharmony_ci struct rsi_xtended_desc *xtend_desc; 16162306a36Sopenharmony_ci u8 ieee80211_size = MIN_802_11_HDR_LEN; 16262306a36Sopenharmony_ci u8 header_size; 16362306a36Sopenharmony_ci u8 vap_id = 0; 16462306a36Sopenharmony_ci u8 dword_align_bytes; 16562306a36Sopenharmony_ci bool tx_eapol; 16662306a36Sopenharmony_ci u16 seq_num; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 16962306a36Sopenharmony_ci vif = info->control.vif; 17062306a36Sopenharmony_ci tx_params = (struct skb_info *)info->driver_data; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci tx_eapol = IEEE80211_SKB_CB(skb)->control.flags & 17362306a36Sopenharmony_ci IEEE80211_TX_CTRL_PORT_CTRL_PROTO; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc); 17662306a36Sopenharmony_ci if (header_size > skb_headroom(skb)) { 17762306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); 17862306a36Sopenharmony_ci return -ENOSPC; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci skb_push(skb, header_size); 18162306a36Sopenharmony_ci dword_align_bytes = ((unsigned long)skb->data & 0x3f); 18262306a36Sopenharmony_ci if (header_size > skb_headroom(skb)) { 18362306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); 18462306a36Sopenharmony_ci return -ENOSPC; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci skb_push(skb, dword_align_bytes); 18762306a36Sopenharmony_ci header_size += dword_align_bytes; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci tx_params->internal_hdr_size = header_size; 19062306a36Sopenharmony_ci data_desc = (struct rsi_data_desc *)skb->data; 19162306a36Sopenharmony_ci memset(data_desc, 0, header_size); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 19462306a36Sopenharmony_ci wh = (struct ieee80211_hdr *)&skb->data[header_size]; 19562306a36Sopenharmony_ci seq_num = IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl)); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (ieee80211_is_data_qos(wh->frame_control)) { 20062306a36Sopenharmony_ci ieee80211_size += 2; 20162306a36Sopenharmony_ci data_desc->mac_flags |= cpu_to_le16(RSI_QOS_ENABLE); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (((vif->type == NL80211_IFTYPE_STATION) || 20562306a36Sopenharmony_ci (vif->type == NL80211_IFTYPE_P2P_CLIENT)) && 20662306a36Sopenharmony_ci (adapter->ps_state == PS_ENABLED)) 20762306a36Sopenharmony_ci wh->frame_control |= cpu_to_le16(RSI_SET_PS_ENABLE); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) && 21062306a36Sopenharmony_ci tx_params->have_key) { 21162306a36Sopenharmony_ci if (rsi_is_cipher_wep(common)) 21262306a36Sopenharmony_ci ieee80211_size += 4; 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci ieee80211_size += 8; 21562306a36Sopenharmony_ci data_desc->mac_flags |= cpu_to_le16(RSI_ENCRYPT_PKT); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci rsi_set_len_qno(&data_desc->len_qno, (skb->len - FRAME_DESC_SZ), 21862306a36Sopenharmony_ci RSI_WIFI_DATA_Q); 21962306a36Sopenharmony_ci data_desc->header_len = ieee80211_size; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (common->rate_config[common->band].fixed_enabled) { 22262306a36Sopenharmony_ci /* Send fixed rate */ 22362306a36Sopenharmony_ci u16 fixed_rate = common->rate_config[common->band].fixed_hw_rate; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 22662306a36Sopenharmony_ci data_desc->rate_info = cpu_to_le16(fixed_rate); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (conf_is_ht40(&common->priv->hw->conf)) 22962306a36Sopenharmony_ci data_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (common->vif_info[0].sgi && (fixed_rate & 0x100)) { 23262306a36Sopenharmony_ci /* Only MCS rates */ 23362306a36Sopenharmony_ci data_desc->rate_info |= 23462306a36Sopenharmony_ci cpu_to_le16(ENABLE_SHORTGI_RATE); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (tx_eapol) { 23962306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "*** Tx EAPOL ***\n"); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 24262306a36Sopenharmony_ci if (common->band == NL80211_BAND_5GHZ) 24362306a36Sopenharmony_ci data_desc->rate_info = cpu_to_le16(RSI_RATE_6); 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci data_desc->rate_info = cpu_to_le16(RSI_RATE_1); 24662306a36Sopenharmony_ci data_desc->mac_flags |= cpu_to_le16(RSI_REKEY_PURPOSE); 24762306a36Sopenharmony_ci data_desc->misc_flags |= RSI_FETCH_RETRY_CNT_FRM_HST; 24862306a36Sopenharmony_ci#define EAPOL_RETRY_CNT 15 24962306a36Sopenharmony_ci xtend_desc->retry_cnt = EAPOL_RETRY_CNT; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (common->eapol4_confirm) 25262306a36Sopenharmony_ci skb->priority = VO_Q; 25362306a36Sopenharmony_ci else 25462306a36Sopenharmony_ci rsi_set_len_qno(&data_desc->len_qno, 25562306a36Sopenharmony_ci (skb->len - FRAME_DESC_SZ), 25662306a36Sopenharmony_ci RSI_WIFI_MGMT_Q); 25762306a36Sopenharmony_ci if (((skb->len - header_size) == EAPOL4_PACKET_LEN) || 25862306a36Sopenharmony_ci ((skb->len - header_size) == EAPOL4_PACKET_LEN - 2)) { 25962306a36Sopenharmony_ci data_desc->misc_flags |= 26062306a36Sopenharmony_ci RSI_DESC_REQUIRE_CFM_TO_HOST; 26162306a36Sopenharmony_ci xtend_desc->confirm_frame_type = EAPOL4_CONFIRM; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci data_desc->mac_flags |= cpu_to_le16(seq_num & 0xfff); 26662306a36Sopenharmony_ci data_desc->qid_tid = ((skb->priority & 0xf) | 26762306a36Sopenharmony_ci ((tx_params->tid & 0xf) << 4)); 26862306a36Sopenharmony_ci data_desc->sta_id = tx_params->sta_id; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if ((is_broadcast_ether_addr(wh->addr1)) || 27162306a36Sopenharmony_ci (is_multicast_ether_addr(wh->addr1))) { 27262306a36Sopenharmony_ci data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 27362306a36Sopenharmony_ci data_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); 27462306a36Sopenharmony_ci data_desc->sta_id = vap_id; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if ((vif->type == NL80211_IFTYPE_AP) || 27762306a36Sopenharmony_ci (vif->type == NL80211_IFTYPE_P2P_GO)) { 27862306a36Sopenharmony_ci if (common->band == NL80211_BAND_5GHZ) 27962306a36Sopenharmony_ci data_desc->rate_info = cpu_to_le16(RSI_RATE_6); 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci data_desc->rate_info = cpu_to_le16(RSI_RATE_1); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci if (((vif->type == NL80211_IFTYPE_AP) || 28562306a36Sopenharmony_ci (vif->type == NL80211_IFTYPE_P2P_GO)) && 28662306a36Sopenharmony_ci (ieee80211_has_moredata(wh->frame_control))) 28762306a36Sopenharmony_ci data_desc->frame_info |= cpu_to_le16(MORE_DATA_PRESENT); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci data_desc->rate_info |= 29062306a36Sopenharmony_ci cpu_to_le16((tx_params->vap_id << RSI_DESC_VAP_ID_OFST) & 29162306a36Sopenharmony_ci RSI_DESC_VAP_ID_MASK); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* This function sends received data packet from driver to device */ 29762306a36Sopenharmony_ciint rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct rsi_hw *adapter = common->priv; 30062306a36Sopenharmony_ci struct ieee80211_vif *vif; 30162306a36Sopenharmony_ci struct ieee80211_tx_info *info; 30262306a36Sopenharmony_ci int status = -EINVAL; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (!skb) 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci if (common->iface_down) 30762306a36Sopenharmony_ci goto err; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 31062306a36Sopenharmony_ci if (!info->control.vif) 31162306a36Sopenharmony_ci goto err; 31262306a36Sopenharmony_ci vif = info->control.vif; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (((vif->type == NL80211_IFTYPE_STATION) || 31562306a36Sopenharmony_ci (vif->type == NL80211_IFTYPE_P2P_CLIENT)) && 31662306a36Sopenharmony_ci (!vif->cfg.assoc)) 31762306a36Sopenharmony_ci goto err; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci status = rsi_send_pkt_to_bus(common, skb); 32062306a36Sopenharmony_ci if (status) 32162306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", __func__); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cierr: 32462306a36Sopenharmony_ci ++common->tx_stats.total_tx_pkt_freed[skb->priority]; 32562306a36Sopenharmony_ci rsi_indicate_tx_status(adapter, skb, status); 32662306a36Sopenharmony_ci return status; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/** 33062306a36Sopenharmony_ci * rsi_send_mgmt_pkt() - This functions sends the received management packet 33162306a36Sopenharmony_ci * from driver to device. 33262306a36Sopenharmony_ci * @common: Pointer to the driver private structure. 33362306a36Sopenharmony_ci * @skb: Pointer to the socket buffer structure. 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Return: status: 0 on success, -1 on failure. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ciint rsi_send_mgmt_pkt(struct rsi_common *common, 33862306a36Sopenharmony_ci struct sk_buff *skb) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct rsi_hw *adapter = common->priv; 34162306a36Sopenharmony_ci struct ieee80211_hdr *wh; 34262306a36Sopenharmony_ci struct ieee80211_tx_info *info; 34362306a36Sopenharmony_ci struct skb_info *tx_params; 34462306a36Sopenharmony_ci struct rsi_mgmt_desc *mgmt_desc; 34562306a36Sopenharmony_ci struct rsi_xtended_desc *xtend_desc; 34662306a36Sopenharmony_ci int status = -E2BIG; 34762306a36Sopenharmony_ci u8 header_size; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 35062306a36Sopenharmony_ci tx_params = (struct skb_info *)info->driver_data; 35162306a36Sopenharmony_ci header_size = tx_params->internal_hdr_size; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (tx_params->flags & INTERNAL_MGMT_PKT) { 35462306a36Sopenharmony_ci status = adapter->host_intf_ops->write_pkt(common->priv, 35562306a36Sopenharmony_ci (u8 *)skb->data, 35662306a36Sopenharmony_ci skb->len); 35762306a36Sopenharmony_ci if (status) { 35862306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 35962306a36Sopenharmony_ci "%s: Failed to write the packet\n", __func__); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci dev_kfree_skb(skb); 36262306a36Sopenharmony_ci return status; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci wh = (struct ieee80211_hdr *)&skb->data[header_size]; 36662306a36Sopenharmony_ci mgmt_desc = (struct rsi_mgmt_desc *)skb->data; 36762306a36Sopenharmony_ci xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Indicate to firmware to give cfm for probe */ 37062306a36Sopenharmony_ci if (ieee80211_is_probe_req(wh->frame_control) && 37162306a36Sopenharmony_ci !info->control.vif->cfg.assoc) { 37262306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, 37362306a36Sopenharmony_ci "%s: blocking mgmt queue\n", __func__); 37462306a36Sopenharmony_ci mgmt_desc->misc_flags = RSI_DESC_REQUIRE_CFM_TO_HOST; 37562306a36Sopenharmony_ci xtend_desc->confirm_frame_type = PROBEREQ_CONFIRM; 37662306a36Sopenharmony_ci common->mgmt_q_block = true; 37762306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "Mgmt queue blocked\n"); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci status = rsi_send_pkt_to_bus(common, skb); 38162306a36Sopenharmony_ci if (status) 38262306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci rsi_indicate_tx_status(common->priv, skb, status); 38562306a36Sopenharmony_ci return status; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ciint rsi_send_bt_pkt(struct rsi_common *common, struct sk_buff *skb) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int status = -EINVAL; 39162306a36Sopenharmony_ci u8 header_size = 0; 39262306a36Sopenharmony_ci struct rsi_bt_desc *bt_desc; 39362306a36Sopenharmony_ci u8 queueno = ((skb->data[1] >> 4) & 0xf); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (queueno == RSI_BT_MGMT_Q) { 39662306a36Sopenharmony_ci status = rsi_send_pkt_to_bus(common, skb); 39762306a36Sopenharmony_ci if (status) 39862306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to write bt mgmt pkt\n", 39962306a36Sopenharmony_ci __func__); 40062306a36Sopenharmony_ci goto out; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci header_size = FRAME_DESC_SZ; 40362306a36Sopenharmony_ci if (header_size > skb_headroom(skb)) { 40462306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); 40562306a36Sopenharmony_ci status = -ENOSPC; 40662306a36Sopenharmony_ci goto out; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci skb_push(skb, header_size); 40962306a36Sopenharmony_ci memset(skb->data, 0, header_size); 41062306a36Sopenharmony_ci bt_desc = (struct rsi_bt_desc *)skb->data; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci rsi_set_len_qno(&bt_desc->len_qno, (skb->len - FRAME_DESC_SZ), 41362306a36Sopenharmony_ci RSI_BT_DATA_Q); 41462306a36Sopenharmony_ci bt_desc->bt_pkt_type = cpu_to_le16(bt_cb(skb)->pkt_type); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci status = rsi_send_pkt_to_bus(common, skb); 41762306a36Sopenharmony_ci if (status) 41862306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to write bt pkt\n", __func__); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ciout: 42162306a36Sopenharmony_ci dev_kfree_skb(skb); 42262306a36Sopenharmony_ci return status; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciint rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct rsi_hw *adapter = common->priv; 42862306a36Sopenharmony_ci struct rsi_data_desc *bcn_frm; 42962306a36Sopenharmony_ci struct ieee80211_hw *hw = common->priv->hw; 43062306a36Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 43162306a36Sopenharmony_ci struct ieee80211_vif *vif; 43262306a36Sopenharmony_ci struct sk_buff *mac_bcn; 43362306a36Sopenharmony_ci u8 vap_id = 0, i; 43462306a36Sopenharmony_ci u16 tim_offset = 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci for (i = 0; i < RSI_MAX_VIFS; i++) { 43762306a36Sopenharmony_ci vif = adapter->vifs[i]; 43862306a36Sopenharmony_ci if (!vif) 43962306a36Sopenharmony_ci continue; 44062306a36Sopenharmony_ci if ((vif->type == NL80211_IFTYPE_AP) || 44162306a36Sopenharmony_ci (vif->type == NL80211_IFTYPE_P2P_GO)) 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci if (!vif) 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci mac_bcn = ieee80211_beacon_get_tim(adapter->hw, 44762306a36Sopenharmony_ci vif, 44862306a36Sopenharmony_ci &tim_offset, NULL, 0); 44962306a36Sopenharmony_ci if (!mac_bcn) { 45062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "Failed to get beacon from mac80211\n"); 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci common->beacon_cnt++; 45562306a36Sopenharmony_ci bcn_frm = (struct rsi_data_desc *)skb->data; 45662306a36Sopenharmony_ci rsi_set_len_qno(&bcn_frm->len_qno, mac_bcn->len, RSI_WIFI_DATA_Q); 45762306a36Sopenharmony_ci bcn_frm->header_len = MIN_802_11_HDR_LEN; 45862306a36Sopenharmony_ci bcn_frm->frame_info = cpu_to_le16(RSI_DATA_DESC_MAC_BBP_INFO | 45962306a36Sopenharmony_ci RSI_DATA_DESC_NO_ACK_IND | 46062306a36Sopenharmony_ci RSI_DATA_DESC_BEACON_FRAME | 46162306a36Sopenharmony_ci RSI_DATA_DESC_INSERT_TSF | 46262306a36Sopenharmony_ci RSI_DATA_DESC_INSERT_SEQ_NO | 46362306a36Sopenharmony_ci RATE_INFO_ENABLE); 46462306a36Sopenharmony_ci bcn_frm->rate_info = cpu_to_le16(vap_id << 14); 46562306a36Sopenharmony_ci bcn_frm->qid_tid = BEACON_HW_Q; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (conf_is_ht40_plus(conf)) { 46862306a36Sopenharmony_ci bcn_frm->bbp_info = cpu_to_le16(LOWER_20_ENABLE); 46962306a36Sopenharmony_ci bcn_frm->bbp_info |= cpu_to_le16(LOWER_20_ENABLE >> 12); 47062306a36Sopenharmony_ci } else if (conf_is_ht40_minus(conf)) { 47162306a36Sopenharmony_ci bcn_frm->bbp_info = cpu_to_le16(UPPER_20_ENABLE); 47262306a36Sopenharmony_ci bcn_frm->bbp_info |= cpu_to_le16(UPPER_20_ENABLE >> 12); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (common->band == NL80211_BAND_2GHZ) 47662306a36Sopenharmony_ci bcn_frm->rate_info |= cpu_to_le16(RSI_RATE_1); 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci bcn_frm->rate_info |= cpu_to_le16(RSI_RATE_6); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (mac_bcn->data[tim_offset + 2] == 0) 48162306a36Sopenharmony_ci bcn_frm->frame_info |= cpu_to_le16(RSI_DATA_DESC_DTIM_BEACON); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci memcpy(&skb->data[FRAME_DESC_SZ], mac_bcn->data, mac_bcn->len); 48462306a36Sopenharmony_ci skb_put(skb, mac_bcn->len + FRAME_DESC_SZ); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci dev_kfree_skb(mac_bcn); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void bl_cmd_timeout(struct timer_list *t) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct rsi_hw *adapter = from_timer(adapter, t, bl_cmd_timer); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci adapter->blcmd_timer_expired = true; 49662306a36Sopenharmony_ci del_timer(&adapter->bl_cmd_timer); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int bl_start_cmd_timer(struct rsi_hw *adapter, u32 timeout) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci timer_setup(&adapter->bl_cmd_timer, bl_cmd_timeout, 0); 50262306a36Sopenharmony_ci adapter->bl_cmd_timer.expires = (msecs_to_jiffies(timeout) + jiffies); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci adapter->blcmd_timer_expired = false; 50562306a36Sopenharmony_ci add_timer(&adapter->bl_cmd_timer); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int bl_stop_cmd_timer(struct rsi_hw *adapter) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci adapter->blcmd_timer_expired = false; 51362306a36Sopenharmony_ci if (timer_pending(&adapter->bl_cmd_timer)) 51462306a36Sopenharmony_ci del_timer(&adapter->bl_cmd_timer); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return 0; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int bl_write_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, 52062306a36Sopenharmony_ci u16 *cmd_resp) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 52362306a36Sopenharmony_ci u32 regin_val = 0, regout_val = 0; 52462306a36Sopenharmony_ci u32 regin_input = 0; 52562306a36Sopenharmony_ci u8 output = 0; 52662306a36Sopenharmony_ci int status; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci regin_input = (REGIN_INPUT | adapter->priv->coex_mode); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci while (!adapter->blcmd_timer_expired) { 53162306a36Sopenharmony_ci regin_val = 0; 53262306a36Sopenharmony_ci status = hif_ops->master_reg_read(adapter, SWBL_REGIN, 53362306a36Sopenharmony_ci ®in_val, 2); 53462306a36Sopenharmony_ci if (status < 0) { 53562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 53662306a36Sopenharmony_ci "%s: Command %0x REGIN reading failed..\n", 53762306a36Sopenharmony_ci __func__, cmd); 53862306a36Sopenharmony_ci return status; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci mdelay(1); 54162306a36Sopenharmony_ci if ((regin_val >> 12) != REGIN_VALID) 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci if (adapter->blcmd_timer_expired) { 54562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 54662306a36Sopenharmony_ci "%s: Command %0x REGIN reading timed out..\n", 54762306a36Sopenharmony_ci __func__, cmd); 54862306a36Sopenharmony_ci return -ETIMEDOUT; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, 55262306a36Sopenharmony_ci "Issuing write to Regin val:%0x sending cmd:%0x\n", 55362306a36Sopenharmony_ci regin_val, (cmd | regin_input << 8)); 55462306a36Sopenharmony_ci status = hif_ops->master_reg_write(adapter, SWBL_REGIN, 55562306a36Sopenharmony_ci (cmd | regin_input << 8), 2); 55662306a36Sopenharmony_ci if (status < 0) 55762306a36Sopenharmony_ci return status; 55862306a36Sopenharmony_ci mdelay(1); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (cmd == LOAD_HOSTED_FW || cmd == JUMP_TO_ZERO_PC) { 56162306a36Sopenharmony_ci /* JUMP_TO_ZERO_PC doesn't expect 56262306a36Sopenharmony_ci * any response. So return from here 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci while (!adapter->blcmd_timer_expired) { 56862306a36Sopenharmony_ci regout_val = 0; 56962306a36Sopenharmony_ci status = hif_ops->master_reg_read(adapter, SWBL_REGOUT, 57062306a36Sopenharmony_ci ®out_val, 2); 57162306a36Sopenharmony_ci if (status < 0) { 57262306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 57362306a36Sopenharmony_ci "%s: Command %0x REGOUT reading failed..\n", 57462306a36Sopenharmony_ci __func__, cmd); 57562306a36Sopenharmony_ci return status; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci mdelay(1); 57862306a36Sopenharmony_ci if ((regout_val >> 8) == REGOUT_VALID) 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci if (adapter->blcmd_timer_expired) { 58262306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 58362306a36Sopenharmony_ci "%s: Command %0x REGOUT reading timed out..\n", 58462306a36Sopenharmony_ci __func__, cmd); 58562306a36Sopenharmony_ci return status; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci *cmd_resp = ((u16 *)®out_val)[0] & 0xffff; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci output = ((u8 *)®out_val)[0] & 0xff; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci status = hif_ops->master_reg_write(adapter, SWBL_REGOUT, 59362306a36Sopenharmony_ci (cmd | REGOUT_INVALID << 8), 2); 59462306a36Sopenharmony_ci if (status < 0) { 59562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 59662306a36Sopenharmony_ci "%s: Command %0x REGOUT writing failed..\n", 59762306a36Sopenharmony_ci __func__, cmd); 59862306a36Sopenharmony_ci return status; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci mdelay(1); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (output != exp_resp) { 60362306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 60462306a36Sopenharmony_ci "%s: Recvd resp %x for cmd %0x\n", 60562306a36Sopenharmony_ci __func__, output, cmd); 60662306a36Sopenharmony_ci return -EINVAL; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, 60962306a36Sopenharmony_ci "%s: Recvd Expected resp %x for cmd %0x\n", 61062306a36Sopenharmony_ci __func__, output, cmd); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int bl_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, char *str) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci u16 regout_val = 0; 61862306a36Sopenharmony_ci u32 timeout; 61962306a36Sopenharmony_ci int status; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if ((cmd == EOF_REACHED) || (cmd == PING_VALID) || (cmd == PONG_VALID)) 62262306a36Sopenharmony_ci timeout = BL_BURN_TIMEOUT; 62362306a36Sopenharmony_ci else 62462306a36Sopenharmony_ci timeout = BL_CMD_TIMEOUT; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci bl_start_cmd_timer(adapter, timeout); 62762306a36Sopenharmony_ci status = bl_write_cmd(adapter, cmd, exp_resp, ®out_val); 62862306a36Sopenharmony_ci if (status < 0) { 62962306a36Sopenharmony_ci bl_stop_cmd_timer(adapter); 63062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 63162306a36Sopenharmony_ci "%s: Command %s (%0x) writing failed..\n", 63262306a36Sopenharmony_ci __func__, str, cmd); 63362306a36Sopenharmony_ci return status; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci bl_stop_cmd_timer(adapter); 63662306a36Sopenharmony_ci return 0; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci#define CHECK_SUM_OFFSET 20 64062306a36Sopenharmony_ci#define LEN_OFFSET 8 64162306a36Sopenharmony_ci#define ADDR_OFFSET 16 64262306a36Sopenharmony_cistatic int bl_write_header(struct rsi_hw *adapter, u8 *flash_content, 64362306a36Sopenharmony_ci u32 content_size) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 64662306a36Sopenharmony_ci struct bl_header *bl_hdr; 64762306a36Sopenharmony_ci u32 write_addr, write_len; 64862306a36Sopenharmony_ci int status; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci bl_hdr = kzalloc(sizeof(*bl_hdr), GFP_KERNEL); 65162306a36Sopenharmony_ci if (!bl_hdr) 65262306a36Sopenharmony_ci return -ENOMEM; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci bl_hdr->flags = 0; 65562306a36Sopenharmony_ci bl_hdr->image_no = cpu_to_le32(adapter->priv->coex_mode); 65662306a36Sopenharmony_ci bl_hdr->check_sum = 65762306a36Sopenharmony_ci cpu_to_le32(*(u32 *)&flash_content[CHECK_SUM_OFFSET]); 65862306a36Sopenharmony_ci bl_hdr->flash_start_address = 65962306a36Sopenharmony_ci cpu_to_le32(*(u32 *)&flash_content[ADDR_OFFSET]); 66062306a36Sopenharmony_ci bl_hdr->flash_len = cpu_to_le32(*(u32 *)&flash_content[LEN_OFFSET]); 66162306a36Sopenharmony_ci write_len = sizeof(struct bl_header); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (adapter->rsi_host_intf == RSI_HOST_INTF_USB) { 66462306a36Sopenharmony_ci write_addr = PING_BUFFER_ADDRESS; 66562306a36Sopenharmony_ci status = hif_ops->write_reg_multiple(adapter, write_addr, 66662306a36Sopenharmony_ci (u8 *)bl_hdr, write_len); 66762306a36Sopenharmony_ci if (status < 0) { 66862306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 66962306a36Sopenharmony_ci "%s: Failed to load Version/CRC structure\n", 67062306a36Sopenharmony_ci __func__); 67162306a36Sopenharmony_ci goto fail; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci } else { 67462306a36Sopenharmony_ci write_addr = PING_BUFFER_ADDRESS >> 16; 67562306a36Sopenharmony_ci status = hif_ops->master_access_msword(adapter, write_addr); 67662306a36Sopenharmony_ci if (status < 0) { 67762306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 67862306a36Sopenharmony_ci "%s: Unable to set ms word to common reg\n", 67962306a36Sopenharmony_ci __func__); 68062306a36Sopenharmony_ci goto fail; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci write_addr = RSI_SD_REQUEST_MASTER | 68362306a36Sopenharmony_ci (PING_BUFFER_ADDRESS & 0xFFFF); 68462306a36Sopenharmony_ci status = hif_ops->write_reg_multiple(adapter, write_addr, 68562306a36Sopenharmony_ci (u8 *)bl_hdr, write_len); 68662306a36Sopenharmony_ci if (status < 0) { 68762306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 68862306a36Sopenharmony_ci "%s: Failed to load Version/CRC structure\n", 68962306a36Sopenharmony_ci __func__); 69062306a36Sopenharmony_ci goto fail; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci status = 0; 69462306a36Sopenharmony_cifail: 69562306a36Sopenharmony_ci kfree(bl_hdr); 69662306a36Sopenharmony_ci return status; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic u32 read_flash_capacity(struct rsi_hw *adapter) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci u32 flash_sz = 0; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if ((adapter->host_intf_ops->master_reg_read(adapter, FLASH_SIZE_ADDR, 70462306a36Sopenharmony_ci &flash_sz, 2)) < 0) { 70562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 70662306a36Sopenharmony_ci "%s: Flash size reading failed..\n", 70762306a36Sopenharmony_ci __func__); 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci rsi_dbg(INIT_ZONE, "Flash capacity: %d KiloBytes\n", flash_sz); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return (flash_sz * 1024); /* Return size in kbytes */ 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic int ping_pong_write(struct rsi_hw *adapter, u8 cmd, u8 *addr, u32 size) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 71862306a36Sopenharmony_ci u32 block_size = adapter->block_size; 71962306a36Sopenharmony_ci u32 cmd_addr; 72062306a36Sopenharmony_ci u16 cmd_resp, cmd_req; 72162306a36Sopenharmony_ci u8 *str; 72262306a36Sopenharmony_ci int status; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (cmd == PING_WRITE) { 72562306a36Sopenharmony_ci cmd_addr = PING_BUFFER_ADDRESS; 72662306a36Sopenharmony_ci cmd_resp = PONG_AVAIL; 72762306a36Sopenharmony_ci cmd_req = PING_VALID; 72862306a36Sopenharmony_ci str = "PING_VALID"; 72962306a36Sopenharmony_ci } else { 73062306a36Sopenharmony_ci cmd_addr = PONG_BUFFER_ADDRESS; 73162306a36Sopenharmony_ci cmd_resp = PING_AVAIL; 73262306a36Sopenharmony_ci cmd_req = PONG_VALID; 73362306a36Sopenharmony_ci str = "PONG_VALID"; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci status = hif_ops->load_data_master_write(adapter, cmd_addr, size, 73762306a36Sopenharmony_ci block_size, addr); 73862306a36Sopenharmony_ci if (status) { 73962306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unable to write blk at addr %0x\n", 74062306a36Sopenharmony_ci __func__, *addr); 74162306a36Sopenharmony_ci return status; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci status = bl_cmd(adapter, cmd_req, cmd_resp, str); 74562306a36Sopenharmony_ci if (status) 74662306a36Sopenharmony_ci return status; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return 0; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic int auto_fw_upgrade(struct rsi_hw *adapter, u8 *flash_content, 75262306a36Sopenharmony_ci u32 content_size) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci u8 cmd; 75562306a36Sopenharmony_ci u32 temp_content_size, num_flash, index; 75662306a36Sopenharmony_ci u32 flash_start_address; 75762306a36Sopenharmony_ci int status; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (content_size > MAX_FLASH_FILE_SIZE) { 76062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 76162306a36Sopenharmony_ci "%s: Flash Content size is more than 400K %u\n", 76262306a36Sopenharmony_ci __func__, MAX_FLASH_FILE_SIZE); 76362306a36Sopenharmony_ci return -EINVAL; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci flash_start_address = *(u32 *)&flash_content[FLASH_START_ADDRESS]; 76762306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "flash start address: %08x\n", flash_start_address); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (flash_start_address < FW_IMAGE_MIN_ADDRESS) { 77062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 77162306a36Sopenharmony_ci "%s: Fw image Flash Start Address is less than 64K\n", 77262306a36Sopenharmony_ci __func__); 77362306a36Sopenharmony_ci return -EINVAL; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (flash_start_address % FLASH_SECTOR_SIZE) { 77762306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 77862306a36Sopenharmony_ci "%s: Flash Start Address is not multiple of 4K\n", 77962306a36Sopenharmony_ci __func__); 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if ((flash_start_address + content_size) > adapter->flash_capacity) { 78462306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 78562306a36Sopenharmony_ci "%s: Flash Content will cross max flash size\n", 78662306a36Sopenharmony_ci __func__); 78762306a36Sopenharmony_ci return -EINVAL; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci temp_content_size = content_size; 79162306a36Sopenharmony_ci num_flash = content_size / FLASH_WRITE_CHUNK_SIZE; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "content_size: %d, num_flash: %d\n", 79462306a36Sopenharmony_ci content_size, num_flash); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci for (index = 0; index <= num_flash; index++) { 79762306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "flash index: %d\n", index); 79862306a36Sopenharmony_ci if (index != num_flash) { 79962306a36Sopenharmony_ci content_size = FLASH_WRITE_CHUNK_SIZE; 80062306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "QSPI content_size:%d\n", 80162306a36Sopenharmony_ci content_size); 80262306a36Sopenharmony_ci } else { 80362306a36Sopenharmony_ci content_size = 80462306a36Sopenharmony_ci temp_content_size % FLASH_WRITE_CHUNK_SIZE; 80562306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, 80662306a36Sopenharmony_ci "Writing last sector content_size:%d\n", 80762306a36Sopenharmony_ci content_size); 80862306a36Sopenharmony_ci if (!content_size) { 80962306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "instruction size zero\n"); 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (index % 2) 81562306a36Sopenharmony_ci cmd = PING_WRITE; 81662306a36Sopenharmony_ci else 81762306a36Sopenharmony_ci cmd = PONG_WRITE; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci status = ping_pong_write(adapter, cmd, flash_content, 82062306a36Sopenharmony_ci content_size); 82162306a36Sopenharmony_ci if (status) { 82262306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unable to load %d block\n", 82362306a36Sopenharmony_ci __func__, index); 82462306a36Sopenharmony_ci return status; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, 82862306a36Sopenharmony_ci "%s: Successfully loaded %d instructions\n", 82962306a36Sopenharmony_ci __func__, index); 83062306a36Sopenharmony_ci flash_content += content_size; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci status = bl_cmd(adapter, EOF_REACHED, FW_LOADING_SUCCESSFUL, 83462306a36Sopenharmony_ci "EOF_REACHED"); 83562306a36Sopenharmony_ci if (status) 83662306a36Sopenharmony_ci return status; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "FW loading is done and FW is running..\n"); 83962306a36Sopenharmony_ci return 0; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic int rsi_hal_prepare_fwload(struct rsi_hw *adapter) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 84562306a36Sopenharmony_ci u32 regout_val = 0; 84662306a36Sopenharmony_ci int status; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci bl_start_cmd_timer(adapter, BL_CMD_TIMEOUT); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci while (!adapter->blcmd_timer_expired) { 85162306a36Sopenharmony_ci status = hif_ops->master_reg_read(adapter, SWBL_REGOUT, 85262306a36Sopenharmony_ci ®out_val, 85362306a36Sopenharmony_ci RSI_COMMON_REG_SIZE); 85462306a36Sopenharmony_ci if (status < 0) { 85562306a36Sopenharmony_ci bl_stop_cmd_timer(adapter); 85662306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 85762306a36Sopenharmony_ci "%s: REGOUT read failed\n", __func__); 85862306a36Sopenharmony_ci return status; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci mdelay(1); 86162306a36Sopenharmony_ci if ((regout_val >> 8) == REGOUT_VALID) 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci if (adapter->blcmd_timer_expired) { 86562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: REGOUT read timedout\n", __func__); 86662306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 86762306a36Sopenharmony_ci "%s: Soft boot loader not present\n", __func__); 86862306a36Sopenharmony_ci return -ETIMEDOUT; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci bl_stop_cmd_timer(adapter); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "Received Board Version Number: %x\n", 87362306a36Sopenharmony_ci (regout_val & 0xff)); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci status = hif_ops->master_reg_write(adapter, SWBL_REGOUT, 87662306a36Sopenharmony_ci (REGOUT_INVALID | 87762306a36Sopenharmony_ci REGOUT_INVALID << 8), 87862306a36Sopenharmony_ci RSI_COMMON_REG_SIZE); 87962306a36Sopenharmony_ci if (status < 0) 88062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: REGOUT writing failed..\n", __func__); 88162306a36Sopenharmony_ci else 88262306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, 88362306a36Sopenharmony_ci "===> Device is ready to load firmware <===\n"); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci return status; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int rsi_load_9113_firmware(struct rsi_hw *adapter) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct rsi_common *common = adapter->priv; 89162306a36Sopenharmony_ci const struct firmware *fw_entry = NULL; 89262306a36Sopenharmony_ci u32 content_size; 89362306a36Sopenharmony_ci u16 tmp_regout_val = 0; 89462306a36Sopenharmony_ci struct ta_metadata *metadata_p; 89562306a36Sopenharmony_ci int status; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci status = bl_cmd(adapter, AUTO_READ_MODE, CMD_PASS, 89862306a36Sopenharmony_ci "AUTO_READ_CMD"); 89962306a36Sopenharmony_ci if (status < 0) 90062306a36Sopenharmony_ci return status; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci adapter->flash_capacity = read_flash_capacity(adapter); 90362306a36Sopenharmony_ci if (adapter->flash_capacity <= 0) { 90462306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 90562306a36Sopenharmony_ci "%s: Unable to read flash size from EEPROM\n", 90662306a36Sopenharmony_ci __func__); 90762306a36Sopenharmony_ci return -EINVAL; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci metadata_p = &metadata_flash_content[adapter->priv->coex_mode]; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: Loading file %s\n", __func__, metadata_p->name); 91362306a36Sopenharmony_ci adapter->fw_file_name = metadata_p->name; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci status = request_firmware(&fw_entry, metadata_p->name, adapter->device); 91662306a36Sopenharmony_ci if (status < 0) { 91762306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to open file %s\n", 91862306a36Sopenharmony_ci __func__, metadata_p->name); 91962306a36Sopenharmony_ci return status; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci content_size = fw_entry->size; 92262306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "FW Length = %d bytes\n", content_size); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Get the firmware version */ 92562306a36Sopenharmony_ci common->lmac_ver.ver.info.fw_ver[0] = 92662306a36Sopenharmony_ci fw_entry->data[LMAC_VER_OFFSET_9113] & 0xFF; 92762306a36Sopenharmony_ci common->lmac_ver.ver.info.fw_ver[1] = 92862306a36Sopenharmony_ci fw_entry->data[LMAC_VER_OFFSET_9113 + 1] & 0xFF; 92962306a36Sopenharmony_ci common->lmac_ver.major = 93062306a36Sopenharmony_ci fw_entry->data[LMAC_VER_OFFSET_9113 + 2] & 0xFF; 93162306a36Sopenharmony_ci common->lmac_ver.release_num = 93262306a36Sopenharmony_ci fw_entry->data[LMAC_VER_OFFSET_9113 + 3] & 0xFF; 93362306a36Sopenharmony_ci common->lmac_ver.minor = 93462306a36Sopenharmony_ci fw_entry->data[LMAC_VER_OFFSET_9113 + 4] & 0xFF; 93562306a36Sopenharmony_ci common->lmac_ver.patch_num = 0; 93662306a36Sopenharmony_ci rsi_print_version(common); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci status = bl_write_header(adapter, (u8 *)fw_entry->data, content_size); 93962306a36Sopenharmony_ci if (status) { 94062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 94162306a36Sopenharmony_ci "%s: RPS Image header loading failed\n", 94262306a36Sopenharmony_ci __func__); 94362306a36Sopenharmony_ci goto fail; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci bl_start_cmd_timer(adapter, BL_CMD_TIMEOUT); 94762306a36Sopenharmony_ci status = bl_write_cmd(adapter, CHECK_CRC, CMD_PASS, &tmp_regout_val); 94862306a36Sopenharmony_ci if (status) { 94962306a36Sopenharmony_ci bl_stop_cmd_timer(adapter); 95062306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 95162306a36Sopenharmony_ci "%s: CHECK_CRC Command writing failed..\n", 95262306a36Sopenharmony_ci __func__); 95362306a36Sopenharmony_ci if ((tmp_regout_val & 0xff) == CMD_FAIL) { 95462306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 95562306a36Sopenharmony_ci "CRC Fail.. Proceeding to Upgrade mode\n"); 95662306a36Sopenharmony_ci goto fw_upgrade; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci bl_stop_cmd_timer(adapter); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci status = bl_cmd(adapter, POLLING_MODE, CMD_PASS, "POLLING_MODE"); 96262306a36Sopenharmony_ci if (status) 96362306a36Sopenharmony_ci goto fail; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ciload_image_cmd: 96662306a36Sopenharmony_ci status = bl_cmd(adapter, LOAD_HOSTED_FW, LOADING_INITIATED, 96762306a36Sopenharmony_ci "LOAD_HOSTED_FW"); 96862306a36Sopenharmony_ci if (status) 96962306a36Sopenharmony_ci goto fail; 97062306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "Load Image command passed..\n"); 97162306a36Sopenharmony_ci goto success; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cifw_upgrade: 97462306a36Sopenharmony_ci status = bl_cmd(adapter, BURN_HOSTED_FW, SEND_RPS_FILE, "FW_UPGRADE"); 97562306a36Sopenharmony_ci if (status) 97662306a36Sopenharmony_ci goto fail; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "Burn Command Pass.. Upgrading the firmware\n"); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci status = auto_fw_upgrade(adapter, (u8 *)fw_entry->data, content_size); 98162306a36Sopenharmony_ci if (status == 0) { 98262306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "Firmware upgradation Done\n"); 98362306a36Sopenharmony_ci goto load_image_cmd; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "Firmware upgrade failed\n"); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci status = bl_cmd(adapter, AUTO_READ_MODE, CMD_PASS, 98862306a36Sopenharmony_ci "AUTO_READ_MODE"); 98962306a36Sopenharmony_ci if (status) 99062306a36Sopenharmony_ci goto fail; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cisuccess: 99362306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "***** Firmware Loading successful *****\n"); 99462306a36Sopenharmony_ci release_firmware(fw_entry); 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cifail: 99862306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "##### Firmware loading failed #####\n"); 99962306a36Sopenharmony_ci release_firmware(fw_entry); 100062306a36Sopenharmony_ci return status; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int rsi_load_9116_firmware(struct rsi_hw *adapter) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct rsi_common *common = adapter->priv; 100662306a36Sopenharmony_ci struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 100762306a36Sopenharmony_ci const struct firmware *fw_entry; 100862306a36Sopenharmony_ci struct ta_metadata *metadata_p; 100962306a36Sopenharmony_ci u8 *ta_firmware, *fw_p; 101062306a36Sopenharmony_ci struct bootload_ds bootload_ds; 101162306a36Sopenharmony_ci u32 instructions_sz, base_address; 101262306a36Sopenharmony_ci u16 block_size = adapter->block_size; 101362306a36Sopenharmony_ci u32 dest, len; 101462306a36Sopenharmony_ci int status, cnt; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci rsi_dbg(INIT_ZONE, "***** Load 9116 TA Instructions *****\n"); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (adapter->rsi_host_intf == RSI_HOST_INTF_USB) { 101962306a36Sopenharmony_ci status = bl_cmd(adapter, POLLING_MODE, CMD_PASS, 102062306a36Sopenharmony_ci "POLLING_MODE"); 102162306a36Sopenharmony_ci if (status < 0) 102262306a36Sopenharmony_ci return status; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci status = hif_ops->master_reg_write(adapter, MEM_ACCESS_CTRL_FROM_HOST, 102662306a36Sopenharmony_ci RAM_384K_ACCESS_FROM_TA, 102762306a36Sopenharmony_ci RSI_9116_REG_SIZE); 102862306a36Sopenharmony_ci if (status < 0) { 102962306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unable to access full RAM memory\n", 103062306a36Sopenharmony_ci __func__); 103162306a36Sopenharmony_ci return status; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci metadata_p = &metadata[adapter->priv->coex_mode]; 103562306a36Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: loading file %s\n", __func__, metadata_p->name); 103662306a36Sopenharmony_ci status = request_firmware(&fw_entry, metadata_p->name, adapter->device); 103762306a36Sopenharmony_ci if (status < 0) { 103862306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to open file %s\n", 103962306a36Sopenharmony_ci __func__, metadata_p->name); 104062306a36Sopenharmony_ci return status; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci ta_firmware = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); 104462306a36Sopenharmony_ci if (!ta_firmware) { 104562306a36Sopenharmony_ci status = -ENOMEM; 104662306a36Sopenharmony_ci goto fail_release_fw; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci fw_p = ta_firmware; 104962306a36Sopenharmony_ci instructions_sz = fw_entry->size; 105062306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "FW Length = %d bytes\n", instructions_sz); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci common->lmac_ver.major = ta_firmware[LMAC_VER_OFFSET_9116]; 105362306a36Sopenharmony_ci common->lmac_ver.minor = ta_firmware[LMAC_VER_OFFSET_9116 + 1]; 105462306a36Sopenharmony_ci common->lmac_ver.release_num = ta_firmware[LMAC_VER_OFFSET_9116 + 2]; 105562306a36Sopenharmony_ci common->lmac_ver.patch_num = ta_firmware[LMAC_VER_OFFSET_9116 + 3]; 105662306a36Sopenharmony_ci common->lmac_ver.ver.info.fw_ver[0] = 105762306a36Sopenharmony_ci ta_firmware[LMAC_VER_OFFSET_9116 + 4]; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (instructions_sz % FW_ALIGN_SIZE) 106062306a36Sopenharmony_ci instructions_sz += 106162306a36Sopenharmony_ci (FW_ALIGN_SIZE - (instructions_sz % FW_ALIGN_SIZE)); 106262306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "instructions_sz : %d\n", instructions_sz); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (*(u16 *)fw_p == RSI_9116_FW_MAGIC_WORD) { 106562306a36Sopenharmony_ci memcpy(&bootload_ds, fw_p, sizeof(struct bootload_ds)); 106662306a36Sopenharmony_ci fw_p += le16_to_cpu(bootload_ds.offset); 106762306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "FW start = %x\n", *(u32 *)fw_p); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci cnt = 0; 107062306a36Sopenharmony_ci do { 107162306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Loading chunk %d\n", 107262306a36Sopenharmony_ci __func__, cnt); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci dest = le32_to_cpu(bootload_ds.bl_entry[cnt].dst_addr); 107562306a36Sopenharmony_ci len = le32_to_cpu(bootload_ds.bl_entry[cnt].control) & 107662306a36Sopenharmony_ci RSI_BL_CTRL_LEN_MASK; 107762306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "length %d destination %x\n", 107862306a36Sopenharmony_ci len, dest); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci status = hif_ops->load_data_master_write(adapter, dest, 108162306a36Sopenharmony_ci len, 108262306a36Sopenharmony_ci block_size, 108362306a36Sopenharmony_ci fw_p); 108462306a36Sopenharmony_ci if (status < 0) { 108562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 108662306a36Sopenharmony_ci "Failed to load chunk %d\n", cnt); 108762306a36Sopenharmony_ci break; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci fw_p += len; 109062306a36Sopenharmony_ci if (le32_to_cpu(bootload_ds.bl_entry[cnt].control) & 109162306a36Sopenharmony_ci RSI_BL_CTRL_LAST_ENTRY) 109262306a36Sopenharmony_ci break; 109362306a36Sopenharmony_ci cnt++; 109462306a36Sopenharmony_ci } while (1); 109562306a36Sopenharmony_ci } else { 109662306a36Sopenharmony_ci base_address = metadata_p->address; 109762306a36Sopenharmony_ci status = hif_ops->load_data_master_write(adapter, 109862306a36Sopenharmony_ci base_address, 109962306a36Sopenharmony_ci instructions_sz, 110062306a36Sopenharmony_ci block_size, 110162306a36Sopenharmony_ci ta_firmware); 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci if (status) { 110462306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 110562306a36Sopenharmony_ci "%s: Unable to load %s blk\n", 110662306a36Sopenharmony_ci __func__, metadata_p->name); 110762306a36Sopenharmony_ci goto fail_free_fw; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: Successfully loaded %s instructions\n", 111162306a36Sopenharmony_ci __func__, metadata_p->name); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (adapter->rsi_host_intf == RSI_HOST_INTF_SDIO) { 111462306a36Sopenharmony_ci if (hif_ops->ta_reset(adapter)) 111562306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, "Unable to put ta in reset\n"); 111662306a36Sopenharmony_ci } else { 111762306a36Sopenharmony_ci if (bl_cmd(adapter, JUMP_TO_ZERO_PC, 111862306a36Sopenharmony_ci CMD_PASS, "JUMP_TO_ZERO") < 0) 111962306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "Jump to zero command failed\n"); 112062306a36Sopenharmony_ci else 112162306a36Sopenharmony_ci rsi_dbg(INFO_ZONE, "Jump to zero command successful\n"); 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cifail_free_fw: 112562306a36Sopenharmony_ci kfree(ta_firmware); 112662306a36Sopenharmony_cifail_release_fw: 112762306a36Sopenharmony_ci release_firmware(fw_entry); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci return status; 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ciint rsi_hal_device_init(struct rsi_hw *adapter) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci struct rsi_common *common = adapter->priv; 113562306a36Sopenharmony_ci int status; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci switch (adapter->device_model) { 113862306a36Sopenharmony_ci case RSI_DEV_9113: 113962306a36Sopenharmony_ci status = rsi_hal_prepare_fwload(adapter); 114062306a36Sopenharmony_ci if (status < 0) 114162306a36Sopenharmony_ci return status; 114262306a36Sopenharmony_ci if (rsi_load_9113_firmware(adapter)) { 114362306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 114462306a36Sopenharmony_ci "%s: Failed to load TA instructions\n", 114562306a36Sopenharmony_ci __func__); 114662306a36Sopenharmony_ci return -EINVAL; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci case RSI_DEV_9116: 115062306a36Sopenharmony_ci status = rsi_hal_prepare_fwload(adapter); 115162306a36Sopenharmony_ci if (status < 0) 115262306a36Sopenharmony_ci return status; 115362306a36Sopenharmony_ci if (rsi_load_9116_firmware(adapter)) { 115462306a36Sopenharmony_ci rsi_dbg(ERR_ZONE, 115562306a36Sopenharmony_ci "%s: Failed to load firmware to 9116 device\n", 115662306a36Sopenharmony_ci __func__); 115762306a36Sopenharmony_ci return -EINVAL; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci break; 116062306a36Sopenharmony_ci default: 116162306a36Sopenharmony_ci return -EINVAL; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci common->fsm_state = FSM_CARD_NOT_READY; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci return 0; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rsi_hal_device_init); 116862306a36Sopenharmony_ci 1169