diff options
Diffstat (limited to 'drivers/net')
41 files changed, 4837 insertions, 112 deletions
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index e493db4..5fdc40d 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -546,7 +546,7 @@ static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc, int ath10k_htc_wait_target(struct ath10k_htc *htc) { - int status = 0; + int i, status = 0; struct ath10k_htc_svc_conn_req conn_req; struct ath10k_htc_svc_conn_resp conn_resp; struct ath10k_htc_msg *msg; @@ -556,10 +556,26 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) status = wait_for_completion_timeout(&htc->ctl_resp, ATH10K_HTC_WAIT_TIMEOUT_HZ); - if (status <= 0) { + if (status == 0) { + /* Workaround: In some cases the PCI HIF doesn't + * receive interrupt for the control response message + * even if the buffer was completed. It is suspected + * iomap writes unmasking PCI CE irqs aren't propagated + * properly in KVM PCI-passthrough sometimes. + */ + ath10k_warn("failed to receive control response completion, polling..\n"); + + for (i = 0; i < CE_COUNT; i++) + ath10k_hif_send_complete_check(htc->ar, i, 1); + + status = wait_for_completion_timeout(&htc->ctl_resp, + ATH10K_HTC_WAIT_TIMEOUT_HZ); + if (status == 0) status = -ETIMEDOUT; + } + if (status < 0) { ath10k_err("ctl_resp never came in (%d)\n", status); return status; } diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index eebc860..80cdac1 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -21,6 +21,7 @@ #include "txrx.h" #include "debug.h" #include "trace.h" +#include "mac.h" #include <linux/log2.h> @@ -307,7 +308,8 @@ static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb) static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, u8 **fw_desc, int *fw_desc_len, struct sk_buff **head_msdu, - struct sk_buff **tail_msdu) + struct sk_buff **tail_msdu, + u32 *attention) { int msdu_len, msdu_chaining = 0; struct sk_buff *msdu; @@ -357,6 +359,11 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, break; } + *attention |= __le32_to_cpu(rx_desc->attention.flags) & + (RX_ATTENTION_FLAGS_TKIP_MIC_ERR | + RX_ATTENTION_FLAGS_DECRYPT_ERR | + RX_ATTENTION_FLAGS_FCS_ERR | + RX_ATTENTION_FLAGS_MGMT_TYPE); /* * Copy the FW rx descriptor for this MSDU from the rx * indication message into the MSDU's netbuf. HL uses the @@ -1215,13 +1222,15 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) { struct sk_buff *msdu_head, *msdu_tail; + attention = 0; msdu_head = NULL; msdu_tail = NULL; ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len, &msdu_head, - &msdu_tail); + &msdu_tail, + &attention); if (ret < 0) { ath10k_warn("failed to pop amsdu from htt rx ring %d\n", @@ -1233,7 +1242,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, rxd = container_of((void *)msdu_head->data, struct htt_rx_desc, msdu_payload); - attention = __le32_to_cpu(rxd->attention.flags); if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head, status, @@ -1286,6 +1294,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, u8 *fw_desc; int fw_desc_len, hdrlen, paramlen; int trim; + u32 attention = 0; fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes); fw_desc = (u8 *)frag->fw_msdu_rx_desc; @@ -1295,7 +1304,8 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, spin_lock_bh(&htt->rx_ring.lock); ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len, - &msdu_head, &msdu_tail); + &msdu_head, &msdu_tail, + &attention); spin_unlock_bh(&htt->rx_ring.lock); ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); @@ -1312,10 +1322,8 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, hdr = (struct ieee80211_hdr *)msdu_head->data; rxd = (void *)msdu_head->data - sizeof(*rxd); - tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) & - RX_ATTENTION_FLAGS_TKIP_MIC_ERR); - decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) & - RX_ATTENTION_FLAGS_DECRYPT_ERR); + tkip_mic_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR); + decrypt_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR); fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), RX_MSDU_START_INFO1_DECAP_FORMAT); @@ -1422,6 +1430,86 @@ static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar, } } +static void ath10k_htt_rx_addba(struct ath10k *ar, struct htt_resp *resp) +{ + struct htt_rx_addba *ev = &resp->rx_addba; + struct ath10k_peer *peer; + struct ath10k_vif *arvif; + u16 info0, tid, peer_id; + + info0 = __le16_to_cpu(ev->info0); + tid = MS(info0, HTT_RX_BA_INFO0_TID); + peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID); + + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx addba tid %hu peer_id %hu size %hhu\n", + tid, peer_id, ev->window_size); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, peer_id); + if (!peer) { + ath10k_warn("received addba event for invalid peer_id: %hu\n", + peer_id); + spin_unlock_bh(&ar->data_lock); + return; + } + + arvif = ath10k_get_arvif(ar, peer->vdev_id); + if (!arvif) { + ath10k_warn("received addba event for invalid vdev_id: %u\n", + peer->vdev_id); + spin_unlock_bh(&ar->data_lock); + return; + } + + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx start rx ba session sta %pM tid %hu size %hhu\n", + peer->addr, tid, ev->window_size); + + ieee80211_start_rx_ba_session_offl(arvif->vif, peer->addr, tid); + spin_unlock_bh(&ar->data_lock); +} + +static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp) +{ + struct htt_rx_delba *ev = &resp->rx_delba; + struct ath10k_peer *peer; + struct ath10k_vif *arvif; + u16 info0, tid, peer_id; + + info0 = __le16_to_cpu(ev->info0); + tid = MS(info0, HTT_RX_BA_INFO0_TID); + peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID); + + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx delba tid %hu peer_id %hu\n", + tid, peer_id); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, peer_id); + if (!peer) { + ath10k_warn("received addba event for invalid peer_id: %hu\n", + peer_id); + spin_unlock_bh(&ar->data_lock); + return; + } + + arvif = ath10k_get_arvif(ar, peer->vdev_id); + if (!arvif) { + ath10k_warn("received addba event for invalid vdev_id: %u\n", + peer->vdev_id); + spin_unlock_bh(&ar->data_lock); + return; + } + + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx stop rx ba session sta %pM tid %hu\n", + peer->addr, tid); + + ieee80211_stop_rx_ba_session_offl(arvif->vif, peer->addr, tid); + spin_unlock_bh(&ar->data_lock); +} + void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_htt *htt = &ar->htt; @@ -1516,9 +1604,25 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) trace_ath10k_htt_stats(skb->data, skb->len); break; case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: + /* Firmware can return tx frames if it's unable to fully + * process them and suspects host may be able to fix it. ath10k + * sends all tx frames as already inspected so this shouldn't + * happen unless fw has a bug. + */ + ath10k_warn("received an unexpected htt tx inspect event\n"); + break; case HTT_T2H_MSG_TYPE_RX_ADDBA: + ath10k_htt_rx_addba(ar, resp); + break; case HTT_T2H_MSG_TYPE_RX_DELBA: - case HTT_T2H_MSG_TYPE_RX_FLUSH: + ath10k_htt_rx_delba(ar, resp); + break; + case HTT_T2H_MSG_TYPE_RX_FLUSH: { + /* Ignore this event because mac80211 takes care of Rx + * aggregation reordering. + */ + break; + } default: ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n", resp->hdr.msg_type); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index accb6b4..8b27bfcc 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -531,6 +531,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; + /* Prevent firmware from sending up tx inspection requests. There's + * nothing ath10k can do with frames requested for inspection so force + * it to simply rely a regular tx completion with discard status. + */ + flags1 |= HTT_DATA_TX_DESC_FLAGS1_POSTPONED; + skb_cb->htt.txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; skb_cb->htt.txbuf->cmd_tx.flags0 = flags0; skb_cb->htt.txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b8314a5..9d61bb1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1865,15 +1865,13 @@ static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, return 0; } -/* - * Frames sent to the FW have to be in "Native Wifi" format. - * Strip the QoS field from the 802.11 header. +/* HTT Tx uses Native Wifi tx mode which expects 802.11 frames without QoS + * Control in the header. */ -static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) +static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; + struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); u8 *qos_ctl; if (!ieee80211_is_data_qos(hdr->frame_control)) @@ -1883,6 +1881,16 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, memmove(skb->data + IEEE80211_QOS_CTL_LEN, skb->data, (void *)qos_ctl - (void *)skb->data); skb_pull(skb, IEEE80211_QOS_CTL_LEN); + + /* Fw/Hw generates a corrupted QoS Control Field for QoS NullFunc + * frames. Powersave is handled by the fw/hw so QoS NyllFunc frames are + * used only for CQM purposes (e.g. hostapd station keepalive ping) so + * it is safe to downgrade to NullFunc. + */ + if (ieee80211_is_qos_nullfunc(hdr->frame_control)) { + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA); + cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + } } static void ath10k_tx_wep_key_work(struct work_struct *work) @@ -1919,14 +1927,13 @@ unlock: mutex_unlock(&arvif->ar->conf_mutex); } -static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) +static void ath10k_tx_h_update_wep_key(struct ieee80211_vif *vif, + struct ieee80211_key_conf *key, + struct sk_buff *skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_vif *vif = info->control.vif; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct ath10k *ar = arvif->ar; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_key_conf *key = info->control.hw_key; if (!ieee80211_has_protected(hdr->frame_control)) return; @@ -1948,11 +1955,11 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) ieee80211_queue_work(ar->hw, &arvif->wep_key_work); } -static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, + struct ieee80211_vif *vif, + struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_vif *vif = info->control.vif; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); /* This is case only for P2P_GO */ @@ -2254,33 +2261,28 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { + struct ath10k *ar = hw->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct ieee80211_key_conf *key = info->control.hw_key; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ath10k *ar = hw->priv; - u8 tid, vdev_id; /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n"); - /* we must calculate tid before we apply qos workaround - * as we'd lose the qos control field */ - tid = ath10k_tx_h_get_tid(hdr); - vdev_id = ath10k_tx_h_get_vdev_id(ar, info); + ATH10K_SKB_CB(skb)->htt.is_offchan = false; + ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr); + ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, info); /* it makes no sense to process injected frames like that */ - if (info->control.vif && - info->control.vif->type != NL80211_IFTYPE_MONITOR) { - ath10k_tx_h_qos_workaround(hw, control, skb); - ath10k_tx_h_update_wep_key(skb); - ath10k_tx_h_add_p2p_noa_ie(ar, skb); - ath10k_tx_h_seq_no(skb); + if (vif && vif->type != NL80211_IFTYPE_MONITOR) { + ath10k_tx_h_nwifi(hw, skb); + ath10k_tx_h_update_wep_key(vif, key, skb); + ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb); + ath10k_tx_h_seq_no(vif, skb); } - ATH10K_SKB_CB(skb)->vdev_id = vdev_id; - ATH10K_SKB_CB(skb)->htt.is_offchan = false; - ATH10K_SKB_CB(skb)->htt.tid = tid; - if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { spin_lock_bh(&ar->data_lock); ATH10K_SKB_CB(skb)->htt.is_offchan = true; @@ -4331,6 +4333,38 @@ static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) return 0; } +static int ath10k_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + ath10k_dbg(ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %hu action %d\n", + arvif->vdev_id, sta->addr, tid, action); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + /* HTT AddBa/DelBa events trigger mac80211 Rx BA session + * creation/removal. Do we need to verify this? + */ + return 0; + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_OPERATIONAL: + /* Firmware offloads Tx aggregation entirely so deny mac80211 + * Tx aggregation requests. + */ + return -EOPNOTSUPP; + } + + return -EINVAL; +} + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -4358,6 +4392,7 @@ static const struct ieee80211_ops ath10k_ops = { .set_bitrate_mask = ath10k_set_bitrate_mask, .sta_rc_update = ath10k_sta_rc_update, .get_tsf = ath10k_get_tsf, + .ampdu_action = ath10k_ampdu_action, #ifdef CONFIG_PM .suspend = ath10k_suspend, .resume = ath10k_resume, @@ -4698,7 +4733,6 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { @@ -4768,6 +4802,8 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->iface_combinations = ath10k_if_comb; ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb); + + ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); } ar->hw->netdev_features = NETIF_F_HW_CSUM; diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index ba10219..ef4f843 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -43,11 +43,11 @@ static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) return (struct ath10k_vif *)vif->drv_priv; } -static inline void ath10k_tx_h_seq_no(struct sk_buff *skb) +static inline void ath10k_tx_h_seq_no(struct ieee80211_vif *vif, + struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_vif *vif = info->control.vif; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 06840d1..0ffff20 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -726,18 +726,12 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) unsigned int nbytes, max_nbytes; unsigned int transfer_id; unsigned int flags; - int err; + int err, num_replenish = 0; while (ath10k_ce_completed_recv_next(ce_state, &transfer_context, &ce_data, &nbytes, &transfer_id, &flags) == 0) { - err = ath10k_pci_post_rx_pipe(pipe_info, 1); - if (unlikely(err)) { - /* FIXME: retry */ - ath10k_warn("failed to replenish CE rx ring %d: %d\n", - pipe_info->pipe_num, err); - } - + num_replenish++; skb = transfer_context; max_nbytes = skb->len + skb_tailroom(skb); dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, @@ -753,6 +747,13 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) skb_put(skb, nbytes); cb->rx_completion(ar, skb, pipe_info->pipe_num); } + + err = ath10k_pci_post_rx_pipe(pipe_info, num_replenish); + if (unlikely(err)) { + /* FIXME: retry */ + ath10k_warn("failed to replenish CE rx ring %d (%d bufs): %d\n", + pipe_info->pipe_num, num_replenish, err); + } } static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 82669a7..f4fa22d 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -119,8 +119,7 @@ struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, return NULL; } -static struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, - int peer_id) +struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, int peer_id) { struct ath10k_peer *peer; diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h index aee3e20..a90e09f 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.h +++ b/drivers/net/wireless/ath/ath10k/txrx.h @@ -24,6 +24,7 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, const u8 *addr); +struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, int peer_id); int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, const u8 *addr); int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 6f83cae..c2c87c9 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1432,7 +1432,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) continue; } - ath10k_tx_h_seq_no(bcn); + ath10k_tx_h_seq_no(arvif->vif, bcn); ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info); ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index be3eb2a..4173838 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -113,6 +113,7 @@ static int ath_ahb_probe(struct platform_device *pdev) irq = res->start; + ath9k_fill_chanctx_ops(); hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index d4c6ae3..64a5b67 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -132,35 +132,31 @@ config B43_PHY_G SoC: BCM4712, BCM5352E config B43_PHY_N - bool "Support for 802.11n (N-PHY) devices" + bool "Support for N-PHY (the main 802.11n series) devices" depends on B43 default y ---help--- - Support for the N-PHY. - - This enables support for devices with N-PHY. - - Say N if you expect high stability and performance. Saying Y will not - affect other devices support and may provide support for basic needs. + This PHY type can be found in the following chipsets: + PCI: BCM4321, BCM4322, + BCM43222, BCM43224, BCM43225, + BCM43131, BCM43217, BCM43227, BCM43228 + SoC: BCM4716, BCM4717, BCM4718, BCM5356, BCM5357, BCM5358 config B43_PHY_LP - bool "Support for low-power (LP-PHY) devices" + bool "Support for LP-PHY (low-power 802.11g) devices" depends on B43 && B43_SSB default y ---help--- - Support for the LP-PHY. The LP-PHY is a low-power PHY built into some notebooks and embedded devices. It supports 802.11a/b/g (802.11a support is optional, and currently disabled). config B43_PHY_HT - bool "Support for HT-PHY (high throughput) devices" + bool "Support for HT-PHY (high throughput 802.11n) devices" depends on B43 && B43_BCMA default y ---help--- - Support for the HT-PHY. - - Enables support for BCM4331 and possibly other chipsets with that PHY. + This PHY type with 3x3:3 MIMO can be found in the BCM4331 PCI chipset. config B43_PHY_LCN bool "Support for LCN-PHY devices (BROKEN)" diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index d7055fe..2af1ac3 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2985,7 +2985,8 @@ void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode) { u16 chip_id = dev->dev->chip_id; - if (chip_id == BCMA_CHIP_ID_BCM43217 || + if (chip_id == BCMA_CHIP_ID_BCM43131 || + chip_id == BCMA_CHIP_ID_BCM43217 || chip_id == BCMA_CHIP_ID_BCM43222 || chip_id == BCMA_CHIP_ID_BCM43224 || chip_id == BCMA_CHIP_ID_BCM43225 || diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index d269fbb..e2a3f0d 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -4982,7 +4982,8 @@ static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev) if (dev->phy.rev == 16) b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); - if (dev->dev->chip_id == BCMA_CHIP_ID_BCM43217) { + /* Verified with BCM43131 and BCM43217 */ + if (dev->phy.rev == 17) { b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); b43_nphy_pa_set_tx_dig_filter(dev, 0x195, tbl_tx_filter_coef_rev4[1]); @@ -6216,6 +6217,9 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, u16 tmp16; if (new_channel->band == IEEE80211_BAND_5GHZ) { + /* Switch to 2 GHz for a moment to access B43_PHY_B_BBCFG */ + b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); + tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); /* Put BPHY in the reset */ diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig index fcfed6b..b8e2561 100644 --- a/drivers/net/wireless/brcm80211/Kconfig +++ b/drivers/net/wireless/brcm80211/Kconfig @@ -48,6 +48,16 @@ config BRCMFMAC_USB IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to use the driver for an USB wireless card. +config BRCMFMAC_PCIE + bool "PCIE bus interface support for FullMAC driver" + depends on BRCMFMAC + depends on PCI + select FW_LOADER + ---help--- + This option enables the PCIE bus interface support for Broadcom + IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to + use the driver for an PCIE wireless card. + config BRCM_TRACING bool "Broadcom device tracing" depends on BRCMSMAC || BRCMFMAC diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index de0cff3..c35adf4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -31,6 +31,9 @@ brcmfmac-objs += \ p2p.o \ proto.o \ bcdc.o \ + commonring.o \ + flowring.o \ + msgbuf.o \ dhd_common.o \ dhd_linux.o \ firmware.o \ @@ -42,7 +45,11 @@ brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ bcmsdh.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o +brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \ + pcie.o brcmfmac-$(CONFIG_BRCMDBG) += \ dhd_dbg.o brcmfmac-$(CONFIG_BRCM_TRACING) += \ tracepoint.o +brcmfmac-$(CONFIG_OF) += \ + of.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index c229210..a159ff3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -337,6 +337,23 @@ brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, return brcmf_bus_txdata(drvr->bus_if, pktbuf); } +static void +brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ +} + +static void +brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]) +{ +} + +static void +brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]) +{ +} int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { @@ -356,6 +373,9 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; drvr->proto->txdata = brcmf_proto_bcdc_txdata; + drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode; + drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; + drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index f467caf..8dbd5db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -38,10 +38,13 @@ #include <brcm_hw_ids.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> +#include <chipcommon.h> #include <soc.h> +#include "chip.h" #include "dhd_bus.h" #include "dhd_dbg.h" #include "sdio_host.h" +#include "of.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -117,6 +120,7 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; u8 data; + u32 addr, gpiocontrol; unsigned long flags; if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { @@ -146,6 +150,19 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) sdio_claim_host(sdiodev->func[1]); + if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) { + /* assign GPIO to SDIO core */ + addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol); + gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret); + gpiocontrol |= 0x2; + brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf, + &ret); + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret); + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret); + } + /* must configure SDIO_CCCR_IENx to enable irq */ data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret); data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; @@ -1044,6 +1061,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, sdiodev->dev = &sdiodev->func[1]->dev; sdiodev->pdata = brcmfmac_sdio_pdata; + if (!sdiodev->pdata) + brcmf_of_probe(sdiodev); + atomic_set(&sdiodev->suspend, false); init_waitqueue_head(&sdiodev->request_word_wait); init_waitqueue_head(&sdiodev->request_buffer_wait); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 96800db..95efde8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -506,9 +506,17 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) break; case BRCM_CC_4339_CHIP_ID: case BRCM_CC_4354_CHIP_ID: + case BRCM_CC_4356_CHIP_ID: + case BRCM_CC_43567_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; + case BRCM_CC_43602_CHIP_ID: + ci->pub.ramsize = 0xf0000; + ci->pub.rambase = 0x180000; + break; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c new file mode 100644 index 0000000..c6d65b8 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/types.h> +#include <linux/netdevice.h> + +#include <brcmu_utils.h> +#include <brcmu_wifi.h> + +#include "dhd.h" +#include "commonring.h" + + +/* dma flushing needs implementation for mips and arm platforms. Should + * be put in util. Note, this is not real flushing. It is virtual non + * cached memory. Only write buffers should have to be drained. Though + * this may be different depending on platform...... + * SEE ALSO msgbuf.c + */ +#define brcmf_dma_flush(addr, len) +#define brcmf_dma_invalidate_cache(addr, len) + + +void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, + int (*cr_ring_bell)(void *ctx), + int (*cr_update_rptr)(void *ctx), + int (*cr_update_wptr)(void *ctx), + int (*cr_write_rptr)(void *ctx), + int (*cr_write_wptr)(void *ctx), void *ctx) +{ + commonring->cr_ring_bell = cr_ring_bell; + commonring->cr_update_rptr = cr_update_rptr; + commonring->cr_update_wptr = cr_update_wptr; + commonring->cr_write_rptr = cr_write_rptr; + commonring->cr_write_wptr = cr_write_wptr; + commonring->cr_ctx = ctx; +} + + +void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, + u16 item_len, void *buf_addr) +{ + commonring->depth = depth; + commonring->item_len = item_len; + commonring->buf_addr = buf_addr; + if (!commonring->inited) { + spin_lock_init(&commonring->lock); + commonring->inited = true; + } + commonring->r_ptr = 0; + if (commonring->cr_write_rptr) + commonring->cr_write_rptr(commonring->cr_ctx); + commonring->w_ptr = 0; + if (commonring->cr_write_wptr) + commonring->cr_write_wptr(commonring->cr_ctx); + commonring->f_ptr = 0; +} + + +void brcmf_commonring_lock(struct brcmf_commonring *commonring) + __acquires(&commonring->lock) +{ + unsigned long flags; + + spin_lock_irqsave(&commonring->lock, flags); + commonring->flags = flags; +} + + +void brcmf_commonring_unlock(struct brcmf_commonring *commonring) + __releases(&commonring->lock) +{ + spin_unlock_irqrestore(&commonring->lock, commonring->flags); +} + + +bool brcmf_commonring_write_available(struct brcmf_commonring *commonring) +{ + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + if (!commonring->was_full) + return true; + if (available > commonring->depth / 8) { + commonring->was_full = false; + return true; + } + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + return false; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return false; +} + + +void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring) +{ + void *ret_ptr; + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + ret_ptr = commonring->buf_addr + + (commonring->w_ptr * commonring->item_len); + commonring->w_ptr++; + if (commonring->w_ptr == commonring->depth) + commonring->w_ptr = 0; + return ret_ptr; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return NULL; +} + + +void * +brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, + u16 n_items, u16 *alloced) +{ + void *ret_ptr; + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + ret_ptr = commonring->buf_addr + + (commonring->w_ptr * commonring->item_len); + *alloced = min_t(u16, n_items, available - 1); + if (*alloced + commonring->w_ptr > commonring->depth) + *alloced = commonring->depth - commonring->w_ptr; + commonring->w_ptr += *alloced; + if (commonring->w_ptr == commonring->depth) + commonring->w_ptr = 0; + return ret_ptr; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return NULL; +} + + +int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) +{ + void *address; + + address = commonring->buf_addr; + address += (commonring->f_ptr * commonring->item_len); + if (commonring->f_ptr > commonring->w_ptr) { + brcmf_dma_flush(address, + (commonring->depth - commonring->f_ptr) * + commonring->item_len); + address = commonring->buf_addr; + commonring->f_ptr = 0; + } + brcmf_dma_flush(address, (commonring->w_ptr - commonring->f_ptr) * + commonring->item_len); + + commonring->f_ptr = commonring->w_ptr; + + if (commonring->cr_write_wptr) + commonring->cr_write_wptr(commonring->cr_ctx); + if (commonring->cr_ring_bell) + return commonring->cr_ring_bell(commonring->cr_ctx); + + return -EIO; +} + + +void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, + u16 n_items) +{ + if (commonring->w_ptr == 0) + commonring->w_ptr = commonring->depth - n_items; + else + commonring->w_ptr -= n_items; +} + + +void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, + u16 *n_items) +{ + void *ret_addr; + + if (commonring->cr_update_wptr) + commonring->cr_update_wptr(commonring->cr_ctx); + + *n_items = (commonring->w_ptr >= commonring->r_ptr) ? + (commonring->w_ptr - commonring->r_ptr) : + (commonring->depth - commonring->r_ptr); + + if (*n_items == 0) + return NULL; + + ret_addr = commonring->buf_addr + + (commonring->r_ptr * commonring->item_len); + + commonring->r_ptr += *n_items; + if (commonring->r_ptr == commonring->depth) + commonring->r_ptr = 0; + + brcmf_dma_invalidate_cache(ret_addr, *n_ items * commonring->item_len); + + return ret_addr; +} + + +int brcmf_commonring_read_complete(struct brcmf_commonring *commonring) +{ + if (commonring->cr_write_rptr) + return commonring->cr_write_rptr(commonring->cr_ctx); + + return -EIO; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h new file mode 100644 index 0000000..002336e --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_COMMONRING_H +#define BRCMFMAC_COMMONRING_H + + +struct brcmf_commonring { + u16 r_ptr; + u16 w_ptr; + u16 f_ptr; + u16 depth; + u16 item_len; + + void *buf_addr; + + int (*cr_ring_bell)(void *ctx); + int (*cr_update_rptr)(void *ctx); + int (*cr_update_wptr)(void *ctx); + int (*cr_write_rptr)(void *ctx); + int (*cr_write_wptr)(void *ctx); + + void *cr_ctx; + + spinlock_t lock; + unsigned long flags; + bool inited; + bool was_full; +}; + + +void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, + int (*cr_ring_bell)(void *ctx), + int (*cr_update_rptr)(void *ctx), + int (*cr_update_wptr)(void *ctx), + int (*cr_write_rptr)(void *ctx), + int (*cr_write_wptr)(void *ctx), void *ctx); +void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, + u16 item_len, void *buf_addr); +void brcmf_commonring_lock(struct brcmf_commonring *commonring); +void brcmf_commonring_unlock(struct brcmf_commonring *commonring); +bool brcmf_commonring_write_available(struct brcmf_commonring *commonring); +void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring); +void * +brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, + u16 n_items, u16 *alloced); +int brcmf_commonring_write_complete(struct brcmf_commonring *commonring); +void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, + u16 n_items); +void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, + u16 *n_items); +int brcmf_commonring_read_complete(struct brcmf_commonring *commonring); + +#define brcmf_commonring_n_items(commonring) (commonring->depth) +#define brcmf_commonring_len_item(commonring) (commonring->item_len) + + +#endif /* BRCMFMAC_COMMONRING_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 7da6441..5e4317d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -121,12 +121,12 @@ struct brcmf_fws_mac_descriptor; * * @BRCMF_NETIF_STOP_REASON_FWS_FC: * netif stopped due to firmware signalling flow control. - * @BRCMF_NETIF_STOP_REASON_BLOCK_BUS: - * netif stopped due to bus blocking. + * @BRCMF_NETIF_STOP_REASON_FLOW: + * netif stopped due to flowring full. */ enum brcmf_netif_stop_reason { BRCMF_NETIF_STOP_REASON_FWS_FC = 1, - BRCMF_NETIF_STOP_REASON_BLOCK_BUS = 2 + BRCMF_NETIF_STOP_REASON_FLOW = 2 }; /** @@ -181,6 +181,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success); +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); /* Sets dongle media info (drv_version, mac address). */ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 7735328..3122b86 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -19,6 +19,18 @@ #include "dhd_dbg.h" +/* IDs of the 6 default common rings of msgbuf protocol */ +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2 +#define BRCMF_D2H_MSGRING_TX_COMPLETE 3 +#define BRCMF_D2H_MSGRING_RX_COMPLETE 4 + +#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2 +#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3 +#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ + BRCMF_NROF_D2H_COMMON_MSGRINGS) + /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_UNKNOWN, /* Not determined yet */ @@ -70,6 +82,25 @@ struct brcmf_bus_ops { struct pktq * (*gettxq)(struct device *dev); }; + +/** + * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf. + * + * @commonrings: commonrings which are always there. + * @flowrings: commonrings which are dynamically created and destroyed for data. + * @rx_dataoffset: if set then all rx data has this this offset. + * @max_rxbufpost: maximum number of buffers to post for rx. + * @nrof_flowrings: number of flowrings. + */ +struct brcmf_bus_msgbuf { + struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; + struct brcmf_commonring **flowrings; + u32 rx_dataoffset; + u32 max_rxbufpost; + u32 nrof_flowrings; +}; + + /** * struct brcmf_bus - interface structure between common and bus layer * @@ -89,6 +120,7 @@ struct brcmf_bus { union { struct brcmf_sdio_dev *sdio; struct brcmf_usbdev *usb; + struct brcmf_pciedev *pcie; } bus_priv; enum brcmf_bus_protocol_type proto_type; struct device *dev; @@ -101,6 +133,7 @@ struct brcmf_bus { bool always_use_fws_queue; struct brcmf_bus_ops *ops; + struct brcmf_bus_msgbuf *msgbuf; }; /* diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 6eade7c..dec40d3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -18,23 +18,25 @@ #define _BRCMF_DBG_H_ /* message levels */ -#define BRCMF_TRACE_VAL 0x00000002 -#define BRCMF_INFO_VAL 0x00000004 -#define BRCMF_DATA_VAL 0x00000008 -#define BRCMF_CTL_VAL 0x00000010 -#define BRCMF_TIMER_VAL 0x00000020 -#define BRCMF_HDRS_VAL 0x00000040 -#define BRCMF_BYTES_VAL 0x00000080 -#define BRCMF_INTR_VAL 0x00000100 -#define BRCMF_GLOM_VAL 0x00000200 -#define BRCMF_EVENT_VAL 0x00000400 -#define BRCMF_BTA_VAL 0x00000800 -#define BRCMF_FIL_VAL 0x00001000 -#define BRCMF_USB_VAL 0x00002000 -#define BRCMF_SCAN_VAL 0x00004000 -#define BRCMF_CONN_VAL 0x00008000 -#define BRCMF_BCDC_VAL 0x00010000 -#define BRCMF_SDIO_VAL 0x00020000 +#define BRCMF_TRACE_VAL 0x00000002 +#define BRCMF_INFO_VAL 0x00000004 +#define BRCMF_DATA_VAL 0x00000008 +#define BRCMF_CTL_VAL 0x00000010 +#define BRCMF_TIMER_VAL 0x00000020 +#define BRCMF_HDRS_VAL 0x00000040 +#define BRCMF_BYTES_VAL 0x00000080 +#define BRCMF_INTR_VAL 0x00000100 +#define BRCMF_GLOM_VAL 0x00000200 +#define BRCMF_EVENT_VAL 0x00000400 +#define BRCMF_BTA_VAL 0x00000800 +#define BRCMF_FIL_VAL 0x00001000 +#define BRCMF_USB_VAL 0x00002000 +#define BRCMF_SCAN_VAL 0x00004000 +#define BRCMF_CONN_VAL 0x00008000 +#define BRCMF_BCDC_VAL 0x00010000 +#define BRCMF_SDIO_VAL 0x00020000 +#define BRCMF_MSGBUF_VAL 0x00040000 +#define BRCMF_PCIE_VAL 0x00080000 /* set default print format */ #undef pr_fmt diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 347b426..fb10439 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -32,6 +32,7 @@ #include "fwsignal.h" #include "feature.h" #include "proto.h" +#include "pcie.h" MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); @@ -288,7 +289,7 @@ void brcmf_txflowblock(struct device *dev, bool state) brcmf_fws_bus_blocked(drvr, state); } -static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) { skb->dev = ifp->ndev; skb->protocol = eth_type_trans(skb, skb->dev); @@ -1085,6 +1086,9 @@ static void brcmf_driver_register(struct work_struct *work) #ifdef CONFIG_BRCMFMAC_USB brcmf_usb_register(); #endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_register(); +#endif } static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); @@ -1110,6 +1114,9 @@ static void __exit brcmfmac_module_exit(void) #ifdef CONFIG_BRCMFMAC_USB brcmf_usb_exit(); #endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_exit(); +#endif brcmf_debugfs_exit(); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 67d91d5..f55f625 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -670,6 +670,8 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, struct brcmf_sdio_dev *sdiodev) { int i; + uint fw_len, nv_len; + char end; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { if (brcmf_fwname_data[i].chipid == ci->chip && @@ -682,16 +684,25 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, return -ENODEV; } + fw_len = sizeof(sdiodev->fw_name) - 1; + nv_len = sizeof(sdiodev->nvram_name) - 1; /* check if firmware path is provided by module parameter */ if (brcmf_firmware_path[0] != '\0') { - if (brcmf_firmware_path[strlen(brcmf_firmware_path) - 1] != '/') - strcat(brcmf_firmware_path, "/"); - - strcpy(sdiodev->fw_name, brcmf_firmware_path); - strcpy(sdiodev->nvram_name, brcmf_firmware_path); + strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len); + strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len); + fw_len -= strlen(sdiodev->fw_name); + nv_len -= strlen(sdiodev->nvram_name); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strncat(sdiodev->fw_name, "/", fw_len); + strncat(sdiodev->nvram_name, "/", nv_len); + fw_len--; + nv_len--; + } } - strcat(sdiodev->fw_name, brcmf_fwname_data[i].bin); - strcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv); + strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len); + strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c new file mode 100644 index 0000000..a1016b8 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -0,0 +1,501 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <brcmu_utils.h> + +#include "dhd.h" +#include "dhd_dbg.h" +#include "dhd_bus.h" +#include "proto.h" +#include "flowring.h" +#include "msgbuf.h" + + +#define BRCMF_FLOWRING_HIGH 1024 +#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256) +#define BRCMF_FLOWRING_INVALID_IFIDX 0xff + +#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) +#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) + +static const u8 ALLZEROMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static const u8 brcmf_flowring_prio2fifo[] = { + 1, + 0, + 0, + 1, + 2, + 2, + 3, + 3 +}; + + +static bool +brcmf_flowring_is_tdls_mac(struct brcmf_flowring *flow, u8 mac[ETH_ALEN]) +{ + struct brcmf_flowring_tdls_entry *search; + + search = flow->tdls_entry; + + while (search) { + if (memcmp(search->mac, mac, ETH_ALEN) == 0) + return true; + search = search->next; + } + + return false; +} + + +u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx) +{ + struct brcmf_flowring_hash *hash; + u8 hash_idx; + u32 i; + bool found; + bool sta; + u8 fifo; + u8 *mac; + + fifo = brcmf_flowring_prio2fifo[prio]; + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + mac = da; + if ((!sta) && (is_multicast_ether_addr(da))) { + mac = (u8 *)ALLFFMAC; + fifo = 0; + } + if ((sta) && (flow->tdls_active) && + (brcmf_flowring_is_tdls_mac(flow, da))) { + sta = false; + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((sta || (memcmp(hash[hash_idx].mac, mac, ETH_ALEN) == 0)) && + (hash[hash_idx].fifo == fifo) && + (hash[hash_idx].ifidx == ifidx)) { + found = true; + break; + } + hash_idx++; + } + if (found) + return hash[hash_idx].flowid; + + return BRCMF_FLOWRING_INVALID_ID; +} + + +u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx) +{ + struct brcmf_flowring_ring *ring; + struct brcmf_flowring_hash *hash; + u8 hash_idx; + u32 i; + bool found; + u8 fifo; + bool sta; + u8 *mac; + + fifo = brcmf_flowring_prio2fifo[prio]; + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + mac = da; + if ((!sta) && (is_multicast_ether_addr(da))) { + mac = (u8 *)ALLFFMAC; + fifo = 0; + } + if ((sta) && (flow->tdls_active) && + (brcmf_flowring_is_tdls_mac(flow, da))) { + sta = false; + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) && + (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0)) { + found = true; + break; + } + hash_idx++; + } + if (found) { + for (i = 0; i < flow->nrofrings; i++) { + if (flow->rings[i] == NULL) + break; + } + if (i == flow->nrofrings) + return -ENOMEM; + + ring = kzalloc(sizeof(*ring), GFP_ATOMIC); + if (!ring) + return -ENOMEM; + + memcpy(hash[hash_idx].mac, mac, ETH_ALEN); + hash[hash_idx].fifo = fifo; + hash[hash_idx].ifidx = ifidx; + hash[hash_idx].flowid = i; + + ring->hash_id = hash_idx; + ring->status = RING_CLOSED; + skb_queue_head_init(&ring->skblist); + flow->rings[i] = ring; + + return i; + } + return BRCMF_FLOWRING_INVALID_ID; +} + + +u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + return flow->hash[ring->hash_id].fifo; +} + + +static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, + bool blocked) +{ + struct brcmf_flowring_ring *ring; + struct brcmf_bus *bus_if; + struct brcmf_pub *drvr; + struct brcmf_if *ifp; + bool currently_blocked; + int i; + u8 ifidx; + unsigned long flags; + + spin_lock_irqsave(&flow->block_lock, flags); + + ring = flow->rings[flowid]; + ifidx = brcmf_flowring_ifidx_get(flow, flowid); + + currently_blocked = false; + for (i = 0; i < flow->nrofrings; i++) { + if (flow->rings[i]) { + ring = flow->rings[i]; + if ((ring->status == RING_OPEN) && + (brcmf_flowring_ifidx_get(flow, i) == ifidx)) { + if (ring->blocked) { + currently_blocked = true; + break; + } + } + } + } + ring->blocked = blocked; + if (currently_blocked == blocked) { + spin_unlock_irqrestore(&flow->block_lock, flags); + return; + } + + bus_if = dev_get_drvdata(flow->dev); + drvr = bus_if->drvr; + ifp = drvr->iflist[ifidx]; + brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); + + spin_unlock_irqrestore(&flow->block_lock, flags); +} + + +void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + u8 hash_idx; + struct sk_buff *skb; + + ring = flow->rings[flowid]; + if (!ring) + return; + brcmf_flowring_block(flow, flowid, false); + hash_idx = ring->hash_id; + flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; + memset(flow->hash[hash_idx].mac, 0, ETH_ALEN); + flow->rings[flowid] = NULL; + + skb = skb_dequeue(&ring->skblist); + while (skb) { + brcmu_pkt_buf_free_skb(skb); + skb = skb_dequeue(&ring->skblist); + } + + kfree(ring); +} + + +void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + skb_queue_tail(&ring->skblist, skb); + + if (!ring->blocked && + (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { + brcmf_flowring_block(flow, flowid, true); + brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); + /* To prevent (work around) possible race condition, check + * queue len again. It is also possible to use locking to + * protect, but that is undesirable for every enqueue and + * dequeue. This simple check will solve a possible race + * condition if it occurs. + */ + if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) + brcmf_flowring_block(flow, flowid, false); + } +} + + +struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + struct sk_buff *skb; + + ring = flow->rings[flowid]; + if (ring->status != RING_OPEN) + return NULL; + + skb = skb_dequeue(&ring->skblist); + + if (ring->blocked && + (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { + brcmf_flowring_block(flow, flowid, false); + brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); + } + + return skb; +} + + +void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + skb_queue_head(&ring->skblist, skb); +} + + +u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + if (!ring) + return 0; + + if (ring->status != RING_OPEN) + return 0; + + return skb_queue_len(&ring->skblist); +} + + +void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + if (!ring) { + brcmf_err("Ring NULL, for flowid %d\n", flowid); + return; + } + + ring->status = RING_OPEN; +} + + +u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + u8 hash_idx; + + ring = flow->rings[flowid]; + hash_idx = ring->hash_id; + + return flow->hash[hash_idx].ifidx; +} + + +struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) +{ + struct brcmf_flowring *flow; + u32 i; + + flow = kzalloc(sizeof(*flow), GFP_ATOMIC); + if (flow) { + flow->dev = dev; + flow->nrofrings = nrofrings; + spin_lock_init(&flow->block_lock); + for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) + flow->addr_mode[i] = ADDR_INDIRECT; + for (i = 0; i < ARRAY_SIZE(flow->hash); i++) + flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; + flow->rings = kcalloc(nrofrings, sizeof(*flow->rings), + GFP_ATOMIC); + if (!flow->rings) { + kfree(flow); + flow = NULL; + } + } + + return flow; +} + + +void brcmf_flowring_detach(struct brcmf_flowring *flow) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_flowring_tdls_entry *search; + struct brcmf_flowring_tdls_entry *remove; + u8 flowid; + + for (flowid = 0; flowid < flow->nrofrings; flowid++) { + if (flow->rings[flowid]) + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + + search = flow->tdls_entry; + while (search) { + remove = search; + search = search->next; + kfree(remove); + } + kfree(flow->rings); + kfree(flow); +} + + +void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, + enum proto_addr_mode addr_mode) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + u32 i; + u8 flowid; + + if (flow->addr_mode[ifidx] != addr_mode) { + for (i = 0; i < ARRAY_SIZE(flow->hash); i++) { + if (flow->hash[i].ifidx == ifidx) { + flowid = flow->hash[i].flowid; + if (flow->rings[flowid]->status != RING_OPEN) + continue; + flow->rings[flowid]->status = RING_CLOSING; + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + } + flow->addr_mode[ifidx] = addr_mode; + } +} + + +void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_flowring_hash *hash; + struct brcmf_flowring_tdls_entry *prev; + struct brcmf_flowring_tdls_entry *search; + u32 i; + u8 flowid; + bool sta; + + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + + search = flow->tdls_entry; + prev = NULL; + while (search) { + if (memcmp(search->mac, peer, ETH_ALEN) == 0) { + sta = false; + break; + } + prev = search; + search = search->next; + } + + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && + (hash[i].ifidx == ifidx)) { + flowid = flow->hash[i].flowid; + if (flow->rings[flowid]->status == RING_OPEN) { + flow->rings[flowid]->status = RING_CLOSING; + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + } + } + + if (search) { + if (prev) + prev->next = search->next; + else + flow->tdls_entry = search->next; + kfree(search); + if (flow->tdls_entry == NULL) + flow->tdls_active = false; + } +} + + +void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]) +{ + struct brcmf_flowring_tdls_entry *tdls_entry; + struct brcmf_flowring_tdls_entry *search; + + tdls_entry = kzalloc(sizeof(*tdls_entry), GFP_ATOMIC); + if (tdls_entry == NULL) + return; + + memcpy(tdls_entry->mac, peer, ETH_ALEN); + tdls_entry->next = NULL; + if (flow->tdls_entry == NULL) { + flow->tdls_entry = tdls_entry; + } else { + search = flow->tdls_entry; + if (memcmp(search->mac, peer, ETH_ALEN) == 0) + return; + while (search->next) { + search = search->next; + if (memcmp(search->mac, peer, ETH_ALEN) == 0) + return; + } + search->next = tdls_entry; + } + + flow->tdls_active = true; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h new file mode 100644 index 0000000..a34cd39 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_FLOWRING_H +#define BRCMFMAC_FLOWRING_H + + +#define BRCMF_FLOWRING_HASHSIZE 256 +#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF + + +struct brcmf_flowring_hash { + u8 mac[ETH_ALEN]; + u8 fifo; + u8 ifidx; + u8 flowid; +}; + +enum ring_status { + RING_CLOSED, + RING_CLOSING, + RING_OPEN +}; + +struct brcmf_flowring_ring { + u8 hash_id; + bool blocked; + enum ring_status status; + struct sk_buff_head skblist; +}; + +struct brcmf_flowring_tdls_entry { + u8 mac[ETH_ALEN]; + struct brcmf_flowring_tdls_entry *next; +}; + +struct brcmf_flowring { + struct device *dev; + struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; + struct brcmf_flowring_ring **rings; + spinlock_t block_lock; + enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; + u16 nrofrings; + bool tdls_active; + struct brcmf_flowring_tdls_entry *tdls_entry; +}; + + +u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx); +u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx); +void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); +u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); +struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); +u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid); +u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid); +struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings); +void brcmf_flowring_detach(struct brcmf_flowring *flow); +void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, + enum proto_addr_mode addr_mode); +void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]); +void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]); + + +#endif /* BRCMFMAC_FLOWRING_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index fad77dd..4f1daab 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -293,7 +293,11 @@ static void brcmf_fweh_event_worker(struct work_struct *work) goto event_free; } - ifp = drvr->iflist[emsg.bsscfgidx]; + if ((event->code == BRCMF_E_TDLS_PEER_EVENT) && + (emsg.bsscfgidx == 1)) + ifp = drvr->iflist[0]; + else + ifp = drvr->iflist[emsg.bsscfgidx]; err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg, event->data); if (err) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index 51b53a7..dd20b18 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -102,6 +102,7 @@ struct brcmf_event; BRCMF_ENUM_DEF(DCS_REQUEST, 73) \ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ + BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128) @@ -155,6 +156,10 @@ enum brcmf_fweh_event_code { #define BRCMF_E_REASON_TSPEC_REJECTED 7 #define BRCMF_E_REASON_BETTER_AP 8 +#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED 0 +#define BRCMF_E_REASON_TDLS_PEER_CONNECTED 1 +#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED 2 + /* action field values for brcmf_ifevent */ #define BRCMF_E_IF_ADD 1 #define BRCMF_E_IF_DEL 2 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c new file mode 100644 index 0000000..535c7eb --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -0,0 +1,1397 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/******************************************************************************* + * Communicates with the dongle by using dcmd codes. + * For certain dcmd codes, the dongle interprets string data from the host. + ******************************************************************************/ + +#include <linux/types.h> +#include <linux/netdevice.h> + +#include <brcmu_utils.h> +#include <brcmu_wifi.h> + +#include "dhd.h" +#include "dhd_dbg.h" +#include "proto.h" +#include "msgbuf.h" +#include "commonring.h" +#include "flowring.h" +#include "dhd_bus.h" +#include "tracepoint.h" + + +#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 + +#define MSGBUF_TYPE_GEN_STATUS 0x1 +#define MSGBUF_TYPE_RING_STATUS 0x2 +#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3 +#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4 +#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5 +#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6 +#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7 +#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8 +#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9 +#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA +#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB +#define MSGBUF_TYPE_IOCTL_CMPLT 0xC +#define MSGBUF_TYPE_EVENT_BUF_POST 0xD +#define MSGBUF_TYPE_WL_EVENT 0xE +#define MSGBUF_TYPE_TX_POST 0xF +#define MSGBUF_TYPE_TX_STATUS 0x10 +#define MSGBUF_TYPE_RXBUF_POST 0x11 +#define MSGBUF_TYPE_RX_CMPLT 0x12 +#define MSGBUF_TYPE_LPBK_DMAXFER 0x13 +#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 + +#define NR_TX_PKTIDS 2048 +#define NR_RX_PKTIDS 1024 + +#define BRCMF_IOCTL_REQ_PKTID 0xFFFE + +#define BRCMF_MSGBUF_MAX_PKT_SIZE 2048 +#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD 32 +#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST 8 +#define BRCMF_MSGBUF_MAX_EVENTBUF_POST 8 + +#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3 0x01 +#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5 + +#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 +#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 + + +struct msgbuf_common_hdr { + u8 msgtype; + u8 ifidx; + u8 flags; + u8 rsvd0; + __le32 request_id; +}; + +struct msgbuf_buf_addr { + __le32 low_addr; + __le32 high_addr; +}; + +struct msgbuf_ioctl_req_hdr { + struct msgbuf_common_hdr msg; + __le32 cmd; + __le16 trans_id; + __le16 input_buf_len; + __le16 output_buf_len; + __le16 rsvd0[3]; + struct msgbuf_buf_addr req_buf_addr; + __le32 rsvd1[2]; +}; + +struct msgbuf_tx_msghdr { + struct msgbuf_common_hdr msg; + u8 txhdr[ETH_HLEN]; + u8 flags; + u8 seg_cnt; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; + __le16 metadata_buf_len; + __le16 data_len; + __le32 rsvd0; +}; + +struct msgbuf_rx_bufpost { + struct msgbuf_common_hdr msg; + __le16 metadata_buf_len; + __le16 data_buf_len; + __le32 rsvd0; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; +}; + +struct msgbuf_rx_ioctl_resp_or_event { + struct msgbuf_common_hdr msg; + __le16 host_buf_len; + __le16 rsvd0[3]; + struct msgbuf_buf_addr host_buf_addr; + __le32 rsvd1[4]; +}; + +struct msgbuf_completion_hdr { + __le16 status; + __le16 flow_ring_id; +}; + +struct msgbuf_rx_event { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 event_data_len; + __le16 seqnum; + __le16 rsvd0[4]; +}; + +struct msgbuf_ioctl_resp_hdr { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 resp_len; + __le16 trans_id; + __le32 cmd; + __le32 rsvd0; +}; + +struct msgbuf_tx_status { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 metadata_len; + __le16 tx_status; +}; + +struct msgbuf_rx_complete { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 metadata_len; + __le16 data_len; + __le16 data_offset; + __le16 flags; + __le32 rx_status_0; + __le32 rx_status_1; + __le32 rsvd0; +}; + +struct msgbuf_tx_flowring_create_req { + struct msgbuf_common_hdr msg; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u8 tid; + u8 if_flags; + __le16 flow_ring_id; + u8 tc; + u8 priority; + __le16 int_vector; + __le16 max_items; + __le16 len_item; + struct msgbuf_buf_addr flow_ring_addr; +}; + +struct msgbuf_tx_flowring_delete_req { + struct msgbuf_common_hdr msg; + __le16 flow_ring_id; + __le16 reason; + __le32 rsvd0[7]; +}; + +struct msgbuf_flowring_create_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct msgbuf_flowring_delete_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct msgbuf_flowring_flush_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct brcmf_msgbuf { + struct brcmf_pub *drvr; + + struct brcmf_commonring **commonrings; + struct brcmf_commonring **flowrings; + dma_addr_t *flowring_dma_handle; + u16 nrof_flowrings; + + u16 rx_dataoffset; + u32 max_rxbufpost; + u16 rx_metadata_offset; + u32 rxbufpost; + + u32 max_ioctlrespbuf; + u32 cur_ioctlrespbuf; + u32 max_eventbuf; + u32 cur_eventbuf; + + void *ioctbuf; + dma_addr_t ioctbuf_handle; + u32 ioctbuf_phys_hi; + u32 ioctbuf_phys_lo; + u32 ioctl_resp_status; + u32 ioctl_resp_ret_len; + u32 ioctl_resp_pktid; + + u16 data_seq_no; + u16 ioctl_seq_no; + u32 reqid; + wait_queue_head_t ioctl_resp_wait; + bool ctl_completed; + + struct brcmf_msgbuf_pktids *tx_pktids; + struct brcmf_msgbuf_pktids *rx_pktids; + struct brcmf_flowring *flow; + + struct workqueue_struct *txflow_wq; + struct work_struct txflow_work; + unsigned long *flow_map; + unsigned long *txstatus_done_map; +}; + +struct brcmf_msgbuf_pktid { + atomic_t allocated; + u16 data_offset; + struct sk_buff *skb; + dma_addr_t physaddr; +}; + +struct brcmf_msgbuf_pktids { + u32 array_size; + u32 last_allocated_idx; + enum dma_data_direction direction; + struct brcmf_msgbuf_pktid *array; +}; + + +/* dma flushing needs implementation for mips and arm platforms. Should + * be put in util. Note, this is not real flushing. It is virtual non + * cached memory. Only write buffers should have to be drained. Though + * this may be different depending on platform...... + */ +#define brcmf_dma_flush(addr, len) +#define brcmf_dma_invalidate_cache(addr, len) + + +static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); + + +static struct brcmf_msgbuf_pktids * +brcmf_msgbuf_init_pktids(u32 nr_array_entries, + enum dma_data_direction direction) +{ + struct brcmf_msgbuf_pktid *array; + struct brcmf_msgbuf_pktids *pktids; + + array = kcalloc(nr_array_entries, sizeof(*array), GFP_ATOMIC); + if (!array) + return NULL; + + pktids = kzalloc(sizeof(*pktids), GFP_ATOMIC); + if (!pktids) { + kfree(array); + return NULL; + } + pktids->array = array; + pktids->array_size = nr_array_entries; + + return pktids; +} + + +static int +brcmf_msgbuf_alloc_pktid(struct device *dev, + struct brcmf_msgbuf_pktids *pktids, + struct sk_buff *skb, u16 data_offset, + dma_addr_t *physaddr, u32 *idx) +{ + struct brcmf_msgbuf_pktid *array; + u32 count; + + array = pktids->array; + + *physaddr = dma_map_single(dev, skb->data + data_offset, + skb->len - data_offset, pktids->direction); + + if (dma_mapping_error(dev, *physaddr)) { + brcmf_err("dma_map_single failed !!\n"); + return -ENOMEM; + } + + *idx = pktids->last_allocated_idx; + + count = 0; + do { + (*idx)++; + if (*idx == pktids->array_size) + *idx = 0; + if (array[*idx].allocated.counter == 0) + if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0) + break; + count++; + } while (count < pktids->array_size); + + if (count == pktids->array_size) + return -ENOMEM; + + array[*idx].data_offset = data_offset; + array[*idx].physaddr = *physaddr; + array[*idx].skb = skb; + + pktids->last_allocated_idx = *idx; + + return 0; +} + + +static struct sk_buff * +brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids, + u32 idx) +{ + struct brcmf_msgbuf_pktid *pktid; + struct sk_buff *skb; + + if (idx >= pktids->array_size) { + brcmf_err("Invalid packet id %d (max %d)\n", idx, + pktids->array_size); + return NULL; + } + if (pktids->array[idx].allocated.counter) { + pktid = &pktids->array[idx]; + dma_unmap_single(dev, pktid->physaddr, + pktid->skb->len - pktid->data_offset, + pktids->direction); + skb = pktid->skb; + pktid->allocated.counter = 0; + return skb; + } else { + brcmf_err("Invalid packet id %d (not in use)\n", idx); + } + + return NULL; +} + + +static void +brcmf_msgbuf_release_array(struct device *dev, + struct brcmf_msgbuf_pktids *pktids) +{ + struct brcmf_msgbuf_pktid *array; + struct brcmf_msgbuf_pktid *pktid; + u32 count; + + array = pktids->array; + count = 0; + do { + if (array[count].allocated.counter) { + pktid = &array[count]; + dma_unmap_single(dev, pktid->physaddr, + pktid->skb->len - pktid->data_offset, + pktids->direction); + brcmu_pkt_buf_free_skb(pktid->skb); + } + count++; + } while (count < pktids->array_size); + + kfree(array); + kfree(pktids); +} + + +static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf) +{ + if (msgbuf->rx_pktids) + brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids); + if (msgbuf->tx_pktids) + brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids); +} + + +static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + struct msgbuf_ioctl_req_hdr *request; + u16 buf_len; + void *ret_ptr; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return -ENOMEM; + } + + msgbuf->reqid++; + + request = (struct msgbuf_ioctl_req_hdr *)ret_ptr; + request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; + request->msg.ifidx = (u8)ifidx; + request->msg.flags = 0; + request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID); + request->cmd = cpu_to_le32(cmd); + request->output_buf_len = cpu_to_le16(len); + request->trans_id = cpu_to_le16(msgbuf->reqid); + + buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE); + request->input_buf_len = cpu_to_le16(buf_len); + request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi); + request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo); + if (buf) + memcpy(msgbuf->ioctbuf, buf, buf_len); + else + memset(msgbuf->ioctbuf, 0, buf_len); + brcmf_dma_flush(ioctl_buf, buf_len); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + + return err; +} + + +static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) +{ + return wait_event_timeout(msgbuf->ioctl_resp_wait, + msgbuf->ctl_completed, + msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT)); +} + + +static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) +{ + if (waitqueue_active(&msgbuf->ioctl_resp_wait)) { + msgbuf->ctl_completed = true; + wake_up(&msgbuf->ioctl_resp_wait); + } +} + + +static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct sk_buff *skb = NULL; + int timeout; + int err; + + brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len); + msgbuf->ctl_completed = false; + err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len); + if (err) + return err; + + timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf); + if (!timeout) { + brcmf_err("Timeout on response for query command\n"); + return -EIO; + } + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, + msgbuf->ioctl_resp_pktid); + if (msgbuf->ioctl_resp_ret_len != 0) { + if (!skb) { + brcmf_err("Invalid packet id idx recv'd %d\n", + msgbuf->ioctl_resp_pktid); + return -EBADF; + } + memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? + len : msgbuf->ioctl_resp_ret_len); + } + if (skb) + brcmu_pkt_buf_free_skb(skb); + + return msgbuf->ioctl_resp_status; +} + + +static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len); +} + + +static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, + u8 *ifidx, struct sk_buff *skb) +{ + return -ENODEV; +} + + +static void +brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid) +{ + u32 dma_sz; + void *dma_buf; + + brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid); + + dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; + dma_buf = msgbuf->flowrings[flowid]->buf_addr; + dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf, + msgbuf->flowring_dma_handle[flowid]); + + brcmf_flowring_delete(msgbuf->flow, flowid); +} + + +static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, + struct sk_buff *skb) +{ + struct msgbuf_tx_flowring_create_req *create; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + struct brcmf_commonring *commonring; + void *ret_ptr; + u32 flowid; + void *dma_buf; + u32 dma_sz; + long long address; + int err; + + flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, + skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) + return flowid; + + dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; + + dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz, + &msgbuf->flowring_dma_handle[flowid], + GFP_ATOMIC); + if (!dma_buf) { + brcmf_err("dma_alloc_coherent failed\n"); + brcmf_flowring_delete(msgbuf->flow, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + brcmf_commonring_config(msgbuf->flowrings[flowid], + BRCMF_H2D_TXFLOWRING_MAX_ITEM, + BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf); + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + create = (struct msgbuf_tx_flowring_create_req *)ret_ptr; + create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE; + create->msg.ifidx = ifidx; + create->msg.request_id = 0; + create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); + create->flow_ring_id = cpu_to_le16(flowid + + BRCMF_NROF_H2D_COMMON_MSGRINGS); + memcpy(create->sa, eh->h_source, ETH_ALEN); + memcpy(create->da, eh->h_dest, ETH_ALEN); + address = (long long)(long)msgbuf->flowring_dma_handle[flowid]; + create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); + create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); + create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM); + create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE); + + brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n", + flowid, eh->h_dest, create->tid, ifidx); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + if (err) { + brcmf_err("Failed to write commonring\n"); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + return flowid; +} + + +static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) +{ + struct brcmf_flowring *flow = msgbuf->flow; + struct brcmf_commonring *commonring; + void *ret_ptr; + u32 count; + struct sk_buff *skb; + dma_addr_t physaddr; + u32 pktid; + struct msgbuf_tx_msghdr *tx_msghdr; + long long address; + + commonring = msgbuf->flowrings[flowid]; + if (!brcmf_commonring_write_available(commonring)) + return; + + brcmf_commonring_lock(commonring); + + count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1; + while (brcmf_flowring_qlen(flow, flowid)) { + skb = brcmf_flowring_dequeue(flow, flowid); + if (skb == NULL) { + brcmf_err("No SKB, but qlen %d\n", + brcmf_flowring_qlen(flow, flowid)); + break; + } + skb_orphan(skb); + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, skb, ETH_HLEN, + &physaddr, &pktid)) { + brcmf_flowring_reinsert(flow, flowid, skb); + brcmf_err("No PKTID available !!\n"); + break; + } + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, pktid); + brcmf_flowring_reinsert(flow, flowid, skb); + break; + } + count++; + + tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr; + + tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST; + tx_msghdr->msg.request_id = cpu_to_le32(pktid); + tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid); + tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3; + tx_msghdr->flags |= (skb->priority & 0x07) << + BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT; + tx_msghdr->seg_cnt = 1; + memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN); + tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN); + address = (long long)(long)physaddr; + tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32); + tx_msghdr->data_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + tx_msghdr->metadata_buf_len = 0; + tx_msghdr->metadata_buf_addr.high_addr = 0; + tx_msghdr->metadata_buf_addr.low_addr = 0; + if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) { + brcmf_commonring_write_complete(commonring); + count = 0; + } + } + if (count) + brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); +} + + +static void brcmf_msgbuf_txflow_worker(struct work_struct *worker) +{ + struct brcmf_msgbuf *msgbuf; + u32 flowid; + + msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work); + for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) { + clear_bit(flowid, msgbuf->flow_map); + brcmf_msgbuf_txflow(msgbuf, flowid); + } +} + + +static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid) +{ + set_bit(flowid, msgbuf->flow_map); + queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); + + return 0; +} + + +static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, + u8 offset, struct sk_buff *skb) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_flowring *flow = msgbuf->flow; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + u32 flowid; + + flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) { + flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb); + if (flowid == BRCMF_FLOWRING_INVALID_ID) + return -ENOMEM; + } + brcmf_flowring_enqueue(flow, flowid, skb); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + + return 0; +} + + +static void +brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode); +} + + +static void +brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer); +} + + +static void +brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer); +} + + +static void +brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_ioctl_resp_hdr *ioctl_resp; + + ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf; + + msgbuf->ioctl_resp_status = le16_to_cpu(ioctl_resp->compl_hdr.status); + msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len); + msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id); + + brcmf_msgbuf_ioctl_resp_wake(msgbuf); + + if (msgbuf->cur_ioctlrespbuf) + msgbuf->cur_ioctlrespbuf--; + brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); +} + + +static void +brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_tx_status *tx_status; + u32 idx; + struct sk_buff *skb; + u16 flowid; + + tx_status = (struct msgbuf_tx_status *)buf; + idx = le32_to_cpu(tx_status->msg.request_id); + flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, idx); + if (!skb) { + brcmf_err("Invalid packet id idx recv'd %d\n", idx); + return; + } + + set_bit(flowid, msgbuf->txstatus_done_map); + + brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true); +} + + +static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) +{ + struct brcmf_commonring *commonring; + void *ret_ptr; + struct sk_buff *skb; + u16 alloced; + u32 pktlen; + dma_addr_t physaddr; + struct msgbuf_rx_bufpost *rx_bufpost; + long long address; + u32 pktid; + u32 i; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT]; + ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, + count, + &alloced); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + return 0; + } + + for (i = 0; i < alloced; i++) { + rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr; + memset(rx_bufpost, 0, sizeof(*rx_bufpost)); + + skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); + + if (skb == NULL) { + brcmf_err("Failed to alloc SKB\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + pktlen = skb->len; + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, skb, 0, + &physaddr, &pktid)) { + dev_kfree_skb_any(skb); + brcmf_err("No PKTID available !!\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + if (msgbuf->rx_metadata_offset) { + address = (long long)(long)physaddr; + rx_bufpost->metadata_buf_len = + cpu_to_le16(msgbuf->rx_metadata_offset); + rx_bufpost->metadata_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->metadata_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + skb_pull(skb, msgbuf->rx_metadata_offset); + pktlen = skb->len; + physaddr += msgbuf->rx_metadata_offset; + } + rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; + rx_bufpost->msg.request_id = cpu_to_le32(pktid); + + address = (long long)(long)physaddr; + rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen); + rx_bufpost->data_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->data_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + ret_ptr += brcmf_commonring_len_item(commonring); + } + + if (i) + brcmf_commonring_write_complete(commonring); + + return i; +} + + +static void +brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf) +{ + u32 fillbufs; + u32 retcount; + + fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost; + + while (fillbufs) { + retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs); + if (!retcount) + break; + msgbuf->rxbufpost += retcount; + fillbufs -= retcount; + } +} + + +static void +brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt) +{ + msgbuf->rxbufpost -= rxcnt; + if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost - + BRCMF_MSGBUF_RXBUFPOST_THRESHOLD)) + brcmf_msgbuf_rxbuf_data_fill(msgbuf); +} + + +static u32 +brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, + u32 count) +{ + struct brcmf_commonring *commonring; + void *ret_ptr; + struct sk_buff *skb; + u16 alloced; + u32 pktlen; + dma_addr_t physaddr; + struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost; + long long address; + u32 pktid; + u32 i; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, + count, + &alloced); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return 0; + } + + for (i = 0; i < alloced; i++) { + rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr; + memset(rx_bufpost, 0, sizeof(*rx_bufpost)); + + skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); + + if (skb == NULL) { + brcmf_err("Failed to alloc SKB\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + pktlen = skb->len; + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, skb, 0, + &physaddr, &pktid)) { + dev_kfree_skb_any(skb); + brcmf_err("No PKTID available !!\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + if (event_buf) + rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST; + else + rx_bufpost->msg.msgtype = + MSGBUF_TYPE_IOCTLRESP_BUF_POST; + rx_bufpost->msg.request_id = cpu_to_le32(pktid); + + address = (long long)(long)physaddr; + rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen); + rx_bufpost->host_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->host_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + ret_ptr += brcmf_commonring_len_item(commonring); + } + + if (i) + brcmf_commonring_write_complete(commonring); + + brcmf_commonring_unlock(commonring); + + return i; +} + + +static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf) +{ + u32 count; + + count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf; + count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count); + msgbuf->cur_ioctlrespbuf += count; +} + + +static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf) +{ + u32 count; + + count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf; + count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count); + msgbuf->cur_eventbuf += count; +} + + +static void +brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, + u8 ifidx) +{ + struct brcmf_if *ifp; + + ifp = msgbuf->drvr->iflist[ifidx]; + if (!ifp || !ifp->ndev) { + brcmu_pkt_buf_free_skb(skb); + return; + } + brcmf_netif_rx(ifp, skb); +} + + +static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_rx_event *event; + u32 idx; + u16 buflen; + struct sk_buff *skb; + + event = (struct msgbuf_rx_event *)buf; + idx = le32_to_cpu(event->msg.request_id); + buflen = le16_to_cpu(event->event_data_len); + + if (msgbuf->cur_eventbuf) + msgbuf->cur_eventbuf--; + brcmf_msgbuf_rxbuf_event_post(msgbuf); + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, idx); + if (!skb) + return; + + if (msgbuf->rx_dataoffset) + skb_pull(skb, msgbuf->rx_dataoffset); + + skb_trim(skb, buflen); + + brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx); +} + + +static void +brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_rx_complete *rx_complete; + struct sk_buff *skb; + u16 data_offset; + u16 buflen; + u32 idx; + + brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1); + + rx_complete = (struct msgbuf_rx_complete *)buf; + data_offset = le16_to_cpu(rx_complete->data_offset); + buflen = le16_to_cpu(rx_complete->data_len); + idx = le32_to_cpu(rx_complete->msg.request_id); + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, idx); + + if (data_offset) + skb_pull(skb, data_offset); + else if (msgbuf->rx_dataoffset) + skb_pull(skb, msgbuf->rx_dataoffset); + + skb_trim(skb, buflen); + + brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx); +} + + +static void +brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_flowring_create_resp *flowring_create_resp; + u16 status; + u16 flowid; + + flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf; + + flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + status = le16_to_cpu(flowring_create_resp->compl_hdr.status); + + if (status) { + brcmf_err("Flowring creation failed, code %d\n", status); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return; + } + brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid, + status); + + brcmf_flowring_open(msgbuf->flow, flowid); + + brcmf_msgbuf_schedule_txdata(msgbuf, flowid); +} + + +static void +brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_flowring_delete_resp *flowring_delete_resp; + u16 status; + u16 flowid; + + flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf; + + flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + status = le16_to_cpu(flowring_delete_resp->compl_hdr.status); + + if (status) { + brcmf_err("Flowring deletion failed, code %d\n", status); + brcmf_flowring_delete(msgbuf->flow, flowid); + return; + } + brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid, + status); + + brcmf_msgbuf_remove_flowring(msgbuf, flowid); +} + + +static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_common_hdr *msg; + + msg = (struct msgbuf_common_hdr *)buf; + switch (msg->msgtype) { + case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n"); + brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf); + break; + case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n"); + brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf); + break; + case MSGBUF_TYPE_IOCTLPTR_REQ_ACK: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n"); + break; + case MSGBUF_TYPE_IOCTL_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n"); + brcmf_msgbuf_process_ioctl_complete(msgbuf, buf); + break; + case MSGBUF_TYPE_WL_EVENT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n"); + brcmf_msgbuf_process_event(msgbuf, buf); + break; + case MSGBUF_TYPE_TX_STATUS: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n"); + brcmf_msgbuf_process_txstatus(msgbuf, buf); + break; + case MSGBUF_TYPE_RX_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n"); + brcmf_msgbuf_process_rx_complete(msgbuf, buf); + break; + default: + brcmf_err("Unsupported msgtype %d\n", msg->msgtype); + break; + } +} + + +static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf, + struct brcmf_commonring *commonring) +{ + void *buf; + u16 count; + +again: + buf = brcmf_commonring_get_read_ptr(commonring, &count); + if (buf == NULL) + return; + + while (count) { + brcmf_msgbuf_process_msgtype(msgbuf, + buf + msgbuf->rx_dataoffset); + buf += brcmf_commonring_len_item(commonring); + count--; + } + brcmf_commonring_read_complete(commonring); + + if (commonring->r_ptr == 0) + goto again; +} + + +int brcmf_proto_msgbuf_rx_trigger(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + void *buf; + u32 flowid; + + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + + for_each_set_bit(flowid, msgbuf->txstatus_done_map, + msgbuf->nrof_flowrings) { + clear_bit(flowid, msgbuf->txstatus_done_map); + if (brcmf_flowring_qlen(msgbuf->flow, flowid)) + brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + } + + return 0; +} + + +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct msgbuf_tx_flowring_delete_req *delete; + struct brcmf_commonring *commonring; + void *ret_ptr; + u8 ifidx; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("FW unaware, flowring will be removed !!\n"); + brcmf_commonring_unlock(commonring); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return; + } + + delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr; + + ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid); + + delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE; + delete->msg.ifidx = ifidx; + delete->msg.request_id = 0; + + delete->flow_ring_id = cpu_to_le16(flowid + + BRCMF_NROF_H2D_COMMON_MSGRINGS); + delete->reason = 0; + + brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n", + flowid, ifidx); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + if (err) { + brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n"); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + } +} + + +int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) +{ + struct brcmf_bus_msgbuf *if_msgbuf; + struct brcmf_msgbuf *msgbuf; + long long address; + u32 count; + + if_msgbuf = drvr->bus_if->msgbuf; + msgbuf = kzalloc(sizeof(*msgbuf), GFP_ATOMIC); + if (!msgbuf) + goto fail; + + msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow"); + if (msgbuf->txflow_wq == NULL) { + brcmf_err("workqueue creation failed\n"); + goto fail; + } + INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); + count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); + msgbuf->flow_map = kzalloc(count, GFP_ATOMIC); + if (!msgbuf->flow_map) + goto fail; + + msgbuf->txstatus_done_map = kzalloc(count, GFP_ATOMIC); + if (!msgbuf->txstatus_done_map) + goto fail; + + msgbuf->drvr = drvr; + msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + &msgbuf->ioctbuf_handle, + GFP_ATOMIC); + if (!msgbuf->ioctbuf) + goto fail; + address = (long long)(long)msgbuf->ioctbuf_handle; + msgbuf->ioctbuf_phys_hi = address >> 32; + msgbuf->ioctbuf_phys_lo = address & 0xffffffff; + + drvr->proto->hdrpull = brcmf_msgbuf_hdrpull; + drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd; + drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd; + drvr->proto->txdata = brcmf_msgbuf_txdata; + drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode; + drvr->proto->delete_peer = brcmf_msgbuf_delete_peer; + drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer; + drvr->proto->pd = msgbuf; + + init_waitqueue_head(&msgbuf->ioctl_resp_wait); + + msgbuf->commonrings = + (struct brcmf_commonring **)if_msgbuf->commonrings; + msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; + msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; + msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * + sizeof(*msgbuf->flowring_dma_handle), GFP_ATOMIC); + + msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset; + msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost; + + msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST; + msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST; + + msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS, + DMA_TO_DEVICE); + if (!msgbuf->tx_pktids) + goto fail; + msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS, + DMA_FROM_DEVICE); + if (!msgbuf->rx_pktids) + goto fail; + + msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev, + if_msgbuf->nrof_flowrings); + if (!msgbuf->flow) + goto fail; + + + brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n", + msgbuf->max_rxbufpost, msgbuf->max_eventbuf, + msgbuf->max_ioctlrespbuf); + count = 0; + do { + brcmf_msgbuf_rxbuf_data_fill(msgbuf); + if (msgbuf->max_rxbufpost != msgbuf->rxbufpost) + msleep(10); + else + break; + count++; + } while (count < 10); + brcmf_msgbuf_rxbuf_event_post(msgbuf); + brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); + + return 0; + +fail: + if (msgbuf) { + kfree(msgbuf->flow_map); + kfree(msgbuf->txstatus_done_map); + brcmf_msgbuf_release_pktids(msgbuf); + if (msgbuf->ioctbuf) + dma_free_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + msgbuf->ioctbuf, + msgbuf->ioctbuf_handle); + kfree(msgbuf); + } + return -ENOMEM; +} + + +void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) +{ + struct brcmf_msgbuf *msgbuf; + + brcmf_dbg(TRACE, "Enter\n"); + if (drvr->proto->pd) { + msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + kfree(msgbuf->flow_map); + kfree(msgbuf->txstatus_done_map); + if (msgbuf->txflow_wq) + destroy_workqueue(msgbuf->txflow_wq); + + brcmf_flowring_detach(msgbuf->flow); + dma_free_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + msgbuf->ioctbuf, msgbuf->ioctbuf_handle); + brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf); + drvr->proto->pd = NULL; + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h new file mode 100644 index 0000000..f901ae5 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_MSGBUF_H +#define BRCMFMAC_MSGBUF_H + + +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 20 +#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 256 +#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512 + +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE 32 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE 24 +#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE 16 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE 32 +#define BRCMF_H2D_TXFLOWRING_ITEMSIZE 48 + + +int brcmf_proto_msgbuf_rx_trigger(struct device *dev); +int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr); +void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr); +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); + + +#endif /* BRCMFMAC_MSGBUF_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c new file mode 100644 index 0000000..f05f527 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/mmc/card.h> +#include <linux/platform_data/brcmfmac-sdio.h> +#include <linux/mmc/sdio_func.h> + +#include <defs.h> +#include "dhd_dbg.h" +#include "sdio_host.h" + +void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +{ + struct device *dev = sdiodev->dev; + struct device_node *np = dev->of_node; + int irq; + u32 irqf; + u32 val; + + if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) + return; + + sdiodev->pdata = devm_kzalloc(dev, sizeof(*sdiodev->pdata), GFP_KERNEL); + if (!sdiodev->pdata) + return; + + irq = irq_of_parse_and_map(np, 0); + if (irq < 0) { + brcmf_err("interrupt could not be mapped: err=%d\n", irq); + devm_kfree(dev, sdiodev->pdata); + return; + } + irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); + + sdiodev->pdata->oob_irq_supported = true; + sdiodev->pdata->oob_irq_nr = irq; + sdiodev->pdata->oob_irq_flags = irqf; + + if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) + sdiodev->pdata->drive_strength = val; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.h b/drivers/net/wireless/brcm80211/brcmfmac/of.h new file mode 100644 index 0000000..5f7c355 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef CONFIG_OF +void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev); +#else +static void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +{ +} +#endif /* CONFIG_OF */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c new file mode 100644 index 0000000..bc972c0 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -0,0 +1,1846 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/firmware.h> +#include <linux/pci.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/unaligned/access_ok.h> +#include <linux/interrupt.h> +#include <linux/bcma/bcma.h> +#include <linux/sched.h> + +#include <soc.h> +#include <chipcommon.h> +#include <brcmu_utils.h> +#include <brcmu_wifi.h> +#include <brcm_hw_ids.h> + +#include "dhd_dbg.h" +#include "dhd_bus.h" +#include "commonring.h" +#include "msgbuf.h" +#include "pcie.h" +#include "firmware.h" +#include "chip.h" + + +enum brcmf_pcie_state { + BRCMFMAC_PCIE_STATE_DOWN, + BRCMFMAC_PCIE_STATE_UP +}; + + +#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" +#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" +#define BRCMF_PCIE_4354_FW_NAME "brcm/brcmfmac4354-pcie.bin" +#define BRCMF_PCIE_4354_NVRAM_NAME "brcm/brcmfmac4354-pcie.txt" +#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" +#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" +#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" +#define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" + +#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ + +#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024) +#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) + +/* backplane addres space accessed by BAR0 */ +#define BRCMF_PCIE_BAR0_WINDOW 0x80 +#define BRCMF_PCIE_BAR0_REG_SIZE 0x1000 +#define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70 + +#define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000 +#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000 + +#define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40 +#define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C + +#define BRCMF_PCIE_REG_INTSTATUS 0x90 +#define BRCMF_PCIE_REG_INTMASK 0x94 +#define BRCMF_PCIE_REG_SBMBX 0x98 + +#define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 +#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 +#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C +#define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120 +#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124 +#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140 + +#define BRCMF_PCIE_GENREV1 1 +#define BRCMF_PCIE_GENREV2 2 + +#define BRCMF_PCIE2_INTA 0x01 +#define BRCMF_PCIE2_INTB 0x02 + +#define BRCMF_PCIE_INT_0 0x01 +#define BRCMF_PCIE_INT_1 0x02 +#define BRCMF_PCIE_INT_DEF (BRCMF_PCIE_INT_0 | \ + BRCMF_PCIE_INT_1) + +#define BRCMF_PCIE_MB_INT_FN0_0 0x0100 +#define BRCMF_PCIE_MB_INT_FN0_1 0x0200 +#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 +#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 +#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 +#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 +#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 +#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 +#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 +#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 + +#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ + BRCMF_PCIE_MB_INT_D2H0_DB1 | \ + BRCMF_PCIE_MB_INT_D2H1_DB0 | \ + BRCMF_PCIE_MB_INT_D2H1_DB1 | \ + BRCMF_PCIE_MB_INT_D2H2_DB0 | \ + BRCMF_PCIE_MB_INT_D2H2_DB1 | \ + BRCMF_PCIE_MB_INT_D2H3_DB0 | \ + BRCMF_PCIE_MB_INT_D2H3_DB1) + +#define BRCMF_PCIE_MIN_SHARED_VERSION 4 +#define BRCMF_PCIE_MAX_SHARED_VERSION 5 +#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF +#define BRCMF_PCIE_SHARED_TXPUSH_SUPPORT 0x4000 + +#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 +#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 + +#define BRCMF_SHARED_MAX_RXBUFPOST_OFFSET 34 +#define BRCMF_SHARED_RING_BASE_OFFSET 52 +#define BRCMF_SHARED_RX_DATAOFFSET_OFFSET 36 +#define BRCMF_SHARED_CONSOLE_ADDR_OFFSET 20 +#define BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET 40 +#define BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET 44 +#define BRCMF_SHARED_RING_INFO_ADDR_OFFSET 48 +#define BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET 52 +#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56 +#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64 +#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68 + +#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0 +#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1 +#define BRCMF_RING_H2D_RING_MEM_OFFSET 4 +#define BRCMF_RING_H2D_RING_STATE_OFFSET 8 + +#define BRCMF_RING_MEM_BASE_ADDR_OFFSET 8 +#define BRCMF_RING_MAX_ITEM_OFFSET 4 +#define BRCMF_RING_LEN_ITEMS_OFFSET 6 +#define BRCMF_RING_MEM_SZ 16 +#define BRCMF_RING_STATE_SZ 8 + +#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET 4 +#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 +#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 +#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 +#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 +#define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 + +#define BRCMF_DEF_MAX_RXBUFPOST 255 + +#define BRCMF_CONSOLE_BUFADDR_OFFSET 8 +#define BRCMF_CONSOLE_BUFSIZE_OFFSET 12 +#define BRCMF_CONSOLE_WRITEIDX_OFFSET 16 + +#define BRCMF_DMA_D2H_SCRATCH_BUF_LEN 8 +#define BRCMF_DMA_D2H_RINGUPD_BUF_LEN 1024 + +#define BRCMF_D2H_DEV_D3_ACK 0x00000001 +#define BRCMF_D2H_DEV_DS_ENTER_REQ 0x00000002 +#define BRCMF_D2H_DEV_DS_EXIT_NOTE 0x00000004 + +#define BRCMF_H2D_HOST_D3_INFORM 0x00000001 +#define BRCMF_H2D_HOST_DS_ACK 0x00000002 + +#define BRCMF_PCIE_MBDATA_TIMEOUT 2000 + +#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4 +#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C +#define BRCMF_PCIE_CFGREG_MSI_CAP 0x58 +#define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C +#define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60 +#define BRCMF_PCIE_CFGREG_MSI_DATA 0x64 +#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC +#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC +#define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228 +#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248 +#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0 +#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4 +#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 + + +MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); + + +struct brcmf_pcie_console { + u32 base_addr; + u32 buf_addr; + u32 bufsize; + u32 read_idx; + u8 log_str[256]; + u8 log_idx; +}; + +struct brcmf_pcie_shared_info { + u32 tcm_base_address; + u32 flags; + struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; + struct brcmf_pcie_ringbuf *flowrings; + u16 max_rxbufpost; + u32 nrof_flowrings; + u32 rx_dataoffset; + u32 htod_mb_data_addr; + u32 dtoh_mb_data_addr; + u32 ring_info_addr; + struct brcmf_pcie_console console; + void *scratch; + dma_addr_t scratch_dmahandle; + void *ringupd; + dma_addr_t ringupd_dmahandle; +}; + +struct brcmf_pcie_core_info { + u32 base; + u32 wrapbase; +}; + +struct brcmf_pciedev_info { + enum brcmf_pcie_state state; + bool in_irq; + bool irq_requested; + struct pci_dev *pdev; + char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + void __iomem *regs; + void __iomem *tcm; + u32 tcm_size; + u32 ram_base; + u32 ram_size; + struct brcmf_chip *ci; + u32 coreid; + u32 generic_corerev; + struct brcmf_pcie_shared_info shared; + void (*ringbell)(struct brcmf_pciedev_info *devinfo); + wait_queue_head_t mbdata_resp_wait; + bool mbdata_completed; + bool irq_allocated; +}; + +struct brcmf_pcie_ringbuf { + struct brcmf_commonring commonring; + dma_addr_t dma_handle; + u32 w_idx_addr; + u32 r_idx_addr; + struct brcmf_pciedev_info *devinfo; + u8 id; +}; + + +static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = { + BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM, + BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM, + BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM, + BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM, + BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM +}; + +static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { + BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE, + BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE, + BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE, + BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE, + BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE +}; + + +/* dma flushing needs implementation for mips and arm platforms. Should + * be put in util. Note, this is not real flushing. It is virtual non + * cached memory. Only write buffers should have to be drained. Though + * this may be different depending on platform...... + */ +#define brcmf_dma_flush(addr, len) +#define brcmf_dma_invalidate_cache(addr, len) + + +static u32 +brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) +{ + void __iomem *address = devinfo->regs + reg_offset; + + return (ioread32(address)); +} + + +static void +brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset, + u32 value) +{ + void __iomem *address = devinfo->regs + reg_offset; + + iowrite32(value, address); +} + + +static u8 +brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread8(address)); +} + + +static u16 +brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread16(address)); +} + + +static void +brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + iowrite16(value, address); +} + + +static u32 +brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread32(address)); +} + + +static void +brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u32 value) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + iowrite32(value, address); +} + + +static u32 +brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + + return (ioread32(addr)); +} + + +static void +brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u32 value) +{ + void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + + iowrite32(value, addr); +} + + +static void +brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + void *srcaddr, u32 len) +{ + void __iomem *address = devinfo->tcm + mem_offset; + __le32 *src32; + __le16 *src16; + u8 *src8; + + if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { + if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { + src8 = (u8 *)srcaddr; + while (len) { + iowrite8(*src8, address); + address++; + src8++; + len--; + } + } else { + len = len / 2; + src16 = (__le16 *)srcaddr; + while (len) { + iowrite16(le16_to_cpu(*src16), address); + address += 2; + src16++; + len--; + } + } + } else { + len = len / 4; + src32 = (__le32 *)srcaddr; + while (len) { + iowrite32(le32_to_cpu(*src32), address); + address += 4; + src32++; + len--; + } + } +} + + +#define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \ + CHIPCREGOFFS(reg), value) + + +static void +brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) +{ + const struct pci_dev *pdev = devinfo->pdev; + struct brcmf_core *core; + u32 bar0_win; + + core = brcmf_chip_get_core(devinfo->ci, coreid); + if (core) { + bar0_win = core->base; + pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, bar0_win); + if (pci_read_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, + &bar0_win) == 0) { + if (bar0_win != core->base) { + bar0_win = core->base; + pci_write_config_dword(pdev, + BRCMF_PCIE_BAR0_WINDOW, + bar0_win); + } + } + } else { + brcmf_err("Unsupported core selected %x\n", coreid); + } +} + + +static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) +{ + u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD, + BRCMF_PCIE_CFGREG_PM_CSR, + BRCMF_PCIE_CFGREG_MSI_CAP, + BRCMF_PCIE_CFGREG_MSI_ADDR_L, + BRCMF_PCIE_CFGREG_MSI_ADDR_H, + BRCMF_PCIE_CFGREG_MSI_DATA, + BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2, + BRCMF_PCIE_CFGREG_RBAR_CTRL, + BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1, + BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG, + BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG }; + u32 i; + u32 val; + u32 lsc; + + if (!devinfo->ci) + return; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, + BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); + lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val); + + brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); + WRITECC32(devinfo, watchdog, 4); + msleep(100); + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, + BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc); + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, + cfg_offset[i]); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", + cfg_offset[i], val); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, + val); + } +} + + +static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) +{ + u32 config; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) + brcmf_pcie_reset_device(devinfo); + /* BAR1 window may not be sized properly */ + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); + config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config); + + device_wakeup_enable(&devinfo->pdev->dev); +} + + +static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) +{ + brcmf_chip_enter_download(devinfo->ci); + + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { + brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, + 5); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, + 0); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, + 7); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, + 0); + } + return 0; +} + + +static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, + u32 resetintr) +{ + struct brcmf_core *core; + + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_INTERNAL_MEM); + brcmf_chip_resetcore(core, 0, 0, 0); + } + + return !brcmf_chip_exit_download(devinfo->ci, resetintr); +} + + +static void +brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 cur_htod_mb_data; + u32 i; + + shared = &devinfo->shared; + addr = shared->htod_mb_data_addr; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + + if (cur_htod_mb_data != 0) + brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", + cur_htod_mb_data); + + i = 0; + while (cur_htod_mb_data != 0) { + msleep(10); + i++; + if (i > 100) + break; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + } + + brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); +} + + +static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 dtoh_mb_data; + + shared = &devinfo->shared; + addr = shared->dtoh_mb_data_addr; + dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + + if (!dtoh_mb_data) + return; + + brcmf_pcie_write_tcm32(devinfo, addr, 0); + + brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data); + if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { + brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n"); + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK); + brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); + } + if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) + brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); + if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) + brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); + if (waitqueue_active(&devinfo->mbdata_resp_wait)) { + devinfo->mbdata_completed = true; + wake_up(&devinfo->mbdata_resp_wait); + } +} + + +static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_shared_info *shared; + struct brcmf_pcie_console *console; + u32 addr; + + shared = &devinfo->shared; + console = &shared->console; + addr = shared->tcm_base_address + BRCMF_SHARED_CONSOLE_ADDR_OFFSET; + console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET; + console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; + console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); + + brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n", + console->base_addr, console->buf_addr, console->bufsize); +} + + +static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_console *console; + u32 addr; + u8 ch; + u32 newidx; + + console = &devinfo->shared.console; + addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; + newidx = brcmf_pcie_read_tcm32(devinfo, addr); + while (newidx != console->read_idx) { + addr = console->buf_addr + console->read_idx; + ch = brcmf_pcie_read_tcm8(devinfo, addr); + console->read_idx++; + if (console->read_idx == console->bufsize) + console->read_idx = 0; + if (ch == '\r') + continue; + console->log_str[console->log_idx] = ch; + console->log_idx++; + if ((ch != '\n') && + (console->log_idx == (sizeof(console->log_str) - 2))) { + ch = '\n'; + console->log_str[console->log_idx] = ch; + console->log_idx++; + } + + if (ch == '\n') { + console->log_str[console->log_idx] = 0; + brcmf_dbg(PCIE, "CONSOLE: %s\n", console->log_str); + console->log_idx = 0; + } + } +} + + +static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo) +{ + u32 reg_value; + + brcmf_dbg(PCIE, "RING !\n"); + reg_value = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT); + reg_value |= BRCMF_PCIE2_INTB; + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + reg_value); +} + + +static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo) +{ + brcmf_dbg(PCIE, "RING !\n"); + /* Any arbitrary value will do, lets use 1 */ + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1); +} + + +static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, + 0); + else + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, + 0); +} + + +static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, + BRCMF_PCIE_INT_DEF); + else + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, + BRCMF_PCIE_MB_INT_D2H_DB | + BRCMF_PCIE_MB_INT_FN0_0 | + BRCMF_PCIE_MB_INT_FN0_1); +} + + +static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + u32 status; + + status = 0; + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + if (status) { + brcmf_pcie_intr_disable(devinfo); + brcmf_dbg(PCIE, "Enter\n"); + return IRQ_WAKE_THREAD; + } + return IRQ_NONE; +} + + +static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + + if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) { + brcmf_pcie_intr_disable(devinfo); + brcmf_dbg(PCIE, "Enter\n"); + return IRQ_WAKE_THREAD; + } + return IRQ_NONE; +} + + +static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + const struct pci_dev *pdev = devinfo->pdev; + u32 status; + + devinfo->in_irq = true; + status = 0; + pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + brcmf_dbg(PCIE, "Enter %x\n", status); + if (status) { + pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev); + } + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_pcie_intr_enable(devinfo); + devinfo->in_irq = false; + return IRQ_HANDLED; +} + + +static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + u32 status; + + devinfo->in_irq = true; + status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + brcmf_dbg(PCIE, "Enter %x\n", status); + if (status) { + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + status); + if (status & (BRCMF_PCIE_MB_INT_FN0_0 | + BRCMF_PCIE_MB_INT_FN0_1)) + brcmf_pcie_handle_mb_data(devinfo); + if (status & BRCMF_PCIE_MB_INT_D2H_DB) { + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_proto_msgbuf_rx_trigger( + &devinfo->pdev->dev); + } + } + brcmf_pcie_bus_console_read(devinfo); + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_pcie_intr_enable(devinfo); + devinfo->in_irq = false; + return IRQ_HANDLED; +} + + +static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + + pdev = devinfo->pdev; + + brcmf_pcie_intr_disable(devinfo); + + brcmf_dbg(PCIE, "Enter\n"); + /* is it a v1 or v2 implementation */ + devinfo->irq_requested = false; + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { + if (request_threaded_irq(pdev->irq, + brcmf_pcie_quick_check_isr_v1, + brcmf_pcie_isr_thread_v1, + IRQF_SHARED, "brcmf_pcie_intr", + devinfo)) { + brcmf_err("Failed to request IRQ %d\n", pdev->irq); + return -EIO; + } + } else { + if (request_threaded_irq(pdev->irq, + brcmf_pcie_quick_check_isr_v2, + brcmf_pcie_isr_thread_v2, + IRQF_SHARED, "brcmf_pcie_intr", + devinfo)) { + brcmf_err("Failed to request IRQ %d\n", pdev->irq); + return -EIO; + } + } + devinfo->irq_requested = true; + devinfo->irq_allocated = true; + return 0; +} + + +static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + u32 status; + u32 count; + + if (!devinfo->irq_allocated) + return; + + pdev = devinfo->pdev; + + brcmf_pcie_intr_disable(devinfo); + if (!devinfo->irq_requested) + return; + devinfo->irq_requested = false; + free_irq(pdev->irq, devinfo); + + msleep(50); + count = 0; + while ((devinfo->in_irq) && (count < 20)) { + msleep(50); + count++; + } + if (devinfo->in_irq) + brcmf_err("Still in IRQ (processing) !!!\n"); + + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { + status = 0; + pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); + } else { + status = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + status); + } + devinfo->irq_allocated = false; +} + + +static int brcmf_pcie_ring_mb_write_rptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, + commonring->w_ptr, ring->id); + + brcmf_pcie_write_tcm16(devinfo, ring->r_idx_addr, commonring->r_ptr); + + return 0; +} + + +static int brcmf_pcie_ring_mb_write_wptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, + commonring->r_ptr, ring->id); + + brcmf_pcie_write_tcm16(devinfo, ring->w_idx_addr, commonring->w_ptr); + + return 0; +} + + +static int brcmf_pcie_ring_mb_ring_bell(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + devinfo->ringbell(devinfo); + + return 0; +} + + +static int brcmf_pcie_ring_mb_update_rptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + commonring->r_ptr = brcmf_pcie_read_tcm16(devinfo, ring->r_idx_addr); + + brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, + commonring->w_ptr, ring->id); + + return 0; +} + + +static int brcmf_pcie_ring_mb_update_wptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + commonring->w_ptr = brcmf_pcie_read_tcm16(devinfo, ring->w_idx_addr); + + brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, + commonring->r_ptr, ring->id); + + return 0; +} + + +static void * +brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo, + u32 size, u32 tcm_dma_phys_addr, + dma_addr_t *dma_handle) +{ + void *ring; + long long address; + + ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle, + GFP_KERNEL); + if (!ring) + return NULL; + + address = (long long)(long)*dma_handle; + brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr, + address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32); + + memset(ring, 0, size); + + return (ring); +} + + +static struct brcmf_pcie_ringbuf * +brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id, + u32 tcm_ring_phys_addr) +{ + void *dma_buf; + dma_addr_t dma_handle; + struct brcmf_pcie_ringbuf *ring; + u32 size; + u32 addr; + + size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id]; + dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size, + tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET, + &dma_handle); + if (!dma_buf) + return NULL; + + addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET; + brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]); + addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET; + brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]); + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) { + dma_free_coherent(&devinfo->pdev->dev, size, dma_buf, + dma_handle); + return NULL; + } + brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id], + brcmf_ring_itemsize[ring_id], dma_buf); + ring->dma_handle = dma_handle; + ring->devinfo = devinfo; + brcmf_commonring_register_cb(&ring->commonring, + brcmf_pcie_ring_mb_ring_bell, + brcmf_pcie_ring_mb_update_rptr, + brcmf_pcie_ring_mb_update_wptr, + brcmf_pcie_ring_mb_write_rptr, + brcmf_pcie_ring_mb_write_wptr, ring); + + return (ring); +} + + +static void brcmf_pcie_release_ringbuffer(struct device *dev, + struct brcmf_pcie_ringbuf *ring) +{ + void *dma_buf; + u32 size; + + if (!ring) + return; + + dma_buf = ring->commonring.buf_addr; + if (dma_buf) { + size = ring->commonring.depth * ring->commonring.item_len; + dma_free_coherent(dev, size, dma_buf, ring->dma_handle); + } + kfree(ring); +} + + +static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo) +{ + u32 i; + + for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) { + brcmf_pcie_release_ringbuffer(&devinfo->pdev->dev, + devinfo->shared.commonrings[i]); + devinfo->shared.commonrings[i] = NULL; + } + kfree(devinfo->shared.flowrings); + devinfo->shared.flowrings = NULL; +} + + +static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_ringbuf *ring; + struct brcmf_pcie_ringbuf *rings; + u32 ring_addr; + u32 d2h_w_idx_ptr; + u32 d2h_r_idx_ptr; + u32 h2d_w_idx_ptr; + u32 h2d_r_idx_ptr; + u32 addr; + u32 ring_mem_ptr; + u32 i; + u16 max_sub_queues; + + ring_addr = devinfo->shared.ring_info_addr; + brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); + + addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; + d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; + d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; + h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; + h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; + ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + + for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) { + ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); + if (!ring) + goto fail; + ring->w_idx_addr = h2d_w_idx_ptr; + ring->r_idx_addr = h2d_r_idx_ptr; + ring->id = i; + devinfo->shared.commonrings[i] = ring; + + h2d_w_idx_ptr += sizeof(u32); + h2d_r_idx_ptr += sizeof(u32); + ring_mem_ptr += BRCMF_RING_MEM_SZ; + } + + for (i = BRCMF_NROF_H2D_COMMON_MSGRINGS; + i < BRCMF_NROF_COMMON_MSGRINGS; i++) { + ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); + if (!ring) + goto fail; + ring->w_idx_addr = d2h_w_idx_ptr; + ring->r_idx_addr = d2h_r_idx_ptr; + ring->id = i; + devinfo->shared.commonrings[i] = ring; + + d2h_w_idx_ptr += sizeof(u32); + d2h_r_idx_ptr += sizeof(u32); + ring_mem_ptr += BRCMF_RING_MEM_SZ; + } + + addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; + max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); + devinfo->shared.nrof_flowrings = + max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; + rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), + GFP_KERNEL); + if (!rings) + goto fail; + + brcmf_dbg(PCIE, "Nr of flowrings is %d\n", + devinfo->shared.nrof_flowrings); + + for (i = 0; i < devinfo->shared.nrof_flowrings; i++) { + ring = &rings[i]; + ring->devinfo = devinfo; + ring->id = i + BRCMF_NROF_COMMON_MSGRINGS; + brcmf_commonring_register_cb(&ring->commonring, + brcmf_pcie_ring_mb_ring_bell, + brcmf_pcie_ring_mb_update_rptr, + brcmf_pcie_ring_mb_update_wptr, + brcmf_pcie_ring_mb_write_rptr, + brcmf_pcie_ring_mb_write_wptr, + ring); + ring->w_idx_addr = h2d_w_idx_ptr; + ring->r_idx_addr = h2d_r_idx_ptr; + h2d_w_idx_ptr += sizeof(u32); + h2d_r_idx_ptr += sizeof(u32); + } + devinfo->shared.flowrings = rings; + + return 0; + +fail: + brcmf_err("Allocating commonring buffers failed\n"); + brcmf_pcie_release_ringbuffers(devinfo); + return -ENOMEM; +} + + +static void +brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->shared.scratch) + dma_free_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_SCRATCH_BUF_LEN, + devinfo->shared.scratch, + devinfo->shared.scratch_dmahandle); + if (devinfo->shared.ringupd) + dma_free_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_RINGUPD_BUF_LEN, + devinfo->shared.ringupd, + devinfo->shared.ringupd_dmahandle); +} + +static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) +{ + long long address; + u32 addr; + + devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_SCRATCH_BUF_LEN, + &devinfo->shared.scratch_dmahandle, GFP_KERNEL); + if (!devinfo->shared.scratch) + goto fail; + + memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + brcmf_dma_flush(devinfo->shared.scratch, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; + address = (long long)(long)devinfo->shared.scratch_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET; + brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + + devinfo->shared.ringupd = dma_alloc_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_RINGUPD_BUF_LEN, + &devinfo->shared.ringupd_dmahandle, GFP_KERNEL); + if (!devinfo->shared.ringupd) + goto fail; + + memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + brcmf_dma_flush(devinfo->shared.ringupd, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; + address = (long long)(long)devinfo->shared.ringupd_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET; + brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + return 0; + +fail: + brcmf_err("Allocating scratch buffers failed\n"); + brcmf_pcie_release_scratchbuffers(devinfo); + return -ENOMEM; +} + + +static void brcmf_pcie_down(struct device *dev) +{ +} + + +static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb) +{ + return 0; +} + + +static int brcmf_pcie_tx_ctlpkt(struct device *dev, unsigned char *msg, + uint len) +{ + return 0; +} + + +static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg, + uint len) +{ + return 0; +} + + +static struct brcmf_bus_ops brcmf_pcie_bus_ops = { + .txdata = brcmf_pcie_tx, + .stop = brcmf_pcie_down, + .txctl = brcmf_pcie_tx_ctlpkt, + .rxctl = brcmf_pcie_rx_ctlpkt, +}; + + +static int +brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, + u32 sharedram_addr) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 version; + + shared = &devinfo->shared; + shared->tcm_base_address = sharedram_addr; + + shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr); + version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK; + brcmf_dbg(PCIE, "PCIe protocol version %d\n", version); + if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) || + (version < BRCMF_PCIE_MIN_SHARED_VERSION)) { + brcmf_err("Unsupported PCIE version %d\n", version); + return -EINVAL; + } + if (shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT) { + brcmf_err("Unsupported legacy TX mode 0x%x\n", + shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT); + return -EINVAL; + } + + addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; + shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); + if (shared->max_rxbufpost == 0) + shared->max_rxbufpost = BRCMF_DEF_MAX_RXBUFPOST; + + addr = sharedram_addr + BRCMF_SHARED_RX_DATAOFFSET_OFFSET; + shared->rx_dataoffset = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET; + shared->htod_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET; + shared->dtoh_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET; + shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n", + shared->max_rxbufpost, shared->rx_dataoffset); + + brcmf_pcie_bus_console_init(devinfo); + + return 0; +} + + +static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) +{ + char *fw_name; + char *nvram_name; + uint fw_len, nv_len; + char end; + + brcmf_dbg(PCIE, "Enter, chip 0x%04x chiprev %d\n", devinfo->ci->chip, + devinfo->ci->chiprev); + + switch (devinfo->ci->chip) { + case BRCM_CC_43602_CHIP_ID: + fw_name = BRCMF_PCIE_43602_FW_NAME; + nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; + break; + case BRCM_CC_4354_CHIP_ID: + fw_name = BRCMF_PCIE_4354_FW_NAME; + nvram_name = BRCMF_PCIE_4354_NVRAM_NAME; + break; + case BRCM_CC_4356_CHIP_ID: + fw_name = BRCMF_PCIE_4356_FW_NAME; + nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; + break; + case BRCM_CC_43567_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: + fw_name = BRCMF_PCIE_43570_FW_NAME; + nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; + break; + default: + brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); + return -ENODEV; + } + + fw_len = sizeof(devinfo->fw_name) - 1; + nv_len = sizeof(devinfo->nvram_name) - 1; + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + strncpy(devinfo->fw_name, brcmf_firmware_path, fw_len); + strncpy(devinfo->nvram_name, brcmf_firmware_path, nv_len); + fw_len -= strlen(devinfo->fw_name); + nv_len -= strlen(devinfo->nvram_name); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strncat(devinfo->fw_name, "/", fw_len); + strncat(devinfo->nvram_name, "/", nv_len); + fw_len--; + nv_len--; + } + } + strncat(devinfo->fw_name, fw_name, fw_len); + strncat(devinfo->nvram_name, nvram_name, nv_len); + + return 0; +} + + +static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, + const struct firmware *fw, void *nvram, + u32 nvram_len) +{ + u32 sharedram_addr; + u32 sharedram_addr_written; + u32 loop_counter; + int err; + u32 address; + u32 resetintr; + + devinfo->ringbell = brcmf_pcie_ringbell_v2; + devinfo->generic_corerev = BRCMF_PCIE_GENREV2; + + brcmf_dbg(PCIE, "Halt ARM.\n"); + err = brcmf_pcie_enter_download_state(devinfo); + if (err) + return err; + + brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); + brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase, + (void *)fw->data, fw->size); + + resetintr = get_unaligned_le32(fw->data); + release_firmware(fw); + + /* reset last 4 bytes of RAM address. to be used for shared + * area. This identifies when FW is running + */ + brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); + + if (nvram) { + brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); + address = devinfo->ci->rambase + devinfo->ci->ramsize - + nvram_len; + brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); + brcmf_fw_nvram_free(nvram); + } else { + brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", + devinfo->nvram_name); + } + + sharedram_addr_written = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - + 4); + brcmf_dbg(PCIE, "Bring ARM in running state\n"); + err = brcmf_pcie_exit_download_state(devinfo, resetintr); + if (err) + return err; + + brcmf_dbg(PCIE, "Wait for FW init\n"); + sharedram_addr = sharedram_addr_written; + loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50; + while ((sharedram_addr == sharedram_addr_written) && (loop_counter)) { + msleep(50); + sharedram_addr = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - + 4); + loop_counter--; + } + if (sharedram_addr == sharedram_addr_written) { + brcmf_err("FW failed to initialize\n"); + return -ENODEV; + } + brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr); + + return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr)); +} + + +static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + int err; + phys_addr_t bar0_addr, bar1_addr; + ulong bar1_size; + + pdev = devinfo->pdev; + + err = pci_enable_device(pdev); + if (err) { + brcmf_err("pci_enable_device failed err=%d\n", err); + return err; + } + + pci_set_master(pdev); + + /* Bar-0 mapped address */ + bar0_addr = pci_resource_start(pdev, 0); + /* Bar-1 mapped address */ + bar1_addr = pci_resource_start(pdev, 2); + /* read Bar-1 mapped memory range */ + bar1_size = pci_resource_len(pdev, 2); + if ((bar1_size == 0) || (bar1_addr == 0)) { + brcmf_err("BAR1 Not enabled, device size=%ld, addr=%#016llx\n", + bar1_size, (unsigned long long)bar1_addr); + return -EINVAL; + } + + devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); + devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE); + devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE; + + if (!devinfo->regs || !devinfo->tcm) { + brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs, + devinfo->tcm); + return -EINVAL; + } + brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n", + devinfo->regs, (unsigned long long)bar0_addr); + brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n", + devinfo->tcm, (unsigned long long)bar1_addr); + + return 0; +} + + +static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->tcm) + iounmap(devinfo->tcm); + if (devinfo->regs) + iounmap(devinfo->regs); + + pci_disable_device(devinfo->pdev); +} + + +static int brcmf_pcie_attach_bus(struct device *dev) +{ + int ret; + + /* Attach to the common driver interface */ + ret = brcmf_attach(dev); + if (ret) { + brcmf_err("brcmf_attach failed\n"); + } else { + ret = brcmf_bus_start(dev); + if (ret) + brcmf_err("dongle is not responding\n"); + } + + return ret; +} + + +static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr) +{ + u32 ret_addr; + + ret_addr = addr & (BRCMF_PCIE_BAR0_REG_SIZE - 1); + addr &= ~(BRCMF_PCIE_BAR0_REG_SIZE - 1); + pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, addr); + + return ret_addr; +} + + +static u32 brcmf_pcie_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); + return brcmf_pcie_read_reg32(devinfo, addr); +} + + +static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); + brcmf_pcie_write_reg32(devinfo, addr, value); +} + + +static int brcmf_pcie_buscoreprep(void *ctx) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + int err; + + err = brcmf_pcie_get_resource(devinfo); + if (err == 0) { + /* Set CC watchdog to reset all the cores on the chip to bring + * back dongle to a sane state. + */ + brcmf_pcie_buscore_write32(ctx, CORE_CC_REG(SI_ENUM_BASE, + watchdog), 4); + msleep(100); + } + + return err; +} + + +static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + brcmf_pcie_write_tcm32(devinfo, 0, rstvec); +} + + +static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { + .prepare = brcmf_pcie_buscoreprep, + .exit_dl = brcmf_pcie_buscore_exitdl, + .read32 = brcmf_pcie_buscore_read32, + .write32 = brcmf_pcie_buscore_write32, +}; + +static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, + void *nvram, u32 nvram_len) +{ + struct brcmf_bus *bus = dev_get_drvdata(dev); + struct brcmf_pciedev *pcie_bus_dev = bus->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + struct brcmf_commonring **flowrings; + int ret; + u32 i; + + brcmf_pcie_attach(devinfo); + + ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len); + if (ret) + goto fail; + + devinfo->state = BRCMFMAC_PCIE_STATE_UP; + + ret = brcmf_pcie_init_ringbuffers(devinfo); + if (ret) + goto fail; + + ret = brcmf_pcie_init_scratchbuffers(devinfo); + if (ret) + goto fail; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + ret = brcmf_pcie_request_irq(devinfo); + if (ret) + goto fail; + + /* hook the commonrings in the bus structure. */ + for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) + bus->msgbuf->commonrings[i] = + &devinfo->shared.commonrings[i]->commonring; + + flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(flowrings), + GFP_KERNEL); + if (!flowrings) + goto fail; + + for (i = 0; i < devinfo->shared.nrof_flowrings; i++) + flowrings[i] = &devinfo->shared.flowrings[i].commonring; + bus->msgbuf->flowrings = flowrings; + + bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset; + bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost; + bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings; + + init_waitqueue_head(&devinfo->mbdata_resp_wait); + + brcmf_pcie_intr_enable(devinfo); + if (brcmf_pcie_attach_bus(bus->dev) == 0) + return; + + brcmf_pcie_bus_console_read(devinfo); + +fail: + device_release_driver(dev); +} + +static int +brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int ret; + struct brcmf_pciedev_info *devinfo; + struct brcmf_pciedev *pcie_bus_dev; + struct brcmf_bus *bus; + + brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device); + + ret = -ENOMEM; + devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); + if (devinfo == NULL) + return ret; + + devinfo->pdev = pdev; + pcie_bus_dev = NULL; + devinfo->ci = brcmf_chip_attach(devinfo, &brcmf_pcie_buscore_ops); + if (IS_ERR(devinfo->ci)) { + ret = PTR_ERR(devinfo->ci); + devinfo->ci = NULL; + goto fail; + } + + pcie_bus_dev = kzalloc(sizeof(*pcie_bus_dev), GFP_KERNEL); + if (pcie_bus_dev == NULL) { + ret = -ENOMEM; + goto fail; + } + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) { + ret = -ENOMEM; + goto fail; + } + bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL); + if (!bus->msgbuf) { + ret = -ENOMEM; + kfree(bus); + goto fail; + } + + /* hook it all together. */ + pcie_bus_dev->devinfo = devinfo; + pcie_bus_dev->bus = bus; + bus->dev = &pdev->dev; + bus->bus_priv.pcie = pcie_bus_dev; + bus->ops = &brcmf_pcie_bus_ops; + bus->proto_type = BRCMF_PROTO_MSGBUF; + bus->chip = devinfo->coreid; + dev_set_drvdata(&pdev->dev, bus); + + ret = brcmf_pcie_get_fwnames(devinfo); + if (ret) + goto fail_bus; + + ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM | + BRCMF_FW_REQ_NV_OPTIONAL, + devinfo->fw_name, devinfo->nvram_name, + brcmf_pcie_setup); + if (ret == 0) + return 0; +fail_bus: + kfree(bus->msgbuf); + kfree(bus); +fail: + brcmf_err("failed %x:%x\n", pdev->vendor, pdev->device); + brcmf_pcie_release_resource(devinfo); + if (devinfo->ci) + brcmf_chip_detach(devinfo->ci); + kfree(pcie_bus_dev); + kfree(devinfo); + return ret; +} + + +static void +brcmf_pcie_remove(struct pci_dev *pdev) +{ + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; + + brcmf_dbg(PCIE, "Enter\n"); + + bus = dev_get_drvdata(&pdev->dev); + if (bus == NULL) + return; + + devinfo = bus->bus_priv.pcie->devinfo; + + devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; + if (devinfo->ci) + brcmf_pcie_intr_disable(devinfo); + + brcmf_detach(&pdev->dev); + + kfree(bus->bus_priv.pcie); + kfree(bus->msgbuf->flowrings); + kfree(bus->msgbuf); + kfree(bus); + + brcmf_pcie_release_irq(devinfo); + brcmf_pcie_release_scratchbuffers(devinfo); + brcmf_pcie_release_ringbuffers(devinfo); + brcmf_pcie_reset_device(devinfo); + brcmf_pcie_release_resource(devinfo); + + if (devinfo->ci) + brcmf_chip_detach(devinfo->ci); + + kfree(devinfo); + dev_set_drvdata(&pdev->dev, NULL); +} + + +#ifdef CONFIG_PM + + +static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; + int err; + + brcmf_dbg(PCIE, "Enter, state=%d, pdev=%p\n", state.event, pdev); + + bus = dev_get_drvdata(&pdev->dev); + devinfo = bus->bus_priv.pcie->devinfo; + + brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); + + devinfo->mbdata_completed = false; + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM); + + wait_event_timeout(devinfo->mbdata_resp_wait, + devinfo->mbdata_completed, + msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT)); + if (!devinfo->mbdata_completed) { + brcmf_err("Timeout on response for entering D3 substate\n"); + return -EIO; + } + brcmf_pcie_release_irq(devinfo); + + err = pci_save_state(pdev); + if (err) { + brcmf_err("pci_save_state failed, err=%d\n", err); + return err; + } + + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + + brcmf_pcie_remove(pdev); + + return pci_prepare_to_sleep(pdev); +} + + +static int brcmf_pcie_resume(struct pci_dev *pdev) +{ + int err; + + brcmf_dbg(PCIE, "Enter, pdev=%p\n", pdev); + + err = pci_set_power_state(pdev, PCI_D0); + if (err) { + brcmf_err("pci_set_power_state failed, err=%d\n", err); + return err; + } + pci_restore_state(pdev); + + err = brcmf_pcie_probe(pdev, NULL); + if (err) + brcmf_err("probe after resume failed, err=%d\n", err); + + return err; +} + + +#endif /* CONFIG_PM */ + + +#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } + +static struct pci_device_id brcmf_pcie_devid_table[] = { + BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), + { /* end: all zeroes */ } +}; + + +MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); + + +static struct pci_driver brcmf_pciedrvr = { + .node = {}, + .name = KBUILD_MODNAME, + .id_table = brcmf_pcie_devid_table, + .probe = brcmf_pcie_probe, + .remove = brcmf_pcie_remove, +#ifdef CONFIG_PM + .suspend = brcmf_pcie_suspend, + .resume = brcmf_pcie_resume +#endif /* CONFIG_PM */ +}; + + +void brcmf_pcie_register(void) +{ + int err; + + brcmf_dbg(PCIE, "Enter\n"); + err = pci_register_driver(&brcmf_pciedrvr); + if (err) + brcmf_err("PCIE driver registration failed, err=%d\n", err); +} + + +void brcmf_pcie_exit(void) +{ + brcmf_dbg(PCIE, "Enter\n"); + pci_unregister_driver(&brcmf_pciedrvr); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/brcm80211/brcmfmac/pcie.h new file mode 100644 index 0000000..6edaaf8 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_PCIE_H +#define BRCMFMAC_PCIE_H + + +struct brcmf_pciedev { + struct brcmf_bus *bus; + struct brcmf_pciedev_info *devinfo; +}; + + +void brcmf_pcie_exit(void); +void brcmf_pcie_register(void); + + +#endif /* BRCMFMAC_PCIE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index b6b4641..62b9407 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -21,26 +21,40 @@ #include <brcmu_wifi.h> #include "dhd.h" +#include "dhd_bus.h" #include "dhd_dbg.h" #include "proto.h" #include "bcdc.h" +#include "msgbuf.h" int brcmf_proto_attach(struct brcmf_pub *drvr) { struct brcmf_proto *proto; + brcmf_dbg(TRACE, "Enter\n"); + proto = kzalloc(sizeof(*proto), GFP_ATOMIC); if (!proto) goto fail; drvr->proto = proto; - /* BCDC protocol is only protocol supported for the moment */ - if (brcmf_proto_bcdc_attach(drvr)) - goto fail; + if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) { + if (brcmf_proto_bcdc_attach(drvr)) + goto fail; + } else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) { + if (brcmf_proto_msgbuf_attach(drvr)) + goto fail; + } else { + brcmf_err("Unsupported proto type %d\n", + drvr->bus_if->proto_type); + goto fail; + } if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || - (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL)) { + (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || + (proto->configure_addr_mode == NULL) || + (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) { brcmf_err("Not all proto handlers have been installed\n"); goto fail; } @@ -54,8 +68,13 @@ fail: void brcmf_proto_detach(struct brcmf_pub *drvr) { + brcmf_dbg(TRACE, "Enter\n"); + if (drvr->proto) { - brcmf_proto_bcdc_detach(drvr); + if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) + brcmf_proto_bcdc_detach(drvr); + else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) + brcmf_proto_msgbuf_detach(drvr); kfree(drvr->proto); drvr->proto = NULL; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h index 482fb0b..971172f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -16,6 +16,13 @@ #ifndef BRCMFMAC_PROTO_H #define BRCMFMAC_PROTO_H + +enum proto_addr_mode { + ADDR_INDIRECT = 0, + ADDR_DIRECT +}; + + struct brcmf_proto { int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, struct sk_buff *skb); @@ -25,6 +32,12 @@ struct brcmf_proto { uint len); int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, struct sk_buff *skb); + void (*configure_addr_mode)(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode); + void (*delete_peer)(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]); + void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]); void *pd; }; @@ -48,10 +61,26 @@ static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx, return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); } static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, - u8 offset, struct sk_buff *skb) + u8 offset, struct sk_buff *skb) { return drvr->proto->txdata(drvr, ifidx, offset, skb); } +static inline void +brcmf_proto_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ + drvr->proto->configure_addr_mode(drvr, ifidx, addr_mode); +} +static inline void +brcmf_proto_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + drvr->proto->delete_peer(drvr, ifidx, peer); +} +static inline void +brcmf_proto_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + drvr->proto->add_tdls_peer(drvr, ifidx, peer); +} #endif /* BRCMFMAC_PROTO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 6c5e585..f2d06ca 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -74,12 +74,12 @@ #define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access addr byte 0 */ #define SBSDIO_SPROM_ADDR_LOW 0x10004 -/* sprom indirect access addr byte 0 */ -#define SBSDIO_SPROM_ADDR_HIGH 0x10005 -/* xtal_pu (gpio) output */ -#define SBSDIO_CHIP_CTRL_DATA 0x10006 -/* xtal_pu (gpio) enable */ -#define SBSDIO_CHIP_CTRL_EN 0x10007 +/* gpio select */ +#define SBSDIO_GPIO_SELECT 0x10005 +/* gpio output */ +#define SBSDIO_GPIO_OUT 0x10006 +/* gpio enable */ +#define SBSDIO_GPIO_EN 0x10007 /* rev < 7, watermark for sdio device */ #define SBSDIO_WATERMARK 0x10008 /* control busy signal generation */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 48078a3..02fe706 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -35,6 +35,7 @@ #include "wl_cfg80211.h" #include "feature.h" #include "fwil.h" +#include "proto.h" #include "vendor.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 @@ -493,6 +494,22 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) return err; } +static void +brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) +{ + struct net_device *ndev = wdev->netdev; + struct brcmf_if *ifp = netdev_priv(ndev); + + if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || + (wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_DIRECT); + else + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_INDIRECT); +} + static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) { enum nl80211_iftype iftype; @@ -512,6 +529,8 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, u32 *flags, struct vif_params *params) { + struct wireless_dev *wdev; + brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); switch (type) { case NL80211_IFTYPE_ADHOC: @@ -525,7 +544,10 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: - return brcmf_p2p_add_vif(wiphy, name, type, flags, params); + wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params); + if (!IS_ERR(wdev)) + brcmf_cfg80211_update_proto_addr_mode(wdev); + return wdev; case NL80211_IFTYPE_UNSPECIFIED: default: return ERR_PTR(-EINVAL); @@ -720,6 +742,8 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, } ndev->ieee80211_ptr->iftype = type; + brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + done: brcmf_dbg(TRACE, "Exit\n"); @@ -4131,6 +4155,27 @@ static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); } +static s32 +brcmf_notify_tdls_peer_event(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + switch (e->reason) { + case BRCMF_E_REASON_TDLS_PEER_DISCOVERED: + brcmf_dbg(TRACE, "TDLS Peer Discovered\n"); + break; + case BRCMF_E_REASON_TDLS_PEER_CONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Connected\n"); + brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Disconnected\n"); + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + } + + return 0; +} + static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) { int ret; @@ -4525,6 +4570,13 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct ieee80211_channel *chan; s32 err = 0; + if ((e->event_code == BRCMF_E_DEAUTH) || + (e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DISASSOC_IND) || + ((e->event_code == BRCMF_E_LINK) && (!e->flags))) { + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + } + if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(e)) { @@ -5660,6 +5712,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, if (err) { brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } else { + brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, + brcmf_notify_tdls_peer_event); } return cfg; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 64d1a7b..af26e0d 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -38,8 +38,12 @@ #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 #define BRCM_CC_4354_CHIP_ID 0x4354 +#define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 +#define BRCM_CC_43567_CHIP_ID 43567 #define BRCM_CC_43569_CHIP_ID 43569 +#define BRCM_CC_43570_CHIP_ID 43570 +#define BRCM_CC_43602_CHIP_ID 43602 /* SDIO Device IDs */ #define BRCM_SDIO_43143_DEVICE_ID BRCM_CC_43143_CHIP_ID @@ -58,6 +62,13 @@ #define BRCM_USB_43569_DEVICE_ID 0xbd27 #define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc +/* PCIE Device IDs */ +#define BRCM_PCIE_4354_DEVICE_ID 0x43df +#define BRCM_PCIE_4356_DEVICE_ID 0x43ec +#define BRCM_PCIE_43567_DEVICE_ID 0x43d3 +#define BRCM_PCIE_43570_DEVICE_ID 0x43d9 +#define BRCM_PCIE_43602_DEVICE_ID 0x43ba + /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ #define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */ diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index 03de746..2c4fa49 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -2980,7 +2980,8 @@ il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id) /* Driver ilate data, only for Tx (not command) queues, * not shared with device. */ if (id != il->cmd_queue) { - txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, sizeof(struct skb *), + txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, + sizeof(struct sk_buff *), GFP_KERNEL); if (!txq->skbs) { IL_ERR("Fail to alloc skbs\n"); |