blob: 19f55055d60c12399813a936b5009d7df05f2e71 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002 * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * DOC: wlan_hdd_ipa.c
30 *
31 * WLAN HDD and ipa interface implementation
32 * Originally written by Qualcomm Atheros, Inc
33 */
34
35#ifdef IPA_OFFLOAD
36
37/* Include Files */
38#include <wlan_hdd_includes.h>
39#include <wlan_hdd_ipa.h>
40
41#include <linux/etherdevice.h>
42#include <linux/atomic.h>
43#include <linux/netdevice.h>
44#include <linux/skbuff.h>
45#include <linux/list.h>
46#include <linux/debugfs.h>
47#include <linux/inetdevice.h>
48#include <linux/ip.h>
49#include <wlan_hdd_softap_tx_rx.h>
50#include <ol_txrx_osif_api.h>
Manjunathappa Prakash3454fd62016-04-01 08:52:06 -070051#include <cdp_txrx_peer_ops.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080052
53#include "cds_sched.h"
54
55#include "wma.h"
56#include "wma_api.h"
57
Dhanashri Atreb08959a2016-03-01 17:28:03 -080058#include "cdp_txrx_ipa.h"
59
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080060#define HDD_IPA_DESC_BUFFER_RATIO 4
61#define HDD_IPA_IPV4_NAME_EXT "_ipv4"
62#define HDD_IPA_IPV6_NAME_EXT "_ipv6"
63
64#define HDD_IPA_RX_INACTIVITY_MSEC_DELAY 1000
65#define HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET 12
66#define HDD_IPA_UC_WLAN_8023_HDR_SIZE 14
67/* WDI TX and RX PIPE */
68#define HDD_IPA_UC_NUM_WDI_PIPE 2
69#define HDD_IPA_UC_MAX_PENDING_EVENT 33
70
71#define HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE 32000
72#define HDD_IPA_UC_RT_DEBUG_PERIOD 300
73#define HDD_IPA_UC_RT_DEBUG_BUF_COUNT 30
74#define HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL 10000
75
76#define HDD_IPA_WLAN_HDR_DES_MAC_OFFSET 0
77#define HDD_IPA_MAX_IFACE 3
78#define HDD_IPA_MAX_SYSBAM_PIPE 4
79#define HDD_IPA_RX_PIPE HDD_IPA_MAX_IFACE
80#define HDD_IPA_ENABLE_MASK BIT(0)
81#define HDD_IPA_PRE_FILTER_ENABLE_MASK BIT(1)
82#define HDD_IPA_IPV6_ENABLE_MASK BIT(2)
83#define HDD_IPA_RM_ENABLE_MASK BIT(3)
84#define HDD_IPA_CLK_SCALING_ENABLE_MASK BIT(4)
85#define HDD_IPA_UC_ENABLE_MASK BIT(5)
86#define HDD_IPA_UC_STA_ENABLE_MASK BIT(6)
87#define HDD_IPA_REAL_TIME_DEBUGGING BIT(8)
88
Yun Parkf19e07d2015-11-20 11:34:27 -080089#define HDD_IPA_MAX_PENDING_EVENT_COUNT 20
90
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080091typedef enum {
92 HDD_IPA_UC_OPCODE_TX_SUSPEND = 0,
93 HDD_IPA_UC_OPCODE_TX_RESUME = 1,
94 HDD_IPA_UC_OPCODE_RX_SUSPEND = 2,
95 HDD_IPA_UC_OPCODE_RX_RESUME = 3,
96 HDD_IPA_UC_OPCODE_STATS = 4,
97 /* keep this last */
98 HDD_IPA_UC_OPCODE_MAX
99} hdd_ipa_uc_op_code;
100
101/**
102 * enum - Reason codes for stat query
103 *
104 * @HDD_IPA_UC_STAT_REASON_NONE: Initial value
105 * @HDD_IPA_UC_STAT_REASON_DEBUG: For debug/info
106 * @HDD_IPA_UC_STAT_REASON_BW_CAL: For bandwidth calibration
107 */
108enum {
109 HDD_IPA_UC_STAT_REASON_NONE,
110 HDD_IPA_UC_STAT_REASON_DEBUG,
111 HDD_IPA_UC_STAT_REASON_BW_CAL
112};
113
114/**
115 * enum hdd_ipa_rm_state - IPA resource manager state
116 * @HDD_IPA_RM_RELEASED: PROD pipe resource released
117 * @HDD_IPA_RM_GRANT_PENDING: PROD pipe resource requested but not granted yet
118 * @HDD_IPA_RM_GRANTED: PROD pipe resource granted
119 */
120enum hdd_ipa_rm_state {
121 HDD_IPA_RM_RELEASED,
122 HDD_IPA_RM_GRANT_PENDING,
123 HDD_IPA_RM_GRANTED,
124};
125
126struct llc_snap_hdr {
127 uint8_t dsap;
128 uint8_t ssap;
129 uint8_t resv[4];
130 __be16 eth_type;
131} __packed;
132
Leo Chang3bc8fed2015-11-13 10:59:47 -0800133/**
134 * struct hdd_ipa_tx_hdr - header type which IPA should handle to TX packet
135 * @eth: ether II header
136 * @llc_snap: LLC snap header
137 *
138 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800139struct hdd_ipa_tx_hdr {
140 struct ethhdr eth;
141 struct llc_snap_hdr llc_snap;
142} __packed;
143
Leo Chang3bc8fed2015-11-13 10:59:47 -0800144/**
145 * struct frag_header - fragment header type registered to IPA hardware
146 * @length: fragment length
147 * @reserved1: Reserved not used
148 * @reserved2: Reserved not used
149 *
150 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800151struct frag_header {
Leo Chang3bc8fed2015-11-13 10:59:47 -0800152 uint16_t length;
153 uint32_t reserved1;
154 uint32_t reserved2;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800155} __packed;
156
Leo Chang3bc8fed2015-11-13 10:59:47 -0800157/**
158 * struct ipa_header - ipa header type registered to IPA hardware
159 * @vdev_id: vdev id
160 * @reserved: Reserved not used
161 *
162 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800163struct ipa_header {
164 uint32_t
165 vdev_id:8, /* vdev_id field is LSB of IPA DESC */
166 reserved:24;
167} __packed;
168
Leo Chang3bc8fed2015-11-13 10:59:47 -0800169/**
170 * struct hdd_ipa_uc_tx_hdr - full tx header registered to IPA hardware
171 * @frag_hd: fragment header
172 * @ipa_hd: ipa header
173 * @eth: ether II header
174 *
175 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800176struct hdd_ipa_uc_tx_hdr {
177 struct frag_header frag_hd;
178 struct ipa_header ipa_hd;
179 struct ethhdr eth;
180} __packed;
181
182#define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header)
183#define HDD_IPA_WLAN_IPA_HEADER sizeof(struct frag_header)
184
185/**
186 * struct hdd_ipa_cld_hdr - IPA CLD Header
187 * @reserved: reserved fields
188 * @iface_id: interface ID
189 * @sta_id: Station ID
190 *
191 * Packed 32-bit structure
192 * +----------+----------+--------------+--------+
193 * | Reserved | QCMAP ID | interface id | STA ID |
194 * +----------+----------+--------------+--------+
195 */
196struct hdd_ipa_cld_hdr {
197 uint8_t reserved[2];
198 uint8_t iface_id;
199 uint8_t sta_id;
200} __packed;
201
202struct hdd_ipa_rx_hdr {
203 struct hdd_ipa_cld_hdr cld_hdr;
204 struct ethhdr eth;
205} __packed;
206
207struct hdd_ipa_pm_tx_cb {
208 struct hdd_ipa_iface_context *iface_context;
209 struct ipa_rx_data *ipa_tx_desc;
210};
211
212struct hdd_ipa_uc_rx_hdr {
213 struct ethhdr eth;
214} __packed;
215
216struct hdd_ipa_sys_pipe {
217 uint32_t conn_hdl;
218 uint8_t conn_hdl_valid;
219 struct ipa_sys_connect_params ipa_sys_params;
220};
221
222struct hdd_ipa_iface_stats {
223 uint64_t num_tx;
224 uint64_t num_tx_drop;
225 uint64_t num_tx_err;
226 uint64_t num_tx_cac_drop;
227 uint64_t num_rx_prefilter;
228 uint64_t num_rx_ipa_excep;
229 uint64_t num_rx_recv;
230 uint64_t num_rx_recv_mul;
231 uint64_t num_rx_send_desc_err;
232 uint64_t max_rx_mul;
233};
234
235struct hdd_ipa_priv;
236
237struct hdd_ipa_iface_context {
238 struct hdd_ipa_priv *hdd_ipa;
239 hdd_adapter_t *adapter;
240 void *tl_context;
241
242 enum ipa_client_type cons_client;
243 enum ipa_client_type prod_client;
244
245 uint8_t iface_id; /* This iface ID */
246 uint8_t sta_id; /* This iface station ID */
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530247 qdf_spinlock_t interface_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800248 uint32_t ifa_address;
249 struct hdd_ipa_iface_stats stats;
250};
251
252struct hdd_ipa_stats {
253 uint32_t event[IPA_WLAN_EVENT_MAX];
254 uint64_t num_send_msg;
255 uint64_t num_free_msg;
256
257 uint64_t num_rm_grant;
258 uint64_t num_rm_release;
259 uint64_t num_rm_grant_imm;
260 uint64_t num_cons_perf_req;
261 uint64_t num_prod_perf_req;
262
263 uint64_t num_rx_drop;
264 uint64_t num_rx_ipa_tx_dp;
265 uint64_t num_rx_ipa_splice;
266 uint64_t num_rx_ipa_loop;
267 uint64_t num_rx_ipa_tx_dp_err;
268 uint64_t num_rx_ipa_write_done;
269 uint64_t num_max_ipa_tx_mul;
270 uint64_t num_rx_ipa_hw_maxed_out;
271 uint64_t max_pend_q_cnt;
272
273 uint64_t num_tx_comp_cnt;
274 uint64_t num_tx_queued;
275 uint64_t num_tx_dequeued;
276 uint64_t num_max_pm_queue;
277
278 uint64_t num_freeq_empty;
279 uint64_t num_pri_freeq_empty;
280 uint64_t num_rx_excep;
281 uint64_t num_tx_bcmc;
282 uint64_t num_tx_bcmc_err;
283};
284
285struct ipa_uc_stas_map {
286 bool is_reserved;
287 uint8_t sta_id;
288};
289struct op_msg_type {
290 uint8_t msg_t;
291 uint8_t rsvd;
292 uint16_t op_code;
293 uint16_t len;
294 uint16_t rsvd_snd;
295};
296
297struct ipa_uc_fw_stats {
298 uint32_t tx_comp_ring_base;
299 uint32_t tx_comp_ring_size;
300 uint32_t tx_comp_ring_dbell_addr;
301 uint32_t tx_comp_ring_dbell_ind_val;
302 uint32_t tx_comp_ring_dbell_cached_val;
303 uint32_t tx_pkts_enqueued;
304 uint32_t tx_pkts_completed;
305 uint32_t tx_is_suspend;
306 uint32_t tx_reserved;
307 uint32_t rx_ind_ring_base;
308 uint32_t rx_ind_ring_size;
309 uint32_t rx_ind_ring_dbell_addr;
310 uint32_t rx_ind_ring_dbell_ind_val;
311 uint32_t rx_ind_ring_dbell_ind_cached_val;
312 uint32_t rx_ind_ring_rdidx_addr;
313 uint32_t rx_ind_ring_rd_idx_cached_val;
314 uint32_t rx_refill_idx;
315 uint32_t rx_num_pkts_indicated;
316 uint32_t rx_buf_refilled;
317 uint32_t rx_num_ind_drop_no_space;
318 uint32_t rx_num_ind_drop_no_buf;
319 uint32_t rx_is_suspend;
320 uint32_t rx_reserved;
321};
322
323struct ipa_uc_pending_event {
Anurag Chouhanffb21542016-02-17 14:33:03 +0530324 qdf_list_node_t node;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800325 hdd_adapter_t *adapter;
326 enum ipa_wlan_event type;
327 uint8_t sta_id;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530328 uint8_t mac_addr[QDF_MAC_ADDR_SIZE];
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800329};
330
331/**
332 * struct uc_rm_work_struct
333 * @work: uC RM work
334 * @event: IPA RM event
335 */
336struct uc_rm_work_struct {
337 struct work_struct work;
338 enum ipa_rm_event event;
339};
340
341/**
342 * struct uc_op_work_struct
343 * @work: uC OP work
344 * @msg: OP message
345 */
346struct uc_op_work_struct {
347 struct work_struct work;
348 struct op_msg_type *msg;
349};
350static uint8_t vdev_to_iface[CSR_ROAM_SESSION_MAX];
351
352/**
353 * struct uc_rt_debug_info
354 * @time: system time
355 * @ipa_excep_count: IPA exception packet count
356 * @rx_drop_count: IPA Rx drop packet count
357 * @net_sent_count: IPA Rx packet sent to network stack count
358 * @rx_discard_count: IPA Rx discard packet count
359 * @rx_mcbc_count: IPA Rx BCMC packet count
360 * @tx_mcbc_count: IPA Tx BCMC packet countt
361 * @tx_fwd_count: IPA Tx forward packet count
362 * @rx_destructor_call: IPA Rx packet destructor count
363 */
364struct uc_rt_debug_info {
Anurag Chouhan6d760662016-02-20 16:05:43 +0530365 unsigned long time;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800366 uint64_t ipa_excep_count;
367 uint64_t rx_drop_count;
368 uint64_t net_sent_count;
369 uint64_t rx_discard_count;
370 uint64_t rx_mcbc_count;
371 uint64_t tx_mcbc_count;
372 uint64_t tx_fwd_count;
373 uint64_t rx_destructor_call;
374};
375
376struct hdd_ipa_priv {
377 struct hdd_ipa_sys_pipe sys_pipe[HDD_IPA_MAX_SYSBAM_PIPE];
378 struct hdd_ipa_iface_context iface_context[HDD_IPA_MAX_IFACE];
379 uint8_t num_iface;
380 enum hdd_ipa_rm_state rm_state;
381 /*
Nirav Shahcbc6d722016-03-01 16:24:53 +0530382 * IPA driver can send RM notifications with IRQ disabled so using qdf
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800383 * APIs as it is taken care gracefully. Without this, kernel would throw
384 * an warning if spin_lock_bh is used while IRQ is disabled
385 */
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530386 qdf_spinlock_t rm_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800387 struct uc_rm_work_struct uc_rm_work;
388 struct uc_op_work_struct uc_op_work[HDD_IPA_UC_OPCODE_MAX];
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530389 qdf_wake_lock_t wake_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800390 struct delayed_work wake_lock_work;
391 bool wake_lock_released;
392
393 enum ipa_client_type prod_client;
394
395 atomic_t tx_ref_cnt;
Nirav Shahcbc6d722016-03-01 16:24:53 +0530396 qdf_nbuf_queue_t pm_queue_head;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800397 struct work_struct pm_work;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530398 qdf_spinlock_t pm_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800399 bool suspended;
400
401 uint32_t pending_hw_desc_cnt;
402 uint32_t hw_desc_cnt;
403 spinlock_t q_lock;
404 uint32_t freeq_cnt;
405 struct list_head free_desc_head;
406
407 uint32_t pend_q_cnt;
408 struct list_head pend_desc_head;
409
410 hdd_context_t *hdd_ctx;
411
412 struct dentry *debugfs_dir;
413 struct hdd_ipa_stats stats;
414
415 struct notifier_block ipv4_notifier;
416 uint32_t curr_prod_bw;
417 uint32_t curr_cons_bw;
418
419 uint8_t activated_fw_pipe;
420 uint8_t sap_num_connected_sta;
421 uint8_t sta_connected;
422 uint32_t tx_pipe_handle;
423 uint32_t rx_pipe_handle;
424 bool resource_loading;
425 bool resource_unloading;
426 bool pending_cons_req;
427 struct ipa_uc_stas_map assoc_stas_map[WLAN_MAX_STA_COUNT];
Anurag Chouhanffb21542016-02-17 14:33:03 +0530428 qdf_list_t pending_event;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530429 qdf_mutex_t event_lock;
Leo Change3e49442015-10-26 20:07:13 -0700430 bool ipa_pipes_down;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800431 uint32_t ipa_tx_packets_diff;
432 uint32_t ipa_rx_packets_diff;
433 uint32_t ipa_p_tx_packets;
434 uint32_t ipa_p_rx_packets;
435 uint32_t stat_req_reason;
436 uint64_t ipa_tx_forward;
437 uint64_t ipa_rx_discard;
438 uint64_t ipa_rx_net_send_count;
439 uint64_t ipa_rx_internel_drop_count;
440 uint64_t ipa_rx_destructor_count;
Anurag Chouhan210db072016-02-22 18:42:15 +0530441 qdf_mc_timer_t rt_debug_timer;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800442 struct uc_rt_debug_info rt_bug_buffer[HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
443 unsigned int rt_buf_fill_index;
Anurag Chouhan210db072016-02-22 18:42:15 +0530444 qdf_mc_timer_t rt_debug_fill_timer;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530445 qdf_mutex_t rt_debug_lock;
446 qdf_mutex_t ipa_lock;
Dhanashri Atreb08959a2016-03-01 17:28:03 -0800447 struct ol_txrx_ipa_resources ipa_resource;
Leo Chang3bc8fed2015-11-13 10:59:47 -0800448 /* IPA UC doorbell registers paddr */
Anurag Chouhan6d760662016-02-20 16:05:43 +0530449 qdf_dma_addr_t tx_comp_doorbell_paddr;
450 qdf_dma_addr_t rx_ready_doorbell_paddr;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800451};
452
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800453/**
454 * FIXME: The following conversion routines will are just stubs.
455 * They will be implemented fully by another update.
456 * The stubs will let the compile go ahead, and functionality
457 * is broken.
458 * This should be OK and IPA is not enabled yet
459 */
460void *wlan_hdd_stub_priv_to_addr(uint32_t priv)
461{
462 void *vaddr;
463 uint32_t ipa_priv = priv;
464
465 vaddr = &ipa_priv; /* just to use the var */
466 vaddr = NULL;
467 return vaddr;
468}
469
470uint32_t wlan_hdd_stub_addr_to_priv(void *ptr)
471{
472 uint32_t ipa_priv = 0;
473
474 BUG_ON(ptr == NULL);
475 return ipa_priv;
476}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800477#define HDD_IPA_WLAN_CLD_HDR_LEN sizeof(struct hdd_ipa_cld_hdr)
478#define HDD_IPA_UC_WLAN_CLD_HDR_LEN 0
479#define HDD_IPA_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_tx_hdr)
480#define HDD_IPA_UC_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_uc_tx_hdr)
481#define HDD_IPA_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_rx_hdr)
482#define HDD_IPA_UC_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_uc_rx_hdr)
483
Leo Chang3bc8fed2015-11-13 10:59:47 -0800484#define HDD_IPA_FW_RX_DESC_DISCARD_M 0x1
485#define HDD_IPA_FW_RX_DESC_FORWARD_M 0x2
486
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800487#define HDD_IPA_GET_IFACE_ID(_data) \
488 (((struct hdd_ipa_cld_hdr *) (_data))->iface_id)
489
490#define HDD_IPA_LOG(LVL, fmt, args ...) \
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530491 QDF_TRACE(QDF_MODULE_ID_HDD, LVL, \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800492 "%s:%d: "fmt, __func__, __LINE__, ## args)
493
494#define HDD_IPA_DBG_DUMP(_lvl, _prefix, _buf, _len) \
495 do { \
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530496 QDF_TRACE(QDF_MODULE_ID_HDD, _lvl, "%s:", _prefix); \
497 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, _lvl, _buf, _len); \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800498 } while (0)
499
500#define HDD_IPA_IS_CONFIG_ENABLED(_hdd_ctx, _mask) \
501 (((_hdd_ctx)->config->IpaConfig & (_mask)) == (_mask))
502
503#define HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa) \
504 do { \
505 hdd_ipa->ipa_rx_internel_drop_count++; \
506 } while (0)
507#define HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa) \
508 do { \
509 hdd_ipa->ipa_rx_net_send_count++; \
510 } while (0)
511#define HDD_BW_GET_DIFF(_x, _y) (unsigned long)((ULONG_MAX - (_y)) + (_x) + 1)
512
Leo Chang3bc8fed2015-11-13 10:59:47 -0800513/* Temporary macro to make a build without IPA V2 */
514#ifdef IPA_V2
Dhanashri Atreb08959a2016-03-01 17:28:03 -0800515#define HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt) \
516do { \
517 pipe_in.u.ul.rdy_ring_rp_va = \
518 ipa_ctxt->ipa_resource.rx_proc_done_idx_vaddr; \
519 pipe_in.u.ul.rdy_comp_ring_base_pa = \
520 ipa_ctxt->ipa_resource.rx2_rdy_ring_base_paddr;\
521 pipe_in.u.ul.rdy_comp_ring_size = \
522 ipa_ctxt->ipa_resource.rx2_rdy_ring_size; \
523 pipe_in.u.ul.rdy_comp_ring_wp_pa = \
524 ipa_ctxt->ipa_resource.rx2_proc_done_idx_paddr; \
525 pipe_in.u.ul.rdy_comp_ring_wp_va = \
526 ipa_ctxt->ipa_resource.rx2_proc_done_idx_vaddr; \
Leo Chang3bc8fed2015-11-13 10:59:47 -0800527} while (0)
528#else
529/* Do nothing */
530#define HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt)
531#endif /* IPA_V2 */
532
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800533static struct hdd_ipa_adapter_2_client {
534 enum ipa_client_type cons_client;
535 enum ipa_client_type prod_client;
536} hdd_ipa_adapter_2_client[HDD_IPA_MAX_IFACE] = {
537 {
538 IPA_CLIENT_WLAN2_CONS, IPA_CLIENT_WLAN1_PROD
539 }, {
540 IPA_CLIENT_WLAN3_CONS, IPA_CLIENT_WLAN1_PROD
541 }, {
542 IPA_CLIENT_WLAN4_CONS, IPA_CLIENT_WLAN1_PROD
543 },
544};
545
546/* For Tx pipes, use Ethernet-II Header format */
547struct hdd_ipa_uc_tx_hdr ipa_uc_tx_hdr = {
548 {
Leo Chang3bc8fed2015-11-13 10:59:47 -0800549 0x0000,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800550 0x00000000,
551 0x00000000
552 },
553 {
554 0x00000000
555 },
556 {
557 {0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc},
558 {0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff},
559 0x0008
560 }
561};
562
563/* For Tx pipes, use 802.3 Header format */
564static struct hdd_ipa_tx_hdr ipa_tx_hdr = {
565 {
566 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
567 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
568 0x00 /* length can be zero */
569 },
570 {
571 /* LLC SNAP header 8 bytes */
572 0xaa, 0xaa,
573 {0x03, 0x00, 0x00, 0x00},
574 0x0008 /* type value(2 bytes) ,filled by wlan */
575 /* 0x0800 - IPV4, 0x86dd - IPV6 */
576 }
577};
578
579static const char *op_string[] = {
580 "TX_SUSPEND",
581 "TX_RESUME",
582 "RX_SUSPEND",
583 "RX_RESUME",
584 "STATS",
585};
586
587static struct hdd_ipa_priv *ghdd_ipa;
588
589/* Local Function Prototypes */
590static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
591 unsigned long data);
592static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
593 unsigned long data);
594
595static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context);
596
597/**
598 * hdd_ipa_is_enabled() - Is IPA enabled?
599 * @hdd_ctx: Global HDD context
600 *
601 * Return: true if IPA is enabled, false otherwise
602 */
603bool hdd_ipa_is_enabled(hdd_context_t *hdd_ctx)
604{
605 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_ENABLE_MASK);
606}
607
608/**
609 * hdd_ipa_uc_is_enabled() - Is IPA uC offload enabled?
610 * @hdd_ctx: Global HDD context
611 *
612 * Return: true if IPA uC offload is enabled, false otherwise
613 */
614bool hdd_ipa_uc_is_enabled(hdd_context_t *hdd_ctx)
615{
616 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_ENABLE_MASK);
617}
618
619/**
620 * hdd_ipa_uc_sta_is_enabled() - Is STA mode IPA uC offload enabled?
621 * @hdd_ctx: Global HDD context
622 *
623 * Return: true if STA mode IPA uC offload is enabled, false otherwise
624 */
625static inline bool hdd_ipa_uc_sta_is_enabled(hdd_context_t *hdd_ctx)
626{
627 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_STA_ENABLE_MASK);
628}
629
630/**
631 * hdd_ipa_is_pre_filter_enabled() - Is IPA pre-filter enabled?
632 * @hdd_ipa: Global HDD IPA context
633 *
634 * Return: true if pre-filter is enabled, otherwise false
635 */
636static inline bool hdd_ipa_is_pre_filter_enabled(hdd_context_t *hdd_ctx)
637{
638 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
639 HDD_IPA_PRE_FILTER_ENABLE_MASK);
640}
641
642/**
643 * hdd_ipa_is_ipv6_enabled() - Is IPA IPv6 enabled?
644 * @hdd_ipa: Global HDD IPA context
645 *
646 * Return: true if IPv6 is enabled, otherwise false
647 */
648static inline bool hdd_ipa_is_ipv6_enabled(hdd_context_t *hdd_ctx)
649{
650 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_IPV6_ENABLE_MASK);
651}
652
653/**
654 * hdd_ipa_is_rm_enabled() - Is IPA resource manager enabled?
655 * @hdd_ipa: Global HDD IPA context
656 *
657 * Return: true if resource manager is enabled, otherwise false
658 */
659static inline bool hdd_ipa_is_rm_enabled(hdd_context_t *hdd_ctx)
660{
661 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_RM_ENABLE_MASK);
662}
663
664/**
665 * hdd_ipa_is_rt_debugging_enabled() - Is IPA real-time debug enabled?
666 * @hdd_ipa: Global HDD IPA context
667 *
668 * Return: true if resource manager is enabled, otherwise false
669 */
670static inline bool hdd_ipa_is_rt_debugging_enabled(hdd_context_t *hdd_ctx)
671{
672 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_REAL_TIME_DEBUGGING);
673}
674
675/**
676 * hdd_ipa_is_clk_scaling_enabled() - Is IPA clock scaling enabled?
677 * @hdd_ipa: Global HDD IPA context
678 *
679 * Return: true if clock scaling is enabled, otherwise false
680 */
681static inline bool hdd_ipa_is_clk_scaling_enabled(hdd_context_t *hdd_ctx)
682{
683 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
684 HDD_IPA_CLK_SCALING_ENABLE_MASK |
685 HDD_IPA_RM_ENABLE_MASK);
686}
687
688/**
689 * hdd_ipa_uc_rt_debug_host_fill - fill rt debug buffer
690 * @ctext: pointer to hdd context.
691 *
692 * If rt debug enabled, periodically called, and fill debug buffer
693 *
694 * Return: none
695 */
696static void hdd_ipa_uc_rt_debug_host_fill(void *ctext)
697{
698 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
699 struct hdd_ipa_priv *hdd_ipa;
700 struct uc_rt_debug_info *dump_info = NULL;
701
702 if (wlan_hdd_validate_context(hdd_ctx))
703 return;
704
705 if (!hdd_ctx->hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530706 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800707 "%s: IPA UC is not enabled", __func__);
708 return;
709 }
710
711 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
712
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530713 qdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800714 dump_info = &hdd_ipa->rt_bug_buffer[
715 hdd_ipa->rt_buf_fill_index % HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
716
Anurag Chouhan210db072016-02-22 18:42:15 +0530717 dump_info->time = qdf_mc_timer_get_system_time();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800718 dump_info->ipa_excep_count = hdd_ipa->stats.num_rx_excep;
719 dump_info->rx_drop_count = hdd_ipa->ipa_rx_internel_drop_count;
720 dump_info->net_sent_count = hdd_ipa->ipa_rx_net_send_count;
721 dump_info->rx_discard_count = hdd_ipa->ipa_rx_discard;
722 dump_info->tx_mcbc_count = hdd_ipa->stats.num_tx_bcmc;
723 dump_info->tx_fwd_count = hdd_ipa->ipa_tx_forward;
724 dump_info->rx_destructor_call = hdd_ipa->ipa_rx_destructor_count;
725 hdd_ipa->rt_buf_fill_index++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530726 qdf_mutex_release(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800727
Anurag Chouhan210db072016-02-22 18:42:15 +0530728 qdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800729 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
730}
731
732/**
733 * hdd_ipa_uc_rt_debug_host_dump - dump rt debug buffer
734 * @hdd_ctx: pointer to hdd context.
735 *
736 * If rt debug enabled, dump debug buffer contents based on requirement
737 *
738 * Return: none
739 */
740void hdd_ipa_uc_rt_debug_host_dump(hdd_context_t *hdd_ctx)
741{
742 struct hdd_ipa_priv *hdd_ipa;
743 unsigned int dump_count;
744 unsigned int dump_index;
745 struct uc_rt_debug_info *dump_info = NULL;
746
747 if (wlan_hdd_validate_context(hdd_ctx))
748 return;
749
750 hdd_ipa = hdd_ctx->hdd_ipa;
751 if (!hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530752 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800753 "%s: IPA UC is not enabled", __func__);
754 return;
755 }
756
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530757 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800758 "========= WLAN-IPA DEBUG BUF DUMP ==========\n");
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530759 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800760 " TM : EXEP : DROP : NETS : MCBC : TXFD : DSTR : DSCD\n");
761
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530762 qdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800763 for (dump_count = 0;
764 dump_count < HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
765 dump_count++) {
766 dump_index = (hdd_ipa->rt_buf_fill_index + dump_count) %
767 HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
768 dump_info = &hdd_ipa->rt_bug_buffer[dump_index];
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530769 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800770 "%12lu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu\n",
771 dump_info->time, dump_info->ipa_excep_count,
772 dump_info->rx_drop_count, dump_info->net_sent_count,
773 dump_info->tx_mcbc_count, dump_info->tx_fwd_count,
774 dump_info->rx_destructor_call,
775 dump_info->rx_discard_count);
776 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530777 qdf_mutex_release(&hdd_ipa->rt_debug_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530778 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800779 "======= WLAN-IPA DEBUG BUF DUMP END ========\n");
780}
781
782/**
783 * hdd_ipa_uc_rt_debug_handler - periodic memory health monitor handler
784 * @ctext: pointer to hdd context.
785 *
786 * periodically called by timer expire
787 * will try to alloc dummy memory and detect out of memory condition
788 * if out of memory detected, dump wlan-ipa stats
789 *
790 * Return: none
791 */
792static void hdd_ipa_uc_rt_debug_handler(void *ctext)
793{
794 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
795 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
796 void *dummy_ptr = NULL;
797
798 if (wlan_hdd_validate_context(hdd_ctx))
799 return;
800
801 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530802 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800803 "%s: IPA RT debug is not enabled", __func__);
804 return;
805 }
806
807 /* Allocate dummy buffer periodically and free immediately. this will
808 * proactively detect OOM and if allocation fails dump ipa stats
809 */
810 dummy_ptr = kmalloc(HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE,
811 GFP_KERNEL | GFP_ATOMIC);
812 if (!dummy_ptr) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530813 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800814 "%s: Dummy alloc fail", __func__);
815 hdd_ipa_uc_rt_debug_host_dump(hdd_ctx);
816 hdd_ipa_uc_stat_request(
Krunal Sonibe766b02016-03-10 13:00:44 -0800817 hdd_get_adapter(hdd_ctx, QDF_SAP_MODE), 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800818 } else {
819 kfree(dummy_ptr);
820 }
821
Anurag Chouhan210db072016-02-22 18:42:15 +0530822 qdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800823 HDD_IPA_UC_RT_DEBUG_PERIOD);
824}
825
826/**
827 * hdd_ipa_uc_rt_debug_destructor - called by data packet free
828 * @skb: packet pinter
829 *
830 * when free data packet, will be invoked by wlan client and will increase
831 * free counter
832 *
833 * Return: none
834 */
835void hdd_ipa_uc_rt_debug_destructor(struct sk_buff *skb)
836{
837 if (!ghdd_ipa) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530838 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800839 "%s: invalid hdd context", __func__);
840 return;
841 }
842
843 ghdd_ipa->ipa_rx_destructor_count++;
844}
845
846/**
847 * hdd_ipa_uc_rt_debug_deinit - remove resources to handle rt debugging
848 * @hdd_ctx: hdd main context
849 *
850 * free all rt debugging resources
851 *
852 * Return: none
853 */
854static void hdd_ipa_uc_rt_debug_deinit(hdd_context_t *hdd_ctx)
855{
856 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
857
Anurag Chouhan210db072016-02-22 18:42:15 +0530858 if (QDF_TIMER_STATE_STOPPED !=
859 qdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_fill_timer)) {
860 qdf_mc_timer_stop(&hdd_ipa->rt_debug_fill_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800861 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530862 qdf_mc_timer_destroy(&hdd_ipa->rt_debug_fill_timer);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530863 qdf_mutex_destroy(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800864
865 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530866 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800867 "%s: IPA RT debug is not enabled", __func__);
868 return;
869 }
870
Anurag Chouhan210db072016-02-22 18:42:15 +0530871 if (QDF_TIMER_STATE_STOPPED !=
872 qdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_timer)) {
873 qdf_mc_timer_stop(&hdd_ipa->rt_debug_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800874 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530875 qdf_mc_timer_destroy(&hdd_ipa->rt_debug_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800876}
877
878/**
879 * hdd_ipa_uc_rt_debug_init - intialize resources to handle rt debugging
880 * @hdd_ctx: hdd main context
881 *
882 * alloc and initialize all rt debugging resources
883 *
884 * Return: none
885 */
886static void hdd_ipa_uc_rt_debug_init(hdd_context_t *hdd_ctx)
887{
888 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
889
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530890 qdf_mutex_create(&hdd_ipa->rt_debug_lock);
Anurag Chouhan210db072016-02-22 18:42:15 +0530891 qdf_mc_timer_init(&hdd_ipa->rt_debug_fill_timer, QDF_TIMER_TYPE_SW,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800892 hdd_ipa_uc_rt_debug_host_fill, (void *)hdd_ctx);
893 hdd_ipa->rt_buf_fill_index = 0;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530894 qdf_mem_zero(hdd_ipa->rt_bug_buffer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800895 sizeof(struct uc_rt_debug_info) *
896 HDD_IPA_UC_RT_DEBUG_BUF_COUNT);
897 hdd_ipa->ipa_tx_forward = 0;
898 hdd_ipa->ipa_rx_discard = 0;
899 hdd_ipa->ipa_rx_net_send_count = 0;
900 hdd_ipa->ipa_rx_internel_drop_count = 0;
901 hdd_ipa->ipa_rx_destructor_count = 0;
902
Anurag Chouhan210db072016-02-22 18:42:15 +0530903 qdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800904 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
905
906 /* Reatime debug enable on feature enable */
907 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530908 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800909 "%s: IPA RT debug is not enabled", __func__);
910 return;
911 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530912 qdf_mc_timer_init(&hdd_ipa->rt_debug_timer, QDF_TIMER_TYPE_SW,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800913 hdd_ipa_uc_rt_debug_handler, (void *)hdd_ctx);
Anurag Chouhan210db072016-02-22 18:42:15 +0530914 qdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800915 HDD_IPA_UC_RT_DEBUG_PERIOD);
916
917}
918
919/**
920 * hdd_ipa_uc_stat_query() - Query the IPA stats
921 * @hdd_ctx: Global HDD context
922 * @ipa_tx_diff: tx packet count diff from previous
923 * tx packet count
924 * @ipa_rx_diff: rx packet count diff from previous
925 * rx packet count
926 *
927 * Return: true if IPA is enabled, false otherwise
928 */
929void hdd_ipa_uc_stat_query(hdd_context_t *pHddCtx,
930 uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff)
931{
932 struct hdd_ipa_priv *hdd_ipa;
933
934 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
935 *ipa_tx_diff = 0;
936 *ipa_rx_diff = 0;
937
938 if (!hdd_ipa_is_enabled(pHddCtx) ||
939 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
940 return;
941 }
942
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530943 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800944 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
945 (false == hdd_ipa->resource_loading)) {
946 *ipa_tx_diff = hdd_ipa->ipa_tx_packets_diff;
947 *ipa_rx_diff = hdd_ipa->ipa_rx_packets_diff;
948 HDD_IPA_LOG(LOG1, "STAT Query TX DIFF %d, RX DIFF %d",
949 *ipa_tx_diff, *ipa_rx_diff);
950 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530951 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800952 return;
953}
954
955/**
956 * hdd_ipa_uc_stat_request() - Get IPA stats from IPA.
957 * @adapter: network adapter
958 * @reason: STAT REQ Reason
959 *
960 * Return: None
961 */
962void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason)
963{
964 hdd_context_t *pHddCtx;
965 struct hdd_ipa_priv *hdd_ipa;
966
967 if (!adapter) {
968 return;
969 }
970
971 pHddCtx = (hdd_context_t *)adapter->pHddCtx;
972 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
973 if (!hdd_ipa_is_enabled(pHddCtx) ||
974 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
975 return;
976 }
977
978 HDD_IPA_LOG(LOG1, "STAT REQ Reason %d", reason);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530979 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800980 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
981 (false == hdd_ipa->resource_loading)) {
982 hdd_ipa->stat_req_reason = reason;
983 wma_cli_set_command(
984 (int)adapter->sessionId,
985 (int)WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID,
986 0, VDEV_CMD);
987 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530988 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800989}
990
991/**
992 * hdd_ipa_uc_find_add_assoc_sta() - Find associated station
993 * @hdd_ipa: Global HDD IPA context
994 * @sta_add: Should station be added
995 * @sta_id: ID of the station being queried
996 *
997 * Return: true if the station was found
998 */
999static bool hdd_ipa_uc_find_add_assoc_sta(struct hdd_ipa_priv *hdd_ipa,
1000 bool sta_add, uint8_t sta_id)
1001{
1002 bool sta_found = false;
1003 uint8_t idx;
1004 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1005 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
1006 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
1007 sta_found = true;
1008 break;
1009 }
1010 }
1011 if (sta_add && sta_found) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301012 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001013 "%s: STA ID %d already exist, cannot add",
1014 __func__, sta_id);
1015 return sta_found;
1016 }
1017 if (sta_add) {
1018 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1019 if (!hdd_ipa->assoc_stas_map[idx].is_reserved) {
1020 hdd_ipa->assoc_stas_map[idx].is_reserved = true;
1021 hdd_ipa->assoc_stas_map[idx].sta_id = sta_id;
1022 return sta_found;
1023 }
1024 }
1025 }
1026 if (!sta_add && !sta_found) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301027 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001028 "%s: STA ID %d does not exist, cannot delete",
1029 __func__, sta_id);
1030 return sta_found;
1031 }
1032 if (!sta_add) {
1033 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1034 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
1035 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
1036 hdd_ipa->assoc_stas_map[idx].is_reserved =
1037 false;
1038 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
1039 return sta_found;
1040 }
1041 }
1042 }
1043 return sta_found;
1044}
1045
1046/**
1047 * hdd_ipa_uc_enable_pipes() - Enable IPA uC pipes
1048 * @hdd_ipa: Global HDD IPA context
1049 *
1050 * Return: 0 on success, negative errno if error
1051 */
1052static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa)
1053{
1054 int result;
1055 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
1056
1057 /* ACTIVATE TX PIPE */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301058 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001059 "%s: Enable TX PIPE(tx_pipe_handle=%d)",
1060 __func__, hdd_ipa->tx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001061 result = ipa_enable_wdi_pipe(hdd_ipa->tx_pipe_handle);
1062 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301063 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001064 "%s: Enable TX PIPE fail, code %d",
1065 __func__, result);
1066 return result;
1067 }
1068 result = ipa_resume_wdi_pipe(hdd_ipa->tx_pipe_handle);
1069 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301070 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001071 "%s: Resume TX PIPE fail, code %d",
1072 __func__, result);
1073 return result;
1074 }
1075 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, true);
1076
1077 /* ACTIVATE RX PIPE */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301078 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001079 "%s: Enable RX PIPE(rx_pipe_handle=%d)",
1080 __func__, hdd_ipa->rx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001081 result = ipa_enable_wdi_pipe(hdd_ipa->rx_pipe_handle);
1082 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301083 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001084 "%s: Enable RX PIPE fail, code %d",
1085 __func__, result);
1086 return result;
1087 }
1088 result = ipa_resume_wdi_pipe(hdd_ipa->rx_pipe_handle);
1089 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301090 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001091 "%s: Resume RX PIPE fail, code %d",
1092 __func__, result);
1093 return result;
1094 }
1095 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, false);
Leo Change3e49442015-10-26 20:07:13 -07001096 hdd_ipa->ipa_pipes_down = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001097 return 0;
1098}
1099
1100/**
1101 * hdd_ipa_uc_disable_pipes() - Disable IPA uC pipes
1102 * @hdd_ipa: Global HDD IPA context
1103 *
1104 * Return: 0 on success, negative errno if error
1105 */
1106static int hdd_ipa_uc_disable_pipes(struct hdd_ipa_priv *hdd_ipa)
1107{
1108 int result;
1109
Leo Change3e49442015-10-26 20:07:13 -07001110 hdd_ipa->ipa_pipes_down = true;
1111
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301112 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable RX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001113 result = ipa_suspend_wdi_pipe(hdd_ipa->rx_pipe_handle);
1114 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301115 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001116 "%s: Suspend RX PIPE fail, code %d",
1117 __func__, result);
1118 return result;
1119 }
1120 result = ipa_disable_wdi_pipe(hdd_ipa->rx_pipe_handle);
1121 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301122 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001123 "%s: Disable RX PIPE fail, code %d",
1124 __func__, result);
1125 return result;
1126 }
1127
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301128 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable TX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001129 result = ipa_suspend_wdi_pipe(hdd_ipa->tx_pipe_handle);
1130 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301131 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001132 "%s: Suspend TX PIPE fail, code %d",
1133 __func__, result);
1134 return result;
1135 }
1136 result = ipa_disable_wdi_pipe(hdd_ipa->tx_pipe_handle);
1137 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301138 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001139 "%s: Disable TX PIPE fail, code %d",
1140 __func__, result);
1141 return result;
1142 }
1143
1144 return 0;
1145}
1146
1147/**
1148 * hdd_ipa_uc_handle_first_con() - Handle first uC IPA connection
1149 * @hdd_ipa: Global HDD IPA context
1150 *
1151 * Return: 0 on success, negative errno if error
1152 */
1153static int hdd_ipa_uc_handle_first_con(struct hdd_ipa_priv *hdd_ipa)
1154{
1155 hdd_ipa->activated_fw_pipe = 0;
1156 hdd_ipa->resource_loading = true;
Yun Park4cab6ee2015-10-27 11:43:40 -07001157
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001158 /* If RM feature enabled
1159 * Request PROD Resource first
1160 * PROD resource may return sync or async manners */
Yun Park4cab6ee2015-10-27 11:43:40 -07001161 if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) {
1162 if (!ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD)) {
1163 /* RM PROD request sync return
1164 * enable pipe immediately
1165 */
1166 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301167 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park4cab6ee2015-10-27 11:43:40 -07001168 "%s: IPA WDI Pipe activation failed",
1169 __func__);
1170 hdd_ipa->resource_loading = false;
1171 return -EBUSY;
1172 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001173 }
1174 } else {
1175 /* RM Disabled
Yun Park4cab6ee2015-10-27 11:43:40 -07001176 * Just enabled all the PIPEs
1177 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001178 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301179 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park4cab6ee2015-10-27 11:43:40 -07001180 "%s: IPA WDI Pipe activation failed",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001181 __func__);
1182 hdd_ipa->resource_loading = false;
1183 return -EBUSY;
1184 }
1185 hdd_ipa->resource_loading = false;
1186 }
Yun Park4cab6ee2015-10-27 11:43:40 -07001187
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301188 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001189 "%s: IPA WDI Pipes activated successfully", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001190 return 0;
1191}
1192
1193/**
1194 * hdd_ipa_uc_handle_last_discon() - Handle last uC IPA disconnection
1195 * @hdd_ipa: Global HDD IPA context
1196 *
1197 * Return: None
1198 */
1199static void hdd_ipa_uc_handle_last_discon(struct hdd_ipa_priv *hdd_ipa)
1200{
1201 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
1202
1203 hdd_ipa->resource_unloading = true;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301204 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable FW RX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001205 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, false);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301206 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable FW TX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001207 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, true);
1208}
1209
1210/**
1211 * hdd_ipa_uc_rm_notify_handler() - IPA uC resource notification handler
1212 * @context: User context registered with TL (the IPA Global context is
1213 * registered
1214 * @rxpkt: Packet containing the notification
1215 * @staid: ID of the station associated with the packet
1216 *
1217 * Return: None
1218 */
1219static void
1220hdd_ipa_uc_rm_notify_handler(void *context, enum ipa_rm_event event)
1221{
1222 struct hdd_ipa_priv *hdd_ipa = context;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301223 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001224
1225 /*
1226 * When SSR is going on or driver is unloading, just return.
1227 */
1228 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
1229 if (0 != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301230 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001231 return;
1232 }
1233
1234 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1235 return;
1236
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301237 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s, event code %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001238 __func__, event);
1239
1240 switch (event) {
1241 case IPA_RM_RESOURCE_GRANTED:
1242 /* Differed RM Granted */
1243 hdd_ipa_uc_enable_pipes(hdd_ipa);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301244 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001245 if ((false == hdd_ipa->resource_unloading) &&
1246 (!hdd_ipa->activated_fw_pipe)) {
1247 hdd_ipa_uc_enable_pipes(hdd_ipa);
1248 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301249 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001250 break;
1251
1252 case IPA_RM_RESOURCE_RELEASED:
1253 /* Differed RM Released */
1254 hdd_ipa->resource_unloading = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001255 break;
1256
1257 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301258 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001259 "%s, invalid event code %d", __func__, event);
1260 break;
1261 }
1262}
1263
1264/**
1265 * hdd_ipa_uc_rm_notify_defer() - Defer IPA uC notification
1266 * @hdd_ipa: Global HDD IPA context
1267 * @event: IPA resource manager event to be deferred
1268 *
1269 * This function is called when a resource manager event is received
1270 * from firmware in interrupt context. This function will defer the
1271 * handling to the OL RX thread
1272 *
1273 * Return: None
1274 */
1275static void hdd_ipa_uc_rm_notify_defer(struct work_struct *work)
1276{
1277 enum ipa_rm_event event;
1278 struct uc_rm_work_struct *uc_rm_work = container_of(work,
1279 struct uc_rm_work_struct, work);
1280 struct hdd_ipa_priv *hdd_ipa = container_of(uc_rm_work,
1281 struct hdd_ipa_priv, uc_rm_work);
1282
1283 cds_ssr_protect(__func__);
1284 event = uc_rm_work->event;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301285 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001286 "%s, posted event %d", __func__, event);
1287
1288 hdd_ipa_uc_rm_notify_handler(hdd_ipa, event);
1289 cds_ssr_unprotect(__func__);
1290
1291 return;
1292}
1293
1294/**
1295 * hdd_ipa_uc_proc_pending_event() - Process IPA uC pending events
1296 * @hdd_ipa: Global HDD IPA context
1297 *
1298 * Return: None
1299 */
1300static void hdd_ipa_uc_proc_pending_event(struct hdd_ipa_priv *hdd_ipa)
1301{
1302 unsigned int pending_event_count;
1303 struct ipa_uc_pending_event *pending_event = NULL;
1304
Anurag Chouhanffb21542016-02-17 14:33:03 +05301305 pending_event_count = qdf_list_size(&hdd_ipa->pending_event);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301306 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001307 "%s, Pending Event Count %d", __func__, pending_event_count);
1308 if (!pending_event_count) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301309 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001310 "%s, No Pending Event", __func__);
1311 return;
1312 }
1313
Anurag Chouhanffb21542016-02-17 14:33:03 +05301314 qdf_list_remove_front(&hdd_ipa->pending_event,
1315 (qdf_list_node_t **)&pending_event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001316 while (pending_event != NULL) {
1317 hdd_ipa_wlan_evt(pending_event->adapter,
1318 pending_event->type,
1319 pending_event->sta_id,
1320 pending_event->mac_addr);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301321 qdf_mem_free(pending_event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001322 pending_event = NULL;
Anurag Chouhanffb21542016-02-17 14:33:03 +05301323 qdf_list_remove_front(&hdd_ipa->pending_event,
1324 (qdf_list_node_t **)&pending_event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001325 }
1326}
1327
1328/**
1329 * hdd_ipa_uc_op_cb() - IPA uC operation callback
1330 * @op_msg: operation message received from firmware
1331 * @usr_ctxt: user context registered with TL (we register the HDD Global
1332 * context)
1333 *
1334 * Return: None
1335 */
1336static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt)
1337{
1338 struct op_msg_type *msg = op_msg;
1339 struct ipa_uc_fw_stats *uc_fw_stat;
1340 struct IpaHwStatsWDIInfoData_t ipa_stat;
1341 struct hdd_ipa_priv *hdd_ipa;
1342 hdd_context_t *hdd_ctx;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301343 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001344
1345 if (!op_msg || !usr_ctxt) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301346 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s, INVALID ARG", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001347 return;
1348 }
1349
1350 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301351 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001352 "%s, INVALID OPCODE %d", __func__, msg->op_code);
1353 return;
1354 }
1355
1356 hdd_ctx = (hdd_context_t *) usr_ctxt;
1357
1358 /*
1359 * When SSR is going on or driver is unloading, just return.
1360 */
1361 status = wlan_hdd_validate_context(hdd_ctx);
1362 if (0 != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301363 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301364 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001365 return;
1366 }
1367
1368 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1369
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301370 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001371 "%s, OPCODE %s", __func__, op_string[msg->op_code]);
1372
1373 if ((HDD_IPA_UC_OPCODE_TX_RESUME == msg->op_code) ||
1374 (HDD_IPA_UC_OPCODE_RX_RESUME == msg->op_code)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301375 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001376 hdd_ipa->activated_fw_pipe++;
1377 if (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) {
1378 hdd_ipa->resource_loading = false;
1379 hdd_ipa_uc_proc_pending_event(hdd_ipa);
Yun Parkccc6d7a2015-12-02 14:50:13 -08001380 if (hdd_ipa->pending_cons_req)
1381 ipa_rm_notify_completion(
1382 IPA_RM_RESOURCE_GRANTED,
1383 IPA_RM_RESOURCE_WLAN_CONS);
Yun Park5b635012015-12-02 15:05:01 -08001384 hdd_ipa->pending_cons_req = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001385 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301386 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001387 }
1388
1389 if ((HDD_IPA_UC_OPCODE_TX_SUSPEND == msg->op_code) ||
1390 (HDD_IPA_UC_OPCODE_RX_SUSPEND == msg->op_code)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301391 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001392 hdd_ipa->activated_fw_pipe--;
1393 if (!hdd_ipa->activated_fw_pipe) {
1394 hdd_ipa_uc_disable_pipes(hdd_ipa);
Yun Park5b635012015-12-02 15:05:01 -08001395 if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1396 ipa_rm_release_resource(
1397 IPA_RM_RESOURCE_WLAN_PROD);
1398 /* Sync return success from IPA
1399 * Enable/resume all the PIPEs */
1400 hdd_ipa->resource_unloading = false;
1401 hdd_ipa_uc_proc_pending_event(hdd_ipa);
1402 hdd_ipa->pending_cons_req = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001403 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301404 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001405 }
1406
1407 if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
1408 (HDD_IPA_UC_STAT_REASON_DEBUG == hdd_ipa->stat_req_reason)) {
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001409 struct ol_txrx_ipa_resources *res = &hdd_ipa->ipa_resource;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001410 /* STATs from host */
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301411 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001412 "==== IPA_UC WLAN_HOST CE ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001413 "CE RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001414 "CE RING SIZE: %d\n"
1415 "CE REG ADDR : 0x%llx",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001416 (unsigned long long)res->ce_sr_base_paddr,
1417 res->ce_sr_ring_size,
1418 (unsigned long long)res->ce_reg_paddr);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301419 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001420 "==== IPA_UC WLAN_HOST TX ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001421 "COMP RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001422 "COMP RING SIZE: %d\n"
1423 "NUM ALLOC BUF: %d\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001424 "COMP RING DBELL : 0x%llx",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001425 (unsigned long long)res->tx_comp_ring_base_paddr,
1426 res->tx_comp_ring_size,
1427 res->tx_num_alloc_buffer,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001428 (unsigned long long)hdd_ipa->tx_comp_doorbell_paddr);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301429 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001430 "==== IPA_UC WLAN_HOST RX ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001431 "IND RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001432 "IND RING SIZE: %d\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001433 "IND RING DBELL : 0x%llx\n"
1434 "PROC DONE IND ADDR : 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001435 "NUM EXCP PKT : %llu\n"
1436 "NUM TX BCMC : %llu\n"
1437 "NUM TX BCMC ERR : %llu",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001438 (unsigned long long)res->rx_rdy_ring_base_paddr,
1439 res->rx_rdy_ring_size,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001440 (unsigned long long)hdd_ipa->rx_ready_doorbell_paddr,
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001441 (unsigned long long)hdd_ipa->ipa_resource.
1442 rx_proc_done_idx_paddr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001443 hdd_ipa->stats.num_rx_excep,
1444 hdd_ipa->stats.num_tx_bcmc,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001445 (unsigned long long)hdd_ipa->stats.num_tx_bcmc_err);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301446 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001447 "==== IPA_UC WLAN_HOST CONTROL ====\n"
1448 "SAP NUM STAs: %d\n"
1449 "STA CONNECTED: %d\n"
1450 "TX PIPE HDL: %d\n"
1451 "RX PIPE HDL : %d\n"
1452 "RSC LOADING : %d\n"
1453 "RSC UNLOADING : %d\n"
1454 "PNDNG CNS RQT : %d",
1455 hdd_ipa->sap_num_connected_sta,
1456 hdd_ipa->sta_connected,
1457 hdd_ipa->tx_pipe_handle,
1458 hdd_ipa->rx_pipe_handle,
1459 (unsigned int)hdd_ipa->resource_loading,
1460 (unsigned int)hdd_ipa->resource_unloading,
1461 (unsigned int)hdd_ipa->pending_cons_req);
1462
1463 /* STATs from FW */
1464 uc_fw_stat = (struct ipa_uc_fw_stats *)
1465 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301466 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001467 "==== IPA_UC WLAN_FW TX ====\n"
1468 "COMP RING BASE: 0x%x\n"
1469 "COMP RING SIZE: %d\n"
1470 "COMP RING DBELL : 0x%x\n"
1471 "COMP RING DBELL IND VAL : %d\n"
1472 "COMP RING DBELL CACHED VAL : %d\n"
1473 "COMP RING DBELL CACHED VAL : %d\n"
1474 "PKTS ENQ : %d\n"
1475 "PKTS COMP : %d\n"
1476 "IS SUSPEND : %d\n"
1477 "RSVD : 0x%x",
1478 uc_fw_stat->tx_comp_ring_base,
1479 uc_fw_stat->tx_comp_ring_size,
1480 uc_fw_stat->tx_comp_ring_dbell_addr,
1481 uc_fw_stat->tx_comp_ring_dbell_ind_val,
1482 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1483 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1484 uc_fw_stat->tx_pkts_enqueued,
1485 uc_fw_stat->tx_pkts_completed,
1486 uc_fw_stat->tx_is_suspend, uc_fw_stat->tx_reserved);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301487 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001488 "==== IPA_UC WLAN_FW RX ====\n"
1489 "IND RING BASE: 0x%x\n"
1490 "IND RING SIZE: %d\n"
1491 "IND RING DBELL : 0x%x\n"
1492 "IND RING DBELL IND VAL : %d\n"
1493 "IND RING DBELL CACHED VAL : %d\n"
1494 "RDY IND ADDR : 0x%x\n"
1495 "RDY IND CACHE VAL : %d\n"
1496 "RFIL IND : %d\n"
1497 "NUM PKT INDICAT : %d\n"
1498 "BUF REFIL : %d\n"
1499 "NUM DROP NO SPC : %d\n"
1500 "NUM DROP NO BUF : %d\n"
1501 "IS SUSPND : %d\n"
1502 "RSVD : 0x%x\n",
1503 uc_fw_stat->rx_ind_ring_base,
1504 uc_fw_stat->rx_ind_ring_size,
1505 uc_fw_stat->rx_ind_ring_dbell_addr,
1506 uc_fw_stat->rx_ind_ring_dbell_ind_val,
1507 uc_fw_stat->rx_ind_ring_dbell_ind_cached_val,
1508 uc_fw_stat->rx_ind_ring_rdidx_addr,
1509 uc_fw_stat->rx_ind_ring_rd_idx_cached_val,
1510 uc_fw_stat->rx_refill_idx,
1511 uc_fw_stat->rx_num_pkts_indicated,
1512 uc_fw_stat->rx_buf_refilled,
1513 uc_fw_stat->rx_num_ind_drop_no_space,
1514 uc_fw_stat->rx_num_ind_drop_no_buf,
1515 uc_fw_stat->rx_is_suspend, uc_fw_stat->rx_reserved);
1516 /* STATs from IPA */
1517 ipa_get_wdi_stats(&ipa_stat);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301518 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001519 "==== IPA_UC IPA TX ====\n"
1520 "NUM PROCD : %d\n"
1521 "CE DBELL : 0x%x\n"
1522 "NUM DBELL FIRED : %d\n"
1523 "COMP RNG FULL : %d\n"
1524 "COMP RNG EMPT : %d\n"
1525 "COMP RNG USE HGH : %d\n"
1526 "COMP RNG USE LOW : %d\n"
1527 "BAM FIFO FULL : %d\n"
1528 "BAM FIFO EMPT : %d\n"
1529 "BAM FIFO USE HGH : %d\n"
1530 "BAM FIFO USE LOW : %d\n"
1531 "NUM DBELL : %d\n"
1532 "NUM UNEXP DBELL : %d\n"
1533 "NUM BAM INT HDL : 0x%x\n"
1534 "NUM BAM INT NON-RUN : 0x%x\n"
1535 "NUM QMB INT HDL : 0x%x",
1536 ipa_stat.tx_ch_stats.num_pkts_processed,
1537 ipa_stat.tx_ch_stats.copy_engine_doorbell_value,
1538 ipa_stat.tx_ch_stats.num_db_fired,
1539 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringFull,
1540 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringEmpty,
1541 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageHigh,
1542 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageLow,
1543 ipa_stat.tx_ch_stats.bam_stats.bamFifoFull,
1544 ipa_stat.tx_ch_stats.bam_stats.bamFifoEmpty,
1545 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageHigh,
1546 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageLow,
1547 ipa_stat.tx_ch_stats.num_db,
1548 ipa_stat.tx_ch_stats.num_unexpected_db,
1549 ipa_stat.tx_ch_stats.num_bam_int_handled,
1550 ipa_stat.tx_ch_stats.
1551 num_bam_int_in_non_runnning_state,
1552 ipa_stat.tx_ch_stats.num_qmb_int_handled);
1553
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301554 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001555 "==== IPA_UC IPA RX ====\n"
1556 "MAX OST PKT : %d\n"
1557 "NUM PKT PRCSD : %d\n"
1558 "RNG RP : 0x%x\n"
1559 "COMP RNG FULL : %d\n"
1560 "COMP RNG EMPT : %d\n"
1561 "COMP RNG USE HGH : %d\n"
1562 "COMP RNG USE LOW : %d\n"
1563 "BAM FIFO FULL : %d\n"
1564 "BAM FIFO EMPT : %d\n"
1565 "BAM FIFO USE HGH : %d\n"
1566 "BAM FIFO USE LOW : %d\n"
1567 "NUM DB : %d\n"
1568 "NUM UNEXP DB : %d\n"
1569 "NUM BAM INT HNDL : 0x%x\n",
1570 ipa_stat.rx_ch_stats.max_outstanding_pkts,
1571 ipa_stat.rx_ch_stats.num_pkts_processed,
1572 ipa_stat.rx_ch_stats.rx_ring_rp_value,
1573 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringFull,
1574 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringEmpty,
1575 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageHigh,
1576 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageLow,
1577 ipa_stat.rx_ch_stats.bam_stats.bamFifoFull,
1578 ipa_stat.rx_ch_stats.bam_stats.bamFifoEmpty,
1579 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageHigh,
1580 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageLow,
1581 ipa_stat.rx_ch_stats.num_db,
1582 ipa_stat.rx_ch_stats.num_unexpected_db,
1583 ipa_stat.rx_ch_stats.num_bam_int_handled);
1584 } else if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
1585 (HDD_IPA_UC_STAT_REASON_BW_CAL == hdd_ipa->stat_req_reason)) {
1586 /* STATs from FW */
1587 uc_fw_stat = (struct ipa_uc_fw_stats *)
1588 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301589 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001590 hdd_ipa->ipa_tx_packets_diff = HDD_BW_GET_DIFF(
1591 uc_fw_stat->tx_pkts_completed,
1592 hdd_ipa->ipa_p_tx_packets);
1593 hdd_ipa->ipa_rx_packets_diff = HDD_BW_GET_DIFF(
1594 (uc_fw_stat->rx_num_ind_drop_no_space +
1595 uc_fw_stat->rx_num_ind_drop_no_buf +
1596 uc_fw_stat->rx_num_pkts_indicated),
1597 hdd_ipa->ipa_p_rx_packets);
1598
1599 hdd_ipa->ipa_p_tx_packets = uc_fw_stat->tx_pkts_completed;
1600 hdd_ipa->ipa_p_rx_packets =
1601 (uc_fw_stat->rx_num_ind_drop_no_space +
1602 uc_fw_stat->rx_num_ind_drop_no_buf +
1603 uc_fw_stat->rx_num_pkts_indicated);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301604 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001605 } else {
1606 HDD_IPA_LOG(LOGE, "INVALID REASON %d",
1607 hdd_ipa->stat_req_reason);
1608 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301609 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001610}
1611
1612
1613/**
1614 * hdd_ipa_uc_offload_enable_disable() - wdi enable/disable notify to fw
1615 * @adapter: device adapter instance
1616 * @offload_type: MCC or SCC
1617 * @enable: TX offload enable or disable
1618 *
1619 * Return: none
1620 */
1621static void hdd_ipa_uc_offload_enable_disable(hdd_adapter_t *adapter,
1622 uint32_t offload_type, uint32_t enable)
1623{
1624 struct sir_ipa_offload_enable_disable ipa_offload_enable_disable;
1625
1626 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
1627 * channel change indication. Since these indications are sent by lower
1628 * layer as SAP updates and IPA doesn't have to do anything for these
1629 * updates so ignoring!
1630 */
Krunal Sonibe766b02016-03-10 13:00:44 -08001631 if (QDF_SAP_MODE == adapter->device_mode && adapter->ipa_context)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001632 return;
1633
1634 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
1635 * channel change indication. Since these indications are sent by lower
1636 * layer as SAP updates and IPA doesn't have to do anything for these
1637 * updates so ignoring!
1638 */
1639 if (adapter->ipa_context)
1640 return;
1641
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301642 qdf_mem_zero(&ipa_offload_enable_disable,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001643 sizeof(ipa_offload_enable_disable));
1644 ipa_offload_enable_disable.offload_type = offload_type;
1645 ipa_offload_enable_disable.vdev_id = adapter->sessionId;
1646 ipa_offload_enable_disable.enable = enable;
1647
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301648 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001649 "%s: offload_type=%d, vdev_id=%d, enable=%d", __func__,
1650 ipa_offload_enable_disable.offload_type,
1651 ipa_offload_enable_disable.vdev_id,
1652 ipa_offload_enable_disable.enable);
1653
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301654 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001655 sme_ipa_offload_enable_disable(WLAN_HDD_GET_HAL_CTX(adapter),
1656 adapter->sessionId, &ipa_offload_enable_disable)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301657 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001658 "%s: Failure to enable IPA offload \
1659 (offload_type=%d, vdev_id=%d, enable=%d)", __func__,
1660 ipa_offload_enable_disable.offload_type,
1661 ipa_offload_enable_disable.vdev_id,
1662 ipa_offload_enable_disable.enable);
1663 }
1664}
1665
1666/**
1667 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1668 * @work: uC OP work
1669 *
1670 * Return: None
1671 */
1672static void hdd_ipa_uc_fw_op_event_handler(struct work_struct *work)
1673{
1674 struct op_msg_type *msg;
1675 struct uc_op_work_struct *uc_op_work = container_of(work,
1676 struct uc_op_work_struct, work);
1677 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1678
1679 cds_ssr_protect(__func__);
1680
1681 msg = uc_op_work->msg;
1682 uc_op_work->msg = NULL;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301683 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001684 "%s, posted msg %d", __func__, msg->op_code);
1685
1686 hdd_ipa_uc_op_cb(msg, hdd_ipa->hdd_ctx);
1687
1688 cds_ssr_unprotect(__func__);
1689
1690 return;
1691}
1692
1693/**
1694 * hdd_ipa_uc_op_event_handler() - Adapter lookup
1695 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1696 * @op_msg: operation message received from firmware
1697 * @hdd_ctx: Global HDD context
1698 *
1699 * Return: None
1700 */
1701static void hdd_ipa_uc_op_event_handler(uint8_t *op_msg, void *hdd_ctx)
1702{
1703 struct hdd_ipa_priv *hdd_ipa;
1704 struct op_msg_type *msg;
1705 struct uc_op_work_struct *uc_op_work;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301706 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001707
1708 status = wlan_hdd_validate_context(hdd_ctx);
1709 if (0 != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301710 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001711 goto end;
1712 }
1713
1714 msg = (struct op_msg_type *)op_msg;
1715 hdd_ipa = ((hdd_context_t *)hdd_ctx)->hdd_ipa;
1716
1717 if (unlikely(!hdd_ipa))
1718 goto end;
1719
1720 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301721 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s: Invalid OP Code (%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001722 __func__, msg->op_code);
1723 goto end;
1724 }
1725
1726 uc_op_work = &hdd_ipa->uc_op_work[msg->op_code];
1727 if (uc_op_work->msg)
1728 /* When the same uC OPCODE is already pended, just return */
1729 goto end;
1730
1731 uc_op_work->msg = msg;
1732 schedule_work(&uc_op_work->work);
1733 return;
1734
1735end:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301736 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001737}
1738
1739/**
Rajeev Kumar217f2172016-01-06 18:11:55 -08001740 * hdd_ipa_init_uc_op_work - init ipa uc op work
1741 * @work: struct work_struct
1742 * @work_handler: work_handler
1743 *
1744 * Return: none
1745 */
1746#ifdef CONFIG_CNSS
1747static void hdd_ipa_init_uc_op_work(struct work_struct *work,
1748 work_func_t work_handler)
1749{
1750 cnss_init_work(work, work_handler);
1751}
1752#else
1753static void hdd_ipa_init_uc_op_work(struct work_struct *work,
1754 work_func_t work_handler)
1755{
1756 INIT_WORK(work, work_handler);
1757}
1758#endif
1759
1760
1761/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001762 * hdd_ipa_uc_ol_init() - Initialize IPA uC offload
1763 * @hdd_ctx: Global HDD context
1764 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301765 * Return: QDF_STATUS
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001766 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301767static QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001768{
1769 struct ipa_wdi_in_params pipe_in;
1770 struct ipa_wdi_out_params pipe_out;
1771 struct hdd_ipa_priv *ipa_ctxt = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1772 p_cds_contextType cds_ctx = hdd_ctx->pcds_context;
1773 uint8_t i;
1774
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301775 qdf_mem_zero(&pipe_in, sizeof(struct ipa_wdi_in_params));
1776 qdf_mem_zero(&pipe_out, sizeof(struct ipa_wdi_out_params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001777
Anurag Chouhanffb21542016-02-17 14:33:03 +05301778 qdf_list_create(&ipa_ctxt->pending_event, 1000);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301779 qdf_mutex_create(&ipa_ctxt->event_lock);
1780 qdf_mutex_create(&ipa_ctxt->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001781
1782 /* TX PIPE */
1783 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1784 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
1785 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
1786 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
1787 pipe_in.sys.ipa_ep_cfg.hdr.hdr_additional_const_len =
1788 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
1789 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1790 pipe_in.sys.client = IPA_CLIENT_WLAN1_CONS;
1791 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize;
1792 pipe_in.sys.priv = hdd_ctx->hdd_ipa;
1793 pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
1794 pipe_in.sys.notify = hdd_ipa_i2w_cb;
1795 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301796 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001797 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1798 pipe_in.sys.keep_ipa_awake = true;
1799 }
1800
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001801 pipe_in.u.dl.comp_ring_base_pa =
1802 ipa_ctxt->ipa_resource.tx_comp_ring_base_paddr;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001803 pipe_in.u.dl.comp_ring_size =
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001804 ipa_ctxt->ipa_resource.tx_comp_ring_size *
1805 sizeof(qdf_dma_addr_t);
1806 pipe_in.u.dl.ce_ring_base_pa =
1807 ipa_ctxt->ipa_resource.ce_sr_base_paddr;
1808 pipe_in.u.dl.ce_door_bell_pa = ipa_ctxt->ipa_resource.ce_reg_paddr;
1809 pipe_in.u.dl.ce_ring_size =
1810 ipa_ctxt->ipa_resource.ce_sr_ring_size;
1811 pipe_in.u.dl.num_tx_buffers =
1812 ipa_ctxt->ipa_resource.tx_num_alloc_buffer;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001813
1814 /* Connect WDI IPA PIPE */
1815 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
1816 /* Micro Controller Doorbell register */
Leo Chang3bc8fed2015-11-13 10:59:47 -08001817 ipa_ctxt->tx_comp_doorbell_paddr = pipe_out.uc_door_bell_pa;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001818 /* WLAN TX PIPE Handle */
1819 ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301820 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001821 "TX : CRBPA 0x%x, CRS %d, CERBPA 0x%x, CEDPA 0x%x,"
1822 " CERZ %d, NB %d, CDBPAD 0x%x",
1823 (unsigned int)pipe_in.u.dl.comp_ring_base_pa,
1824 pipe_in.u.dl.comp_ring_size,
1825 (unsigned int)pipe_in.u.dl.ce_ring_base_pa,
1826 (unsigned int)pipe_in.u.dl.ce_door_bell_pa,
1827 pipe_in.u.dl.ce_ring_size,
1828 pipe_in.u.dl.num_tx_buffers,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001829 (unsigned int)ipa_ctxt->tx_comp_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001830
1831 /* RX PIPE */
1832 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1833 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_RX_HDR_LEN;
1834 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 0;
1835 pipe_in.sys.ipa_ep_cfg.hdr.hdr_metadata_reg_valid = 1;
1836 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1837 pipe_in.sys.client = IPA_CLIENT_WLAN1_PROD;
1838 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize +
1839 sizeof(struct sps_iovec);
1840 pipe_in.sys.notify = hdd_ipa_w2i_cb;
1841 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301842 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001843 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1844 pipe_in.sys.keep_ipa_awake = true;
1845 }
1846
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001847 pipe_in.u.ul.rdy_ring_base_pa =
1848 ipa_ctxt->ipa_resource.rx_rdy_ring_base_paddr;
1849 pipe_in.u.ul.rdy_ring_size =
1850 ipa_ctxt->ipa_resource.rx_rdy_ring_size;
1851 pipe_in.u.ul.rdy_ring_rp_pa =
1852 ipa_ctxt->ipa_resource.rx_proc_done_idx_paddr;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001853 HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001854 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
Leo Chang3bc8fed2015-11-13 10:59:47 -08001855 ipa_ctxt->rx_ready_doorbell_paddr = pipe_out.uc_door_bell_pa;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001856 ipa_ctxt->rx_pipe_handle = pipe_out.clnt_hdl;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301857 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001858 "RX : RRBPA 0x%x, RRS %d, PDIPA 0x%x, RDY_DB_PAD 0x%x",
1859 (unsigned int)pipe_in.u.ul.rdy_ring_base_pa,
1860 pipe_in.u.ul.rdy_ring_size,
1861 (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001862 (unsigned int)ipa_ctxt->rx_ready_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001863
1864 ol_txrx_ipa_uc_set_doorbell_paddr(cds_ctx->pdev_txrx_ctx,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001865 ipa_ctxt->tx_comp_doorbell_paddr,
1866 ipa_ctxt->rx_ready_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001867
1868 ol_txrx_ipa_uc_register_op_cb(cds_ctx->pdev_txrx_ctx,
1869 hdd_ipa_uc_op_event_handler, (void *)hdd_ctx);
1870
1871 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
Rajeev Kumar217f2172016-01-06 18:11:55 -08001872 hdd_ipa_init_uc_op_work(&ipa_ctxt->uc_op_work[i].work,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001873 hdd_ipa_uc_fw_op_event_handler);
1874 ipa_ctxt->uc_op_work[i].msg = NULL;
1875 }
1876
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301877 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001878}
1879
Leo Change3e49442015-10-26 20:07:13 -07001880/**
1881 * hdd_ipa_uc_force_pipe_shutdown() - Force shutdown IPA pipe
1882 * @hdd_ctx: hdd main context
1883 *
1884 * Force shutdown IPA pipe
1885 * Independent of FW pipe status, IPA pipe shutdonw progress
1886 * in case, any STA does not leave properly, IPA HW pipe should cleaned up
1887 * independent from FW pipe status
1888 *
1889 * Return: NONE
1890 */
1891void hdd_ipa_uc_force_pipe_shutdown(hdd_context_t *hdd_ctx)
1892{
1893 struct hdd_ipa_priv *hdd_ipa;
1894
1895 if (!hdd_ipa_is_enabled(hdd_ctx) || !hdd_ctx->hdd_ipa)
1896 return;
1897
1898 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1899 if (false == hdd_ipa->ipa_pipes_down) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301900 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Leo Change3e49442015-10-26 20:07:13 -07001901 "IPA pipes are not down yet, force shutdown");
1902 hdd_ipa_uc_disable_pipes(hdd_ipa);
1903 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301904 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Leo Change3e49442015-10-26 20:07:13 -07001905 "IPA pipes are down, do nothing");
1906 }
1907
1908 return;
1909}
1910
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001911/**
1912 * hdd_ipa_uc_ssr_deinit() - handle ipa deinit for SSR
1913 *
1914 * Deinit basic IPA UC host side to be in sync reloaded FW during
1915 * SSR
1916 *
1917 * Return: 0 - Success
1918 */
1919int hdd_ipa_uc_ssr_deinit(void)
1920{
1921 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1922 int idx;
1923 struct hdd_ipa_iface_context *iface_context;
1924
Leo Chang3bc8fed2015-11-13 10:59:47 -08001925 if ((!hdd_ipa) || (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001926 return 0;
1927
1928 /* Clean up HDD IPA interfaces */
1929 for (idx = 0; (hdd_ipa->num_iface > 0) &&
1930 (idx < HDD_IPA_MAX_IFACE); idx++) {
1931 iface_context = &hdd_ipa->iface_context[idx];
1932 if (iface_context && iface_context->adapter)
1933 hdd_ipa_cleanup_iface(iface_context);
1934 }
1935
1936 /* After SSR, wlan driver reloads FW again. But we need to protect
1937 * IPA submodule during SSR transient state. So deinit basic IPA
1938 * UC host side to be in sync with reloaded FW during SSR
1939 */
Yun Parkf7dc8cd2015-11-17 15:25:12 -08001940 if (!hdd_ipa->ipa_pipes_down)
1941 hdd_ipa_uc_disable_pipes(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001942
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301943 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001944 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1945 hdd_ipa->assoc_stas_map[idx].is_reserved = false;
1946 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
1947 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301948 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001949
1950 /* Full IPA driver cleanup not required since wlan driver is now
1951 * unloaded and reloaded after SSR.
1952 */
1953 return 0;
1954}
1955
1956/**
1957 * hdd_ipa_uc_ssr_reinit() - handle ipa reinit after SSR
1958 *
1959 * Init basic IPA UC host side to be in sync with reloaded FW after
1960 * SSR to resume IPA UC operations
1961 *
1962 * Return: 0 - Success
1963 */
1964int hdd_ipa_uc_ssr_reinit(void)
1965{
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001966
1967 /* After SSR is complete, IPA UC can resume operation. But now wlan
1968 * driver will be unloaded and reloaded, which takes care of IPA cleanup
1969 * and initialization. This is a placeholder func if IPA has to resume
1970 * operations without driver reload.
1971 */
1972 return 0;
1973}
Leo Chang3bc8fed2015-11-13 10:59:47 -08001974
1975/**
1976 * hdd_ipa_tx_packet_ipa() - send packet to IPA
1977 * @hdd_ctx: Global HDD context
1978 * @skb: skb sent to IPA
1979 * @session_id: send packet instance session id
1980 *
1981 * Send TX packet which generated by system to IPA.
1982 * This routine only will be used for function verification
1983 *
1984 * Return: NULL packet sent to IPA properly
1985 * NULL invalid packet drop
1986 * skb packet not sent to IPA. legacy data path should handle
1987 */
1988struct sk_buff *hdd_ipa_tx_packet_ipa(hdd_context_t *hdd_ctx,
1989 struct sk_buff *skb, uint8_t session_id)
Leo Change3e49442015-10-26 20:07:13 -07001990{
Leo Chang3bc8fed2015-11-13 10:59:47 -08001991 struct ipa_header *ipa_header;
1992 struct frag_header *frag_header;
1993
1994 if (!hdd_ipa_uc_is_enabled(hdd_ctx))
1995 return skb;
1996
1997 ipa_header = (struct ipa_header *) skb_push(skb,
1998 sizeof(struct ipa_header));
1999 if (!ipa_header) {
2000 /* No headroom, legacy */
2001 return skb;
2002 }
2003 memset(ipa_header, 0, sizeof(*ipa_header));
2004 ipa_header->vdev_id = 0;
2005
2006 frag_header = (struct frag_header *) skb_push(skb,
2007 sizeof(struct frag_header));
2008 if (!frag_header) {
2009 /* No headroom, drop */
2010 kfree_skb(skb);
2011 return NULL;
2012 }
2013 memset(frag_header, 0, sizeof(*frag_header));
2014 frag_header->length = skb->len - sizeof(struct frag_header)
2015 - sizeof(struct ipa_header);
2016
2017 ipa_tx_dp(IPA_CLIENT_WLAN1_CONS, skb, NULL);
2018 return NULL;
Leo Change3e49442015-10-26 20:07:13 -07002019}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002020
2021/**
2022 * hdd_ipa_wake_lock_timer_func() - Wake lock work handler
2023 * @work: scheduled work
2024 *
2025 * When IPA resources are released in hdd_ipa_rm_try_release() we do
2026 * not want to immediately release the wake lock since the system
2027 * would then potentially try to suspend when there is a healthy data
2028 * rate. Deferred work is scheduled and this function handles the
2029 * work. When this function is called, if the IPA resource is still
2030 * released then we release the wake lock.
2031 *
2032 * Return: None
2033 */
2034static void hdd_ipa_wake_lock_timer_func(struct work_struct *work)
2035{
2036 struct hdd_ipa_priv *hdd_ipa = container_of(to_delayed_work(work),
2037 struct hdd_ipa_priv,
2038 wake_lock_work);
2039
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302040 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002041
2042 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED)
2043 goto end;
2044
2045 hdd_ipa->wake_lock_released = true;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302046 qdf_wake_lock_release(&hdd_ipa->wake_lock,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002047 WIFI_POWER_EVENT_WAKELOCK_IPA);
2048
2049end:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302050 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002051}
2052
2053/**
2054 * hdd_ipa_rm_request() - Request resource from IPA
2055 * @hdd_ipa: Global HDD IPA context
2056 *
2057 * Return: 0 on success, negative errno on error
2058 */
2059static int hdd_ipa_rm_request(struct hdd_ipa_priv *hdd_ipa)
2060{
2061 int ret = 0;
2062
2063 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2064 return 0;
2065
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302066 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002067
2068 switch (hdd_ipa->rm_state) {
2069 case HDD_IPA_RM_GRANTED:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302070 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002071 return 0;
2072 case HDD_IPA_RM_GRANT_PENDING:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302073 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002074 return -EINPROGRESS;
2075 case HDD_IPA_RM_RELEASED:
2076 hdd_ipa->rm_state = HDD_IPA_RM_GRANT_PENDING;
2077 break;
2078 }
2079
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302080 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002081
2082 ret = ipa_rm_inactivity_timer_request_resource(
2083 IPA_RM_RESOURCE_WLAN_PROD);
2084
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302085 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002086 if (ret == 0) {
2087 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
2088 hdd_ipa->stats.num_rm_grant_imm++;
2089 }
2090
2091 cancel_delayed_work(&hdd_ipa->wake_lock_work);
2092 if (hdd_ipa->wake_lock_released) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302093 qdf_wake_lock_acquire(&hdd_ipa->wake_lock,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002094 WIFI_POWER_EVENT_WAKELOCK_IPA);
2095 hdd_ipa->wake_lock_released = false;
2096 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302097 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002098
2099 return ret;
2100}
2101
2102/**
2103 * hdd_ipa_rm_try_release() - Attempt to release IPA resource
2104 * @hdd_ipa: Global HDD IPA context
2105 *
2106 * Return: 0 if resources released, negative errno otherwise
2107 */
2108static int hdd_ipa_rm_try_release(struct hdd_ipa_priv *hdd_ipa)
2109{
2110 int ret = 0;
2111
2112 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2113 return 0;
2114
2115 if (atomic_read(&hdd_ipa->tx_ref_cnt))
2116 return -EAGAIN;
2117
2118 spin_lock_bh(&hdd_ipa->q_lock);
2119 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
2120 (hdd_ipa->pending_hw_desc_cnt || hdd_ipa->pend_q_cnt)) {
2121 spin_unlock_bh(&hdd_ipa->q_lock);
2122 return -EAGAIN;
2123 }
2124 spin_unlock_bh(&hdd_ipa->q_lock);
2125
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302126 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002127
Nirav Shahcbc6d722016-03-01 16:24:53 +05302128 if (!qdf_nbuf_is_queue_empty(&hdd_ipa->pm_queue_head)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302129 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002130 return -EAGAIN;
2131 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302132 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002133
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302134 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002135 switch (hdd_ipa->rm_state) {
2136 case HDD_IPA_RM_GRANTED:
2137 break;
2138 case HDD_IPA_RM_GRANT_PENDING:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302139 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002140 return -EINPROGRESS;
2141 case HDD_IPA_RM_RELEASED:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302142 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002143 return 0;
2144 }
2145
2146 /* IPA driver returns immediately so set the state here to avoid any
2147 * race condition.
2148 */
2149 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
2150 hdd_ipa->stats.num_rm_release++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302151 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002152
2153 ret =
2154 ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_WLAN_PROD);
2155
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302156 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002157 if (unlikely(ret != 0)) {
2158 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
2159 WARN_ON(1);
2160 }
2161
2162 /*
2163 * If wake_lock is released immediately, kernel would try to suspend
2164 * immediately as well, Just avoid ping-pong between suspend-resume
2165 * while there is healthy amount of data transfer going on by
2166 * releasing the wake_lock after some delay.
2167 */
2168 schedule_delayed_work(&hdd_ipa->wake_lock_work,
2169 msecs_to_jiffies
2170 (HDD_IPA_RX_INACTIVITY_MSEC_DELAY));
2171
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302172 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002173
2174 return ret;
2175}
2176
2177/**
2178 * hdd_ipa_rm_notify() - IPA resource manager notifier callback
2179 * @user_data: user data registered with IPA
2180 * @event: the IPA resource manager event that occurred
2181 * @data: the data associated with the event
2182 *
2183 * Return: None
2184 */
2185static void hdd_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
2186 unsigned long data)
2187{
2188 struct hdd_ipa_priv *hdd_ipa = user_data;
2189
2190 if (unlikely(!hdd_ipa))
2191 return;
2192
2193 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2194 return;
2195
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302196 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Evt: %d", event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002197
2198 switch (event) {
2199 case IPA_RM_RESOURCE_GRANTED:
2200 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2201 /* RM Notification comes with ISR context
2202 * it should be serialized into work queue to avoid
2203 * ISR sleep problem
2204 */
2205 hdd_ipa->uc_rm_work.event = event;
2206 schedule_work(&hdd_ipa->uc_rm_work.work);
2207 break;
2208 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302209 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002210 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302211 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002212 hdd_ipa->stats.num_rm_grant++;
2213 break;
2214
2215 case IPA_RM_RESOURCE_RELEASED:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302216 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "RM Release");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002217 hdd_ipa->resource_unloading = false;
2218 break;
2219
2220 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302221 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Unknown RM Evt: %d", event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002222 break;
2223 }
2224}
2225
2226/**
2227 * hdd_ipa_rm_cons_release() - WLAN consumer resource release handler
2228 *
2229 * Callback function registered with IPA that is called when IPA wants
2230 * to release the WLAN consumer resource
2231 *
2232 * Return: 0 if the request is granted, negative errno otherwise
2233 */
2234static int hdd_ipa_rm_cons_release(void)
2235{
2236 return 0;
2237}
2238
2239/**
2240 * hdd_ipa_rm_cons_request() - WLAN consumer resource request handler
2241 *
2242 * Callback function registered with IPA that is called when IPA wants
2243 * to access the WLAN consumer resource
2244 *
2245 * Return: 0 if the request is granted, negative errno otherwise
2246 */
2247static int hdd_ipa_rm_cons_request(void)
2248{
Yun Park4d8b60a2015-10-22 13:59:32 -07002249 int ret = 0;
2250
2251 if (ghdd_ipa->resource_loading) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302252 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Yun Park4d8b60a2015-10-22 13:59:32 -07002253 "%s: IPA resource loading in progress",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002254 __func__);
2255 ghdd_ipa->pending_cons_req = true;
Yun Park4d8b60a2015-10-22 13:59:32 -07002256 ret = -EINPROGRESS;
2257 } else if (ghdd_ipa->resource_unloading) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302258 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Yun Park4d8b60a2015-10-22 13:59:32 -07002259 "%s: IPA resource unloading in progress",
2260 __func__);
2261 ghdd_ipa->pending_cons_req = true;
2262 ret = -EPERM;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002263 }
Yun Park4d8b60a2015-10-22 13:59:32 -07002264
2265 return ret;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002266}
2267
2268/**
2269 * hdd_ipa_set_perf_level() - Set IPA performance level
2270 * @hdd_ctx: Global HDD context
2271 * @tx_packets: Number of packets transmitted in the last sample period
2272 * @rx_packets: Number of packets received in the last sample period
2273 *
2274 * Return: 0 on success, negative errno on error
2275 */
2276int hdd_ipa_set_perf_level(hdd_context_t *hdd_ctx, uint64_t tx_packets,
2277 uint64_t rx_packets)
2278{
2279 uint32_t next_cons_bw, next_prod_bw;
2280 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2281 struct ipa_rm_perf_profile profile;
2282 int ret;
2283
2284 if ((!hdd_ipa_is_enabled(hdd_ctx)) ||
2285 (!hdd_ipa_is_clk_scaling_enabled(hdd_ctx)))
2286 return 0;
2287
2288 memset(&profile, 0, sizeof(profile));
2289
2290 if (tx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2291 next_cons_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2292 else if (tx_packets >
2293 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2294 next_cons_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2295 else
2296 next_cons_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2297
2298 if (rx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2299 next_prod_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2300 else if (rx_packets >
2301 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2302 next_prod_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2303 else
2304 next_prod_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2305
2306 HDD_IPA_LOG(LOG1,
2307 "CONS perf curr: %d, next: %d",
2308 hdd_ipa->curr_cons_bw, next_cons_bw);
2309 HDD_IPA_LOG(LOG1,
2310 "PROD perf curr: %d, next: %d",
2311 hdd_ipa->curr_prod_bw, next_prod_bw);
2312
2313 if (hdd_ipa->curr_cons_bw != next_cons_bw) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302314 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002315 "Requesting CONS perf curr: %d, next: %d",
2316 hdd_ipa->curr_cons_bw, next_cons_bw);
2317 profile.max_supported_bandwidth_mbps = next_cons_bw;
2318 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_CONS,
2319 &profile);
2320 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302321 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002322 "RM CONS set perf profile failed: %d", ret);
2323
2324 return ret;
2325 }
2326 hdd_ipa->curr_cons_bw = next_cons_bw;
2327 hdd_ipa->stats.num_cons_perf_req++;
2328 }
2329
2330 if (hdd_ipa->curr_prod_bw != next_prod_bw) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302331 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002332 "Requesting PROD perf curr: %d, next: %d",
2333 hdd_ipa->curr_prod_bw, next_prod_bw);
2334 profile.max_supported_bandwidth_mbps = next_prod_bw;
2335 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_PROD,
2336 &profile);
2337 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302338 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002339 "RM PROD set perf profile failed: %d", ret);
2340 return ret;
2341 }
2342 hdd_ipa->curr_prod_bw = next_prod_bw;
2343 hdd_ipa->stats.num_prod_perf_req++;
2344 }
2345
2346 return 0;
2347}
2348
2349/**
Rajeev Kumar217f2172016-01-06 18:11:55 -08002350 * hdd_ipa_init_uc_rm_work - init ipa uc resource manager work
2351 * @work: struct work_struct
2352 * @work_handler: work_handler
2353 *
2354 * Return: none
2355 */
2356#ifdef CONFIG_CNSS
2357static void hdd_ipa_init_uc_rm_work(struct work_struct *work,
2358 work_func_t work_handler)
2359{
2360 cnss_init_work(work, work_handler);
2361}
2362#else
2363static void hdd_ipa_init_uc_rm_work(struct work_struct *work,
2364 work_func_t work_handler)
2365{
2366 INIT_WORK(work, work_handler);
2367}
2368#endif
2369
2370/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002371 * hdd_ipa_setup_rm() - Setup IPA resource management
2372 * @hdd_ipa: Global HDD IPA context
2373 *
2374 * Return: 0 on success, negative errno on error
2375 */
2376static int hdd_ipa_setup_rm(struct hdd_ipa_priv *hdd_ipa)
2377{
2378 struct ipa_rm_create_params create_params = { 0 };
2379 int ret;
2380
2381 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2382 return 0;
2383
Rajeev Kumar217f2172016-01-06 18:11:55 -08002384 hdd_ipa_init_uc_rm_work(&hdd_ipa->uc_rm_work.work,
2385 hdd_ipa_uc_rm_notify_defer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002386 memset(&create_params, 0, sizeof(create_params));
2387 create_params.name = IPA_RM_RESOURCE_WLAN_PROD;
2388 create_params.reg_params.user_data = hdd_ipa;
2389 create_params.reg_params.notify_cb = hdd_ipa_rm_notify;
2390 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2391
2392 ret = ipa_rm_create_resource(&create_params);
2393 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302394 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002395 "Create RM resource failed: %d", ret);
2396 goto setup_rm_fail;
2397 }
2398
2399 memset(&create_params, 0, sizeof(create_params));
2400 create_params.name = IPA_RM_RESOURCE_WLAN_CONS;
2401 create_params.request_resource = hdd_ipa_rm_cons_request;
2402 create_params.release_resource = hdd_ipa_rm_cons_release;
2403 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2404
2405 ret = ipa_rm_create_resource(&create_params);
2406 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302407 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002408 "Create RM CONS resource failed: %d", ret);
2409 goto delete_prod;
2410 }
2411
2412 ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD,
2413 IPA_RM_RESOURCE_APPS_CONS);
2414
2415 ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WLAN_PROD,
2416 HDD_IPA_RX_INACTIVITY_MSEC_DELAY);
2417 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302418 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Timer init failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002419 ret);
2420 goto timer_init_failed;
2421 }
2422
2423 /* Set the lowest bandwidth to start with */
2424 ret = hdd_ipa_set_perf_level(hdd_ipa->hdd_ctx, 0, 0);
2425
2426 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302427 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002428 "Set perf level failed: %d", ret);
2429 goto set_perf_failed;
2430 }
2431
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302432 qdf_wake_lock_create(&hdd_ipa->wake_lock, "wlan_ipa");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002433#ifdef CONFIG_CNSS
2434 cnss_init_delayed_work(&hdd_ipa->wake_lock_work,
2435 hdd_ipa_wake_lock_timer_func);
2436#else
2437 INIT_DELAYED_WORK(&hdd_ipa->wake_lock_work,
2438 hdd_ipa_wake_lock_timer_func);
2439#endif
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302440 qdf_spinlock_create(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002441 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
2442 hdd_ipa->wake_lock_released = true;
2443 atomic_set(&hdd_ipa->tx_ref_cnt, 0);
2444
2445 return ret;
2446
2447set_perf_failed:
2448 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2449
2450timer_init_failed:
2451 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2452
2453delete_prod:
2454 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2455
2456setup_rm_fail:
2457 return ret;
2458}
2459
2460/**
2461 * hdd_ipa_destroy_rm_resource() - Destroy IPA resources
2462 * @hdd_ipa: Global HDD IPA context
2463 *
2464 * Destroys all resources associated with the IPA resource manager
2465 *
2466 * Return: None
2467 */
2468static void hdd_ipa_destroy_rm_resource(struct hdd_ipa_priv *hdd_ipa)
2469{
2470 int ret;
2471
2472 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2473 return;
2474
2475 cancel_delayed_work_sync(&hdd_ipa->wake_lock_work);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302476 qdf_wake_lock_destroy(&hdd_ipa->wake_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002477
2478#ifdef WLAN_OPEN_SOURCE
2479 cancel_work_sync(&hdd_ipa->uc_rm_work.work);
2480#endif
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302481 qdf_spinlock_destroy(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002482
2483 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2484
2485 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2486 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302487 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002488 "RM PROD resource delete failed %d", ret);
2489
2490 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2491 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302492 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002493 "RM CONS resource delete failed %d", ret);
2494}
2495
2496/**
2497 * hdd_ipa_send_skb_to_network() - Send skb to kernel
2498 * @skb: network buffer
2499 * @adapter: network adapter
2500 *
2501 * Called when a network buffer is received which should not be routed
2502 * to the IPA module.
2503 *
2504 * Return: None
2505 */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302506static void hdd_ipa_send_skb_to_network(qdf_nbuf_t skb,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002507 hdd_adapter_t *adapter)
2508{
2509 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2510 unsigned int cpu_index;
2511
2512 if (!adapter || adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302513 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_LOW, "Invalid adapter: 0x%p",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002514 adapter);
2515 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302516 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002517 return;
2518 }
2519
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002520 if (cds_is_driver_unloading()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002521 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302522 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002523 return;
2524 }
2525
2526 skb->destructor = hdd_ipa_uc_rt_debug_destructor;
2527 skb->dev = adapter->dev;
2528 skb->protocol = eth_type_trans(skb, skb->dev);
2529 skb->ip_summed = CHECKSUM_NONE;
2530
2531 cpu_index = wlan_hdd_get_cpu();
2532
2533 ++adapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index];
2534 if (netif_rx_ni(skb) == NET_RX_SUCCESS)
2535 ++adapter->hdd_stats.hddTxRxStats.rxDelivered[cpu_index];
2536 else
2537 ++adapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index];
2538
2539 HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa);
2540 adapter->dev->last_rx = jiffies;
2541}
2542
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002543/**
2544 * hdd_ipa_w2i_cb() - WLAN to IPA callback handler
2545 * @priv: pointer to private data registered with IPA (we register a
2546 * pointer to the global IPA context)
2547 * @evt: the IPA event which triggered the callback
2548 * @data: data associated with the event
2549 *
2550 * Return: None
2551 */
2552static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
2553 unsigned long data)
2554{
2555 struct hdd_ipa_priv *hdd_ipa = NULL;
2556 hdd_adapter_t *adapter = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302557 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002558 uint8_t iface_id;
2559 uint8_t session_id;
2560 struct hdd_ipa_iface_context *iface_context;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302561 qdf_nbuf_t copy;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002562 uint8_t fw_desc;
2563 int ret;
2564
2565 hdd_ipa = (struct hdd_ipa_priv *)priv;
2566
2567 switch (evt) {
2568 case IPA_RECEIVE:
Nirav Shahcbc6d722016-03-01 16:24:53 +05302569 skb = (qdf_nbuf_t) data;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002570 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2571 session_id = (uint8_t)skb->cb[0];
2572 iface_id = vdev_to_iface[session_id];
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302573 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002574 "IPA_RECEIVE: session_id=%u, iface_id=%u",
2575 session_id, iface_id);
2576 } else {
2577 iface_id = HDD_IPA_GET_IFACE_ID(skb->data);
2578 }
2579
2580 if (iface_id >= HDD_IPA_MAX_IFACE) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302581 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002582 "IPA_RECEIVE: Invalid iface_id: %u",
2583 iface_id);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302584 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002585 "w2i -- skb", skb->data, 8);
2586 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302587 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002588 return;
2589 }
2590
2591 iface_context = &hdd_ipa->iface_context[iface_id];
2592 adapter = iface_context->adapter;
2593
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302594 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002595 "w2i -- skb", skb->data, 8);
2596 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2597 hdd_ipa->stats.num_rx_excep++;
2598 skb_pull(skb, HDD_IPA_UC_WLAN_CLD_HDR_LEN);
2599 } else {
2600 skb_pull(skb, HDD_IPA_WLAN_CLD_HDR_LEN);
2601 }
2602
2603 iface_context->stats.num_rx_ipa_excep++;
2604
2605 /* Disable to forward Intra-BSS Rx packets when
2606 * ap_isolate=1 in hostapd.conf
2607 */
2608 if (adapter->sessionCtx.ap.apDisableIntraBssFwd) {
2609 /*
2610 * When INTRA_BSS_FWD_OFFLOAD is enabled, FW will send
2611 * all Rx packets to IPA uC, which need to be forwarded
2612 * to other interface.
2613 * And, IPA driver will send back to WLAN host driver
2614 * through exception pipe with fw_desc field set by FW.
2615 * Here we are checking fw_desc field for FORWARD bit
2616 * set, and forward to Tx. Then copy to kernel stack
2617 * only when DISCARD bit is not set.
2618 */
2619 fw_desc = (uint8_t)skb->cb[1];
2620
Leo Chang3bc8fed2015-11-13 10:59:47 -08002621 if (fw_desc & HDD_IPA_FW_RX_DESC_FORWARD_M) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002622 HDD_IPA_LOG(
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302623 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002624 "Forward packet to Tx (fw_desc=%d)",
2625 fw_desc);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302626 copy = qdf_nbuf_copy(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002627 if (copy) {
2628 hdd_ipa->ipa_tx_forward++;
2629 ret = hdd_softap_hard_start_xmit(
2630 (struct sk_buff *)copy,
2631 adapter->dev);
2632 if (ret) {
2633 HDD_IPA_LOG(
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302634 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002635 "Forward packet tx fail");
2636 hdd_ipa->stats.
2637 num_tx_bcmc_err++;
2638 } else {
2639 hdd_ipa->stats.num_tx_bcmc++;
2640 }
2641 }
2642 }
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002643
Leo Chang3bc8fed2015-11-13 10:59:47 -08002644 if (fw_desc & HDD_IPA_FW_RX_DESC_DISCARD_M) {
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002645 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
2646 hdd_ipa->ipa_rx_discard++;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302647 qdf_nbuf_free(skb);
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002648 break;
2649 }
2650
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002651 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302652 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002653 "Intra-BSS FWD is disabled-skip forward to Tx");
2654 }
2655
2656 hdd_ipa_send_skb_to_network(skb, adapter);
2657 break;
2658
2659 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302660 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002661 "w2i cb wrong event: 0x%x", evt);
2662 return;
2663 }
2664}
2665
2666/**
2667 * hdd_ipa_nbuf_cb() - IPA TX complete callback
2668 * @skb: packet buffer which was transmitted
2669 *
2670 * Return: None
2671 */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302672void hdd_ipa_nbuf_cb(qdf_nbuf_t skb)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002673{
2674 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2675
Nirav Shahcbc6d722016-03-01 16:24:53 +05302676 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "%p",
2677 wlan_hdd_stub_priv_to_addr(QDF_NBUF_CB_TX_IPA_PRIV(skb)));
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002678 /* FIXME: This is broken; PRIV_DATA is now 31 bits */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302679 ipa_free_skb((struct ipa_rx_data *)
2680 wlan_hdd_stub_priv_to_addr(QDF_NBUF_CB_TX_IPA_PRIV(skb)));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002681
2682 hdd_ipa->stats.num_tx_comp_cnt++;
2683
2684 atomic_dec(&hdd_ipa->tx_ref_cnt);
2685
2686 hdd_ipa_rm_try_release(hdd_ipa);
2687}
2688
2689/**
2690 * hdd_ipa_send_pkt_to_tl() - Send an IPA packet to TL
2691 * @iface_context: interface-specific IPA context
2692 * @ipa_tx_desc: packet data descriptor
2693 *
2694 * Return: None
2695 */
2696static void hdd_ipa_send_pkt_to_tl(
2697 struct hdd_ipa_iface_context *iface_context,
2698 struct ipa_rx_data *ipa_tx_desc)
2699{
2700 struct hdd_ipa_priv *hdd_ipa = iface_context->hdd_ipa;
2701 uint8_t interface_id;
2702 hdd_adapter_t *adapter = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302703 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002704
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302705 qdf_spin_lock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002706 adapter = iface_context->adapter;
2707 if (!adapter) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302708 HDD_IPA_LOG(QDF_TRACE_LEVEL_WARN, "Interface Down");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002709 ipa_free_skb(ipa_tx_desc);
2710 iface_context->stats.num_tx_drop++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302711 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002712 hdd_ipa_rm_try_release(hdd_ipa);
2713 return;
2714 }
2715
2716 /*
2717 * During CAC period, data packets shouldn't be sent over the air so
2718 * drop all the packets here
2719 */
2720 if (WLAN_HDD_GET_AP_CTX_PTR(adapter)->dfs_cac_block_tx) {
2721 ipa_free_skb(ipa_tx_desc);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302722 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002723 iface_context->stats.num_tx_cac_drop++;
2724 hdd_ipa_rm_try_release(hdd_ipa);
2725 return;
2726 }
2727
2728 interface_id = adapter->sessionId;
2729 ++adapter->stats.tx_packets;
2730
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302731 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002732
2733 skb = ipa_tx_desc->skb;
2734
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302735 qdf_mem_set(skb->cb, sizeof(skb->cb), 0);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302736 qdf_nbuf_ipa_owned_set(skb);
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002737 /* FIXME: This is broken. No such field in cb any more:
2738 NBUF_CALLBACK_FN(skb) = hdd_ipa_nbuf_cb; */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002739 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
Nirav Shahcbc6d722016-03-01 16:24:53 +05302740 qdf_nbuf_mapped_paddr_set(skb,
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002741 ipa_tx_desc->dma_addr
2742 + HDD_IPA_WLAN_FRAG_HEADER
2743 + HDD_IPA_WLAN_IPA_HEADER);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002744 ipa_tx_desc->skb->len -=
2745 HDD_IPA_WLAN_FRAG_HEADER + HDD_IPA_WLAN_IPA_HEADER;
2746 } else
Nirav Shahcbc6d722016-03-01 16:24:53 +05302747 qdf_nbuf_mapped_paddr_set(skb, ipa_tx_desc->dma_addr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002748
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002749 /* FIXME: This is broken: priv_data is 31 bits */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302750 qdf_nbuf_ipa_priv_set(skb, wlan_hdd_stub_addr_to_priv(ipa_tx_desc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002751
2752 adapter->stats.tx_bytes += ipa_tx_desc->skb->len;
2753
2754 skb = ol_tx_send_ipa_data_frame(iface_context->tl_context,
2755 ipa_tx_desc->skb);
2756 if (skb) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302757 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "TLSHIM tx fail");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002758 ipa_free_skb(ipa_tx_desc);
2759 iface_context->stats.num_tx_err++;
2760 hdd_ipa_rm_try_release(hdd_ipa);
2761 return;
2762 }
2763
2764 atomic_inc(&hdd_ipa->tx_ref_cnt);
2765
2766 iface_context->stats.num_tx++;
2767
2768}
2769
2770/**
2771 * hdd_ipa_pm_send_pkt_to_tl() - Send queued packets to TL
2772 * @work: pointer to the scheduled work
2773 *
2774 * Called during PM resume to send packets to TL which were queued
2775 * while host was in the process of suspending.
2776 *
2777 * Return: None
2778 */
2779static void hdd_ipa_pm_send_pkt_to_tl(struct work_struct *work)
2780{
2781 struct hdd_ipa_priv *hdd_ipa = container_of(work,
2782 struct hdd_ipa_priv,
2783 pm_work);
2784 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302785 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002786 uint32_t dequeued = 0;
2787
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302788 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002789
Nirav Shahcbc6d722016-03-01 16:24:53 +05302790 while (((skb = qdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head))
2791 != NULL)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302792 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002793
2794 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2795
2796 dequeued++;
2797
2798 hdd_ipa_send_pkt_to_tl(pm_tx_cb->iface_context,
2799 pm_tx_cb->ipa_tx_desc);
2800
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302801 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002802 }
2803
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302804 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002805
2806 hdd_ipa->stats.num_tx_dequeued += dequeued;
2807 if (dequeued > hdd_ipa->stats.num_max_pm_queue)
2808 hdd_ipa->stats.num_max_pm_queue = dequeued;
2809}
2810
2811/**
2812 * hdd_ipa_i2w_cb() - IPA to WLAN callback
2813 * @priv: pointer to private data registered with IPA (we register a
2814 * pointer to the interface-specific IPA context)
2815 * @evt: the IPA event which triggered the callback
2816 * @data: data associated with the event
2817 *
2818 * Return: None
2819 */
2820static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
2821 unsigned long data)
2822{
2823 struct hdd_ipa_priv *hdd_ipa = NULL;
2824 struct ipa_rx_data *ipa_tx_desc;
2825 struct hdd_ipa_iface_context *iface_context;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302826 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002827 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302828 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002829
Mukul Sharma81661ae2015-10-30 20:26:02 +05302830 iface_context = (struct hdd_ipa_iface_context *)priv;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002831 if (evt != IPA_RECEIVE) {
Nirav Shahcbc6d722016-03-01 16:24:53 +05302832 skb = (qdf_nbuf_t) data;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002833 dev_kfree_skb_any(skb);
2834 iface_context->stats.num_tx_drop++;
2835 return;
2836 }
2837
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002838 ipa_tx_desc = (struct ipa_rx_data *)data;
2839
2840 hdd_ipa = iface_context->hdd_ipa;
2841
2842 /*
2843 * When SSR is going on or driver is unloading, just drop the packets.
2844 * During SSR, there is no use in queueing the packets as STA has to
2845 * connect back any way
2846 */
2847 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
2848 if (0 != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302849 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002850 ipa_free_skb(ipa_tx_desc);
2851 iface_context->stats.num_tx_drop++;
2852 return;
2853 }
2854
2855 skb = ipa_tx_desc->skb;
2856
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302857 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG, "i2w", skb->data, 8);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002858
2859 /*
2860 * If PROD resource is not requested here then there may be cases where
2861 * IPA hardware may be clocked down because of not having proper
2862 * dependency graph between WLAN CONS and modem PROD pipes. Adding the
2863 * workaround to request PROD resource while data is going over CONS
2864 * pipe to prevent the IPA hardware clockdown.
2865 */
2866 hdd_ipa_rm_request(hdd_ipa);
2867
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302868 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002869 /*
2870 * If host is still suspended then queue the packets and these will be
2871 * drained later when resume completes. When packet is arrived here and
2872 * host is suspended, this means that there is already resume is in
2873 * progress.
2874 */
2875 if (hdd_ipa->suspended) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302876 qdf_mem_set(skb->cb, sizeof(skb->cb), 0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002877 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2878 pm_tx_cb->iface_context = iface_context;
2879 pm_tx_cb->ipa_tx_desc = ipa_tx_desc;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302880 qdf_nbuf_queue_add(&hdd_ipa->pm_queue_head, skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002881 hdd_ipa->stats.num_tx_queued++;
2882
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302883 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002884 return;
2885 }
2886
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302887 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002888
2889 /*
2890 * If we are here means, host is not suspended, wait for the work queue
2891 * to finish.
2892 */
2893#ifdef WLAN_OPEN_SOURCE
2894 flush_work(&hdd_ipa->pm_work);
2895#endif
2896
2897 return hdd_ipa_send_pkt_to_tl(iface_context, ipa_tx_desc);
2898}
2899
2900/**
2901 * hdd_ipa_suspend() - Suspend IPA
2902 * @hdd_ctx: Global HDD context
2903 *
2904 * Return: 0 on success, negativer errno on error
2905 */
2906int hdd_ipa_suspend(hdd_context_t *hdd_ctx)
2907{
2908 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2909
2910 if (!hdd_ipa_is_enabled(hdd_ctx))
2911 return 0;
2912
2913 /*
2914 * Check if IPA is ready for suspend, If we are here means, there is
2915 * high chance that suspend would go through but just to avoid any race
2916 * condition after suspend started, these checks are conducted before
2917 * allowing to suspend.
2918 */
2919 if (atomic_read(&hdd_ipa->tx_ref_cnt))
2920 return -EAGAIN;
2921
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302922 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002923
2924 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302925 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002926 return -EAGAIN;
2927 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302928 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002929
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302930 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002931 hdd_ipa->suspended = true;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302932 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002933
2934 return 0;
2935}
2936
2937/**
2938 * hdd_ipa_resume() - Resume IPA following suspend
2939 * hdd_ctx: Global HDD context
2940 *
2941 * Return: 0 on success, negative errno on error
2942 */
2943int hdd_ipa_resume(hdd_context_t *hdd_ctx)
2944{
2945 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2946
2947 if (!hdd_ipa_is_enabled(hdd_ctx))
2948 return 0;
2949
2950 schedule_work(&hdd_ipa->pm_work);
2951
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302952 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002953 hdd_ipa->suspended = false;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302954 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002955
2956 return 0;
2957}
2958
2959/**
2960 * hdd_ipa_setup_sys_pipe() - Setup all IPA Sys pipes
2961 * @hdd_ipa: Global HDD IPA context
2962 *
2963 * Return: 0 on success, negative errno on error
2964 */
2965static int hdd_ipa_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
2966{
2967 int i, ret = 0;
2968 struct ipa_sys_connect_params *ipa;
2969 uint32_t desc_fifo_sz;
2970
2971 /* The maximum number of descriptors that can be provided to a BAM at
2972 * once is one less than the total number of descriptors that the buffer
2973 * can contain.
2974 * If max_num_of_descriptors = (BAM_PIPE_DESCRIPTOR_FIFO_SIZE / sizeof
2975 * (SPS_DESCRIPTOR)), then (max_num_of_descriptors - 1) descriptors can
2976 * be provided at once.
2977 * Because of above requirement, one extra descriptor will be added to
2978 * make sure hardware always has one descriptor.
2979 */
2980 desc_fifo_sz = hdd_ipa->hdd_ctx->config->IpaDescSize
2981 + sizeof(struct sps_iovec);
2982
2983 /*setup TX pipes */
2984 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
2985 ipa = &hdd_ipa->sys_pipe[i].ipa_sys_params;
2986
2987 ipa->client = hdd_ipa_adapter_2_client[i].cons_client;
2988 ipa->desc_fifo_sz = desc_fifo_sz;
2989 ipa->priv = &hdd_ipa->iface_context[i];
2990 ipa->notify = hdd_ipa_i2w_cb;
2991
2992 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
2993 ipa->ipa_ep_cfg.hdr.hdr_len =
2994 HDD_IPA_UC_WLAN_TX_HDR_LEN;
2995 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
2996 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
2997 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
2998 ipa->ipa_ep_cfg.hdr.hdr_additional_const_len =
2999 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
3000 ipa->ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
3001 } else {
3002 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
3003 }
3004 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
3005
3006 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
3007 ipa->keep_ipa_awake = 1;
3008
3009 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
3010 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303011 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed for pipe %d"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003012 " ret: %d", i, ret);
3013 goto setup_sys_pipe_fail;
3014 }
3015 hdd_ipa->sys_pipe[i].conn_hdl_valid = 1;
3016 }
3017
3018 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
3019 /*
3020 * Hard code it here, this can be extended if in case
3021 * PROD pipe is also per interface.
3022 * Right now there is no advantage of doing this.
3023 */
3024 hdd_ipa->prod_client = IPA_CLIENT_WLAN1_PROD;
3025
3026 ipa = &hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].ipa_sys_params;
3027
3028 ipa->client = hdd_ipa->prod_client;
3029
3030 ipa->desc_fifo_sz = desc_fifo_sz;
3031 ipa->priv = hdd_ipa;
3032 ipa->notify = hdd_ipa_w2i_cb;
3033
3034 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
3035 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_RX_HDR_LEN;
3036 ipa->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
3037 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
3038
3039 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
3040 ipa->keep_ipa_awake = 1;
3041
3042 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
3043 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303044 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003045 "Failed for RX pipe: %d", ret);
3046 goto setup_sys_pipe_fail;
3047 }
3048 hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].conn_hdl_valid = 1;
3049 }
3050
3051 return ret;
3052
3053setup_sys_pipe_fail:
3054
3055 while (--i >= 0) {
3056 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].conn_hdl);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303057 qdf_mem_zero(&hdd_ipa->sys_pipe[i],
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003058 sizeof(struct hdd_ipa_sys_pipe));
3059 }
3060
3061 return ret;
3062}
3063
3064/**
3065 * hdd_ipa_teardown_sys_pipe() - Tear down all IPA Sys pipes
3066 * @hdd_ipa: Global HDD IPA context
3067 *
3068 * Return: None
3069 */
3070static void hdd_ipa_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
3071{
3072 int ret = 0, i;
3073 for (i = 0; i < HDD_IPA_MAX_SYSBAM_PIPE; i++) {
3074 if (hdd_ipa->sys_pipe[i].conn_hdl_valid) {
3075 ret =
3076 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].
3077 conn_hdl);
3078 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303079 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003080 ret);
3081
3082 hdd_ipa->sys_pipe[i].conn_hdl_valid = 0;
3083 }
3084 }
3085}
3086
3087/**
3088 * hdd_ipa_register_interface() - register IPA interface
3089 * @hdd_ipa: Global IPA context
3090 * @iface_context: Per-interface IPA context
3091 *
3092 * Return: 0 on success, negative errno on error
3093 */
3094static int hdd_ipa_register_interface(struct hdd_ipa_priv *hdd_ipa,
3095 struct hdd_ipa_iface_context
3096 *iface_context)
3097{
3098 struct ipa_tx_intf tx_intf;
3099 struct ipa_rx_intf rx_intf;
3100 struct ipa_ioc_tx_intf_prop *tx_prop = NULL;
3101 struct ipa_ioc_rx_intf_prop *rx_prop = NULL;
3102 char *ifname = iface_context->adapter->dev->name;
3103
3104 char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX];
3105 char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX];
3106
3107 int num_prop = 1;
3108 int ret = 0;
3109
3110 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx))
3111 num_prop++;
3112
3113 /* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */
3114 tx_prop =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303115 qdf_mem_malloc(sizeof(struct ipa_ioc_tx_intf_prop) * num_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003116 if (!tx_prop) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303117 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "tx_prop allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003118 goto register_interface_fail;
3119 }
3120
3121 /* Allocate RX properties, 1 each for IPv4 & IPv6 */
3122 rx_prop =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303123 qdf_mem_malloc(sizeof(struct ipa_ioc_rx_intf_prop) * num_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003124 if (!rx_prop) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303125 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "rx_prop allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003126 goto register_interface_fail;
3127 }
3128
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303129 qdf_mem_zero(&tx_intf, sizeof(tx_intf));
3130 qdf_mem_zero(&rx_intf, sizeof(rx_intf));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003131
3132 snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
3133 ifname, HDD_IPA_IPV4_NAME_EXT);
3134 snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
3135 ifname, HDD_IPA_IPV6_NAME_EXT);
3136
3137 rx_prop[IPA_IP_v4].ip = IPA_IP_v4;
3138 rx_prop[IPA_IP_v4].src_pipe = iface_context->prod_client;
3139 rx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3140 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
3141
3142 /*
3143 * Interface ID is 3rd byte in the CLD header. Add the meta data and
3144 * mask to identify the interface in IPA hardware
3145 */
3146 rx_prop[IPA_IP_v4].attrib.meta_data =
3147 htonl(iface_context->adapter->sessionId << 16);
3148 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
3149
3150 rx_intf.num_props++;
3151 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3152 rx_prop[IPA_IP_v6].ip = IPA_IP_v6;
3153 rx_prop[IPA_IP_v6].src_pipe = iface_context->prod_client;
3154 rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3155 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
3156 rx_prop[IPA_IP_v4].attrib.meta_data =
3157 htonl(iface_context->adapter->sessionId << 16);
3158 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
3159
3160 rx_intf.num_props++;
3161 }
3162
3163 tx_prop[IPA_IP_v4].ip = IPA_IP_v4;
3164 tx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3165 tx_prop[IPA_IP_v4].dst_pipe = IPA_CLIENT_WLAN1_CONS;
3166 tx_prop[IPA_IP_v4].alt_dst_pipe = iface_context->cons_client;
3167 strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name,
3168 IPA_RESOURCE_NAME_MAX);
3169 tx_intf.num_props++;
3170
3171 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3172 tx_prop[IPA_IP_v6].ip = IPA_IP_v6;
3173 tx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3174 tx_prop[IPA_IP_v6].dst_pipe = IPA_CLIENT_WLAN1_CONS;
3175 tx_prop[IPA_IP_v6].alt_dst_pipe = iface_context->cons_client;
3176 strlcpy(tx_prop[IPA_IP_v6].hdr_name, ipv6_hdr_name,
3177 IPA_RESOURCE_NAME_MAX);
3178 tx_intf.num_props++;
3179 }
3180
3181 tx_intf.prop = tx_prop;
3182 rx_intf.prop = rx_prop;
3183
3184 /* Call the ipa api to register interface */
3185 ret = ipa_register_intf(ifname, &tx_intf, &rx_intf);
3186
3187register_interface_fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303188 qdf_mem_free(tx_prop);
3189 qdf_mem_free(rx_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003190 return ret;
3191}
3192
3193/**
3194 * hdd_remove_ipa_header() - Remove a specific header from IPA
3195 * @name: Name of the header to be removed
3196 *
3197 * Return: None
3198 */
3199static void hdd_ipa_remove_header(char *name)
3200{
3201 struct ipa_ioc_get_hdr hdrlookup;
3202 int ret = 0, len;
3203 struct ipa_ioc_del_hdr *ipa_hdr;
3204
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303205 qdf_mem_zero(&hdrlookup, sizeof(hdrlookup));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003206 strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name));
3207 ret = ipa_get_hdr(&hdrlookup);
3208 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303209 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Hdr deleted already %s, %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003210 name, ret);
3211 return;
3212 }
3213
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303214 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "hdl: 0x%x", hdrlookup.hdl);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003215 len = sizeof(struct ipa_ioc_del_hdr) + sizeof(struct ipa_hdr_del) * 1;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303216 ipa_hdr = (struct ipa_ioc_del_hdr *)qdf_mem_malloc(len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003217 if (ipa_hdr == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303218 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "ipa_hdr allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003219 return;
3220 }
3221 ipa_hdr->num_hdls = 1;
3222 ipa_hdr->commit = 0;
3223 ipa_hdr->hdl[0].hdl = hdrlookup.hdl;
3224 ipa_hdr->hdl[0].status = -1;
3225 ret = ipa_del_hdr(ipa_hdr);
3226 if (ret != 0)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303227 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Delete header failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003228 ret);
3229
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303230 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003231}
3232
3233/**
3234 * hdd_ipa_add_header_info() - Add IPA header for a given interface
3235 * @hdd_ipa: Global HDD IPA context
3236 * @iface_context: Interface-specific HDD IPA context
3237 * @mac_addr: Interface MAC address
3238 *
3239 * Return: 0 on success, negativer errno value on error
3240 */
3241static int hdd_ipa_add_header_info(struct hdd_ipa_priv *hdd_ipa,
3242 struct hdd_ipa_iface_context *iface_context,
3243 uint8_t *mac_addr)
3244{
3245 hdd_adapter_t *adapter = iface_context->adapter;
3246 char *ifname;
3247 struct ipa_ioc_add_hdr *ipa_hdr = NULL;
3248 int ret = -EINVAL;
3249 struct hdd_ipa_tx_hdr *tx_hdr = NULL;
3250 struct hdd_ipa_uc_tx_hdr *uc_tx_hdr = NULL;
3251
3252 ifname = adapter->dev->name;
3253
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303254 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Add Partial hdr: %s, %pM",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003255 ifname, mac_addr);
3256
3257 /* dynamically allocate the memory to add the hdrs */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303258 ipa_hdr = qdf_mem_malloc(sizeof(struct ipa_ioc_add_hdr)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003259 + sizeof(struct ipa_hdr_add));
3260 if (!ipa_hdr) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303261 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003262 "%s: ipa_hdr allocation failed", ifname);
3263 ret = -ENOMEM;
3264 goto end;
3265 }
3266
3267 ipa_hdr->commit = 0;
3268 ipa_hdr->num_hdrs = 1;
3269
3270 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3271 uc_tx_hdr = (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3272 memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN);
3273 memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3274 uc_tx_hdr->ipa_hd.vdev_id = iface_context->adapter->sessionId;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303275 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003276 "ifname=%s, vdev_id=%d",
3277 ifname, uc_tx_hdr->ipa_hd.vdev_id);
3278 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3279 ifname, HDD_IPA_IPV4_NAME_EXT);
3280 ipa_hdr->hdr[0].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
3281 ipa_hdr->hdr[0].type = IPA_HDR_L2_ETHERNET_II;
3282 ipa_hdr->hdr[0].is_partial = 1;
3283 ipa_hdr->hdr[0].hdr_hdl = 0;
3284 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3285 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3286
3287 ret = ipa_add_hdr(ipa_hdr);
3288 } else {
3289 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3290
3291 /* Set the Source MAC */
3292 memcpy(tx_hdr, &ipa_tx_hdr, HDD_IPA_WLAN_TX_HDR_LEN);
3293 memcpy(tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3294
3295 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3296 ifname, HDD_IPA_IPV4_NAME_EXT);
3297 ipa_hdr->hdr[0].hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
3298 ipa_hdr->hdr[0].is_partial = 1;
3299 ipa_hdr->hdr[0].hdr_hdl = 0;
3300 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3301 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3302
3303 /* Set the type to IPV4 in the header */
3304 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IP);
3305
3306 ret = ipa_add_hdr(ipa_hdr);
3307 }
3308 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303309 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s IPv4 add hdr failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003310 ifname, ret);
3311 goto end;
3312 }
3313
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303314 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: IPv4 hdr_hdl: 0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003315 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3316
3317 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3318 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3319 ifname, HDD_IPA_IPV6_NAME_EXT);
3320
3321 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3322 uc_tx_hdr =
3323 (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3324 uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6);
3325 } else {
3326 /* Set the type to IPV6 in the header */
3327 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3328 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IPV6);
3329 }
3330
3331 ret = ipa_add_hdr(ipa_hdr);
3332 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303333 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003334 "%s: IPv6 add hdr failed: %d", ifname, ret);
3335 goto clean_ipv4_hdr;
3336 }
3337
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303338 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: IPv6 hdr_hdl: 0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003339 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3340 }
3341
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303342 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003343
3344 return ret;
3345
3346clean_ipv4_hdr:
3347 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3348 ifname, HDD_IPA_IPV4_NAME_EXT);
3349 hdd_ipa_remove_header(ipa_hdr->hdr[0].name);
3350end:
3351 if (ipa_hdr)
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303352 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003353
3354 return ret;
3355}
3356
3357/**
3358 * hdd_ipa_clean_hdr() - Cleanup IPA on a given adapter
3359 * @adapter: Adapter upon which IPA was previously configured
3360 *
3361 * Return: None
3362 */
3363static void hdd_ipa_clean_hdr(hdd_adapter_t *adapter)
3364{
3365 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3366 int ret;
3367 char name_ipa[IPA_RESOURCE_NAME_MAX];
3368
3369 /* Remove the headers */
3370 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3371 adapter->dev->name, HDD_IPA_IPV4_NAME_EXT);
3372 hdd_ipa_remove_header(name_ipa);
3373
3374 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3375 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3376 adapter->dev->name, HDD_IPA_IPV6_NAME_EXT);
3377 hdd_ipa_remove_header(name_ipa);
3378 }
3379 /* unregister the interface with IPA */
3380 ret = ipa_deregister_intf(adapter->dev->name);
3381 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303382 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003383 "%s: ipa_deregister_intf fail: %d",
3384 adapter->dev->name, ret);
3385}
3386
3387/**
3388 * hdd_ipa_cleanup_iface() - Cleanup IPA on a given interface
3389 * @iface_context: interface-specific IPA context
3390 *
3391 * Return: None
3392 */
3393static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context)
3394{
3395 if (iface_context == NULL)
3396 return;
3397
3398 hdd_ipa_clean_hdr(iface_context->adapter);
3399
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303400 qdf_spin_lock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003401 iface_context->adapter->ipa_context = NULL;
3402 iface_context->adapter = NULL;
3403 iface_context->tl_context = NULL;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303404 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003405 iface_context->ifa_address = 0;
3406 if (!iface_context->hdd_ipa->num_iface) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303407 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003408 "NUM INTF 0, Invalid");
Anurag Chouhandf2b2682016-02-29 14:15:27 +05303409 QDF_ASSERT(0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003410 }
3411 iface_context->hdd_ipa->num_iface--;
3412}
3413
3414/**
3415 * hdd_ipa_setup_iface() - Setup IPA on a given interface
3416 * @hdd_ipa: HDD IPA global context
3417 * @adapter: Interface upon which IPA is being setup
3418 * @sta_id: Station ID of the API instance
3419 *
3420 * Return: 0 on success, negative errno value on error
3421 */
3422static int hdd_ipa_setup_iface(struct hdd_ipa_priv *hdd_ipa,
3423 hdd_adapter_t *adapter, uint8_t sta_id)
3424{
3425 struct hdd_ipa_iface_context *iface_context = NULL;
3426 void *tl_context = NULL;
3427 int i, ret = 0;
3428
3429 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
3430 * channel change indication. Since these indications are sent by lower
3431 * layer as SAP updates and IPA doesn't have to do anything for these
3432 * updates so ignoring!
3433 */
Krunal Sonibe766b02016-03-10 13:00:44 -08003434 if (QDF_SAP_MODE == adapter->device_mode && adapter->ipa_context)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003435 return 0;
3436
3437 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
3438 if (hdd_ipa->iface_context[i].adapter == NULL) {
3439 iface_context = &(hdd_ipa->iface_context[i]);
3440 break;
3441 }
3442 }
3443
3444 if (iface_context == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303445 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003446 "All the IPA interfaces are in use");
3447 ret = -ENOMEM;
3448 goto end;
3449 }
3450
3451 adapter->ipa_context = iface_context;
3452 iface_context->adapter = adapter;
3453 iface_context->sta_id = sta_id;
3454 tl_context = ol_txrx_get_vdev_by_sta_id(sta_id);
3455
3456 if (tl_context == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303457 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003458 "Not able to get TL context sta_id: %d", sta_id);
3459 ret = -EINVAL;
3460 goto end;
3461 }
3462
3463 iface_context->tl_context = tl_context;
3464
3465 ret = hdd_ipa_add_header_info(hdd_ipa, iface_context,
3466 adapter->dev->dev_addr);
3467
3468 if (ret)
3469 goto end;
3470
3471 /* Configure the TX and RX pipes filter rules */
3472 ret = hdd_ipa_register_interface(hdd_ipa, iface_context);
3473 if (ret)
3474 goto cleanup_header;
3475
3476 hdd_ipa->num_iface++;
3477 return ret;
3478
3479cleanup_header:
3480
3481 hdd_ipa_clean_hdr(adapter);
3482end:
3483 if (iface_context)
3484 hdd_ipa_cleanup_iface(iface_context);
3485 return ret;
3486}
3487
3488/**
3489 * hdd_ipa_msg_free_fn() - Free an IPA message
3490 * @buff: pointer to the IPA message
3491 * @len: length of the IPA message
3492 * @type: type of IPA message
3493 *
3494 * Return: None
3495 */
3496static void hdd_ipa_msg_free_fn(void *buff, uint32_t len, uint32_t type)
3497{
3498 hddLog(LOG1, "msg type:%d, len:%d", type, len);
3499 ghdd_ipa->stats.num_free_msg++;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303500 qdf_mem_free(buff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003501}
3502
3503/**
3504 * hdd_ipa_send_mcc_scc_msg() - send IPA WLAN_SWITCH_TO_MCC/SCC message
3505 * @mcc_mode: 0=MCC/1=SCC
3506 *
3507 * Return: 0 on success, negative errno value on error
3508 */
3509int hdd_ipa_send_mcc_scc_msg(hdd_context_t *pHddCtx, bool mcc_mode)
3510{
3511 hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303512 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003513 hdd_adapter_t *pAdapter;
3514 struct ipa_msg_meta meta;
3515 struct ipa_wlan_msg *msg;
3516 int ret;
3517
3518 if (!hdd_ipa_uc_sta_is_enabled(pHddCtx))
3519 return -EINVAL;
3520
3521 if (!pHddCtx->mcc_mode) {
3522 /* Flush TxRx queue for each adapter before switch to SCC */
3523 status = hdd_get_front_adapter(pHddCtx, &adapter_node);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303524 while (NULL != adapter_node && QDF_STATUS_SUCCESS == status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003525 pAdapter = adapter_node->pAdapter;
Krunal Sonibe766b02016-03-10 13:00:44 -08003526 if (pAdapter->device_mode == QDF_STA_MODE ||
3527 pAdapter->device_mode == QDF_SAP_MODE) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303528 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003529 "MCC->SCC: Flush TxRx queue(d_mode=%d)",
3530 pAdapter->device_mode);
3531 hdd_deinit_tx_rx(pAdapter);
3532 }
3533 status = hdd_get_next_adapter(
3534 pHddCtx, adapter_node, &next);
3535 adapter_node = next;
3536 }
3537 }
3538
3539 /* Send SCC/MCC Switching event to IPA */
3540 meta.msg_len = sizeof(*msg);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303541 msg = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003542 if (msg == NULL) {
3543 hddLog(LOGE, "msg allocation failed");
3544 return -ENOMEM;
3545 }
3546
3547 meta.msg_type = mcc_mode ?
3548 WLAN_SWITCH_TO_MCC : WLAN_SWITCH_TO_SCC;
3549 hddLog(LOG1, "ipa_send_msg(Evt:%d)", meta.msg_type);
3550
3551 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3552
3553 if (ret) {
3554 hddLog(LOGE, "ipa_send_msg(Evt:%d) - fail=%d",
3555 meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303556 qdf_mem_free(msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003557 }
3558
3559 return ret;
3560}
3561
3562/**
3563 * hdd_ipa_wlan_event_to_str() - convert IPA WLAN event to string
3564 * @event: IPA WLAN event to be converted to a string
3565 *
3566 * Return: ASCII string representing the IPA WLAN event
3567 */
3568static inline char *hdd_ipa_wlan_event_to_str(enum ipa_wlan_event event)
3569{
3570 switch (event) {
3571 case WLAN_CLIENT_CONNECT:
3572 return "WLAN_CLIENT_CONNECT";
3573 case WLAN_CLIENT_DISCONNECT:
3574 return "WLAN_CLIENT_DISCONNECT";
3575 case WLAN_CLIENT_POWER_SAVE_MODE:
3576 return "WLAN_CLIENT_POWER_SAVE_MODE";
3577 case WLAN_CLIENT_NORMAL_MODE:
3578 return "WLAN_CLIENT_NORMAL_MODE";
3579 case SW_ROUTING_ENABLE:
3580 return "SW_ROUTING_ENABLE";
3581 case SW_ROUTING_DISABLE:
3582 return "SW_ROUTING_DISABLE";
3583 case WLAN_AP_CONNECT:
3584 return "WLAN_AP_CONNECT";
3585 case WLAN_AP_DISCONNECT:
3586 return "WLAN_AP_DISCONNECT";
3587 case WLAN_STA_CONNECT:
3588 return "WLAN_STA_CONNECT";
3589 case WLAN_STA_DISCONNECT:
3590 return "WLAN_STA_DISCONNECT";
3591 case WLAN_CLIENT_CONNECT_EX:
3592 return "WLAN_CLIENT_CONNECT_EX";
3593
3594 case IPA_WLAN_EVENT_MAX:
3595 default:
3596 return "UNKNOWN";
3597 }
3598}
3599
3600/**
3601 * hdd_ipa_wlan_evt() - IPA event handler
3602 * @adapter: adapter upon which the event was received
3603 * @sta_id: station id for the event
3604 * @type: the event type
3605 * @mac_address: MAC address associated with the event
3606 *
3607 * Return: 0 on success, negative errno value on error
3608 */
3609int hdd_ipa_wlan_evt(hdd_adapter_t *adapter, uint8_t sta_id,
3610 enum ipa_wlan_event type, uint8_t *mac_addr)
3611{
3612 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3613 struct ipa_msg_meta meta;
3614 struct ipa_wlan_msg *msg;
3615 struct ipa_wlan_msg_ex *msg_ex = NULL;
3616 int ret;
3617
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303618 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: %s evt, MAC: %pM sta_id: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003619 adapter->dev->name, hdd_ipa_wlan_event_to_str(type),
3620 mac_addr, sta_id);
3621
3622 if (type >= IPA_WLAN_EVENT_MAX)
3623 return -EINVAL;
3624
3625 if (WARN_ON(is_zero_ether_addr(mac_addr)))
3626 return -EINVAL;
3627
3628 if (!hdd_ipa || !hdd_ipa_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303629 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "IPA OFFLOAD NOT ENABLED");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003630 return -EINVAL;
3631 }
3632
3633 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3634 !hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
Krunal Sonibe766b02016-03-10 13:00:44 -08003635 (QDF_SAP_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003636 return 0;
3637 }
3638
3639 /*
3640 * During IPA UC resource loading/unloading new events can be issued.
3641 * Store the events separately and handle them later.
3642 */
3643 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3644 ((hdd_ipa->resource_loading) ||
3645 (hdd_ipa->resource_unloading))) {
Yun Parkf19e07d2015-11-20 11:34:27 -08003646 unsigned int pending_event_count;
3647 struct ipa_uc_pending_event *pending_event = NULL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003648
Yun Parkf19e07d2015-11-20 11:34:27 -08003649 hdd_err("IPA resource %s inprogress",
3650 hdd_ipa->resource_loading ? "load":"unload");
3651
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303652 qdf_mutex_acquire(&hdd_ipa->event_lock);
Yun Parkf19e07d2015-11-20 11:34:27 -08003653
Anurag Chouhanffb21542016-02-17 14:33:03 +05303654 pending_event_count = qdf_list_size(&hdd_ipa->pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08003655 if (pending_event_count >= HDD_IPA_MAX_PENDING_EVENT_COUNT) {
3656 hdd_notice("Reached max pending event count");
Anurag Chouhanffb21542016-02-17 14:33:03 +05303657 qdf_list_remove_front(&hdd_ipa->pending_event,
3658 (qdf_list_node_t **)&pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08003659 } else {
3660 pending_event =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303661 (struct ipa_uc_pending_event *)qdf_mem_malloc(
Yun Parkf19e07d2015-11-20 11:34:27 -08003662 sizeof(struct ipa_uc_pending_event));
3663 }
3664
3665 if (!pending_event) {
3666 hdd_err("Pending event memory alloc fail");
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303667 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003668 return -ENOMEM;
3669 }
Yun Parkf19e07d2015-11-20 11:34:27 -08003670
3671 pending_event->adapter = adapter;
3672 pending_event->sta_id = sta_id;
3673 pending_event->type = type;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303674 qdf_mem_copy(pending_event->mac_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003675 mac_addr,
Anurag Chouhan6d760662016-02-20 16:05:43 +05303676 QDF_MAC_ADDR_SIZE);
Anurag Chouhanffb21542016-02-17 14:33:03 +05303677 qdf_list_insert_back(&hdd_ipa->pending_event,
Yun Parkf19e07d2015-11-20 11:34:27 -08003678 &pending_event->node);
3679
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303680 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003681 return 0;
3682 }
3683
3684 hdd_ipa->stats.event[type]++;
3685
Leo Chang3bc8fed2015-11-13 10:59:47 -08003686 meta.msg_type = type;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003687 switch (type) {
3688 case WLAN_STA_CONNECT:
3689 /* STA already connected and without disconnect, connect again
3690 * This is Roaming scenario
3691 */
3692 if (hdd_ipa->sta_connected)
3693 hdd_ipa_cleanup_iface(adapter->ipa_context);
3694
3695 if ((hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) &&
3696 (!hdd_ipa->sta_connected))
3697 hdd_ipa_uc_offload_enable_disable(adapter,
3698 SIR_STA_RX_DATA_OFFLOAD, 1);
3699
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303700 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003701
3702 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303703 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003704 "%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED",
3705 msg_ex->name, meta.msg_type);
3706 } else if ((!hdd_ipa->sap_num_connected_sta) &&
3707 (!hdd_ipa->sta_connected)) {
3708 /* Enable IPA UC TX PIPE when STA connected */
3709 ret = hdd_ipa_uc_handle_first_con(hdd_ipa);
Yun Park4cab6ee2015-10-27 11:43:40 -07003710 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303711 qdf_mutex_release(&hdd_ipa->event_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303712 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003713 "handle 1st con ret %d", ret);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003714 hdd_ipa_uc_offload_enable_disable(adapter,
3715 SIR_STA_RX_DATA_OFFLOAD, 0);
3716 goto end;
3717 }
3718 }
3719 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3720 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303721 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003722 hdd_ipa_uc_offload_enable_disable(adapter,
3723 SIR_STA_RX_DATA_OFFLOAD, 0);
3724 goto end;
3725
3726#ifdef IPA_UC_OFFLOAD
3727 vdev_to_iface[adapter->sessionId] =
3728 ((struct hdd_ipa_iface_context *)
3729 (adapter->ipa_context))->iface_id;
3730#endif /* IPA_UC_OFFLOAD */
3731 }
3732
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303733 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003734
3735 hdd_ipa->sta_connected = 1;
3736 break;
3737
3738 case WLAN_AP_CONNECT:
3739 /* For DFS channel we get two start_bss event (before and after
3740 * CAC). Also when ACS range includes both DFS and non DFS
3741 * channels, we could possibly change channel many times due to
3742 * RADAR detection and chosen channel may not be a DFS channels.
3743 * So dont return error here. Just discard the event.
3744 */
3745 if (adapter->ipa_context)
3746 return 0;
3747
3748 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3749 hdd_ipa_uc_offload_enable_disable(adapter,
3750 SIR_AP_RX_DATA_OFFLOAD, 1);
3751 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303752 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003753 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3754 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303755 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003756 "%s: Evt: %d, Interface setup failed",
3757 msg_ex->name, meta.msg_type);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303758 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003759 goto end;
3760
3761#ifdef IPA_UC_OFFLOAD
3762 vdev_to_iface[adapter->sessionId] =
3763 ((struct hdd_ipa_iface_context *)
3764 (adapter->ipa_context))->iface_id;
3765#endif /* IPA_UC_OFFLOAD */
3766 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303767 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003768 break;
3769
3770 case WLAN_STA_DISCONNECT:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303771 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003772 hdd_ipa_cleanup_iface(adapter->ipa_context);
3773
3774 if (!hdd_ipa->sta_connected) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303775 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003776 "%s: Evt: %d, STA already disconnected",
3777 msg_ex->name, meta.msg_type);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303778 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003779 return -EINVAL;
3780 }
3781 hdd_ipa->sta_connected = 0;
3782 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303783 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003784 "%s: IPA UC OFFLOAD NOT ENABLED",
3785 msg_ex->name);
3786 } else {
3787 /* Disable IPA UC TX PIPE when STA disconnected */
3788 if ((!hdd_ipa->sap_num_connected_sta) ||
3789 ((!hdd_ipa->num_iface) &&
3790 (HDD_IPA_UC_NUM_WDI_PIPE ==
3791 hdd_ipa->activated_fw_pipe))) {
3792 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3793 }
3794 }
3795
3796 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
3797 hdd_ipa_uc_offload_enable_disable(adapter,
3798 SIR_STA_RX_DATA_OFFLOAD, 0);
3799 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3800 }
3801
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303802 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003803 break;
3804
3805 case WLAN_AP_DISCONNECT:
3806 if (!adapter->ipa_context) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303807 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003808 "%s: Evt: %d, SAP already disconnected",
3809 msg_ex->name, meta.msg_type);
3810 return -EINVAL;
3811 }
3812
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303813 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003814 hdd_ipa_cleanup_iface(adapter->ipa_context);
3815 if ((!hdd_ipa->num_iface) &&
3816 (HDD_IPA_UC_NUM_WDI_PIPE ==
3817 hdd_ipa->activated_fw_pipe)) {
Prashanth Bhatta9e143052015-12-04 11:56:47 -08003818 if (cds_is_driver_unloading()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003819 /*
3820 * We disable WDI pipes directly here since
3821 * IPA_OPCODE_TX/RX_SUSPEND message will not be
3822 * processed when unloading WLAN driver is in
3823 * progress
3824 */
3825 hdd_ipa_uc_disable_pipes(hdd_ipa);
3826 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303827 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003828 "NO INTF left but still pipe clean up");
3829 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3830 }
3831 }
3832
3833 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3834 hdd_ipa_uc_offload_enable_disable(adapter,
3835 SIR_AP_RX_DATA_OFFLOAD, 0);
3836 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3837 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303838 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003839 break;
3840
3841 case WLAN_CLIENT_CONNECT_EX:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303842 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%d %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003843 adapter->dev->ifindex, sta_id);
3844
3845 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303846 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003847 "%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED",
3848 adapter->dev->name, meta.msg_type);
3849 return 0;
3850 }
3851
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303852 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003853 if (hdd_ipa_uc_find_add_assoc_sta(hdd_ipa,
3854 true, sta_id)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303855 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003856 "%s: STA ID %d found, not valid",
3857 adapter->dev->name, sta_id);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303858 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003859 return 0;
3860 }
Yun Park312f71a2015-12-08 10:22:42 -08003861
3862 /* Enable IPA UC Data PIPEs when first STA connected */
3863 if ((0 == hdd_ipa->sap_num_connected_sta) &&
3864 (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) ||
3865 !hdd_ipa->sta_connected)) {
3866 ret = hdd_ipa_uc_handle_first_con(hdd_ipa);
3867 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303868 qdf_mutex_release(&hdd_ipa->event_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303869 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park312f71a2015-12-08 10:22:42 -08003870 "%s: handle 1st con ret %d",
3871 adapter->dev->name, ret);
3872 return ret;
3873 }
3874 }
3875
3876 hdd_ipa->sap_num_connected_sta++;
Yun Park312f71a2015-12-08 10:22:42 -08003877
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303878 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003879
3880 meta.msg_type = type;
3881 meta.msg_len = (sizeof(struct ipa_wlan_msg_ex) +
3882 sizeof(struct ipa_wlan_hdr_attrib_val));
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303883 msg_ex = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003884
3885 if (msg_ex == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303886 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003887 "msg_ex allocation failed");
3888 return -ENOMEM;
3889 }
3890 strlcpy(msg_ex->name, adapter->dev->name,
3891 IPA_RESOURCE_NAME_MAX);
3892 msg_ex->num_of_attribs = 1;
3893 msg_ex->attribs[0].attrib_type = WLAN_HDR_ATTRIB_MAC_ADDR;
3894 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3895 msg_ex->attribs[0].offset =
3896 HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3897 } else {
3898 msg_ex->attribs[0].offset =
3899 HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3900 }
3901 memcpy(msg_ex->attribs[0].u.mac_addr, mac_addr,
3902 IPA_MAC_ADDR_SIZE);
3903
3904 ret = ipa_send_msg(&meta, msg_ex, hdd_ipa_msg_free_fn);
3905
3906 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303907 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d : %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003908 msg_ex->name, meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303909 qdf_mem_free(msg_ex);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003910 return ret;
3911 }
3912 hdd_ipa->stats.num_send_msg++;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003913 return ret;
3914
3915 case WLAN_CLIENT_DISCONNECT:
3916 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303917 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003918 "%s: IPA UC OFFLOAD NOT ENABLED",
3919 msg_ex->name);
3920 return 0;
3921 }
3922
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303923 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003924 if (!hdd_ipa_uc_find_add_assoc_sta(hdd_ipa, false, sta_id)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303925 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003926 "%s: STA ID %d NOT found, not valid",
3927 msg_ex->name, sta_id);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303928 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003929 return 0;
3930 }
3931 hdd_ipa->sap_num_connected_sta--;
3932 /* Disable IPA UC TX PIPE when last STA disconnected */
3933 if (!hdd_ipa->sap_num_connected_sta
3934 && (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) ||
3935 !hdd_ipa->sta_connected)
3936 && (false == hdd_ipa->resource_unloading)
3937 && (HDD_IPA_UC_NUM_WDI_PIPE ==
3938 hdd_ipa->activated_fw_pipe))
3939 hdd_ipa_uc_handle_last_discon(hdd_ipa);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303940 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003941 break;
3942
3943 default:
3944 return 0;
3945 }
3946
3947 meta.msg_len = sizeof(struct ipa_wlan_msg);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303948 msg = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003949 if (msg == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303950 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "msg allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003951 return -ENOMEM;
3952 }
3953
3954 meta.msg_type = type;
3955 strlcpy(msg->name, adapter->dev->name, IPA_RESOURCE_NAME_MAX);
3956 memcpy(msg->mac_addr, mac_addr, ETH_ALEN);
3957
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303958 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003959 msg->name, meta.msg_type);
3960
3961 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3962
3963 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303964 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d fail:%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003965 msg->name, meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303966 qdf_mem_free(msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003967 return ret;
3968 }
3969
3970 hdd_ipa->stats.num_send_msg++;
3971
3972end:
3973 return ret;
3974}
3975
3976/**
3977 * hdd_ipa_rm_state_to_str() - Convert IPA RM state to string
3978 * @state: IPA RM state value
3979 *
3980 * Return: ASCII string representing the IPA RM state
3981 */
3982static inline char *hdd_ipa_rm_state_to_str(enum hdd_ipa_rm_state state)
3983{
3984 switch (state) {
3985 case HDD_IPA_RM_RELEASED:
3986 return "RELEASED";
3987 case HDD_IPA_RM_GRANT_PENDING:
3988 return "GRANT_PENDING";
3989 case HDD_IPA_RM_GRANTED:
3990 return "GRANTED";
3991 }
3992
3993 return "UNKNOWN";
3994}
3995
3996/**
3997 * hdd_ipa_init() - IPA initialization function
3998 * @hdd_ctx: HDD global context
3999 *
4000 * Allocate hdd_ipa resources, ipa pipe resource and register
4001 * wlan interface with IPA module.
4002 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304003 * Return: QDF_STATUS enumeration
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004004 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304005QDF_STATUS hdd_ipa_init(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004006{
4007 struct hdd_ipa_priv *hdd_ipa = NULL;
4008 int ret, i;
4009 struct hdd_ipa_iface_context *iface_context = NULL;
4010
4011 if (!hdd_ipa_is_enabled(hdd_ctx))
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304012 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004013
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304014 hdd_ipa = qdf_mem_malloc(sizeof(*hdd_ipa));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004015 if (!hdd_ipa) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304016 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, "hdd_ipa allocation failed");
Leo Chang3bc8fed2015-11-13 10:59:47 -08004017 goto fail_return;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004018 }
4019
4020 hdd_ctx->hdd_ipa = hdd_ipa;
4021 ghdd_ipa = hdd_ipa;
4022 hdd_ipa->hdd_ctx = hdd_ctx;
4023 hdd_ipa->num_iface = 0;
Anurag Chouhan6d760662016-02-20 16:05:43 +05304024 ol_txrx_ipa_uc_get_resource(cds_get_context(QDF_MODULE_ID_TXRX),
Dhanashri Atreb08959a2016-03-01 17:28:03 -08004025 &hdd_ipa->ipa_resource);
4026 if ((0 == hdd_ipa->ipa_resource.ce_sr_base_paddr) ||
4027 (0 == hdd_ipa->ipa_resource.tx_comp_ring_base_paddr) ||
4028 (0 == hdd_ipa->ipa_resource.rx_rdy_ring_base_paddr) ||
4029 (0 == hdd_ipa->ipa_resource.rx2_rdy_ring_base_paddr)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304030 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Leo Chang3bc8fed2015-11-13 10:59:47 -08004031 "IPA UC resource alloc fail");
4032 goto fail_get_resource;
4033 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004034
4035 /* Create the interface context */
4036 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
4037 iface_context = &hdd_ipa->iface_context[i];
4038 iface_context->hdd_ipa = hdd_ipa;
4039 iface_context->cons_client =
4040 hdd_ipa_adapter_2_client[i].cons_client;
4041 iface_context->prod_client =
4042 hdd_ipa_adapter_2_client[i].prod_client;
4043 iface_context->iface_id = i;
4044 iface_context->adapter = NULL;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304045 qdf_spinlock_create(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004046 }
4047
4048#ifdef CONFIG_CNSS
4049 cnss_init_work(&hdd_ipa->pm_work, hdd_ipa_pm_send_pkt_to_tl);
4050#else
4051 INIT_WORK(&hdd_ipa->pm_work, hdd_ipa_pm_send_pkt_to_tl);
4052#endif
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304053 qdf_spinlock_create(&hdd_ipa->pm_lock);
Nirav Shahcbc6d722016-03-01 16:24:53 +05304054 qdf_nbuf_queue_init(&hdd_ipa->pm_queue_head);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004055
4056 ret = hdd_ipa_setup_rm(hdd_ipa);
4057 if (ret)
4058 goto fail_setup_rm;
4059
4060 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
4061 hdd_ipa_uc_rt_debug_init(hdd_ctx);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304062 qdf_mem_zero(&hdd_ipa->stats, sizeof(hdd_ipa->stats));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004063 hdd_ipa->sap_num_connected_sta = 0;
4064 hdd_ipa->ipa_tx_packets_diff = 0;
4065 hdd_ipa->ipa_rx_packets_diff = 0;
4066 hdd_ipa->ipa_p_tx_packets = 0;
4067 hdd_ipa->ipa_p_rx_packets = 0;
4068 hdd_ipa->resource_loading = false;
4069 hdd_ipa->resource_unloading = false;
4070 hdd_ipa->sta_connected = 0;
Leo Change3e49442015-10-26 20:07:13 -07004071 hdd_ipa->ipa_pipes_down = true;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004072 /* Setup IPA sys_pipe for MCC */
4073 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
4074 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
4075 if (ret)
4076 goto fail_create_sys_pipe;
4077 }
4078 hdd_ipa_uc_ol_init(hdd_ctx);
4079 } else {
4080 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
4081 if (ret)
4082 goto fail_create_sys_pipe;
4083 }
4084
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304085 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004086
4087fail_create_sys_pipe:
4088 hdd_ipa_destroy_rm_resource(hdd_ipa);
4089fail_setup_rm:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304090 qdf_spinlock_destroy(&hdd_ipa->pm_lock);
Leo Chang3bc8fed2015-11-13 10:59:47 -08004091fail_get_resource:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304092 qdf_mem_free(hdd_ipa);
Leo Chang3bc8fed2015-11-13 10:59:47 -08004093 hdd_ctx->hdd_ipa = NULL;
4094 ghdd_ipa = NULL;
4095fail_return:
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304096 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004097}
4098
4099/**
Yun Parkf19e07d2015-11-20 11:34:27 -08004100 * hdd_ipa_cleanup_pending_event() - Cleanup IPA pending event list
4101 * @hdd_ipa: pointer to HDD IPA struct
4102 *
4103 * Return: none
4104 */
4105void hdd_ipa_cleanup_pending_event(struct hdd_ipa_priv *hdd_ipa)
4106{
4107 struct ipa_uc_pending_event *pending_event = NULL;
4108
Anurag Chouhanffb21542016-02-17 14:33:03 +05304109 while (qdf_list_remove_front(&hdd_ipa->pending_event,
4110 (qdf_list_node_t **)&pending_event) == QDF_STATUS_SUCCESS) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304111 qdf_mem_free(pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08004112 }
4113
Anurag Chouhanffb21542016-02-17 14:33:03 +05304114 qdf_list_destroy(&hdd_ipa->pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08004115}
4116
4117/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004118 * hdd_ipa_cleanup - IPA cleanup function
4119 * @hdd_ctx: HDD global context
4120 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304121 * Return: QDF_STATUS enumeration
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004122 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304123QDF_STATUS hdd_ipa_cleanup(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004124{
4125 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
4126 int i;
4127 struct hdd_ipa_iface_context *iface_context = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05304128 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004129 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
4130
4131 if (!hdd_ipa_is_enabled(hdd_ctx))
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304132 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004133
4134 if (!hdd_ipa_uc_is_enabled(hdd_ctx)) {
4135 unregister_inetaddr_notifier(&hdd_ipa->ipv4_notifier);
4136 hdd_ipa_teardown_sys_pipe(hdd_ipa);
4137 }
4138
4139 /* Teardown IPA sys_pipe for MCC */
4140 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx))
4141 hdd_ipa_teardown_sys_pipe(hdd_ipa);
4142
4143 hdd_ipa_destroy_rm_resource(hdd_ipa);
4144
4145#ifdef WLAN_OPEN_SOURCE
4146 cancel_work_sync(&hdd_ipa->pm_work);
4147#endif
4148
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304149 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004150
Nirav Shahcbc6d722016-03-01 16:24:53 +05304151 while (((skb = qdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head))
4152 != NULL)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304153 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004154
4155 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
4156 ipa_free_skb(pm_tx_cb->ipa_tx_desc);
4157
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304158 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004159 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304160 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004161
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304162 qdf_spinlock_destroy(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004163
4164 /* destory the interface lock */
4165 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
4166 iface_context = &hdd_ipa->iface_context[i];
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304167 qdf_spinlock_destroy(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004168 }
4169
4170 /* This should never hit but still make sure that there are no pending
4171 * descriptor in IPA hardware
4172 */
4173 if (hdd_ipa->pending_hw_desc_cnt != 0) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304174 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004175 "IPA Pending write done: %d Waiting!",
4176 hdd_ipa->pending_hw_desc_cnt);
4177
4178 for (i = 0; hdd_ipa->pending_hw_desc_cnt != 0 && i < 10; i++) {
4179 usleep_range(100, 100);
4180 }
4181
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304182 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004183 "IPA Pending write done: desc: %d %s(%d)!",
4184 hdd_ipa->pending_hw_desc_cnt,
4185 hdd_ipa->pending_hw_desc_cnt == 0 ? "completed"
4186 : "leak", i);
4187 }
4188 if (hdd_ipa_uc_is_enabled(hdd_ctx)) {
4189 hdd_ipa_uc_rt_debug_deinit(hdd_ctx);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304190 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004191 "%s: Disconnect TX PIPE", __func__);
4192 ipa_disconnect_wdi_pipe(hdd_ipa->tx_pipe_handle);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304193 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004194 "%s: Disconnect RX PIPE", __func__);
4195 ipa_disconnect_wdi_pipe(hdd_ipa->rx_pipe_handle);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304196 qdf_mutex_destroy(&hdd_ipa->event_lock);
4197 qdf_mutex_destroy(&hdd_ipa->ipa_lock);
Yun Parkf19e07d2015-11-20 11:34:27 -08004198 hdd_ipa_cleanup_pending_event(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004199
4200#ifdef WLAN_OPEN_SOURCE
4201 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
4202 cancel_work_sync(&hdd_ipa->uc_op_work[i].work);
4203 hdd_ipa->uc_op_work[i].msg = NULL;
4204 }
4205#endif
4206 }
4207
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304208 qdf_mem_free(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004209 hdd_ctx->hdd_ipa = NULL;
4210
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304211 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004212}
4213#endif /* IPA_OFFLOAD */