Commit 81c1f74d authored by Kalle Valo's avatar Kalle Valo

Merge ath-next from ath.git

Major changes:

ath10k

* add board 2 API support for automatically choosing correct board file
* data path optimisations
* disable PCI power save for qca988x and QCA99x0 due to interop reasons

wil6210

* BlockAckReq support
* firmware crashdump using devcoredump
* capture all frames with sniffer
parents f8273baf bc6f9ae6
...@@ -82,6 +82,16 @@ enum bmi_cmd_id { ...@@ -82,6 +82,16 @@ enum bmi_cmd_id {
#define BMI_NVRAM_SEG_NAME_SZ 16 #define BMI_NVRAM_SEG_NAME_SZ 16
#define BMI_PARAM_GET_EEPROM_BOARD_ID 0x10
#define ATH10K_BMI_BOARD_ID_FROM_OTP_MASK 0x7c00
#define ATH10K_BMI_BOARD_ID_FROM_OTP_LSB 10
#define ATH10K_BMI_CHIP_ID_FROM_OTP_MASK 0x18000
#define ATH10K_BMI_CHIP_ID_FROM_OTP_LSB 15
#define ATH10K_BMI_BOARD_ID_STATUS_MASK 0xff
struct bmi_cmd { struct bmi_cmd {
__le32 id; /* enum bmi_cmd_id */ __le32 id; /* enum bmi_cmd_id */
union { union {
......
...@@ -413,7 +413,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr) ...@@ -413,7 +413,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
lockdep_assert_held(&ar_pci->ce_lock); lockdep_assert_held(&ar_pci->ce_lock);
if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0) if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0)
return -EIO; return -ENOSPC;
desc->addr = __cpu_to_le32(paddr); desc->addr = __cpu_to_le32(paddr);
desc->nbytes = 0; desc->nbytes = 0;
...@@ -1076,9 +1076,7 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id) ...@@ -1076,9 +1076,7 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
} }
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
const struct ce_attr *attr, const struct ce_attr *attr)
void (*send_cb)(struct ath10k_ce_pipe *),
void (*recv_cb)(struct ath10k_ce_pipe *))
{ {
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
...@@ -1104,10 +1102,10 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, ...@@ -1104,10 +1102,10 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
ce_state->src_sz_max = attr->src_sz_max; ce_state->src_sz_max = attr->src_sz_max;
if (attr->src_nentries) if (attr->src_nentries)
ce_state->send_cb = send_cb; ce_state->send_cb = attr->send_cb;
if (attr->dest_nentries) if (attr->dest_nentries)
ce_state->recv_cb = recv_cb; ce_state->recv_cb = attr->recv_cb;
if (attr->src_nentries) { if (attr->src_nentries) {
ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr); ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr);
......
...@@ -209,9 +209,7 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, ...@@ -209,9 +209,7 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
const struct ce_attr *attr); const struct ce_attr *attr);
void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id); void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
const struct ce_attr *attr, const struct ce_attr *attr);
void (*send_cb)(struct ath10k_ce_pipe *),
void (*recv_cb)(struct ath10k_ce_pipe *));
void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id); void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
/*==================CE Engine Shutdown=======================*/ /*==================CE Engine Shutdown=======================*/
...@@ -277,6 +275,9 @@ struct ce_attr { ...@@ -277,6 +275,9 @@ struct ce_attr {
/* #entries in destination ring - Must be a power of 2 */ /* #entries in destination ring - Must be a power of 2 */
unsigned int dest_nentries; unsigned int dest_nentries;
void (*send_cb)(struct ath10k_ce_pipe *);
void (*recv_cb)(struct ath10k_ce_pipe *);
}; };
#define SR_BA_ADDRESS 0x0000 #define SR_BA_ADDRESS 0x0000
......
This diff is collapsed.
...@@ -250,6 +250,30 @@ struct ath10k_fw_stats { ...@@ -250,6 +250,30 @@ struct ath10k_fw_stats {
struct list_head peers; struct list_head peers;
}; };
#define ATH10K_TPC_TABLE_TYPE_FLAG 1
#define ATH10K_TPC_PREAM_TABLE_END 0xFFFF
struct ath10k_tpc_table {
u32 pream_idx[WMI_TPC_RATE_MAX];
u8 rate_code[WMI_TPC_RATE_MAX];
char tpc_value[WMI_TPC_RATE_MAX][WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE];
};
struct ath10k_tpc_stats {
u32 reg_domain;
u32 chan_freq;
u32 phy_mode;
u32 twice_antenna_reduction;
u32 twice_max_rd_power;
s32 twice_antenna_gain;
u32 power_limit;
u32 num_tx_chain;
u32 ctl;
u32 rate_max;
u8 flag[WMI_TPC_FLAG];
struct ath10k_tpc_table tpc_table[WMI_TPC_FLAG];
};
struct ath10k_dfs_stats { struct ath10k_dfs_stats {
u32 phy_errors; u32 phy_errors;
u32 pulses_total; u32 pulses_total;
...@@ -378,6 +402,11 @@ struct ath10k_debug { ...@@ -378,6 +402,11 @@ struct ath10k_debug {
struct ath10k_dfs_stats dfs_stats; struct ath10k_dfs_stats dfs_stats;
struct ath_dfs_pool_stats dfs_pool_stats; struct ath_dfs_pool_stats dfs_pool_stats;
/* used for tpc-dump storage, protected by data-lock */
struct ath10k_tpc_stats *tpc_stats;
struct completion tpc_complete;
/* protected by conf_mutex */ /* protected by conf_mutex */
u32 fw_dbglog_mask; u32 fw_dbglog_mask;
u32 fw_dbglog_level; u32 fw_dbglog_level;
...@@ -647,10 +676,19 @@ struct ath10k { ...@@ -647,10 +676,19 @@ struct ath10k {
struct ath10k_swap_code_seg_info *firmware_swap_code_seg_info; struct ath10k_swap_code_seg_info *firmware_swap_code_seg_info;
} swap; } swap;
char spec_board_id[100]; struct {
bool spec_board_loaded; u32 vendor;
u32 device;
u32 subsystem_vendor;
u32 subsystem_device;
bool bmi_ids_valid;
u8 bmi_board_id;
u8 bmi_chip_id;
} id;
int fw_api; int fw_api;
int bd_api;
enum ath10k_cal_mode cal_mode; enum ath10k_cal_mode cal_mode;
struct { struct {
......
This diff is collapsed.
...@@ -55,6 +55,9 @@ enum ath10k_dbg_aggr_mode { ...@@ -55,6 +55,9 @@ enum ath10k_dbg_aggr_mode {
ATH10K_DBG_AGGR_MODE_MAX, ATH10K_DBG_AGGR_MODE_MAX,
}; };
/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)
extern unsigned int ath10k_debug_mask; extern unsigned int ath10k_debug_mask;
__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...); __printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
...@@ -70,6 +73,8 @@ void ath10k_debug_destroy(struct ath10k *ar); ...@@ -70,6 +73,8 @@ void ath10k_debug_destroy(struct ath10k *ar);
int ath10k_debug_register(struct ath10k *ar); int ath10k_debug_register(struct ath10k *ar);
void ath10k_debug_unregister(struct ath10k *ar); void ath10k_debug_unregister(struct ath10k *ar);
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb); void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
void ath10k_debug_tpc_stats_process(struct ath10k *ar,
struct ath10k_tpc_stats *tpc_stats);
struct ath10k_fw_crash_data * struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar); ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
...@@ -117,6 +122,12 @@ static inline void ath10k_debug_fw_stats_process(struct ath10k *ar, ...@@ -117,6 +122,12 @@ static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
{ {
} }
static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar,
struct ath10k_tpc_stats *tpc_stats)
{
kfree(tpc_stats);
}
static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer,
int len) int len)
{ {
......
...@@ -30,13 +30,6 @@ struct ath10k_hif_sg_item { ...@@ -30,13 +30,6 @@ struct ath10k_hif_sg_item {
u16 len; u16 len;
}; };
struct ath10k_hif_cb {
int (*tx_completion)(struct ath10k *ar,
struct sk_buff *wbuf);
int (*rx_completion)(struct ath10k *ar,
struct sk_buff *wbuf);
};
struct ath10k_hif_ops { struct ath10k_hif_ops {
/* send a scatter-gather list to the target */ /* send a scatter-gather list to the target */
int (*tx_sg)(struct ath10k *ar, u8 pipe_id, int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
...@@ -65,8 +58,7 @@ struct ath10k_hif_ops { ...@@ -65,8 +58,7 @@ struct ath10k_hif_ops {
void (*stop)(struct ath10k *ar); void (*stop)(struct ath10k *ar);
int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe, u8 *ul_pipe, u8 *dl_pipe);
int *ul_is_polled, int *dl_is_polled);
void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe); void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe);
...@@ -80,9 +72,6 @@ struct ath10k_hif_ops { ...@@ -80,9 +72,6 @@ struct ath10k_hif_ops {
*/ */
void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force); void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
void (*set_callbacks)(struct ath10k *ar,
struct ath10k_hif_cb *callbacks);
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
u32 (*read32)(struct ath10k *ar, u32 address); u32 (*read32)(struct ath10k *ar, u32 address);
...@@ -142,13 +131,10 @@ static inline void ath10k_hif_stop(struct ath10k *ar) ...@@ -142,13 +131,10 @@ static inline void ath10k_hif_stop(struct ath10k *ar)
static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar, static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
u16 service_id, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe, u8 *ul_pipe, u8 *dl_pipe)
int *ul_is_polled,
int *dl_is_polled)
{ {
return ar->hif.ops->map_service_to_pipe(ar, service_id, return ar->hif.ops->map_service_to_pipe(ar, service_id,
ul_pipe, dl_pipe, ul_pipe, dl_pipe);
ul_is_polled, dl_is_polled);
} }
static inline void ath10k_hif_get_default_pipe(struct ath10k *ar, static inline void ath10k_hif_get_default_pipe(struct ath10k *ar,
...@@ -163,12 +149,6 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar, ...@@ -163,12 +149,6 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
ar->hif.ops->send_complete_check(ar, pipe_id, force); ar->hif.ops->send_complete_check(ar, pipe_id, force);
} }
static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
struct ath10k_hif_cb *callbacks)
{
ar->hif.ops->set_callbacks(ar, callbacks);
}
static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
u8 pipe_id) u8 pipe_id)
{ {
......
...@@ -23,16 +23,6 @@ ...@@ -23,16 +23,6 @@
/* Send */ /* Send */
/********/ /********/
static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep,
int force)
{
/*
* Check whether HIF has any prior sends that have finished,
* have not had the post-processing done.
*/
ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force);
}
static void ath10k_htc_control_tx_complete(struct ath10k *ar, static void ath10k_htc_control_tx_complete(struct ath10k *ar,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -181,24 +171,22 @@ int ath10k_htc_send(struct ath10k_htc *htc, ...@@ -181,24 +171,22 @@ int ath10k_htc_send(struct ath10k_htc *htc,
return ret; return ret;
} }
static int ath10k_htc_tx_completion_handler(struct ath10k *ar, void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb)
struct sk_buff *skb)
{ {
struct ath10k_htc *htc = &ar->htc; struct ath10k_htc *htc = &ar->htc;
struct ath10k_skb_cb *skb_cb; struct ath10k_skb_cb *skb_cb;
struct ath10k_htc_ep *ep; struct ath10k_htc_ep *ep;
if (WARN_ON_ONCE(!skb)) if (WARN_ON_ONCE(!skb))
return 0; return;
skb_cb = ATH10K_SKB_CB(skb); skb_cb = ATH10K_SKB_CB(skb);
ep = &htc->endpoint[skb_cb->eid]; ep = &htc->endpoint[skb_cb->eid];
ath10k_htc_notify_tx_completion(ep, skb); ath10k_htc_notify_tx_completion(ep, skb);
/* the skb now belongs to the completion handler */ /* the skb now belongs to the completion handler */
return 0;
} }
EXPORT_SYMBOL(ath10k_htc_tx_completion_handler);
/***********/ /***********/
/* Receive */ /* Receive */
...@@ -304,8 +292,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, ...@@ -304,8 +292,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
return status; return status;
} }
static int ath10k_htc_rx_completion_handler(struct ath10k *ar, void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb)
struct sk_buff *skb)
{ {
int status = 0; int status = 0;
struct ath10k_htc *htc = &ar->htc; struct ath10k_htc *htc = &ar->htc;
...@@ -326,21 +313,11 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ...@@ -326,21 +313,11 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
ath10k_warn(ar, "HTC Rx: invalid eid %d\n", eid); ath10k_warn(ar, "HTC Rx: invalid eid %d\n", eid);
ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad header", "", ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad header", "",
hdr, sizeof(*hdr)); hdr, sizeof(*hdr));
status = -EINVAL;
goto out; goto out;
} }
ep = &htc->endpoint[eid]; ep = &htc->endpoint[eid];
/*
* If this endpoint that received a message from the target has
* a to-target HIF pipe whose send completions are polled rather
* than interrupt-driven, this is a good point to ask HIF to check
* whether it has any completed sends to handle.
*/
if (ep->ul_is_polled)
ath10k_htc_send_complete_check(ep, 1);
payload_len = __le16_to_cpu(hdr->len); payload_len = __le16_to_cpu(hdr->len);
if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) { if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
...@@ -348,7 +325,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ...@@ -348,7 +325,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
payload_len + sizeof(*hdr)); payload_len + sizeof(*hdr));
ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "", ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "",
hdr, sizeof(*hdr)); hdr, sizeof(*hdr));
status = -EINVAL;
goto out; goto out;
} }
...@@ -358,7 +334,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ...@@ -358,7 +334,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
skb->len, payload_len); skb->len, payload_len);
ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len",
"", hdr, sizeof(*hdr)); "", hdr, sizeof(*hdr));
status = -EINVAL;
goto out; goto out;
} }
...@@ -374,7 +349,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ...@@ -374,7 +349,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
(trailer_len > payload_len)) { (trailer_len > payload_len)) {
ath10k_warn(ar, "Invalid trailer length: %d\n", ath10k_warn(ar, "Invalid trailer length: %d\n",
trailer_len); trailer_len);
status = -EPROTO;
goto out; goto out;
} }
...@@ -407,7 +381,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ...@@ -407,7 +381,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
* sending unsolicited messages on the ep 0 * sending unsolicited messages on the ep 0
*/ */
ath10k_warn(ar, "HTC rx ctrl still processing\n"); ath10k_warn(ar, "HTC rx ctrl still processing\n");
status = -EINVAL;
complete(&htc->ctl_resp); complete(&htc->ctl_resp);
goto out; goto out;
} }
...@@ -439,9 +412,8 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ...@@ -439,9 +412,8 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
skb = NULL; skb = NULL;
out: out:
kfree_skb(skb); kfree_skb(skb);
return status;
} }
EXPORT_SYMBOL(ath10k_htc_rx_completion_handler);
static void ath10k_htc_control_rx_complete(struct ath10k *ar, static void ath10k_htc_control_rx_complete(struct ath10k *ar,
struct sk_buff *skb) struct sk_buff *skb)
...@@ -767,9 +739,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, ...@@ -767,9 +739,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
status = ath10k_hif_map_service_to_pipe(htc->ar, status = ath10k_hif_map_service_to_pipe(htc->ar,
ep->service_id, ep->service_id,
&ep->ul_pipe_id, &ep->ul_pipe_id,
&ep->dl_pipe_id, &ep->dl_pipe_id);
&ep->ul_is_polled,
&ep->dl_is_polled);
if (status) if (status)
return status; return status;
...@@ -778,10 +748,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, ...@@ -778,10 +748,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
htc_service_name(ep->service_id), ep->ul_pipe_id, htc_service_name(ep->service_id), ep->ul_pipe_id,
ep->dl_pipe_id, ep->eid); ep->dl_pipe_id, ep->eid);
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"boot htc ep %d ul polled %d dl polled %d\n",
ep->eid, ep->ul_is_polled, ep->dl_is_polled);
if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
ep->tx_credit_flow_enabled = false; ep->tx_credit_flow_enabled = false;
ath10k_dbg(ar, ATH10K_DBG_BOOT, ath10k_dbg(ar, ATH10K_DBG_BOOT,
...@@ -841,7 +807,6 @@ int ath10k_htc_start(struct ath10k_htc *htc) ...@@ -841,7 +807,6 @@ int ath10k_htc_start(struct ath10k_htc *htc)
/* registered target arrival callback from the HIF layer */ /* registered target arrival callback from the HIF layer */
int ath10k_htc_init(struct ath10k *ar) int ath10k_htc_init(struct ath10k *ar)
{ {
struct ath10k_hif_cb htc_callbacks;
struct ath10k_htc_ep *ep = NULL; struct ath10k_htc_ep *ep = NULL;
struct ath10k_htc *htc = &ar->htc; struct ath10k_htc *htc = &ar->htc;
...@@ -849,15 +814,11 @@ int ath10k_htc_init(struct ath10k *ar) ...@@ -849,15 +814,11 @@ int ath10k_htc_init(struct ath10k *ar)
ath10k_htc_reset_endpoint_states(htc); ath10k_htc_reset_endpoint_states(htc);
/* setup HIF layer callbacks */
htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler;
htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler;
htc->ar = ar; htc->ar = ar;
/* Get HIF default pipe for HTC message exchange */ /* Get HIF default pipe for HTC message exchange */
ep = &htc->endpoint[ATH10K_HTC_EP_0]; ep = &htc->endpoint[ATH10K_HTC_EP_0];
ath10k_hif_set_callbacks(ar, &htc_callbacks);
ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id); ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
init_completion(&htc->ctl_resp); init_completion(&htc->ctl_resp);
......
...@@ -312,8 +312,6 @@ struct ath10k_htc_ep { ...@@ -312,8 +312,6 @@ struct ath10k_htc_ep {
int max_ep_message_len; int max_ep_message_len;
u8 ul_pipe_id; u8 ul_pipe_id;
u8 dl_pipe_id; u8 dl_pipe_id;
int ul_is_polled; /* call HIF to get tx completions */
int dl_is_polled; /* call HIF to fetch rx (not implemented) */
u8 seq_no; /* for debugging */ u8 seq_no; /* for debugging */
int tx_credits; int tx_credits;
...@@ -355,5 +353,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, ...@@ -355,5 +353,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
struct sk_buff *packet); struct sk_buff *packet);
struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size); struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size);
void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
#endif #endif
...@@ -1488,7 +1488,6 @@ struct ath10k_htt { ...@@ -1488,7 +1488,6 @@ struct ath10k_htt {
int num_pending_mgmt_tx; int num_pending_mgmt_tx;
struct idr pending_tx; struct idr pending_tx;
wait_queue_head_t empty_tx_wq; wait_queue_head_t empty_tx_wq;
struct dma_pool *tx_pool;
/* set if host-fw communication goes haywire /* set if host-fw communication goes haywire
* used to avoid further failures */ * used to avoid further failures */
...@@ -1509,6 +1508,11 @@ struct ath10k_htt { ...@@ -1509,6 +1508,11 @@ struct ath10k_htt {
dma_addr_t paddr; dma_addr_t paddr;
struct htt_msdu_ext_desc *vaddr; struct htt_msdu_ext_desc *vaddr;
} frag_desc; } frag_desc;
struct {
dma_addr_t paddr;
struct ath10k_htt_txbuf *vaddr;
} txbuf;
}; };
#define RX_HTT_HDR_STATUS_LEN 64 #define RX_HTT_HDR_STATUS_LEN 64
...@@ -1587,6 +1591,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt); ...@@ -1587,6 +1591,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
u8 max_subfrms_ampdu, u8 max_subfrms_ampdu,
u8 max_subfrms_amsdu); u8 max_subfrms_amsdu);
void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);
void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc); void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc);
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb); int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
......
...@@ -2125,6 +2125,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) ...@@ -2125,6 +2125,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
/* Free the indication buffer */ /* Free the indication buffer */
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
} }
EXPORT_SYMBOL(ath10k_htt_t2h_msg_handler);
static void ath10k_htt_txrx_compl_task(unsigned long ptr) static void ath10k_htt_txrx_compl_task(unsigned long ptr)
{ {
......
...@@ -108,9 +108,12 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) ...@@ -108,9 +108,12 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
spin_lock_init(&htt->tx_lock); spin_lock_init(&htt->tx_lock);
idr_init(&htt->pending_tx); idr_init(&htt->pending_tx);
htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev, size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf);
sizeof(struct ath10k_htt_txbuf), 4, 0); htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size,
if (!htt->tx_pool) { &htt->txbuf.paddr,
GFP_DMA);
if (!htt->txbuf.vaddr) {
ath10k_err(ar, "failed to alloc tx buffer\n");
ret = -ENOMEM; ret = -ENOMEM;
goto free_idr_pending_tx; goto free_idr_pending_tx;
} }
...@@ -125,14 +128,17 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) ...@@ -125,14 +128,17 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
if (!htt->frag_desc.vaddr) { if (!htt->frag_desc.vaddr) {
ath10k_warn(ar, "failed to alloc fragment desc memory\n"); ath10k_warn(ar, "failed to alloc fragment desc memory\n");
ret = -ENOMEM; ret = -ENOMEM;
goto free_tx_pool; goto free_txbuf;
} }
skip_frag_desc_alloc: skip_frag_desc_alloc:
return 0; return 0;
free_tx_pool: free_txbuf:
dma_pool_destroy(htt->tx_pool); size = htt->max_num_pending_tx *
sizeof(struct ath10k_htt_txbuf);
dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr,
htt->txbuf.paddr);
free_idr_pending_tx: free_idr_pending_tx:
idr_destroy(&htt->pending_tx); idr_destroy(&htt->pending_tx);
return ret; return ret;
...@@ -160,7 +166,13 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt) ...@@ -160,7 +166,13 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt)
idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar); idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
idr_destroy(&htt->pending_tx); idr_destroy(&htt->pending_tx);
dma_pool_destroy(htt->tx_pool);
if (htt->txbuf.vaddr) {
size = htt->max_num_pending_tx *
sizeof(struct ath10k_htt_txbuf);
dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr,
htt->txbuf.paddr);
}
if (htt->frag_desc.vaddr) { if (htt->frag_desc.vaddr) {
size = htt->max_num_pending_tx * size = htt->max_num_pending_tx *
...@@ -175,6 +187,12 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) ...@@ -175,6 +187,12 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
} }
void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb)
{
dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL(ath10k_htt_hif_tx_complete);
int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
{ {
struct ath10k *ar = htt->ar; struct ath10k *ar = htt->ar;
...@@ -454,9 +472,9 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ...@@ -454,9 +472,9 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
spin_lock_bh(&htt->tx_lock); spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu); res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock); spin_unlock_bh(&htt->tx_lock);
if (res < 0) { if (res < 0)
goto err_tx_dec; goto err_tx_dec;
}
msdu_id = res; msdu_id = res;
txdesc = ath10k_htc_alloc_skb(ar, len); txdesc = ath10k_htc_alloc_skb(ar, len);
...@@ -521,7 +539,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ...@@ -521,7 +539,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
int res; int res;
u8 flags0 = 0; u8 flags0 = 0;
u16 msdu_id, flags1 = 0; u16 msdu_id, flags1 = 0;
dma_addr_t paddr = 0;
u32 frags_paddr = 0; u32 frags_paddr = 0;
struct htt_msdu_ext_desc *ext_desc = NULL; struct htt_msdu_ext_desc *ext_desc = NULL;
bool limit_mgmt_desc = false; bool limit_mgmt_desc = false;
...@@ -542,21 +559,17 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ...@@ -542,21 +559,17 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
spin_lock_bh(&htt->tx_lock); spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu); res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock); spin_unlock_bh(&htt->tx_lock);
if (res < 0) { if (res < 0)
goto err_tx_dec; goto err_tx_dec;
}
msdu_id = res; msdu_id = res;
prefetch_len = min(htt->prefetch_len, msdu->len); prefetch_len = min(htt->prefetch_len, msdu->len);
prefetch_len = roundup(prefetch_len, 4); prefetch_len = roundup(prefetch_len, 4);
skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC, skb_cb->htt.txbuf = &htt->txbuf.vaddr[msdu_id];
&paddr); skb_cb->htt.txbuf_paddr = htt->txbuf.paddr +
if (!skb_cb->htt.txbuf) { (sizeof(struct ath10k_htt_txbuf) * msdu_id);
res = -ENOMEM;
goto err_free_msdu_id;
}
skb_cb->htt.txbuf_paddr = paddr;
if ((ieee80211_is_action(hdr->frame_control) || if ((ieee80211_is_action(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control) ||
...@@ -574,7 +587,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ...@@ -574,7 +587,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res = dma_mapping_error(dev, skb_cb->paddr); res = dma_mapping_error(dev, skb_cb->paddr);
if (res) { if (res) {
res = -EIO; res = -EIO;
goto err_free_txbuf; goto err_free_msdu_id;
} }
switch (skb_cb->txmode) { switch (skb_cb->txmode) {
...@@ -706,10 +719,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ...@@ -706,10 +719,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
err_unmap_msdu: err_unmap_msdu:
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
err_free_txbuf:
dma_pool_free(htt->tx_pool,
skb_cb->htt.txbuf,
skb_cb->htt.txbuf_paddr);
err_free_msdu_id: err_free_msdu_id:
spin_lock_bh(&htt->tx_lock); spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id); ath10k_htt_tx_free_msdu_id(htt, msdu_id);
......
...@@ -97,6 +97,9 @@ enum qca6174_chip_id_rev { ...@@ -97,6 +97,9 @@ enum qca6174_chip_id_rev {
/* includes also the null byte */ /* includes also the null byte */
#define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" #define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K"
#define ATH10K_BOARD_MAGIC "QCA-ATH10K-BOARD"
#define ATH10K_BOARD_API2_FILE "board-2.bin"
#define REG_DUMP_COUNT_QCA988X 60 #define REG_DUMP_COUNT_QCA988X 60
...@@ -159,6 +162,16 @@ enum ath10k_fw_htt_op_version { ...@@ -159,6 +162,16 @@ enum ath10k_fw_htt_op_version {
ATH10K_FW_HTT_OP_VERSION_MAX, ATH10K_FW_HTT_OP_VERSION_MAX,
}; };
enum ath10k_bd_ie_type {
/* contains sub IEs of enum ath10k_bd_ie_board_type */
ATH10K_BD_IE_BOARD = 0,
};
enum ath10k_bd_ie_board_type {
ATH10K_BD_IE_BOARD_NAME = 0,
ATH10K_BD_IE_BOARD_DATA = 1,
};
enum ath10k_hw_rev { enum ath10k_hw_rev {
ATH10K_HW_QCA988X, ATH10K_HW_QCA988X,
ATH10K_HW_QCA6174, ATH10K_HW_QCA6174,
......
...@@ -197,9 +197,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif, ...@@ -197,9 +197,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
}
if (cmd == DISABLE_KEY) { if (cmd == DISABLE_KEY) {
arg.key_cipher = WMI_CIPHER_NONE; arg.key_cipher = WMI_CIPHER_NONE;
...@@ -1111,7 +1110,8 @@ static int ath10k_monitor_recalc(struct ath10k *ar) ...@@ -1111,7 +1110,8 @@ static int ath10k_monitor_recalc(struct ath10k *ar)
ret = ath10k_monitor_stop(ar); ret = ath10k_monitor_stop(ar);
if (ret) if (ret)
ath10k_warn(ar, "failed to stop disallowed monitor: %d\n", ret); ath10k_warn(ar, "failed to stop disallowed monitor: %d\n",
ret);
/* not serious */ /* not serious */
} }
...@@ -2084,7 +2084,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, ...@@ -2084,7 +2084,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
enum ieee80211_band band; enum ieee80211_band band;
const u8 *ht_mcs_mask; const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask; const u16 *vht_mcs_mask;
int i, n, max_nss; int i, n;
u8 max_nss;
u32 stbc; u32 stbc;
lockdep_assert_held(&ar->conf_mutex); lockdep_assert_held(&ar->conf_mutex);
...@@ -2169,7 +2170,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, ...@@ -2169,7 +2170,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
arg->peer_ht_rates.rates[i] = i; arg->peer_ht_rates.rates[i] = i;
} else { } else {
arg->peer_ht_rates.num_rates = n; arg->peer_ht_rates.num_rates = n;
arg->peer_num_spatial_streams = max_nss; arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss);
} }
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
...@@ -4065,6 +4066,7 @@ static u32 get_nss_from_chainmask(u16 chain_mask) ...@@ -4065,6 +4066,7 @@ static u32 get_nss_from_chainmask(u16 chain_mask)
static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar) static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar)
{ {
int nsts = ar->vht_cap_info; int nsts = ar->vht_cap_info;
nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
...@@ -4081,8 +4083,9 @@ static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar) ...@@ -4081,8 +4083,9 @@ static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar)
static int ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar) static int ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar)
{ {
int sound_dim = ar->vht_cap_info; int sound_dim = ar->vht_cap_info;
sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
sound_dim >>=IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
/* If the sounding dimension is not advertised by the firmware, /* If the sounding dimension is not advertised by the firmware,
* let's use a default value of 1 * let's use a default value of 1
...@@ -4656,7 +4659,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ...@@ -4656,7 +4659,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
info->use_cts_prot ? 1 : 0); info->use_cts_prot ? 1 : 0);
if (ret) if (ret)
ath10k_warn(ar, "failed to set protection mode %d on vdev %i: %d\n", ath10k_warn(ar, "failed to set protection mode %d on vdev %i: %d\n",
info->use_cts_prot, arvif->vdev_id, ret); info->use_cts_prot, arvif->vdev_id, ret);
} }
if (changed & BSS_CHANGED_ERP_SLOT) { if (changed & BSS_CHANGED_ERP_SLOT) {
...@@ -6268,8 +6271,8 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, ...@@ -6268,8 +6271,8 @@ ath10k_mac_update_rx_channel(struct ath10k *ar,
rcu_read_lock(); rcu_read_lock();
if (!ctx && ath10k_mac_num_chanctxs(ar) == 1) { if (!ctx && ath10k_mac_num_chanctxs(ar) == 1) {
ieee80211_iter_chan_contexts_atomic(ar->hw, ieee80211_iter_chan_contexts_atomic(ar->hw,
ath10k_mac_get_any_chandef_iter, ath10k_mac_get_any_chandef_iter,
&def); &def);
if (vifs) if (vifs)
def = &vifs[0].new_ctx->def; def = &vifs[0].new_ctx->def;
...@@ -7301,7 +7304,7 @@ int ath10k_mac_register(struct ath10k *ar) ...@@ -7301,7 +7304,7 @@ int ath10k_mac_register(struct ath10k *ar)
ath10k_reg_notifier); ath10k_reg_notifier);
if (ret) { if (ret) {
ath10k_err(ar, "failed to initialise regulatory: %i\n", ret); ath10k_err(ar, "failed to initialise regulatory: %i\n", ret);
goto err_free; goto err_dfs_detector_exit;
} }
ar->hw->wiphy->cipher_suites = cipher_suites; ar->hw->wiphy->cipher_suites = cipher_suites;
...@@ -7310,7 +7313,7 @@ int ath10k_mac_register(struct ath10k *ar) ...@@ -7310,7 +7313,7 @@ int ath10k_mac_register(struct ath10k *ar)
ret = ieee80211_register_hw(ar->hw); ret = ieee80211_register_hw(ar->hw);
if (ret) { if (ret) {
ath10k_err(ar, "failed to register ieee80211: %d\n", ret); ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
goto err_free; goto err_dfs_detector_exit;
} }
if (!ath_is_world_regd(&ar->ath_common.regulatory)) { if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
...@@ -7324,10 +7327,16 @@ int ath10k_mac_register(struct ath10k *ar) ...@@ -7324,10 +7327,16 @@ int ath10k_mac_register(struct ath10k *ar)
err_unregister: err_unregister:
ieee80211_unregister_hw(ar->hw); ieee80211_unregister_hw(ar->hw);
err_dfs_detector_exit:
if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector)
ar->dfs_detector->exit(ar->dfs_detector);
err_free: err_free:
kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels); kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels); kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
SET_IEEE80211_DEV(ar->hw, NULL);
return ret; return ret;
} }
......
This diff is collapsed.
...@@ -175,8 +175,6 @@ struct ath10k_pci { ...@@ -175,8 +175,6 @@ struct ath10k_pci {
struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX]; struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
struct ath10k_hif_cb msg_callbacks_current;
/* Copy Engine used for Diagnostic Accesses */ /* Copy Engine used for Diagnostic Accesses */
struct ath10k_ce_pipe *ce_diag; struct ath10k_ce_pipe *ce_diag;
...@@ -221,6 +219,12 @@ struct ath10k_pci { ...@@ -221,6 +219,12 @@ struct ath10k_pci {
* powersave register state changes. * powersave register state changes.
*/ */
bool ps_awake; bool ps_awake;
/* pci power save, disable for QCA988X and QCA99X0.
* Writing 'false' to this variable avoids frequent locking
* on MMIO read/write.
*/
bool pci_ps;
}; };
static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
...@@ -230,7 +234,8 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) ...@@ -230,7 +234,8 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
#define ATH10K_PCI_RX_POST_RETRY_MS 50 #define ATH10K_PCI_RX_POST_RETRY_MS 50
#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
#define PCIE_WAKE_TIMEOUT 10000 /* 10ms */ #define PCIE_WAKE_TIMEOUT 30000 /* 30ms */
#define PCIE_WAKE_LATE_US 10000 /* 10ms */
#define BAR_NUM 0 #define BAR_NUM 0
......
...@@ -215,6 +215,6 @@ int ath10k_thermal_register(struct ath10k *ar) ...@@ -215,6 +215,6 @@ int ath10k_thermal_register(struct ath10k *ar)
void ath10k_thermal_unregister(struct ath10k *ar) void ath10k_thermal_unregister(struct ath10k *ar)
{ {
thermal_cooling_device_unregister(ar->thermal.cdev);
sysfs_remove_link(&ar->dev->kobj, "cooling_device"); sysfs_remove_link(&ar->dev->kobj, "cooling_device");
thermal_cooling_device_unregister(ar->thermal.cdev);
} }
...@@ -92,11 +92,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, ...@@ -92,11 +92,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
skb_cb = ATH10K_SKB_CB(msdu); skb_cb = ATH10K_SKB_CB(msdu);
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
if (skb_cb->htt.txbuf)
dma_pool_free(htt->tx_pool,
skb_cb->htt.txbuf,
skb_cb->htt.txbuf_paddr);
ath10k_report_offchan_tx(htt->ar, msdu); ath10k_report_offchan_tx(htt->ar, msdu);
info = IEEE80211_SKB_CB(msdu); info = IEEE80211_SKB_CB(msdu);
......
...@@ -177,6 +177,11 @@ struct wmi_ops { ...@@ -177,6 +177,11 @@ struct wmi_ops {
const struct wmi_tdls_peer_capab_arg *cap, const struct wmi_tdls_peer_capab_arg *cap,
const struct wmi_channel_arg *chan); const struct wmi_channel_arg *chan);
struct sk_buff *(*gen_adaptive_qcs)(struct ath10k *ar, bool enable); struct sk_buff *(*gen_adaptive_qcs)(struct ath10k *ar, bool enable);
struct sk_buff *(*gen_pdev_get_tpc_config)(struct ath10k *ar,
u32 param);
void (*fw_stats_fill)(struct ath10k *ar,
struct ath10k_fw_stats *fw_stats,
char *buf);
}; };
int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
...@@ -1270,4 +1275,31 @@ ath10k_wmi_adaptive_qcs(struct ath10k *ar, bool enable) ...@@ -1270,4 +1275,31 @@ ath10k_wmi_adaptive_qcs(struct ath10k *ar, bool enable)
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->adaptive_qcs_cmdid); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->adaptive_qcs_cmdid);
} }
static inline int
ath10k_wmi_pdev_get_tpc_config(struct ath10k *ar, u32 param)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_pdev_get_tpc_config)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_pdev_get_tpc_config(ar, param);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb,
ar->wmi.cmd->pdev_get_tpc_config_cmdid);
}
static inline int
ath10k_wmi_fw_stats_fill(struct ath10k *ar, struct ath10k_fw_stats *fw_stats,
char *buf)
{
if (!ar->wmi.ops->fw_stats_fill)
return -EOPNOTSUPP;
ar->wmi.ops->fw_stats_fill(ar, fw_stats, buf);
return 0;
}
#endif #endif
...@@ -3468,6 +3468,7 @@ static const struct wmi_ops wmi_tlv_ops = { ...@@ -3468,6 +3468,7 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_update_fw_tdls_state = ath10k_wmi_tlv_op_gen_update_fw_tdls_state, .gen_update_fw_tdls_state = ath10k_wmi_tlv_op_gen_update_fw_tdls_state,
.gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update, .gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update,
.gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs, .gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs,
.fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
}; };
/************/ /************/
......
This diff is collapsed.
...@@ -73,6 +73,25 @@ struct wmi_cmd_hdr { ...@@ -73,6 +73,25 @@ struct wmi_cmd_hdr {
#define HTC_PROTOCOL_VERSION 0x0002 #define HTC_PROTOCOL_VERSION 0x0002
#define WMI_PROTOCOL_VERSION 0x0002 #define WMI_PROTOCOL_VERSION 0x0002
/*
* There is no signed version of __le32, so for a temporary solution come
* up with our own version. The idea is from fs/ntfs/types.h.
*
* Use a_ prefix so that it doesn't conflict if we get proper support to
* linux/types.h.
*/
typedef __s32 __bitwise a_sle32;
static inline a_sle32 a_cpu_to_sle32(s32 val)
{
return (__force a_sle32)cpu_to_le32(val);
}
static inline s32 a_sle32_to_cpu(a_sle32 val)
{
return le32_to_cpu((__force __le32)val);
}
enum wmi_service { enum wmi_service {
WMI_SERVICE_BEACON_OFFLOAD = 0, WMI_SERVICE_BEACON_OFFLOAD = 0,
WMI_SERVICE_SCAN_OFFLOAD, WMI_SERVICE_SCAN_OFFLOAD,
...@@ -3642,8 +3661,18 @@ struct wmi_pdev_get_tpc_config_cmd { ...@@ -3642,8 +3661,18 @@ struct wmi_pdev_get_tpc_config_cmd {
__le32 param; __le32 param;
} __packed; } __packed;
#define WMI_TPC_CONFIG_PARAM 1
#define WMI_TPC_RATE_MAX 160 #define WMI_TPC_RATE_MAX 160
#define WMI_TPC_TX_N_CHAIN 4 #define WMI_TPC_TX_N_CHAIN 4
#define WMI_TPC_PREAM_TABLE_MAX 10
#define WMI_TPC_FLAG 3
#define WMI_TPC_BUF_SIZE 10
enum wmi_tpc_table_type {
WMI_TPC_TABLE_TYPE_CDD = 0,
WMI_TPC_TABLE_TYPE_STBC = 1,
WMI_TPC_TABLE_TYPE_TXBF = 2,
};
enum wmi_tpc_config_event_flag { enum wmi_tpc_config_event_flag {
WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD = 0x1, WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD = 0x1,
...@@ -3657,7 +3686,7 @@ struct wmi_pdev_tpc_config_event { ...@@ -3657,7 +3686,7 @@ struct wmi_pdev_tpc_config_event {
__le32 phy_mode; __le32 phy_mode;
__le32 twice_antenna_reduction; __le32 twice_antenna_reduction;
__le32 twice_max_rd_power; __le32 twice_max_rd_power;
s32 twice_antenna_gain; a_sle32 twice_antenna_gain;
__le32 power_limit; __le32 power_limit;
__le32 rate_max; __le32 rate_max;
__le32 num_tx_chain; __le32 num_tx_chain;
...@@ -4253,6 +4282,11 @@ enum wmi_rate_preamble { ...@@ -4253,6 +4282,11 @@ enum wmi_rate_preamble {
WMI_RATE_PREAMBLE_VHT, WMI_RATE_PREAMBLE_VHT,
}; };
#define ATH10K_HW_NSS(rate) (1 + (((rate) >> 4) & 0x3))
#define ATH10K_HW_PREAMBLE(rate) (((rate) >> 6) & 0x3)
#define ATH10K_HW_RATECODE(rate, nss, preamble) \
(((preamble) << 6) | ((nss) << 4) | (rate))
/* Value to disable fixed rate setting */ /* Value to disable fixed rate setting */
#define WMI_FIXED_RATE_NONE (0xff) #define WMI_FIXED_RATE_NONE (0xff)
...@@ -6064,6 +6098,7 @@ struct ath10k; ...@@ -6064,6 +6098,7 @@ struct ath10k;
struct ath10k_vif; struct ath10k_vif;
struct ath10k_fw_stats_pdev; struct ath10k_fw_stats_pdev;
struct ath10k_fw_stats_peer; struct ath10k_fw_stats_peer;
struct ath10k_fw_stats;
int ath10k_wmi_attach(struct ath10k *ar); int ath10k_wmi_attach(struct ath10k *ar);
void ath10k_wmi_detach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar);
...@@ -6145,4 +6180,13 @@ void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb); ...@@ -6145,4 +6180,13 @@ void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb);
int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb); int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb);
int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, const void *phyerr_buf, int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, const void *phyerr_buf,
int left_len, struct wmi_phyerr_ev_arg *arg); int left_len, struct wmi_phyerr_ev_arg *arg);
void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar,
struct ath10k_fw_stats *fw_stats,
char *buf);
void ath10k_wmi_10x_op_fw_stats_fill(struct ath10k *ar,
struct ath10k_fw_stats *fw_stats,
char *buf);
size_t ath10k_wmi_fw_stats_num_peers(struct list_head *head);
size_t ath10k_wmi_fw_stats_num_vdevs(struct list_head *head);
#endif /* _WMI_H_ */ #endif /* _WMI_H_ */
...@@ -2217,7 +2217,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) ...@@ -2217,7 +2217,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
/* enter / leave wow suspend on first vif always */ /* enter / leave wow suspend on first vif always */
first_vif = ath6kl_vif_first(ar); first_vif = ath6kl_vif_first(ar);
if (WARN_ON(unlikely(!first_vif)) || if (WARN_ON(!first_vif) ||
!ath6kl_cfg80211_ready(first_vif)) !ath6kl_cfg80211_ready(first_vif))
return -EIO; return -EIO;
...@@ -2297,7 +2297,7 @@ static int ath6kl_wow_resume(struct ath6kl *ar) ...@@ -2297,7 +2297,7 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
int ret; int ret;
vif = ath6kl_vif_first(ar); vif = ath6kl_vif_first(ar);
if (WARN_ON(unlikely(!vif)) || if (WARN_ON(!vif) ||
!ath6kl_cfg80211_ready(vif)) !ath6kl_cfg80211_ready(vif))
return -EIO; return -EIO;
......
...@@ -1085,9 +1085,7 @@ static int htc_setup_tx_complete(struct htc_target *target) ...@@ -1085,9 +1085,7 @@ static int htc_setup_tx_complete(struct htc_target *target)
send_pkt->completion = NULL; send_pkt->completion = NULL;
ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0); ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0);
status = ath6kl_htc_tx_issue(target, send_pkt); status = ath6kl_htc_tx_issue(target, send_pkt);
htc_reclaim_txctrl_buf(target, send_pkt);
if (send_pkt != NULL)
htc_reclaim_txctrl_buf(target, send_pkt);
return status; return status;
} }
......
...@@ -284,10 +284,10 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) ...@@ -284,10 +284,10 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
if (cd == NULL) if (cd == NULL)
return false; return false;
dpd->last_pulse_ts = event->ts;
/* reset detector on time stamp wraparound, caused by TSF reset */ /* reset detector on time stamp wraparound, caused by TSF reset */
if (event->ts < dpd->last_pulse_ts) if (event->ts < dpd->last_pulse_ts)
dpd_reset(dpd); dpd_reset(dpd);
dpd->last_pulse_ts = event->ts;
/* do type individual pattern matching */ /* do type individual pattern matching */
for (i = 0; i < dpd->num_radar_types; i++) { for (i = 0; i < dpd->num_radar_types; i++) {
......
config WIL6210 config WIL6210
tristate "Wilocity 60g WiFi card wil6210 support" tristate "Wilocity 60g WiFi card wil6210 support"
select WANT_DEV_COREDUMP
depends on CFG80211 depends on CFG80211
depends on PCI depends on PCI
default n default n
......
...@@ -17,6 +17,7 @@ wil6210-y += pmc.o ...@@ -17,6 +17,7 @@ wil6210-y += pmc.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
wil6210-y += wil_platform.o wil6210-y += wil_platform.o
wil6210-y += ethtool.o wil6210-y += ethtool.o
wil6210-y += wil_crash_dump.o
# for tracing framework to find trace.h # for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src) CFLAGS_trace.o := -I$(src)
......
...@@ -1373,6 +1373,12 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) ...@@ -1373,6 +1373,12 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
} }
} }
spin_unlock_bh(&p->tid_rx_lock); spin_unlock_bh(&p->tid_rx_lock);
seq_printf(s,
"Rx invalid frame: non-data %lu, short %lu, large %lu\n",
p->stats.rx_non_data_frame,
p->stats.rx_short_frame,
p->stats.rx_large_frame);
seq_puts(s, "Rx/MCS:"); seq_puts(s, "Rx/MCS:");
for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs); for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs);
mcs++) mcs++)
......
/* /*
* Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
...@@ -347,7 +347,12 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) ...@@ -347,7 +347,12 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
wil6210_mask_irq_misc(wil); wil6210_mask_irq_misc(wil);
if (isr & ISR_MISC_FW_ERROR) { if (isr & ISR_MISC_FW_ERROR) {
wil_err(wil, "Firmware error detected\n"); u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE);
u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE);
wil_err(wil,
"Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n",
fw_assert_code, ucode_assert_code);
clear_bit(wil_status_fwready, wil->status); clear_bit(wil_status_fwready, wil->status);
/* /*
* do not clear @isr here - we do 2-nd part in thread * do not clear @isr here - we do 2-nd part in thread
...@@ -386,6 +391,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) ...@@ -386,6 +391,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_FW_ERROR) { if (isr & ISR_MISC_FW_ERROR) {
wil_fw_core_dump(wil);
wil_notify_fw_error(wil); wil_notify_fw_error(wil);
isr &= ~ISR_MISC_FW_ERROR; isr &= ~ISR_MISC_FW_ERROR;
wil_fw_error_recovery(wil); wil_fw_error_recovery(wil);
......
...@@ -203,11 +203,13 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, ...@@ -203,11 +203,13 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
* - disconnect single STA, already disconnected * - disconnect single STA, already disconnected
* - disconnect all * - disconnect all
* *
* For "disconnect all", there are 2 options: * For "disconnect all", there are 3 options:
* - bssid == NULL * - bssid == NULL
* - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
* - bssid is our MAC address * - bssid is our MAC address
*/ */
if (bssid && memcmp(ndev->dev_addr, bssid, ETH_ALEN)) { if (bssid && !is_broadcast_ether_addr(bssid) &&
!ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
cid = wil_find_cid(wil, bssid); cid = wil_find_cid(wil, bssid);
wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n", wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
bssid, cid, reason_code); bssid, cid, reason_code);
...@@ -765,6 +767,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) ...@@ -765,6 +767,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (wil->hw_version == HW_VER_UNKNOWN) if (wil->hw_version == HW_VER_UNKNOWN)
return -ENODEV; return -ENODEV;
set_bit(wil_status_resetting, wil->status);
cancel_work_sync(&wil->disconnect_worker); cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
wil_bcast_fini(wil); wil_bcast_fini(wil);
...@@ -851,6 +855,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) ...@@ -851,6 +855,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
void wil_fw_error_recovery(struct wil6210_priv *wil) void wil_fw_error_recovery(struct wil6210_priv *wil)
{ {
wil_dbg_misc(wil, "starting fw error recovery\n"); wil_dbg_misc(wil, "starting fw error recovery\n");
if (test_bit(wil_status_resetting, wil->status)) {
wil_info(wil, "Reset already in progress\n");
return;
}
wil->recovery_state = fw_recovery_pending; wil->recovery_state = fw_recovery_pending;
schedule_work(&wil->fw_error_worker); schedule_work(&wil->fw_error_worker);
} }
......
...@@ -260,6 +260,7 @@ static const struct pci_device_id wil6210_pcie_ids[] = { ...@@ -260,6 +260,7 @@ static const struct pci_device_id wil6210_pcie_ids[] = {
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
#ifdef CONFIG_PM #ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_suspend(struct device *dev, bool is_runtime) static int wil6210_suspend(struct device *dev, bool is_runtime)
{ {
...@@ -307,7 +308,6 @@ static int wil6210_resume(struct device *dev, bool is_runtime) ...@@ -307,7 +308,6 @@ static int wil6210_resume(struct device *dev, bool is_runtime)
return rc; return rc;
} }
#ifdef CONFIG_PM_SLEEP
static int wil6210_pm_suspend(struct device *dev) static int wil6210_pm_suspend(struct device *dev)
{ {
return wil6210_suspend(dev, false); return wil6210_suspend(dev, false);
......
...@@ -110,7 +110,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil, ...@@ -110,7 +110,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
*/ */
for (i = 0; i < num_descriptors; i++) { for (i = 0; i < num_descriptors; i++) {
struct vring_tx_desc *_d = &pmc->pring_va[i]; struct vring_tx_desc *_d = &pmc->pring_va[i];
struct vring_tx_desc dd, *d = &dd; struct vring_tx_desc dd = {}, *d = &dd;
int j = 0; int j = 0;
pmc->descriptors[i].va = dma_alloc_coherent(dev, pmc->descriptors[i].va = dma_alloc_coherent(dev,
......
...@@ -205,6 +205,32 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) ...@@ -205,6 +205,32 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
spin_unlock(&sta->tid_rx_lock); spin_unlock(&sta->tid_rx_lock);
} }
/* process BAR frame, called in NAPI context */
void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq)
{
struct wil_sta_info *sta = &wil->sta[cid];
struct wil_tid_ampdu_rx *r;
spin_lock(&sta->tid_rx_lock);
r = sta->tid_rx[tid];
if (!r) {
wil_err(wil, "BAR for non-existing CID %d TID %d\n", cid, tid);
goto out;
}
if (seq_less(seq, r->head_seq_num)) {
wil_err(wil, "BAR Seq 0x%03x preceding head 0x%03x\n",
seq, r->head_seq_num);
goto out;
}
wil_dbg_txrx(wil, "BAR: CID %d TID %d Seq 0x%03x head 0x%03x\n",
cid, tid, seq, r->head_seq_num);
wil_release_reorder_frames(wil, r, seq);
out:
spin_unlock(&sta->tid_rx_lock);
}
struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
int size, u16 ssn) int size, u16 ssn)
{ {
......
...@@ -358,6 +358,13 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, ...@@ -358,6 +358,13 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
} }
} }
/* similar to ieee80211_ version, but FC contain only 1-st byte */
static inline int wil_is_back_req(u8 fc)
{
return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ);
}
/** /**
* reap 1 frame from @swhead * reap 1 frame from @swhead
* *
...@@ -379,14 +386,16 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -379,14 +386,16 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
u16 dmalen; u16 dmalen;
u8 ftype; u8 ftype;
int cid; int cid;
int i = (int)vring->swhead; int i;
struct wil_net_stats *stats; struct wil_net_stats *stats;
BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb)); BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
again:
if (unlikely(wil_vring_is_empty(vring))) if (unlikely(wil_vring_is_empty(vring)))
return NULL; return NULL;
i = (int)vring->swhead;
_d = &vring->va[i].rx; _d = &vring->va[i].rx;
if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) { if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) {
/* it is not error, we just reached end of Rx done area */ /* it is not error, we just reached end of Rx done area */
...@@ -398,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -398,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
wil_vring_advance_head(vring, 1); wil_vring_advance_head(vring, 1);
if (!skb) { if (!skb) {
wil_err(wil, "No Rx skb at [%d]\n", i); wil_err(wil, "No Rx skb at [%d]\n", i);
return NULL; goto again;
} }
d = wil_skb_rxdesc(skb); d = wil_skb_rxdesc(skb);
*d = *_d; *d = *_d;
...@@ -409,13 +418,17 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -409,13 +418,17 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
trace_wil6210_rx(i, d); trace_wil6210_rx(i, d);
wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen); wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false); (const void *)d, sizeof(*d), false);
cid = wil_rxdesc_cid(d);
stats = &wil->sta[cid].stats;
if (unlikely(dmalen > sz)) { if (unlikely(dmalen > sz)) {
wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
stats->rx_large_frame++;
kfree_skb(skb); kfree_skb(skb);
return NULL; goto again;
} }
skb_trim(skb, dmalen); skb_trim(skb, dmalen);
...@@ -424,8 +437,6 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -424,8 +437,6 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false); skb->data, skb_headlen(skb), false);
cid = wil_rxdesc_cid(d);
stats = &wil->sta[cid].stats;
stats->last_mcs_rx = wil_rxdesc_mcs(d); stats->last_mcs_rx = wil_rxdesc_mcs(d);
if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs)) if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs))
stats->rx_per_mcs[stats->last_mcs_rx]++; stats->rx_per_mcs[stats->last_mcs_rx]++;
...@@ -437,24 +448,47 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -437,24 +448,47 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
/* no extra checks if in sniffer mode */ /* no extra checks if in sniffer mode */
if (ndev->type != ARPHRD_ETHER) if (ndev->type != ARPHRD_ETHER)
return skb; return skb;
/* /* Non-data frames may be delivered through Rx DMA channel (ex: BAR)
* Non-data frames may be delivered through Rx DMA channel (ex: BAR)
* Driver should recognize it by frame type, that is found * Driver should recognize it by frame type, that is found
* in Rx descriptor. If type is not data, it is 802.11 frame as is * in Rx descriptor. If type is not data, it is 802.11 frame as is
*/ */
ftype = wil_rxdesc_ftype(d) << 2; ftype = wil_rxdesc_ftype(d) << 2;
if (unlikely(ftype != IEEE80211_FTYPE_DATA)) { if (unlikely(ftype != IEEE80211_FTYPE_DATA)) {
wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype); u8 fc1 = wil_rxdesc_fc1(d);
/* TODO: process it */ int mid = wil_rxdesc_mid(d);
int tid = wil_rxdesc_tid(d);
u16 seq = wil_rxdesc_seq(d);
wil_dbg_txrx(wil,
"Non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n",
fc1, mid, cid, tid, seq);
stats->rx_non_data_frame++;
if (wil_is_back_req(fc1)) {
wil_dbg_txrx(wil,
"BAR: MID %d CID %d TID %d Seq 0x%03x\n",
mid, cid, tid, seq);
wil_rx_bar(wil, cid, tid, seq);
} else {
/* print again all info. One can enable only this
* without overhead for printing every Rx frame
*/
wil_dbg_txrx(wil,
"Unhandled non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n",
fc1, mid, cid, tid, seq);
wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
}
kfree_skb(skb); kfree_skb(skb);
return NULL; goto again;
} }
if (unlikely(skb->len < ETH_HLEN + snaplen)) { if (unlikely(skb->len < ETH_HLEN + snaplen)) {
wil_err(wil, "Short frame, len = %d\n", skb->len); wil_err(wil, "Short frame, len = %d\n", skb->len);
/* TODO: process it (i.e. BAR) */ stats->rx_short_frame++;
kfree_skb(skb); kfree_skb(skb);
return NULL; goto again;
} }
/* L4 IDENT is on when HW calculated checksum, check status /* L4 IDENT is on when HW calculated checksum, check status
...@@ -1633,7 +1667,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -1633,7 +1667,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
goto drop; goto drop;
} }
if (unlikely(!test_bit(wil_status_fwconnected, wil->status))) { if (unlikely(!test_bit(wil_status_fwconnected, wil->status))) {
wil_err(wil, "FW not connected\n"); wil_err_ratelimited(wil, "FW not connected\n");
goto drop; goto drop;
} }
if (unlikely(wil->wdev->iftype == NL80211_IFTYPE_MONITOR)) { if (unlikely(wil->wdev->iftype == NL80211_IFTYPE_MONITOR)) {
......
...@@ -464,6 +464,12 @@ static inline int wil_rxdesc_subtype(struct vring_rx_desc *d) ...@@ -464,6 +464,12 @@ static inline int wil_rxdesc_subtype(struct vring_rx_desc *d)
return WIL_GET_BITS(d->mac.d0, 12, 15); return WIL_GET_BITS(d->mac.d0, 12, 15);
} }
/* 1-st byte (with frame type/subtype) of FC field */
static inline u8 wil_rxdesc_fc1(struct vring_rx_desc *d)
{
return (u8)(WIL_GET_BITS(d->mac.d0, 10, 15) << 2);
}
static inline int wil_rxdesc_seq(struct vring_rx_desc *d) static inline int wil_rxdesc_seq(struct vring_rx_desc *d)
{ {
return WIL_GET_BITS(d->mac.d0, 16, 27); return WIL_GET_BITS(d->mac.d0, 16, 27);
...@@ -501,6 +507,7 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb) ...@@ -501,6 +507,7 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb)
void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev);
void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb); void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb);
void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq);
struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
int size, u16 ssn); int size, u16 ssn);
void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
......
...@@ -246,6 +246,10 @@ struct RGF_ICR { ...@@ -246,6 +246,10 @@ struct RGF_ICR {
#define RGF_USER_JTAG_DEV_ID (0x880b34) /* device ID */ #define RGF_USER_JTAG_DEV_ID (0x880b34) /* device ID */
#define JTAG_DEV_ID_SPARROW_B0 (0x2632072f) #define JTAG_DEV_ID_SPARROW_B0 (0x2632072f)
/* crash codes for FW/Ucode stored here */
#define RGF_FW_ASSERT_CODE (0x91f020)
#define RGF_UCODE_ASSERT_CODE (0x91f028)
enum { enum {
HW_VER_UNKNOWN, HW_VER_UNKNOWN,
HW_VER_SPARROW_B0, /* JTAG_DEV_ID_SPARROW_B0 */ HW_VER_SPARROW_B0, /* JTAG_DEV_ID_SPARROW_B0 */
...@@ -405,6 +409,7 @@ enum { /* for wil6210_priv.status */ ...@@ -405,6 +409,7 @@ enum { /* for wil6210_priv.status */
wil_status_reset_done, wil_status_reset_done,
wil_status_irqen, /* FIXME: interrupts enabled - for debug */ wil_status_irqen, /* FIXME: interrupts enabled - for debug */
wil_status_napi_en, /* NAPI enabled protected by wil->mutex */ wil_status_napi_en, /* NAPI enabled protected by wil->mutex */
wil_status_resetting, /* reset in progress */
wil_status_last /* keep last */ wil_status_last /* keep last */
}; };
...@@ -465,6 +470,9 @@ struct wil_net_stats { ...@@ -465,6 +470,9 @@ struct wil_net_stats {
unsigned long tx_bytes; unsigned long tx_bytes;
unsigned long tx_errors; unsigned long tx_errors;
unsigned long rx_dropped; unsigned long rx_dropped;
unsigned long rx_non_data_frame;
unsigned long rx_short_frame;
unsigned long rx_large_frame;
u16 last_mcs_rx; u16 last_mcs_rx;
u64 rx_per_mcs[WIL_MCS_MAX + 1]; u64 rx_per_mcs[WIL_MCS_MAX + 1];
}; };
...@@ -820,4 +828,6 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime); ...@@ -820,4 +828,6 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_resume(struct wil6210_priv *wil, bool is_runtime); int wil_resume(struct wil6210_priv *wil, bool is_runtime);
void wil_fw_core_dump(struct wil6210_priv *wil);
#endif /* __WIL6210_H__ */ #endif /* __WIL6210_H__ */
/*
* Copyright (c) 2015 Qualcomm Atheros, Inc.
*
* 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 "wil6210.h"
#include <linux/devcoredump.h>
static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil,
u32 *out_dump_size, u32 *out_host_min)
{
int i;
const struct fw_map *map;
u32 host_min, host_max, tmp_max;
if (!out_dump_size)
return -EINVAL;
/* calculate the total size of the unpacked crash dump */
BUILD_BUG_ON(ARRAY_SIZE(fw_mapping) == 0);
map = &fw_mapping[0];
host_min = map->host;
host_max = map->host + (map->to - map->from);
for (i = 1; i < ARRAY_SIZE(fw_mapping); i++) {
map = &fw_mapping[i];
if (map->host < host_min)
host_min = map->host;
tmp_max = map->host + (map->to - map->from);
if (tmp_max > host_max)
host_max = tmp_max;
}
*out_dump_size = host_max - host_min;
if (out_host_min)
*out_host_min = host_min;
return 0;
}
static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest,
u32 size)
{
int i;
const struct fw_map *map;
void *data;
u32 host_min, dump_size, offset, len;
if (wil_fw_get_crash_dump_bounds(wil, &dump_size, &host_min)) {
wil_err(wil, "%s: fail to obtain crash dump size\n", __func__);
return -EINVAL;
}
if (dump_size > size) {
wil_err(wil, "%s: not enough space for dump. Need %d have %d\n",
__func__, dump_size, size);
return -EINVAL;
}
/* copy to crash dump area */
for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
map = &fw_mapping[i];
data = (void * __force)wil->csr + HOSTADDR(map->host);
len = map->to - map->from;
offset = map->host - host_min;
wil_dbg_misc(wil, "%s() - dump %s, size %d, offset %d\n",
__func__, fw_mapping[i].name, len, offset);
wil_memcpy_fromio_32((void * __force)(dest + offset),
(const void __iomem * __force)data, len);
}
return 0;
}
void wil_fw_core_dump(struct wil6210_priv *wil)
{
void *fw_dump_data;
u32 fw_dump_size;
if (wil_fw_get_crash_dump_bounds(wil, &fw_dump_size, NULL)) {
wil_err(wil, "%s: fail to get fw dump size\n", __func__);
return;
}
fw_dump_data = vzalloc(fw_dump_size);
if (!fw_dump_data)
return;
if (wil_fw_copy_crash_dump(wil, fw_dump_data, fw_dump_size)) {
vfree(fw_dump_data);
return;
}
/* fw_dump_data will be free in device coredump release function
* after 5 min
*/
dev_coredumpv(wil_to_dev(wil), fw_dump_data, fw_dump_size, GFP_KERNEL);
wil_info(wil, "%s: fw core dumped, size %d bytes\n", __func__,
fw_dump_size);
}
...@@ -1120,7 +1120,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) ...@@ -1120,7 +1120,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
cmd.sniffer_cfg.phy_support = cmd.sniffer_cfg.phy_support =
cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
? WMI_SNIFFER_CP : WMI_SNIFFER_DP); ? WMI_SNIFFER_CP : WMI_SNIFFER_BOTH_PHYS);
} else { } else {
/* Initialize offload (in non-sniffer mode). /* Initialize offload (in non-sniffer mode).
* Linux IP stack always calculates IP checksum * Linux IP stack always calculates IP checksum
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment