blob: 7f6ed0b4593819dd0c0d74a017de23a33c6d3d64 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
2 * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
3 *
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>
51
52#include "cds_sched.h"
53
54#include "wma.h"
55#include "wma_api.h"
56
57#define HDD_IPA_DESC_BUFFER_RATIO 4
58#define HDD_IPA_IPV4_NAME_EXT "_ipv4"
59#define HDD_IPA_IPV6_NAME_EXT "_ipv6"
60
61#define HDD_IPA_RX_INACTIVITY_MSEC_DELAY 1000
62#define HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET 12
63#define HDD_IPA_UC_WLAN_8023_HDR_SIZE 14
64/* WDI TX and RX PIPE */
65#define HDD_IPA_UC_NUM_WDI_PIPE 2
66#define HDD_IPA_UC_MAX_PENDING_EVENT 33
67
68#define HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE 32000
69#define HDD_IPA_UC_RT_DEBUG_PERIOD 300
70#define HDD_IPA_UC_RT_DEBUG_BUF_COUNT 30
71#define HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL 10000
72
73#define HDD_IPA_WLAN_HDR_DES_MAC_OFFSET 0
74#define HDD_IPA_MAX_IFACE 3
75#define HDD_IPA_MAX_SYSBAM_PIPE 4
76#define HDD_IPA_RX_PIPE HDD_IPA_MAX_IFACE
77#define HDD_IPA_ENABLE_MASK BIT(0)
78#define HDD_IPA_PRE_FILTER_ENABLE_MASK BIT(1)
79#define HDD_IPA_IPV6_ENABLE_MASK BIT(2)
80#define HDD_IPA_RM_ENABLE_MASK BIT(3)
81#define HDD_IPA_CLK_SCALING_ENABLE_MASK BIT(4)
82#define HDD_IPA_UC_ENABLE_MASK BIT(5)
83#define HDD_IPA_UC_STA_ENABLE_MASK BIT(6)
84#define HDD_IPA_REAL_TIME_DEBUGGING BIT(8)
85
86typedef enum {
87 HDD_IPA_UC_OPCODE_TX_SUSPEND = 0,
88 HDD_IPA_UC_OPCODE_TX_RESUME = 1,
89 HDD_IPA_UC_OPCODE_RX_SUSPEND = 2,
90 HDD_IPA_UC_OPCODE_RX_RESUME = 3,
91 HDD_IPA_UC_OPCODE_STATS = 4,
92 /* keep this last */
93 HDD_IPA_UC_OPCODE_MAX
94} hdd_ipa_uc_op_code;
95
96/**
97 * enum - Reason codes for stat query
98 *
99 * @HDD_IPA_UC_STAT_REASON_NONE: Initial value
100 * @HDD_IPA_UC_STAT_REASON_DEBUG: For debug/info
101 * @HDD_IPA_UC_STAT_REASON_BW_CAL: For bandwidth calibration
102 */
103enum {
104 HDD_IPA_UC_STAT_REASON_NONE,
105 HDD_IPA_UC_STAT_REASON_DEBUG,
106 HDD_IPA_UC_STAT_REASON_BW_CAL
107};
108
109/**
110 * enum hdd_ipa_rm_state - IPA resource manager state
111 * @HDD_IPA_RM_RELEASED: PROD pipe resource released
112 * @HDD_IPA_RM_GRANT_PENDING: PROD pipe resource requested but not granted yet
113 * @HDD_IPA_RM_GRANTED: PROD pipe resource granted
114 */
115enum hdd_ipa_rm_state {
116 HDD_IPA_RM_RELEASED,
117 HDD_IPA_RM_GRANT_PENDING,
118 HDD_IPA_RM_GRANTED,
119};
120
121struct llc_snap_hdr {
122 uint8_t dsap;
123 uint8_t ssap;
124 uint8_t resv[4];
125 __be16 eth_type;
126} __packed;
127
128struct hdd_ipa_tx_hdr {
129 struct ethhdr eth;
130 struct llc_snap_hdr llc_snap;
131} __packed;
132
133struct frag_header {
134 uint32_t
135 length:16, /* length field is LSB of the FRAG DESC */
136 reserved16:16;
137 uint32_t reserved32;
138} __packed;
139
140struct ipa_header {
141 uint32_t
142 vdev_id:8, /* vdev_id field is LSB of IPA DESC */
143 reserved:24;
144} __packed;
145
146struct hdd_ipa_uc_tx_hdr {
147 struct frag_header frag_hd;
148 struct ipa_header ipa_hd;
149 struct ethhdr eth;
150} __packed;
151
152#define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header)
153#define HDD_IPA_WLAN_IPA_HEADER sizeof(struct frag_header)
154
155/**
156 * struct hdd_ipa_cld_hdr - IPA CLD Header
157 * @reserved: reserved fields
158 * @iface_id: interface ID
159 * @sta_id: Station ID
160 *
161 * Packed 32-bit structure
162 * +----------+----------+--------------+--------+
163 * | Reserved | QCMAP ID | interface id | STA ID |
164 * +----------+----------+--------------+--------+
165 */
166struct hdd_ipa_cld_hdr {
167 uint8_t reserved[2];
168 uint8_t iface_id;
169 uint8_t sta_id;
170} __packed;
171
172struct hdd_ipa_rx_hdr {
173 struct hdd_ipa_cld_hdr cld_hdr;
174 struct ethhdr eth;
175} __packed;
176
177struct hdd_ipa_pm_tx_cb {
178 struct hdd_ipa_iface_context *iface_context;
179 struct ipa_rx_data *ipa_tx_desc;
180};
181
182struct hdd_ipa_uc_rx_hdr {
183 struct ethhdr eth;
184} __packed;
185
186struct hdd_ipa_sys_pipe {
187 uint32_t conn_hdl;
188 uint8_t conn_hdl_valid;
189 struct ipa_sys_connect_params ipa_sys_params;
190};
191
192struct hdd_ipa_iface_stats {
193 uint64_t num_tx;
194 uint64_t num_tx_drop;
195 uint64_t num_tx_err;
196 uint64_t num_tx_cac_drop;
197 uint64_t num_rx_prefilter;
198 uint64_t num_rx_ipa_excep;
199 uint64_t num_rx_recv;
200 uint64_t num_rx_recv_mul;
201 uint64_t num_rx_send_desc_err;
202 uint64_t max_rx_mul;
203};
204
205struct hdd_ipa_priv;
206
207struct hdd_ipa_iface_context {
208 struct hdd_ipa_priv *hdd_ipa;
209 hdd_adapter_t *adapter;
210 void *tl_context;
211
212 enum ipa_client_type cons_client;
213 enum ipa_client_type prod_client;
214
215 uint8_t iface_id; /* This iface ID */
216 uint8_t sta_id; /* This iface station ID */
217 cdf_spinlock_t interface_lock;
218 uint32_t ifa_address;
219 struct hdd_ipa_iface_stats stats;
220};
221
222struct hdd_ipa_stats {
223 uint32_t event[IPA_WLAN_EVENT_MAX];
224 uint64_t num_send_msg;
225 uint64_t num_free_msg;
226
227 uint64_t num_rm_grant;
228 uint64_t num_rm_release;
229 uint64_t num_rm_grant_imm;
230 uint64_t num_cons_perf_req;
231 uint64_t num_prod_perf_req;
232
233 uint64_t num_rx_drop;
234 uint64_t num_rx_ipa_tx_dp;
235 uint64_t num_rx_ipa_splice;
236 uint64_t num_rx_ipa_loop;
237 uint64_t num_rx_ipa_tx_dp_err;
238 uint64_t num_rx_ipa_write_done;
239 uint64_t num_max_ipa_tx_mul;
240 uint64_t num_rx_ipa_hw_maxed_out;
241 uint64_t max_pend_q_cnt;
242
243 uint64_t num_tx_comp_cnt;
244 uint64_t num_tx_queued;
245 uint64_t num_tx_dequeued;
246 uint64_t num_max_pm_queue;
247
248 uint64_t num_freeq_empty;
249 uint64_t num_pri_freeq_empty;
250 uint64_t num_rx_excep;
251 uint64_t num_tx_bcmc;
252 uint64_t num_tx_bcmc_err;
253};
254
255struct ipa_uc_stas_map {
256 bool is_reserved;
257 uint8_t sta_id;
258};
259struct op_msg_type {
260 uint8_t msg_t;
261 uint8_t rsvd;
262 uint16_t op_code;
263 uint16_t len;
264 uint16_t rsvd_snd;
265};
266
267struct ipa_uc_fw_stats {
268 uint32_t tx_comp_ring_base;
269 uint32_t tx_comp_ring_size;
270 uint32_t tx_comp_ring_dbell_addr;
271 uint32_t tx_comp_ring_dbell_ind_val;
272 uint32_t tx_comp_ring_dbell_cached_val;
273 uint32_t tx_pkts_enqueued;
274 uint32_t tx_pkts_completed;
275 uint32_t tx_is_suspend;
276 uint32_t tx_reserved;
277 uint32_t rx_ind_ring_base;
278 uint32_t rx_ind_ring_size;
279 uint32_t rx_ind_ring_dbell_addr;
280 uint32_t rx_ind_ring_dbell_ind_val;
281 uint32_t rx_ind_ring_dbell_ind_cached_val;
282 uint32_t rx_ind_ring_rdidx_addr;
283 uint32_t rx_ind_ring_rd_idx_cached_val;
284 uint32_t rx_refill_idx;
285 uint32_t rx_num_pkts_indicated;
286 uint32_t rx_buf_refilled;
287 uint32_t rx_num_ind_drop_no_space;
288 uint32_t rx_num_ind_drop_no_buf;
289 uint32_t rx_is_suspend;
290 uint32_t rx_reserved;
291};
292
293struct ipa_uc_pending_event {
294 cdf_list_node_t node;
295 hdd_adapter_t *adapter;
296 enum ipa_wlan_event type;
297 uint8_t sta_id;
298 uint8_t mac_addr[CDF_MAC_ADDR_SIZE];
299};
300
301/**
302 * struct uc_rm_work_struct
303 * @work: uC RM work
304 * @event: IPA RM event
305 */
306struct uc_rm_work_struct {
307 struct work_struct work;
308 enum ipa_rm_event event;
309};
310
311/**
312 * struct uc_op_work_struct
313 * @work: uC OP work
314 * @msg: OP message
315 */
316struct uc_op_work_struct {
317 struct work_struct work;
318 struct op_msg_type *msg;
319};
320static uint8_t vdev_to_iface[CSR_ROAM_SESSION_MAX];
321
322/**
323 * struct uc_rt_debug_info
324 * @time: system time
325 * @ipa_excep_count: IPA exception packet count
326 * @rx_drop_count: IPA Rx drop packet count
327 * @net_sent_count: IPA Rx packet sent to network stack count
328 * @rx_discard_count: IPA Rx discard packet count
329 * @rx_mcbc_count: IPA Rx BCMC packet count
330 * @tx_mcbc_count: IPA Tx BCMC packet countt
331 * @tx_fwd_count: IPA Tx forward packet count
332 * @rx_destructor_call: IPA Rx packet destructor count
333 */
334struct uc_rt_debug_info {
335 v_TIME_t time;
336 uint64_t ipa_excep_count;
337 uint64_t rx_drop_count;
338 uint64_t net_sent_count;
339 uint64_t rx_discard_count;
340 uint64_t rx_mcbc_count;
341 uint64_t tx_mcbc_count;
342 uint64_t tx_fwd_count;
343 uint64_t rx_destructor_call;
344};
345
346struct hdd_ipa_priv {
347 struct hdd_ipa_sys_pipe sys_pipe[HDD_IPA_MAX_SYSBAM_PIPE];
348 struct hdd_ipa_iface_context iface_context[HDD_IPA_MAX_IFACE];
349 uint8_t num_iface;
350 enum hdd_ipa_rm_state rm_state;
351 /*
352 * IPA driver can send RM notifications with IRQ disabled so using cdf
353 * APIs as it is taken care gracefully. Without this, kernel would throw
354 * an warning if spin_lock_bh is used while IRQ is disabled
355 */
356 cdf_spinlock_t rm_lock;
357 struct uc_rm_work_struct uc_rm_work;
358 struct uc_op_work_struct uc_op_work[HDD_IPA_UC_OPCODE_MAX];
359 cdf_wake_lock_t wake_lock;
360 struct delayed_work wake_lock_work;
361 bool wake_lock_released;
362
363 enum ipa_client_type prod_client;
364
365 atomic_t tx_ref_cnt;
366 cdf_nbuf_queue_t pm_queue_head;
367 struct work_struct pm_work;
368 cdf_spinlock_t pm_lock;
369 bool suspended;
370
371 uint32_t pending_hw_desc_cnt;
372 uint32_t hw_desc_cnt;
373 spinlock_t q_lock;
374 uint32_t freeq_cnt;
375 struct list_head free_desc_head;
376
377 uint32_t pend_q_cnt;
378 struct list_head pend_desc_head;
379
380 hdd_context_t *hdd_ctx;
381
382 struct dentry *debugfs_dir;
383 struct hdd_ipa_stats stats;
384
385 struct notifier_block ipv4_notifier;
386 uint32_t curr_prod_bw;
387 uint32_t curr_cons_bw;
388
389 uint8_t activated_fw_pipe;
390 uint8_t sap_num_connected_sta;
391 uint8_t sta_connected;
392 uint32_t tx_pipe_handle;
393 uint32_t rx_pipe_handle;
394 bool resource_loading;
395 bool resource_unloading;
396 bool pending_cons_req;
397 struct ipa_uc_stas_map assoc_stas_map[WLAN_MAX_STA_COUNT];
398 cdf_list_t pending_event;
399 cdf_mutex_t event_lock;
400 uint32_t ipa_tx_packets_diff;
401 uint32_t ipa_rx_packets_diff;
402 uint32_t ipa_p_tx_packets;
403 uint32_t ipa_p_rx_packets;
404 uint32_t stat_req_reason;
405 uint64_t ipa_tx_forward;
406 uint64_t ipa_rx_discard;
407 uint64_t ipa_rx_net_send_count;
408 uint64_t ipa_rx_internel_drop_count;
409 uint64_t ipa_rx_destructor_count;
410 cdf_mc_timer_t rt_debug_timer;
411 struct uc_rt_debug_info rt_bug_buffer[HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
412 unsigned int rt_buf_fill_index;
413 cdf_mc_timer_t rt_debug_fill_timer;
414 cdf_mutex_t rt_debug_lock;
415};
416
417#define HDD_IPA_WLAN_CLD_HDR_LEN sizeof(struct hdd_ipa_cld_hdr)
418#define HDD_IPA_UC_WLAN_CLD_HDR_LEN 0
419#define HDD_IPA_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_tx_hdr)
420#define HDD_IPA_UC_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_uc_tx_hdr)
421#define HDD_IPA_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_rx_hdr)
422#define HDD_IPA_UC_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_uc_rx_hdr)
423
424#define HDD_IPA_GET_IFACE_ID(_data) \
425 (((struct hdd_ipa_cld_hdr *) (_data))->iface_id)
426
427#define HDD_IPA_LOG(LVL, fmt, args ...) \
428 CDF_TRACE(CDF_MODULE_ID_HDD, LVL, \
429 "%s:%d: "fmt, __func__, __LINE__, ## args)
430
431#define HDD_IPA_DBG_DUMP(_lvl, _prefix, _buf, _len) \
432 do { \
433 CDF_TRACE(CDF_MODULE_ID_HDD, _lvl, "%s:", _prefix); \
434 CDF_TRACE_HEX_DUMP(CDF_MODULE_ID_HDD, _lvl, _buf, _len); \
435 } while (0)
436
437#define HDD_IPA_IS_CONFIG_ENABLED(_hdd_ctx, _mask) \
438 (((_hdd_ctx)->config->IpaConfig & (_mask)) == (_mask))
439
440#define HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa) \
441 do { \
442 hdd_ipa->ipa_rx_internel_drop_count++; \
443 } while (0)
444#define HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa) \
445 do { \
446 hdd_ipa->ipa_rx_net_send_count++; \
447 } while (0)
448#define HDD_BW_GET_DIFF(_x, _y) (unsigned long)((ULONG_MAX - (_y)) + (_x) + 1)
449
450static struct hdd_ipa_adapter_2_client {
451 enum ipa_client_type cons_client;
452 enum ipa_client_type prod_client;
453} hdd_ipa_adapter_2_client[HDD_IPA_MAX_IFACE] = {
454 {
455 IPA_CLIENT_WLAN2_CONS, IPA_CLIENT_WLAN1_PROD
456 }, {
457 IPA_CLIENT_WLAN3_CONS, IPA_CLIENT_WLAN1_PROD
458 }, {
459 IPA_CLIENT_WLAN4_CONS, IPA_CLIENT_WLAN1_PROD
460 },
461};
462
463/* For Tx pipes, use Ethernet-II Header format */
464struct hdd_ipa_uc_tx_hdr ipa_uc_tx_hdr = {
465 {
466 0x00000000,
467 0x00000000
468 },
469 {
470 0x00000000
471 },
472 {
473 {0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc},
474 {0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff},
475 0x0008
476 }
477};
478
479/* For Tx pipes, use 802.3 Header format */
480static struct hdd_ipa_tx_hdr ipa_tx_hdr = {
481 {
482 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
483 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
484 0x00 /* length can be zero */
485 },
486 {
487 /* LLC SNAP header 8 bytes */
488 0xaa, 0xaa,
489 {0x03, 0x00, 0x00, 0x00},
490 0x0008 /* type value(2 bytes) ,filled by wlan */
491 /* 0x0800 - IPV4, 0x86dd - IPV6 */
492 }
493};
494
495static const char *op_string[] = {
496 "TX_SUSPEND",
497 "TX_RESUME",
498 "RX_SUSPEND",
499 "RX_RESUME",
500 "STATS",
501};
502
503static struct hdd_ipa_priv *ghdd_ipa;
504
505/* Local Function Prototypes */
506static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
507 unsigned long data);
508static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
509 unsigned long data);
510
511static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context);
512
513/**
514 * hdd_ipa_is_enabled() - Is IPA enabled?
515 * @hdd_ctx: Global HDD context
516 *
517 * Return: true if IPA is enabled, false otherwise
518 */
519bool hdd_ipa_is_enabled(hdd_context_t *hdd_ctx)
520{
521 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_ENABLE_MASK);
522}
523
524/**
525 * hdd_ipa_uc_is_enabled() - Is IPA uC offload enabled?
526 * @hdd_ctx: Global HDD context
527 *
528 * Return: true if IPA uC offload is enabled, false otherwise
529 */
530bool hdd_ipa_uc_is_enabled(hdd_context_t *hdd_ctx)
531{
532 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_ENABLE_MASK);
533}
534
535/**
536 * hdd_ipa_uc_sta_is_enabled() - Is STA mode IPA uC offload enabled?
537 * @hdd_ctx: Global HDD context
538 *
539 * Return: true if STA mode IPA uC offload is enabled, false otherwise
540 */
541static inline bool hdd_ipa_uc_sta_is_enabled(hdd_context_t *hdd_ctx)
542{
543 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_STA_ENABLE_MASK);
544}
545
546/**
547 * hdd_ipa_is_pre_filter_enabled() - Is IPA pre-filter enabled?
548 * @hdd_ipa: Global HDD IPA context
549 *
550 * Return: true if pre-filter is enabled, otherwise false
551 */
552static inline bool hdd_ipa_is_pre_filter_enabled(hdd_context_t *hdd_ctx)
553{
554 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
555 HDD_IPA_PRE_FILTER_ENABLE_MASK);
556}
557
558/**
559 * hdd_ipa_is_ipv6_enabled() - Is IPA IPv6 enabled?
560 * @hdd_ipa: Global HDD IPA context
561 *
562 * Return: true if IPv6 is enabled, otherwise false
563 */
564static inline bool hdd_ipa_is_ipv6_enabled(hdd_context_t *hdd_ctx)
565{
566 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_IPV6_ENABLE_MASK);
567}
568
569/**
570 * hdd_ipa_is_rm_enabled() - Is IPA resource manager enabled?
571 * @hdd_ipa: Global HDD IPA context
572 *
573 * Return: true if resource manager is enabled, otherwise false
574 */
575static inline bool hdd_ipa_is_rm_enabled(hdd_context_t *hdd_ctx)
576{
577 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_RM_ENABLE_MASK);
578}
579
580/**
581 * hdd_ipa_is_rt_debugging_enabled() - Is IPA real-time debug enabled?
582 * @hdd_ipa: Global HDD IPA context
583 *
584 * Return: true if resource manager is enabled, otherwise false
585 */
586static inline bool hdd_ipa_is_rt_debugging_enabled(hdd_context_t *hdd_ctx)
587{
588 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_REAL_TIME_DEBUGGING);
589}
590
591/**
592 * hdd_ipa_is_clk_scaling_enabled() - Is IPA clock scaling enabled?
593 * @hdd_ipa: Global HDD IPA context
594 *
595 * Return: true if clock scaling is enabled, otherwise false
596 */
597static inline bool hdd_ipa_is_clk_scaling_enabled(hdd_context_t *hdd_ctx)
598{
599 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
600 HDD_IPA_CLK_SCALING_ENABLE_MASK |
601 HDD_IPA_RM_ENABLE_MASK);
602}
603
604/**
605 * hdd_ipa_uc_rt_debug_host_fill - fill rt debug buffer
606 * @ctext: pointer to hdd context.
607 *
608 * If rt debug enabled, periodically called, and fill debug buffer
609 *
610 * Return: none
611 */
612static void hdd_ipa_uc_rt_debug_host_fill(void *ctext)
613{
614 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
615 struct hdd_ipa_priv *hdd_ipa;
616 struct uc_rt_debug_info *dump_info = NULL;
617
618 if (wlan_hdd_validate_context(hdd_ctx))
619 return;
620
621 if (!hdd_ctx->hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
622 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
623 "%s: IPA UC is not enabled", __func__);
624 return;
625 }
626
627 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
628
629 cdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
630 dump_info = &hdd_ipa->rt_bug_buffer[
631 hdd_ipa->rt_buf_fill_index % HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
632
633 dump_info->time = cdf_mc_timer_get_system_time();
634 dump_info->ipa_excep_count = hdd_ipa->stats.num_rx_excep;
635 dump_info->rx_drop_count = hdd_ipa->ipa_rx_internel_drop_count;
636 dump_info->net_sent_count = hdd_ipa->ipa_rx_net_send_count;
637 dump_info->rx_discard_count = hdd_ipa->ipa_rx_discard;
638 dump_info->tx_mcbc_count = hdd_ipa->stats.num_tx_bcmc;
639 dump_info->tx_fwd_count = hdd_ipa->ipa_tx_forward;
640 dump_info->rx_destructor_call = hdd_ipa->ipa_rx_destructor_count;
641 hdd_ipa->rt_buf_fill_index++;
642 cdf_mutex_release(&hdd_ipa->rt_debug_lock);
643
644 cdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
645 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
646}
647
648/**
649 * hdd_ipa_uc_rt_debug_host_dump - dump rt debug buffer
650 * @hdd_ctx: pointer to hdd context.
651 *
652 * If rt debug enabled, dump debug buffer contents based on requirement
653 *
654 * Return: none
655 */
656void hdd_ipa_uc_rt_debug_host_dump(hdd_context_t *hdd_ctx)
657{
658 struct hdd_ipa_priv *hdd_ipa;
659 unsigned int dump_count;
660 unsigned int dump_index;
661 struct uc_rt_debug_info *dump_info = NULL;
662
663 if (wlan_hdd_validate_context(hdd_ctx))
664 return;
665
666 hdd_ipa = hdd_ctx->hdd_ipa;
667 if (!hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
668 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
669 "%s: IPA UC is not enabled", __func__);
670 return;
671 }
672
673 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
674 "========= WLAN-IPA DEBUG BUF DUMP ==========\n");
675 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
676 " TM : EXEP : DROP : NETS : MCBC : TXFD : DSTR : DSCD\n");
677
678 cdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
679 for (dump_count = 0;
680 dump_count < HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
681 dump_count++) {
682 dump_index = (hdd_ipa->rt_buf_fill_index + dump_count) %
683 HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
684 dump_info = &hdd_ipa->rt_bug_buffer[dump_index];
685 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
686 "%12lu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu\n",
687 dump_info->time, dump_info->ipa_excep_count,
688 dump_info->rx_drop_count, dump_info->net_sent_count,
689 dump_info->tx_mcbc_count, dump_info->tx_fwd_count,
690 dump_info->rx_destructor_call,
691 dump_info->rx_discard_count);
692 }
693 cdf_mutex_release(&hdd_ipa->rt_debug_lock);
694 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
695 "======= WLAN-IPA DEBUG BUF DUMP END ========\n");
696}
697
698/**
699 * hdd_ipa_uc_rt_debug_handler - periodic memory health monitor handler
700 * @ctext: pointer to hdd context.
701 *
702 * periodically called by timer expire
703 * will try to alloc dummy memory and detect out of memory condition
704 * if out of memory detected, dump wlan-ipa stats
705 *
706 * Return: none
707 */
708static void hdd_ipa_uc_rt_debug_handler(void *ctext)
709{
710 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
711 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
712 void *dummy_ptr = NULL;
713
714 if (wlan_hdd_validate_context(hdd_ctx))
715 return;
716
717 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
718 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
719 "%s: IPA RT debug is not enabled", __func__);
720 return;
721 }
722
723 /* Allocate dummy buffer periodically and free immediately. this will
724 * proactively detect OOM and if allocation fails dump ipa stats
725 */
726 dummy_ptr = kmalloc(HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE,
727 GFP_KERNEL | GFP_ATOMIC);
728 if (!dummy_ptr) {
729 HDD_IPA_LOG(CDF_TRACE_LEVEL_FATAL,
730 "%s: Dummy alloc fail", __func__);
731 hdd_ipa_uc_rt_debug_host_dump(hdd_ctx);
732 hdd_ipa_uc_stat_request(
733 hdd_get_adapter(hdd_ctx, WLAN_HDD_SOFTAP), 1);
734 } else {
735 kfree(dummy_ptr);
736 }
737
738 cdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
739 HDD_IPA_UC_RT_DEBUG_PERIOD);
740}
741
742/**
743 * hdd_ipa_uc_rt_debug_destructor - called by data packet free
744 * @skb: packet pinter
745 *
746 * when free data packet, will be invoked by wlan client and will increase
747 * free counter
748 *
749 * Return: none
750 */
751void hdd_ipa_uc_rt_debug_destructor(struct sk_buff *skb)
752{
753 if (!ghdd_ipa) {
754 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
755 "%s: invalid hdd context", __func__);
756 return;
757 }
758
759 ghdd_ipa->ipa_rx_destructor_count++;
760}
761
762/**
763 * hdd_ipa_uc_rt_debug_deinit - remove resources to handle rt debugging
764 * @hdd_ctx: hdd main context
765 *
766 * free all rt debugging resources
767 *
768 * Return: none
769 */
770static void hdd_ipa_uc_rt_debug_deinit(hdd_context_t *hdd_ctx)
771{
772 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
773
774 if (CDF_TIMER_STATE_STOPPED !=
775 cdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_fill_timer)) {
776 cdf_mc_timer_stop(&hdd_ipa->rt_debug_fill_timer);
777 }
778 cdf_mc_timer_destroy(&hdd_ipa->rt_debug_fill_timer);
779 cdf_mutex_destroy(&hdd_ipa->rt_debug_lock);
780
781 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
782 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
783 "%s: IPA RT debug is not enabled", __func__);
784 return;
785 }
786
787 if (CDF_TIMER_STATE_STOPPED !=
788 cdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_timer)) {
789 cdf_mc_timer_stop(&hdd_ipa->rt_debug_timer);
790 }
791 cdf_mc_timer_destroy(&hdd_ipa->rt_debug_timer);
792}
793
794/**
795 * hdd_ipa_uc_rt_debug_init - intialize resources to handle rt debugging
796 * @hdd_ctx: hdd main context
797 *
798 * alloc and initialize all rt debugging resources
799 *
800 * Return: none
801 */
802static void hdd_ipa_uc_rt_debug_init(hdd_context_t *hdd_ctx)
803{
804 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
805
806 cdf_mutex_init(&hdd_ipa->rt_debug_lock);
807 cdf_mc_timer_init(&hdd_ipa->rt_debug_fill_timer, CDF_TIMER_TYPE_SW,
808 hdd_ipa_uc_rt_debug_host_fill, (void *)hdd_ctx);
809 hdd_ipa->rt_buf_fill_index = 0;
810 cdf_mem_zero(hdd_ipa->rt_bug_buffer,
811 sizeof(struct uc_rt_debug_info) *
812 HDD_IPA_UC_RT_DEBUG_BUF_COUNT);
813 hdd_ipa->ipa_tx_forward = 0;
814 hdd_ipa->ipa_rx_discard = 0;
815 hdd_ipa->ipa_rx_net_send_count = 0;
816 hdd_ipa->ipa_rx_internel_drop_count = 0;
817 hdd_ipa->ipa_rx_destructor_count = 0;
818
819 cdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
820 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
821
822 /* Reatime debug enable on feature enable */
823 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
824 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
825 "%s: IPA RT debug is not enabled", __func__);
826 return;
827 }
828 cdf_mc_timer_init(&hdd_ipa->rt_debug_timer, CDF_TIMER_TYPE_SW,
829 hdd_ipa_uc_rt_debug_handler, (void *)hdd_ctx);
830 cdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
831 HDD_IPA_UC_RT_DEBUG_PERIOD);
832
833}
834
835/**
836 * hdd_ipa_uc_stat_query() - Query the IPA stats
837 * @hdd_ctx: Global HDD context
838 * @ipa_tx_diff: tx packet count diff from previous
839 * tx packet count
840 * @ipa_rx_diff: rx packet count diff from previous
841 * rx packet count
842 *
843 * Return: true if IPA is enabled, false otherwise
844 */
845void hdd_ipa_uc_stat_query(hdd_context_t *pHddCtx,
846 uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff)
847{
848 struct hdd_ipa_priv *hdd_ipa;
849
850 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
851 *ipa_tx_diff = 0;
852 *ipa_rx_diff = 0;
853
854 if (!hdd_ipa_is_enabled(pHddCtx) ||
855 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
856 return;
857 }
858
859 cdf_mutex_acquire(&hdd_ipa->event_lock);
860 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
861 (false == hdd_ipa->resource_loading)) {
862 *ipa_tx_diff = hdd_ipa->ipa_tx_packets_diff;
863 *ipa_rx_diff = hdd_ipa->ipa_rx_packets_diff;
864 HDD_IPA_LOG(LOG1, "STAT Query TX DIFF %d, RX DIFF %d",
865 *ipa_tx_diff, *ipa_rx_diff);
866 }
867 cdf_mutex_release(&hdd_ipa->event_lock);
868 return;
869}
870
871/**
872 * hdd_ipa_uc_stat_request() - Get IPA stats from IPA.
873 * @adapter: network adapter
874 * @reason: STAT REQ Reason
875 *
876 * Return: None
877 */
878void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason)
879{
880 hdd_context_t *pHddCtx;
881 struct hdd_ipa_priv *hdd_ipa;
882
883 if (!adapter) {
884 return;
885 }
886
887 pHddCtx = (hdd_context_t *)adapter->pHddCtx;
888 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
889 if (!hdd_ipa_is_enabled(pHddCtx) ||
890 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
891 return;
892 }
893
894 HDD_IPA_LOG(LOG1, "STAT REQ Reason %d", reason);
895 cdf_mutex_acquire(&hdd_ipa->event_lock);
896 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
897 (false == hdd_ipa->resource_loading)) {
898 hdd_ipa->stat_req_reason = reason;
899 wma_cli_set_command(
900 (int)adapter->sessionId,
901 (int)WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID,
902 0, VDEV_CMD);
903 }
904 cdf_mutex_release(&hdd_ipa->event_lock);
905}
906
907/**
908 * hdd_ipa_uc_find_add_assoc_sta() - Find associated station
909 * @hdd_ipa: Global HDD IPA context
910 * @sta_add: Should station be added
911 * @sta_id: ID of the station being queried
912 *
913 * Return: true if the station was found
914 */
915static bool hdd_ipa_uc_find_add_assoc_sta(struct hdd_ipa_priv *hdd_ipa,
916 bool sta_add, uint8_t sta_id)
917{
918 bool sta_found = false;
919 uint8_t idx;
920 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
921 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
922 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
923 sta_found = true;
924 break;
925 }
926 }
927 if (sta_add && sta_found) {
928 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
929 "%s: STA ID %d already exist, cannot add",
930 __func__, sta_id);
931 return sta_found;
932 }
933 if (sta_add) {
934 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
935 if (!hdd_ipa->assoc_stas_map[idx].is_reserved) {
936 hdd_ipa->assoc_stas_map[idx].is_reserved = true;
937 hdd_ipa->assoc_stas_map[idx].sta_id = sta_id;
938 return sta_found;
939 }
940 }
941 }
942 if (!sta_add && !sta_found) {
943 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
944 "%s: STA ID %d does not exist, cannot delete",
945 __func__, sta_id);
946 return sta_found;
947 }
948 if (!sta_add) {
949 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
950 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
951 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
952 hdd_ipa->assoc_stas_map[idx].is_reserved =
953 false;
954 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
955 return sta_found;
956 }
957 }
958 }
959 return sta_found;
960}
961
962/**
963 * hdd_ipa_uc_enable_pipes() - Enable IPA uC pipes
964 * @hdd_ipa: Global HDD IPA context
965 *
966 * Return: 0 on success, negative errno if error
967 */
968static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa)
969{
970 int result;
971 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
972
973 /* ACTIVATE TX PIPE */
974 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Enable TX PIPE", __func__);
975 result = ipa_enable_wdi_pipe(hdd_ipa->tx_pipe_handle);
976 if (result) {
977 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
978 "%s: Enable TX PIPE fail, code %d",
979 __func__, result);
980 return result;
981 }
982 result = ipa_resume_wdi_pipe(hdd_ipa->tx_pipe_handle);
983 if (result) {
984 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
985 "%s: Resume TX PIPE fail, code %d",
986 __func__, result);
987 return result;
988 }
989 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, true);
990
991 /* ACTIVATE RX PIPE */
992 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Enable RX PIPE", __func__);
993 result = ipa_enable_wdi_pipe(hdd_ipa->rx_pipe_handle);
994 if (result) {
995 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
996 "%s: Enable RX PIPE fail, code %d",
997 __func__, result);
998 return result;
999 }
1000 result = ipa_resume_wdi_pipe(hdd_ipa->rx_pipe_handle);
1001 if (result) {
1002 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1003 "%s: Resume RX PIPE fail, code %d",
1004 __func__, result);
1005 return result;
1006 }
1007 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, false);
1008
1009 return 0;
1010}
1011
1012/**
1013 * hdd_ipa_uc_disable_pipes() - Disable IPA uC pipes
1014 * @hdd_ipa: Global HDD IPA context
1015 *
1016 * Return: 0 on success, negative errno if error
1017 */
1018static int hdd_ipa_uc_disable_pipes(struct hdd_ipa_priv *hdd_ipa)
1019{
1020 int result;
1021
1022 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Disable RX PIPE", __func__);
1023 result = ipa_suspend_wdi_pipe(hdd_ipa->rx_pipe_handle);
1024 if (result) {
1025 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1026 "%s: Suspend RX PIPE fail, code %d",
1027 __func__, result);
1028 return result;
1029 }
1030 result = ipa_disable_wdi_pipe(hdd_ipa->rx_pipe_handle);
1031 if (result) {
1032 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1033 "%s: Disable RX PIPE fail, code %d",
1034 __func__, result);
1035 return result;
1036 }
1037
1038 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Disable TX PIPE", __func__);
1039 result = ipa_suspend_wdi_pipe(hdd_ipa->tx_pipe_handle);
1040 if (result) {
1041 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1042 "%s: Suspend TX PIPE fail, code %d",
1043 __func__, result);
1044 return result;
1045 }
1046 result = ipa_disable_wdi_pipe(hdd_ipa->tx_pipe_handle);
1047 if (result) {
1048 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1049 "%s: Disable TX PIPE fail, code %d",
1050 __func__, result);
1051 return result;
1052 }
1053
1054 return 0;
1055}
1056
1057/**
1058 * hdd_ipa_uc_handle_first_con() - Handle first uC IPA connection
1059 * @hdd_ipa: Global HDD IPA context
1060 *
1061 * Return: 0 on success, negative errno if error
1062 */
1063static int hdd_ipa_uc_handle_first_con(struct hdd_ipa_priv *hdd_ipa)
1064{
1065 hdd_ipa->activated_fw_pipe = 0;
1066 hdd_ipa->resource_loading = true;
1067 /* If RM feature enabled
1068 * Request PROD Resource first
1069 * PROD resource may return sync or async manners */
1070 if ((hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) &&
1071 (!ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD))) {
1072 /* RM PROD request sync return
1073 * enable pipe immediately */
1074 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
1075 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1076 "%s: IPA WDI Pipes activate fail",
1077 __func__);
1078 hdd_ipa->resource_loading = false;
1079 return -EBUSY;
1080 }
1081 } else {
1082 /* RM Disabled
1083 * Just enabled all the PIPEs */
1084 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
1085 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1086 "%s: IPA WDI Pipes activate fail",
1087 __func__);
1088 hdd_ipa->resource_loading = false;
1089 return -EBUSY;
1090 }
1091 hdd_ipa->resource_loading = false;
1092 }
1093 return 0;
1094}
1095
1096/**
1097 * hdd_ipa_uc_handle_last_discon() - Handle last uC IPA disconnection
1098 * @hdd_ipa: Global HDD IPA context
1099 *
1100 * Return: None
1101 */
1102static void hdd_ipa_uc_handle_last_discon(struct hdd_ipa_priv *hdd_ipa)
1103{
1104 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
1105
1106 hdd_ipa->resource_unloading = true;
1107 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Disable FW RX PIPE", __func__);
1108 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, false);
1109 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Disable FW TX PIPE", __func__);
1110 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, true);
1111}
1112
1113/**
1114 * hdd_ipa_uc_rm_notify_handler() - IPA uC resource notification handler
1115 * @context: User context registered with TL (the IPA Global context is
1116 * registered
1117 * @rxpkt: Packet containing the notification
1118 * @staid: ID of the station associated with the packet
1119 *
1120 * Return: None
1121 */
1122static void
1123hdd_ipa_uc_rm_notify_handler(void *context, enum ipa_rm_event event)
1124{
1125 struct hdd_ipa_priv *hdd_ipa = context;
1126 CDF_STATUS status = CDF_STATUS_SUCCESS;
1127
1128 /*
1129 * When SSR is going on or driver is unloading, just return.
1130 */
1131 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
1132 if (0 != status) {
1133 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
1134 return;
1135 }
1136
1137 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1138 return;
1139
1140 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s, event code %d",
1141 __func__, event);
1142
1143 switch (event) {
1144 case IPA_RM_RESOURCE_GRANTED:
1145 /* Differed RM Granted */
1146 hdd_ipa_uc_enable_pipes(hdd_ipa);
1147 cdf_mutex_acquire(&hdd_ipa->event_lock);
1148 if ((false == hdd_ipa->resource_unloading) &&
1149 (!hdd_ipa->activated_fw_pipe)) {
1150 hdd_ipa_uc_enable_pipes(hdd_ipa);
1151 }
1152 cdf_mutex_release(&hdd_ipa->event_lock);
1153 if (hdd_ipa->pending_cons_req) {
1154 ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
1155 IPA_RM_RESOURCE_WLAN_CONS);
1156 }
1157 hdd_ipa->pending_cons_req = false;
1158 break;
1159
1160 case IPA_RM_RESOURCE_RELEASED:
1161 /* Differed RM Released */
1162 hdd_ipa->resource_unloading = false;
1163 if (hdd_ipa->pending_cons_req) {
1164 ipa_rm_notify_completion(IPA_RM_RESOURCE_RELEASED,
1165 IPA_RM_RESOURCE_WLAN_CONS);
1166 }
1167 hdd_ipa->pending_cons_req = false;
1168 break;
1169
1170 default:
1171 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1172 "%s, invalid event code %d", __func__, event);
1173 break;
1174 }
1175}
1176
1177/**
1178 * hdd_ipa_uc_rm_notify_defer() - Defer IPA uC notification
1179 * @hdd_ipa: Global HDD IPA context
1180 * @event: IPA resource manager event to be deferred
1181 *
1182 * This function is called when a resource manager event is received
1183 * from firmware in interrupt context. This function will defer the
1184 * handling to the OL RX thread
1185 *
1186 * Return: None
1187 */
1188static void hdd_ipa_uc_rm_notify_defer(struct work_struct *work)
1189{
1190 enum ipa_rm_event event;
1191 struct uc_rm_work_struct *uc_rm_work = container_of(work,
1192 struct uc_rm_work_struct, work);
1193 struct hdd_ipa_priv *hdd_ipa = container_of(uc_rm_work,
1194 struct hdd_ipa_priv, uc_rm_work);
1195
1196 cds_ssr_protect(__func__);
1197 event = uc_rm_work->event;
1198 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO_HIGH,
1199 "%s, posted event %d", __func__, event);
1200
1201 hdd_ipa_uc_rm_notify_handler(hdd_ipa, event);
1202 cds_ssr_unprotect(__func__);
1203
1204 return;
1205}
1206
1207/**
1208 * hdd_ipa_uc_proc_pending_event() - Process IPA uC pending events
1209 * @hdd_ipa: Global HDD IPA context
1210 *
1211 * Return: None
1212 */
1213static void hdd_ipa_uc_proc_pending_event(struct hdd_ipa_priv *hdd_ipa)
1214{
1215 unsigned int pending_event_count;
1216 struct ipa_uc_pending_event *pending_event = NULL;
1217
1218 cdf_list_size(&hdd_ipa->pending_event, &pending_event_count);
1219 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
1220 "%s, Pending Event Count %d", __func__, pending_event_count);
1221 if (!pending_event_count) {
1222 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
1223 "%s, No Pending Event", __func__);
1224 return;
1225 }
1226
1227 cdf_list_remove_front(&hdd_ipa->pending_event,
1228 (cdf_list_node_t **)&pending_event);
1229 while (pending_event != NULL) {
1230 hdd_ipa_wlan_evt(pending_event->adapter,
1231 pending_event->type,
1232 pending_event->sta_id,
1233 pending_event->mac_addr);
1234 cdf_mem_free(pending_event);
1235 pending_event = NULL;
1236 cdf_list_remove_front(&hdd_ipa->pending_event,
1237 (cdf_list_node_t **)&pending_event);
1238 }
1239}
1240
1241/**
1242 * hdd_ipa_uc_op_cb() - IPA uC operation callback
1243 * @op_msg: operation message received from firmware
1244 * @usr_ctxt: user context registered with TL (we register the HDD Global
1245 * context)
1246 *
1247 * Return: None
1248 */
1249static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt)
1250{
1251 struct op_msg_type *msg = op_msg;
1252 struct ipa_uc_fw_stats *uc_fw_stat;
1253 struct IpaHwStatsWDIInfoData_t ipa_stat;
1254 struct hdd_ipa_priv *hdd_ipa;
1255 hdd_context_t *hdd_ctx;
1256 CDF_STATUS status = CDF_STATUS_SUCCESS;
1257
1258 if (!op_msg || !usr_ctxt) {
1259 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "%s, INVALID ARG", __func__);
1260 return;
1261 }
1262
1263 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
1264 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1265 "%s, INVALID OPCODE %d", __func__, msg->op_code);
1266 return;
1267 }
1268
1269 hdd_ctx = (hdd_context_t *) usr_ctxt;
1270
1271 /*
1272 * When SSR is going on or driver is unloading, just return.
1273 */
1274 status = wlan_hdd_validate_context(hdd_ctx);
1275 if (0 != status) {
1276 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
1277 cdf_mem_free(op_msg);
1278 return;
1279 }
1280
1281 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1282
1283 HDD_IPA_LOG(CDF_TRACE_LEVEL_DEBUG,
1284 "%s, OPCODE %s", __func__, op_string[msg->op_code]);
1285
1286 if ((HDD_IPA_UC_OPCODE_TX_RESUME == msg->op_code) ||
1287 (HDD_IPA_UC_OPCODE_RX_RESUME == msg->op_code)) {
1288 cdf_mutex_acquire(&hdd_ipa->event_lock);
1289 hdd_ipa->activated_fw_pipe++;
1290 if (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) {
1291 hdd_ipa->resource_loading = false;
1292 hdd_ipa_uc_proc_pending_event(hdd_ipa);
1293 }
1294 cdf_mutex_release(&hdd_ipa->event_lock);
1295 }
1296
1297 if ((HDD_IPA_UC_OPCODE_TX_SUSPEND == msg->op_code) ||
1298 (HDD_IPA_UC_OPCODE_RX_SUSPEND == msg->op_code)) {
1299 cdf_mutex_acquire(&hdd_ipa->event_lock);
1300 hdd_ipa->activated_fw_pipe--;
1301 if (!hdd_ipa->activated_fw_pipe) {
1302 hdd_ipa_uc_disable_pipes(hdd_ipa);
1303 if ((hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) &&
1304 (!ipa_rm_release_resource(IPA_RM_RESOURCE_WLAN_PROD))) {
1305 /* Sync return success from IPA
1306 * Enable/resume all the PIPEs */
1307 hdd_ipa->resource_unloading = false;
1308 hdd_ipa_uc_proc_pending_event(hdd_ipa);
1309 } else {
1310 hdd_ipa->resource_unloading = false;
1311 hdd_ipa_uc_proc_pending_event(hdd_ipa);
1312 }
1313 }
1314 cdf_mutex_release(&hdd_ipa->event_lock);
1315 }
1316
1317 if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
1318 (HDD_IPA_UC_STAT_REASON_DEBUG == hdd_ipa->stat_req_reason)) {
1319
1320 /* STATs from host */
1321 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1322 "==== IPA_UC WLAN_HOST CE ====\n"
1323 "CE RING BASE: 0x%x\n"
1324 "CE RING SIZE: %d\n"
1325 "CE REG ADDR : 0x%llx",
1326 hdd_ctx->ce_sr_base_paddr,
1327 hdd_ctx->ce_sr_ring_size,
1328 (uint64_t) hdd_ctx->ce_reg_paddr);
1329 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1330 "==== IPA_UC WLAN_HOST TX ====\n"
1331 "COMP RING BASE: 0x%x\n"
1332 "COMP RING SIZE: %d\n"
1333 "NUM ALLOC BUF: %d\n"
1334 "COMP RING DBELL : 0x%x",
1335 hdd_ctx->tx_comp_ring_base_paddr,
1336 hdd_ctx->tx_comp_ring_size,
1337 hdd_ctx->tx_num_alloc_buffer,
1338 hdd_ctx->tx_comp_doorbell_paddr);
1339 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1340 "==== IPA_UC WLAN_HOST RX ====\n"
1341 "IND RING BASE: 0x%x\n"
1342 "IND RING SIZE: %d\n"
1343 "IND RING DBELL : 0x%x\n"
1344 "PROC DONE IND ADDR : 0x%x\n"
1345 "NUM EXCP PKT : %llu\n"
1346 "NUM TX BCMC : %llu\n"
1347 "NUM TX BCMC ERR : %llu",
1348 hdd_ctx->rx_rdy_ring_base_paddr,
1349 hdd_ctx->rx_rdy_ring_size,
1350 hdd_ctx->rx_ready_doorbell_paddr,
1351 hdd_ctx->rx_proc_done_idx_paddr,
1352 hdd_ipa->stats.num_rx_excep,
1353 hdd_ipa->stats.num_tx_bcmc,
1354 hdd_ipa->stats.num_tx_bcmc_err);
1355 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1356 "==== IPA_UC WLAN_HOST CONTROL ====\n"
1357 "SAP NUM STAs: %d\n"
1358 "STA CONNECTED: %d\n"
1359 "TX PIPE HDL: %d\n"
1360 "RX PIPE HDL : %d\n"
1361 "RSC LOADING : %d\n"
1362 "RSC UNLOADING : %d\n"
1363 "PNDNG CNS RQT : %d",
1364 hdd_ipa->sap_num_connected_sta,
1365 hdd_ipa->sta_connected,
1366 hdd_ipa->tx_pipe_handle,
1367 hdd_ipa->rx_pipe_handle,
1368 (unsigned int)hdd_ipa->resource_loading,
1369 (unsigned int)hdd_ipa->resource_unloading,
1370 (unsigned int)hdd_ipa->pending_cons_req);
1371
1372 /* STATs from FW */
1373 uc_fw_stat = (struct ipa_uc_fw_stats *)
1374 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
1375 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1376 "==== IPA_UC WLAN_FW TX ====\n"
1377 "COMP RING BASE: 0x%x\n"
1378 "COMP RING SIZE: %d\n"
1379 "COMP RING DBELL : 0x%x\n"
1380 "COMP RING DBELL IND VAL : %d\n"
1381 "COMP RING DBELL CACHED VAL : %d\n"
1382 "COMP RING DBELL CACHED VAL : %d\n"
1383 "PKTS ENQ : %d\n"
1384 "PKTS COMP : %d\n"
1385 "IS SUSPEND : %d\n"
1386 "RSVD : 0x%x",
1387 uc_fw_stat->tx_comp_ring_base,
1388 uc_fw_stat->tx_comp_ring_size,
1389 uc_fw_stat->tx_comp_ring_dbell_addr,
1390 uc_fw_stat->tx_comp_ring_dbell_ind_val,
1391 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1392 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1393 uc_fw_stat->tx_pkts_enqueued,
1394 uc_fw_stat->tx_pkts_completed,
1395 uc_fw_stat->tx_is_suspend, uc_fw_stat->tx_reserved);
1396 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1397 "==== IPA_UC WLAN_FW RX ====\n"
1398 "IND RING BASE: 0x%x\n"
1399 "IND RING SIZE: %d\n"
1400 "IND RING DBELL : 0x%x\n"
1401 "IND RING DBELL IND VAL : %d\n"
1402 "IND RING DBELL CACHED VAL : %d\n"
1403 "RDY IND ADDR : 0x%x\n"
1404 "RDY IND CACHE VAL : %d\n"
1405 "RFIL IND : %d\n"
1406 "NUM PKT INDICAT : %d\n"
1407 "BUF REFIL : %d\n"
1408 "NUM DROP NO SPC : %d\n"
1409 "NUM DROP NO BUF : %d\n"
1410 "IS SUSPND : %d\n"
1411 "RSVD : 0x%x\n",
1412 uc_fw_stat->rx_ind_ring_base,
1413 uc_fw_stat->rx_ind_ring_size,
1414 uc_fw_stat->rx_ind_ring_dbell_addr,
1415 uc_fw_stat->rx_ind_ring_dbell_ind_val,
1416 uc_fw_stat->rx_ind_ring_dbell_ind_cached_val,
1417 uc_fw_stat->rx_ind_ring_rdidx_addr,
1418 uc_fw_stat->rx_ind_ring_rd_idx_cached_val,
1419 uc_fw_stat->rx_refill_idx,
1420 uc_fw_stat->rx_num_pkts_indicated,
1421 uc_fw_stat->rx_buf_refilled,
1422 uc_fw_stat->rx_num_ind_drop_no_space,
1423 uc_fw_stat->rx_num_ind_drop_no_buf,
1424 uc_fw_stat->rx_is_suspend, uc_fw_stat->rx_reserved);
1425 /* STATs from IPA */
1426 ipa_get_wdi_stats(&ipa_stat);
1427 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1428 "==== IPA_UC IPA TX ====\n"
1429 "NUM PROCD : %d\n"
1430 "CE DBELL : 0x%x\n"
1431 "NUM DBELL FIRED : %d\n"
1432 "COMP RNG FULL : %d\n"
1433 "COMP RNG EMPT : %d\n"
1434 "COMP RNG USE HGH : %d\n"
1435 "COMP RNG USE LOW : %d\n"
1436 "BAM FIFO FULL : %d\n"
1437 "BAM FIFO EMPT : %d\n"
1438 "BAM FIFO USE HGH : %d\n"
1439 "BAM FIFO USE LOW : %d\n"
1440 "NUM DBELL : %d\n"
1441 "NUM UNEXP DBELL : %d\n"
1442 "NUM BAM INT HDL : 0x%x\n"
1443 "NUM BAM INT NON-RUN : 0x%x\n"
1444 "NUM QMB INT HDL : 0x%x",
1445 ipa_stat.tx_ch_stats.num_pkts_processed,
1446 ipa_stat.tx_ch_stats.copy_engine_doorbell_value,
1447 ipa_stat.tx_ch_stats.num_db_fired,
1448 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringFull,
1449 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringEmpty,
1450 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageHigh,
1451 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageLow,
1452 ipa_stat.tx_ch_stats.bam_stats.bamFifoFull,
1453 ipa_stat.tx_ch_stats.bam_stats.bamFifoEmpty,
1454 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageHigh,
1455 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageLow,
1456 ipa_stat.tx_ch_stats.num_db,
1457 ipa_stat.tx_ch_stats.num_unexpected_db,
1458 ipa_stat.tx_ch_stats.num_bam_int_handled,
1459 ipa_stat.tx_ch_stats.
1460 num_bam_int_in_non_runnning_state,
1461 ipa_stat.tx_ch_stats.num_qmb_int_handled);
1462
1463 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1464 "==== IPA_UC IPA RX ====\n"
1465 "MAX OST PKT : %d\n"
1466 "NUM PKT PRCSD : %d\n"
1467 "RNG RP : 0x%x\n"
1468 "COMP RNG FULL : %d\n"
1469 "COMP RNG EMPT : %d\n"
1470 "COMP RNG USE HGH : %d\n"
1471 "COMP RNG USE LOW : %d\n"
1472 "BAM FIFO FULL : %d\n"
1473 "BAM FIFO EMPT : %d\n"
1474 "BAM FIFO USE HGH : %d\n"
1475 "BAM FIFO USE LOW : %d\n"
1476 "NUM DB : %d\n"
1477 "NUM UNEXP DB : %d\n"
1478 "NUM BAM INT HNDL : 0x%x\n",
1479 ipa_stat.rx_ch_stats.max_outstanding_pkts,
1480 ipa_stat.rx_ch_stats.num_pkts_processed,
1481 ipa_stat.rx_ch_stats.rx_ring_rp_value,
1482 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringFull,
1483 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringEmpty,
1484 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageHigh,
1485 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageLow,
1486 ipa_stat.rx_ch_stats.bam_stats.bamFifoFull,
1487 ipa_stat.rx_ch_stats.bam_stats.bamFifoEmpty,
1488 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageHigh,
1489 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageLow,
1490 ipa_stat.rx_ch_stats.num_db,
1491 ipa_stat.rx_ch_stats.num_unexpected_db,
1492 ipa_stat.rx_ch_stats.num_bam_int_handled);
1493 } else if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
1494 (HDD_IPA_UC_STAT_REASON_BW_CAL == hdd_ipa->stat_req_reason)) {
1495 /* STATs from FW */
1496 uc_fw_stat = (struct ipa_uc_fw_stats *)
1497 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
1498 cdf_mutex_acquire(&hdd_ipa->event_lock);
1499 hdd_ipa->ipa_tx_packets_diff = HDD_BW_GET_DIFF(
1500 uc_fw_stat->tx_pkts_completed,
1501 hdd_ipa->ipa_p_tx_packets);
1502 hdd_ipa->ipa_rx_packets_diff = HDD_BW_GET_DIFF(
1503 (uc_fw_stat->rx_num_ind_drop_no_space +
1504 uc_fw_stat->rx_num_ind_drop_no_buf +
1505 uc_fw_stat->rx_num_pkts_indicated),
1506 hdd_ipa->ipa_p_rx_packets);
1507
1508 hdd_ipa->ipa_p_tx_packets = uc_fw_stat->tx_pkts_completed;
1509 hdd_ipa->ipa_p_rx_packets =
1510 (uc_fw_stat->rx_num_ind_drop_no_space +
1511 uc_fw_stat->rx_num_ind_drop_no_buf +
1512 uc_fw_stat->rx_num_pkts_indicated);
1513 cdf_mutex_release(&hdd_ipa->event_lock);
1514 } else {
1515 HDD_IPA_LOG(LOGE, "INVALID REASON %d",
1516 hdd_ipa->stat_req_reason);
1517 }
1518 cdf_mem_free(op_msg);
1519}
1520
1521
1522/**
1523 * hdd_ipa_uc_offload_enable_disable() - wdi enable/disable notify to fw
1524 * @adapter: device adapter instance
1525 * @offload_type: MCC or SCC
1526 * @enable: TX offload enable or disable
1527 *
1528 * Return: none
1529 */
1530static void hdd_ipa_uc_offload_enable_disable(hdd_adapter_t *adapter,
1531 uint32_t offload_type, uint32_t enable)
1532{
1533 struct sir_ipa_offload_enable_disable ipa_offload_enable_disable;
1534
1535 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
1536 * channel change indication. Since these indications are sent by lower
1537 * layer as SAP updates and IPA doesn't have to do anything for these
1538 * updates so ignoring!
1539 */
1540 if (WLAN_HDD_SOFTAP == adapter->device_mode && adapter->ipa_context)
1541 return;
1542
1543 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
1544 * channel change indication. Since these indications are sent by lower
1545 * layer as SAP updates and IPA doesn't have to do anything for these
1546 * updates so ignoring!
1547 */
1548 if (adapter->ipa_context)
1549 return;
1550
1551 cdf_mem_zero(&ipa_offload_enable_disable,
1552 sizeof(ipa_offload_enable_disable));
1553 ipa_offload_enable_disable.offload_type = offload_type;
1554 ipa_offload_enable_disable.vdev_id = adapter->sessionId;
1555 ipa_offload_enable_disable.enable = enable;
1556
1557 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
1558 "%s: offload_type=%d, vdev_id=%d, enable=%d", __func__,
1559 ipa_offload_enable_disable.offload_type,
1560 ipa_offload_enable_disable.vdev_id,
1561 ipa_offload_enable_disable.enable);
1562
1563 if (CDF_STATUS_SUCCESS !=
1564 sme_ipa_offload_enable_disable(WLAN_HDD_GET_HAL_CTX(adapter),
1565 adapter->sessionId, &ipa_offload_enable_disable)) {
1566 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1567 "%s: Failure to enable IPA offload \
1568 (offload_type=%d, vdev_id=%d, enable=%d)", __func__,
1569 ipa_offload_enable_disable.offload_type,
1570 ipa_offload_enable_disable.vdev_id,
1571 ipa_offload_enable_disable.enable);
1572 }
1573}
1574
1575/**
1576 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1577 * @work: uC OP work
1578 *
1579 * Return: None
1580 */
1581static void hdd_ipa_uc_fw_op_event_handler(struct work_struct *work)
1582{
1583 struct op_msg_type *msg;
1584 struct uc_op_work_struct *uc_op_work = container_of(work,
1585 struct uc_op_work_struct, work);
1586 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1587
1588 cds_ssr_protect(__func__);
1589
1590 msg = uc_op_work->msg;
1591 uc_op_work->msg = NULL;
1592 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO_HIGH,
1593 "%s, posted msg %d", __func__, msg->op_code);
1594
1595 hdd_ipa_uc_op_cb(msg, hdd_ipa->hdd_ctx);
1596
1597 cds_ssr_unprotect(__func__);
1598
1599 return;
1600}
1601
1602/**
1603 * hdd_ipa_uc_op_event_handler() - Adapter lookup
1604 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1605 * @op_msg: operation message received from firmware
1606 * @hdd_ctx: Global HDD context
1607 *
1608 * Return: None
1609 */
1610static void hdd_ipa_uc_op_event_handler(uint8_t *op_msg, void *hdd_ctx)
1611{
1612 struct hdd_ipa_priv *hdd_ipa;
1613 struct op_msg_type *msg;
1614 struct uc_op_work_struct *uc_op_work;
1615 CDF_STATUS status = CDF_STATUS_SUCCESS;
1616
1617 status = wlan_hdd_validate_context(hdd_ctx);
1618 if (0 != status) {
1619 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
1620 goto end;
1621 }
1622
1623 msg = (struct op_msg_type *)op_msg;
1624 hdd_ipa = ((hdd_context_t *)hdd_ctx)->hdd_ipa;
1625
1626 if (unlikely(!hdd_ipa))
1627 goto end;
1628
1629 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
1630 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "%s: Invalid OP Code (%d)",
1631 __func__, msg->op_code);
1632 goto end;
1633 }
1634
1635 uc_op_work = &hdd_ipa->uc_op_work[msg->op_code];
1636 if (uc_op_work->msg)
1637 /* When the same uC OPCODE is already pended, just return */
1638 goto end;
1639
1640 uc_op_work->msg = msg;
1641 schedule_work(&uc_op_work->work);
1642 return;
1643
1644end:
1645 cdf_mem_free(op_msg);
1646}
1647
1648/**
1649 * hdd_ipa_uc_ol_init() - Initialize IPA uC offload
1650 * @hdd_ctx: Global HDD context
1651 *
1652 * Return: CDF_STATUS
1653 */
1654static CDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx)
1655{
1656 struct ipa_wdi_in_params pipe_in;
1657 struct ipa_wdi_out_params pipe_out;
1658 struct hdd_ipa_priv *ipa_ctxt = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1659 p_cds_contextType cds_ctx = hdd_ctx->pcds_context;
1660 uint8_t i;
1661
1662 cdf_mem_zero(&pipe_in, sizeof(struct ipa_wdi_in_params));
1663 cdf_mem_zero(&pipe_out, sizeof(struct ipa_wdi_out_params));
1664
1665 cdf_list_init(&ipa_ctxt->pending_event, 1000);
1666 cdf_mutex_init(&ipa_ctxt->event_lock);
1667
1668 /* TX PIPE */
1669 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1670 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
1671 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
1672 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
1673 pipe_in.sys.ipa_ep_cfg.hdr.hdr_additional_const_len =
1674 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
1675 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1676 pipe_in.sys.client = IPA_CLIENT_WLAN1_CONS;
1677 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize;
1678 pipe_in.sys.priv = hdd_ctx->hdd_ipa;
1679 pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
1680 pipe_in.sys.notify = hdd_ipa_i2w_cb;
1681 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
1682 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
1683 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1684 pipe_in.sys.keep_ipa_awake = true;
1685 }
1686
1687 pipe_in.u.dl.comp_ring_base_pa = hdd_ctx->tx_comp_ring_base_paddr;
1688 pipe_in.u.dl.comp_ring_size = hdd_ctx->tx_comp_ring_size * 4;
1689 pipe_in.u.dl.ce_ring_base_pa = hdd_ctx->ce_sr_base_paddr;
1690 pipe_in.u.dl.ce_door_bell_pa = hdd_ctx->ce_reg_paddr;
1691 pipe_in.u.dl.ce_ring_size = hdd_ctx->ce_sr_ring_size * 8;
1692 pipe_in.u.dl.num_tx_buffers = hdd_ctx->tx_num_alloc_buffer;
1693
1694 /* Connect WDI IPA PIPE */
1695 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
1696 /* Micro Controller Doorbell register */
1697 hdd_ctx->tx_comp_doorbell_paddr = (uint32_t) pipe_out.uc_door_bell_pa;
1698 /* WLAN TX PIPE Handle */
1699 ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl;
1700 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO_HIGH,
1701 "TX : CRBPA 0x%x, CRS %d, CERBPA 0x%x, CEDPA 0x%x,"
1702 " CERZ %d, NB %d, CDBPAD 0x%x",
1703 (unsigned int)pipe_in.u.dl.comp_ring_base_pa,
1704 pipe_in.u.dl.comp_ring_size,
1705 (unsigned int)pipe_in.u.dl.ce_ring_base_pa,
1706 (unsigned int)pipe_in.u.dl.ce_door_bell_pa,
1707 pipe_in.u.dl.ce_ring_size,
1708 pipe_in.u.dl.num_tx_buffers,
1709 (unsigned int)hdd_ctx->tx_comp_doorbell_paddr);
1710
1711 /* RX PIPE */
1712 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1713 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_RX_HDR_LEN;
1714 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 0;
1715 pipe_in.sys.ipa_ep_cfg.hdr.hdr_metadata_reg_valid = 1;
1716 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1717 pipe_in.sys.client = IPA_CLIENT_WLAN1_PROD;
1718 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize +
1719 sizeof(struct sps_iovec);
1720 pipe_in.sys.notify = hdd_ipa_w2i_cb;
1721 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
1722 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
1723 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1724 pipe_in.sys.keep_ipa_awake = true;
1725 }
1726
1727 pipe_in.u.ul.rdy_ring_base_pa = hdd_ctx->rx_rdy_ring_base_paddr;
1728 pipe_in.u.ul.rdy_ring_size = hdd_ctx->rx_rdy_ring_size;
1729 pipe_in.u.ul.rdy_ring_rp_pa = hdd_ctx->rx_proc_done_idx_paddr;
1730
1731 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
1732 hdd_ctx->rx_ready_doorbell_paddr = pipe_out.uc_door_bell_pa;
1733 ipa_ctxt->rx_pipe_handle = pipe_out.clnt_hdl;
1734 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO_HIGH,
1735 "RX : RRBPA 0x%x, RRS %d, PDIPA 0x%x, RDY_DB_PAD 0x%x",
1736 (unsigned int)pipe_in.u.ul.rdy_ring_base_pa,
1737 pipe_in.u.ul.rdy_ring_size,
1738 (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa,
1739 (unsigned int)hdd_ctx->rx_ready_doorbell_paddr);
1740
1741 ol_txrx_ipa_uc_set_doorbell_paddr(cds_ctx->pdev_txrx_ctx,
1742 (uint32_t) hdd_ctx->tx_comp_doorbell_paddr,
1743 (uint32_t) hdd_ctx->rx_ready_doorbell_paddr);
1744
1745 ol_txrx_ipa_uc_register_op_cb(cds_ctx->pdev_txrx_ctx,
1746 hdd_ipa_uc_op_event_handler, (void *)hdd_ctx);
1747
1748 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
1749 cnss_init_work(&ipa_ctxt->uc_op_work[i].work,
1750 hdd_ipa_uc_fw_op_event_handler);
1751 ipa_ctxt->uc_op_work[i].msg = NULL;
1752 }
1753
1754 return CDF_STATUS_SUCCESS;
1755}
1756
1757#ifdef IPA_UC_OFFLOAD
1758/**
1759 * hdd_ipa_uc_ssr_deinit() - handle ipa deinit for SSR
1760 *
1761 * Deinit basic IPA UC host side to be in sync reloaded FW during
1762 * SSR
1763 *
1764 * Return: 0 - Success
1765 */
1766int hdd_ipa_uc_ssr_deinit(void)
1767{
1768 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1769 int idx;
1770 struct hdd_ipa_iface_context *iface_context;
1771
1772 if (!hdd_ipa_uc_is_enabled(hdd_ipa))
1773 return 0;
1774
1775 /* Clean up HDD IPA interfaces */
1776 for (idx = 0; (hdd_ipa->num_iface > 0) &&
1777 (idx < HDD_IPA_MAX_IFACE); idx++) {
1778 iface_context = &hdd_ipa->iface_context[idx];
1779 if (iface_context && iface_context->adapter)
1780 hdd_ipa_cleanup_iface(iface_context);
1781 }
1782
1783 /* After SSR, wlan driver reloads FW again. But we need to protect
1784 * IPA submodule during SSR transient state. So deinit basic IPA
1785 * UC host side to be in sync with reloaded FW during SSR
1786 */
1787 hdd_ipa_uc_disable_pipes(hdd_ipa);
1788
1789 cdf_wake_lock_acquire(&hdd_ipa->event_lock);
1790 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1791 hdd_ipa->assoc_stas_map[idx].is_reserved = false;
1792 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
1793 }
1794 cdf_wake_lock_release(&hdd_ipa->event_lock);
1795
1796 /* Full IPA driver cleanup not required since wlan driver is now
1797 * unloaded and reloaded after SSR.
1798 */
1799 return 0;
1800}
1801
1802/**
1803 * hdd_ipa_uc_ssr_reinit() - handle ipa reinit after SSR
1804 *
1805 * Init basic IPA UC host side to be in sync with reloaded FW after
1806 * SSR to resume IPA UC operations
1807 *
1808 * Return: 0 - Success
1809 */
1810int hdd_ipa_uc_ssr_reinit(void)
1811{
1812 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1813
1814 if (!hdd_ipa_uc_is_enabled(hdd_ipa))
1815 return 0;
1816
1817 /* After SSR is complete, IPA UC can resume operation. But now wlan
1818 * driver will be unloaded and reloaded, which takes care of IPA cleanup
1819 * and initialization. This is a placeholder func if IPA has to resume
1820 * operations without driver reload.
1821 */
1822 return 0;
1823}
1824#endif
1825
1826/**
1827 * hdd_ipa_wake_lock_timer_func() - Wake lock work handler
1828 * @work: scheduled work
1829 *
1830 * When IPA resources are released in hdd_ipa_rm_try_release() we do
1831 * not want to immediately release the wake lock since the system
1832 * would then potentially try to suspend when there is a healthy data
1833 * rate. Deferred work is scheduled and this function handles the
1834 * work. When this function is called, if the IPA resource is still
1835 * released then we release the wake lock.
1836 *
1837 * Return: None
1838 */
1839static void hdd_ipa_wake_lock_timer_func(struct work_struct *work)
1840{
1841 struct hdd_ipa_priv *hdd_ipa = container_of(to_delayed_work(work),
1842 struct hdd_ipa_priv,
1843 wake_lock_work);
1844
1845 cdf_spin_lock_bh(&hdd_ipa->rm_lock);
1846
1847 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED)
1848 goto end;
1849
1850 hdd_ipa->wake_lock_released = true;
1851 cdf_wake_lock_release(&hdd_ipa->wake_lock,
1852 WIFI_POWER_EVENT_WAKELOCK_IPA);
1853
1854end:
1855 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1856}
1857
1858/**
1859 * hdd_ipa_rm_request() - Request resource from IPA
1860 * @hdd_ipa: Global HDD IPA context
1861 *
1862 * Return: 0 on success, negative errno on error
1863 */
1864static int hdd_ipa_rm_request(struct hdd_ipa_priv *hdd_ipa)
1865{
1866 int ret = 0;
1867
1868 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1869 return 0;
1870
1871 cdf_spin_lock_bh(&hdd_ipa->rm_lock);
1872
1873 switch (hdd_ipa->rm_state) {
1874 case HDD_IPA_RM_GRANTED:
1875 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1876 return 0;
1877 case HDD_IPA_RM_GRANT_PENDING:
1878 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1879 return -EINPROGRESS;
1880 case HDD_IPA_RM_RELEASED:
1881 hdd_ipa->rm_state = HDD_IPA_RM_GRANT_PENDING;
1882 break;
1883 }
1884
1885 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1886
1887 ret = ipa_rm_inactivity_timer_request_resource(
1888 IPA_RM_RESOURCE_WLAN_PROD);
1889
1890 cdf_spin_lock_bh(&hdd_ipa->rm_lock);
1891 if (ret == 0) {
1892 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
1893 hdd_ipa->stats.num_rm_grant_imm++;
1894 }
1895
1896 cancel_delayed_work(&hdd_ipa->wake_lock_work);
1897 if (hdd_ipa->wake_lock_released) {
1898 cdf_wake_lock_acquire(&hdd_ipa->wake_lock,
1899 WIFI_POWER_EVENT_WAKELOCK_IPA);
1900 hdd_ipa->wake_lock_released = false;
1901 }
1902 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1903
1904 return ret;
1905}
1906
1907/**
1908 * hdd_ipa_rm_try_release() - Attempt to release IPA resource
1909 * @hdd_ipa: Global HDD IPA context
1910 *
1911 * Return: 0 if resources released, negative errno otherwise
1912 */
1913static int hdd_ipa_rm_try_release(struct hdd_ipa_priv *hdd_ipa)
1914{
1915 int ret = 0;
1916
1917 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1918 return 0;
1919
1920 if (atomic_read(&hdd_ipa->tx_ref_cnt))
1921 return -EAGAIN;
1922
1923 spin_lock_bh(&hdd_ipa->q_lock);
1924 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
1925 (hdd_ipa->pending_hw_desc_cnt || hdd_ipa->pend_q_cnt)) {
1926 spin_unlock_bh(&hdd_ipa->q_lock);
1927 return -EAGAIN;
1928 }
1929 spin_unlock_bh(&hdd_ipa->q_lock);
1930
1931 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
1932
1933 if (!cdf_nbuf_is_queue_empty(&hdd_ipa->pm_queue_head)) {
1934 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
1935 return -EAGAIN;
1936 }
1937 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
1938
1939 cdf_spin_lock_bh(&hdd_ipa->rm_lock);
1940 switch (hdd_ipa->rm_state) {
1941 case HDD_IPA_RM_GRANTED:
1942 break;
1943 case HDD_IPA_RM_GRANT_PENDING:
1944 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1945 return -EINPROGRESS;
1946 case HDD_IPA_RM_RELEASED:
1947 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1948 return 0;
1949 }
1950
1951 /* IPA driver returns immediately so set the state here to avoid any
1952 * race condition.
1953 */
1954 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
1955 hdd_ipa->stats.num_rm_release++;
1956 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1957
1958 ret =
1959 ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_WLAN_PROD);
1960
1961 cdf_spin_lock_bh(&hdd_ipa->rm_lock);
1962 if (unlikely(ret != 0)) {
1963 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
1964 WARN_ON(1);
1965 }
1966
1967 /*
1968 * If wake_lock is released immediately, kernel would try to suspend
1969 * immediately as well, Just avoid ping-pong between suspend-resume
1970 * while there is healthy amount of data transfer going on by
1971 * releasing the wake_lock after some delay.
1972 */
1973 schedule_delayed_work(&hdd_ipa->wake_lock_work,
1974 msecs_to_jiffies
1975 (HDD_IPA_RX_INACTIVITY_MSEC_DELAY));
1976
1977 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
1978
1979 return ret;
1980}
1981
1982/**
1983 * hdd_ipa_rm_notify() - IPA resource manager notifier callback
1984 * @user_data: user data registered with IPA
1985 * @event: the IPA resource manager event that occurred
1986 * @data: the data associated with the event
1987 *
1988 * Return: None
1989 */
1990static void hdd_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
1991 unsigned long data)
1992{
1993 struct hdd_ipa_priv *hdd_ipa = user_data;
1994
1995 if (unlikely(!hdd_ipa))
1996 return;
1997
1998 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1999 return;
2000
2001 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "Evt: %d", event);
2002
2003 switch (event) {
2004 case IPA_RM_RESOURCE_GRANTED:
2005 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2006 /* RM Notification comes with ISR context
2007 * it should be serialized into work queue to avoid
2008 * ISR sleep problem
2009 */
2010 hdd_ipa->uc_rm_work.event = event;
2011 schedule_work(&hdd_ipa->uc_rm_work.work);
2012 break;
2013 }
2014 cdf_spin_lock_bh(&hdd_ipa->rm_lock);
2015 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
2016 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
2017 hdd_ipa->stats.num_rm_grant++;
2018 break;
2019
2020 case IPA_RM_RESOURCE_RELEASED:
2021 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "RM Release");
2022 hdd_ipa->resource_unloading = false;
2023 break;
2024
2025 default:
2026 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "Unknown RM Evt: %d", event);
2027 break;
2028 }
2029}
2030
2031/**
2032 * hdd_ipa_rm_cons_release() - WLAN consumer resource release handler
2033 *
2034 * Callback function registered with IPA that is called when IPA wants
2035 * to release the WLAN consumer resource
2036 *
2037 * Return: 0 if the request is granted, negative errno otherwise
2038 */
2039static int hdd_ipa_rm_cons_release(void)
2040{
2041 return 0;
2042}
2043
2044/**
2045 * hdd_ipa_rm_cons_request() - WLAN consumer resource request handler
2046 *
2047 * Callback function registered with IPA that is called when IPA wants
2048 * to access the WLAN consumer resource
2049 *
2050 * Return: 0 if the request is granted, negative errno otherwise
2051 */
2052static int hdd_ipa_rm_cons_request(void)
2053{
2054 if ((ghdd_ipa->resource_loading) || (ghdd_ipa->resource_unloading)) {
2055 HDD_IPA_LOG(CDF_TRACE_LEVEL_FATAL,
2056 "%s: ipa resource loading/unloading in progress",
2057 __func__);
2058 ghdd_ipa->pending_cons_req = true;
2059 return -EPERM;
2060 }
2061 return 0;
2062}
2063
2064/**
2065 * hdd_ipa_set_perf_level() - Set IPA performance level
2066 * @hdd_ctx: Global HDD context
2067 * @tx_packets: Number of packets transmitted in the last sample period
2068 * @rx_packets: Number of packets received in the last sample period
2069 *
2070 * Return: 0 on success, negative errno on error
2071 */
2072int hdd_ipa_set_perf_level(hdd_context_t *hdd_ctx, uint64_t tx_packets,
2073 uint64_t rx_packets)
2074{
2075 uint32_t next_cons_bw, next_prod_bw;
2076 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2077 struct ipa_rm_perf_profile profile;
2078 int ret;
2079
2080 if ((!hdd_ipa_is_enabled(hdd_ctx)) ||
2081 (!hdd_ipa_is_clk_scaling_enabled(hdd_ctx)))
2082 return 0;
2083
2084 memset(&profile, 0, sizeof(profile));
2085
2086 if (tx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2087 next_cons_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2088 else if (tx_packets >
2089 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2090 next_cons_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2091 else
2092 next_cons_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2093
2094 if (rx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2095 next_prod_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2096 else if (rx_packets >
2097 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2098 next_prod_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2099 else
2100 next_prod_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2101
2102 HDD_IPA_LOG(LOG1,
2103 "CONS perf curr: %d, next: %d",
2104 hdd_ipa->curr_cons_bw, next_cons_bw);
2105 HDD_IPA_LOG(LOG1,
2106 "PROD perf curr: %d, next: %d",
2107 hdd_ipa->curr_prod_bw, next_prod_bw);
2108
2109 if (hdd_ipa->curr_cons_bw != next_cons_bw) {
2110 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
2111 "Requesting CONS perf curr: %d, next: %d",
2112 hdd_ipa->curr_cons_bw, next_cons_bw);
2113 profile.max_supported_bandwidth_mbps = next_cons_bw;
2114 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_CONS,
2115 &profile);
2116 if (ret) {
2117 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2118 "RM CONS set perf profile failed: %d", ret);
2119
2120 return ret;
2121 }
2122 hdd_ipa->curr_cons_bw = next_cons_bw;
2123 hdd_ipa->stats.num_cons_perf_req++;
2124 }
2125
2126 if (hdd_ipa->curr_prod_bw != next_prod_bw) {
2127 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
2128 "Requesting PROD perf curr: %d, next: %d",
2129 hdd_ipa->curr_prod_bw, next_prod_bw);
2130 profile.max_supported_bandwidth_mbps = next_prod_bw;
2131 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_PROD,
2132 &profile);
2133 if (ret) {
2134 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2135 "RM PROD set perf profile failed: %d", ret);
2136 return ret;
2137 }
2138 hdd_ipa->curr_prod_bw = next_prod_bw;
2139 hdd_ipa->stats.num_prod_perf_req++;
2140 }
2141
2142 return 0;
2143}
2144
2145/**
2146 * hdd_ipa_setup_rm() - Setup IPA resource management
2147 * @hdd_ipa: Global HDD IPA context
2148 *
2149 * Return: 0 on success, negative errno on error
2150 */
2151static int hdd_ipa_setup_rm(struct hdd_ipa_priv *hdd_ipa)
2152{
2153 struct ipa_rm_create_params create_params = { 0 };
2154 int ret;
2155
2156 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2157 return 0;
2158
2159 cnss_init_work(&hdd_ipa->uc_rm_work.work, hdd_ipa_uc_rm_notify_defer);
2160 memset(&create_params, 0, sizeof(create_params));
2161 create_params.name = IPA_RM_RESOURCE_WLAN_PROD;
2162 create_params.reg_params.user_data = hdd_ipa;
2163 create_params.reg_params.notify_cb = hdd_ipa_rm_notify;
2164 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2165
2166 ret = ipa_rm_create_resource(&create_params);
2167 if (ret) {
2168 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2169 "Create RM resource failed: %d", ret);
2170 goto setup_rm_fail;
2171 }
2172
2173 memset(&create_params, 0, sizeof(create_params));
2174 create_params.name = IPA_RM_RESOURCE_WLAN_CONS;
2175 create_params.request_resource = hdd_ipa_rm_cons_request;
2176 create_params.release_resource = hdd_ipa_rm_cons_release;
2177 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2178
2179 ret = ipa_rm_create_resource(&create_params);
2180 if (ret) {
2181 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2182 "Create RM CONS resource failed: %d", ret);
2183 goto delete_prod;
2184 }
2185
2186 ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD,
2187 IPA_RM_RESOURCE_APPS_CONS);
2188
2189 ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WLAN_PROD,
2190 HDD_IPA_RX_INACTIVITY_MSEC_DELAY);
2191 if (ret) {
2192 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "Timer init failed: %d",
2193 ret);
2194 goto timer_init_failed;
2195 }
2196
2197 /* Set the lowest bandwidth to start with */
2198 ret = hdd_ipa_set_perf_level(hdd_ipa->hdd_ctx, 0, 0);
2199
2200 if (ret) {
2201 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2202 "Set perf level failed: %d", ret);
2203 goto set_perf_failed;
2204 }
2205
2206 cdf_wake_lock_init(&hdd_ipa->wake_lock, "wlan_ipa");
2207#ifdef CONFIG_CNSS
2208 cnss_init_delayed_work(&hdd_ipa->wake_lock_work,
2209 hdd_ipa_wake_lock_timer_func);
2210#else
2211 INIT_DELAYED_WORK(&hdd_ipa->wake_lock_work,
2212 hdd_ipa_wake_lock_timer_func);
2213#endif
2214 cdf_spinlock_init(&hdd_ipa->rm_lock);
2215 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
2216 hdd_ipa->wake_lock_released = true;
2217 atomic_set(&hdd_ipa->tx_ref_cnt, 0);
2218
2219 return ret;
2220
2221set_perf_failed:
2222 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2223
2224timer_init_failed:
2225 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2226
2227delete_prod:
2228 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2229
2230setup_rm_fail:
2231 return ret;
2232}
2233
2234/**
2235 * hdd_ipa_destroy_rm_resource() - Destroy IPA resources
2236 * @hdd_ipa: Global HDD IPA context
2237 *
2238 * Destroys all resources associated with the IPA resource manager
2239 *
2240 * Return: None
2241 */
2242static void hdd_ipa_destroy_rm_resource(struct hdd_ipa_priv *hdd_ipa)
2243{
2244 int ret;
2245
2246 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2247 return;
2248
2249 cancel_delayed_work_sync(&hdd_ipa->wake_lock_work);
2250 cdf_wake_lock_destroy(&hdd_ipa->wake_lock);
2251
2252#ifdef WLAN_OPEN_SOURCE
2253 cancel_work_sync(&hdd_ipa->uc_rm_work.work);
2254#endif
2255 cdf_spinlock_destroy(&hdd_ipa->rm_lock);
2256
2257 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2258
2259 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2260 if (ret)
2261 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2262 "RM PROD resource delete failed %d", ret);
2263
2264 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2265 if (ret)
2266 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2267 "RM CONS resource delete failed %d", ret);
2268}
2269
2270/**
2271 * hdd_ipa_send_skb_to_network() - Send skb to kernel
2272 * @skb: network buffer
2273 * @adapter: network adapter
2274 *
2275 * Called when a network buffer is received which should not be routed
2276 * to the IPA module.
2277 *
2278 * Return: None
2279 */
2280static void hdd_ipa_send_skb_to_network(cdf_nbuf_t skb,
2281 hdd_adapter_t *adapter)
2282{
2283 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2284 unsigned int cpu_index;
2285
2286 if (!adapter || adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
2287 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO_LOW, "Invalid adapter: 0x%p",
2288 adapter);
2289 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
2290 cdf_nbuf_free(skb);
2291 return;
2292 }
2293
2294 if (hdd_ipa->hdd_ctx->isUnloadInProgress) {
2295 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
2296 cdf_nbuf_free(skb);
2297 return;
2298 }
2299
2300 skb->destructor = hdd_ipa_uc_rt_debug_destructor;
2301 skb->dev = adapter->dev;
2302 skb->protocol = eth_type_trans(skb, skb->dev);
2303 skb->ip_summed = CHECKSUM_NONE;
2304
2305 cpu_index = wlan_hdd_get_cpu();
2306
2307 ++adapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index];
2308 if (netif_rx_ni(skb) == NET_RX_SUCCESS)
2309 ++adapter->hdd_stats.hddTxRxStats.rxDelivered[cpu_index];
2310 else
2311 ++adapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index];
2312
2313 HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa);
2314 adapter->dev->last_rx = jiffies;
2315}
2316
2317#define FW_RX_DESC_DISCARD_M 0x1
2318#define FW_RX_DESC_FORWARD_M 0x2
2319
2320/**
2321 * hdd_ipa_w2i_cb() - WLAN to IPA callback handler
2322 * @priv: pointer to private data registered with IPA (we register a
2323 * pointer to the global IPA context)
2324 * @evt: the IPA event which triggered the callback
2325 * @data: data associated with the event
2326 *
2327 * Return: None
2328 */
2329static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
2330 unsigned long data)
2331{
2332 struct hdd_ipa_priv *hdd_ipa = NULL;
2333 hdd_adapter_t *adapter = NULL;
2334 cdf_nbuf_t skb;
2335 uint8_t iface_id;
2336 uint8_t session_id;
2337 struct hdd_ipa_iface_context *iface_context;
2338 cdf_nbuf_t copy;
2339 uint8_t fw_desc;
2340 int ret;
2341
2342 hdd_ipa = (struct hdd_ipa_priv *)priv;
2343
2344 switch (evt) {
2345 case IPA_RECEIVE:
2346 skb = (cdf_nbuf_t) data;
2347 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2348 session_id = (uint8_t)skb->cb[0];
2349 iface_id = vdev_to_iface[session_id];
2350 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO_HIGH,
2351 "IPA_RECEIVE: session_id=%u, iface_id=%u",
2352 session_id, iface_id);
2353 } else {
2354 iface_id = HDD_IPA_GET_IFACE_ID(skb->data);
2355 }
2356
2357 if (iface_id >= HDD_IPA_MAX_IFACE) {
2358 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2359 "IPA_RECEIVE: Invalid iface_id: %u",
2360 iface_id);
2361 HDD_IPA_DBG_DUMP(CDF_TRACE_LEVEL_INFO_HIGH,
2362 "w2i -- skb", skb->data, 8);
2363 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
2364 cdf_nbuf_free(skb);
2365 return;
2366 }
2367
2368 iface_context = &hdd_ipa->iface_context[iface_id];
2369 adapter = iface_context->adapter;
2370
2371 HDD_IPA_DBG_DUMP(CDF_TRACE_LEVEL_DEBUG,
2372 "w2i -- skb", skb->data, 8);
2373 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2374 hdd_ipa->stats.num_rx_excep++;
2375 skb_pull(skb, HDD_IPA_UC_WLAN_CLD_HDR_LEN);
2376 } else {
2377 skb_pull(skb, HDD_IPA_WLAN_CLD_HDR_LEN);
2378 }
2379
2380 iface_context->stats.num_rx_ipa_excep++;
2381
2382 /* Disable to forward Intra-BSS Rx packets when
2383 * ap_isolate=1 in hostapd.conf
2384 */
2385 if (adapter->sessionCtx.ap.apDisableIntraBssFwd) {
2386 /*
2387 * When INTRA_BSS_FWD_OFFLOAD is enabled, FW will send
2388 * all Rx packets to IPA uC, which need to be forwarded
2389 * to other interface.
2390 * And, IPA driver will send back to WLAN host driver
2391 * through exception pipe with fw_desc field set by FW.
2392 * Here we are checking fw_desc field for FORWARD bit
2393 * set, and forward to Tx. Then copy to kernel stack
2394 * only when DISCARD bit is not set.
2395 */
2396 fw_desc = (uint8_t)skb->cb[1];
2397
2398 if (fw_desc & FW_RX_DESC_DISCARD_M) {
2399 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
2400 hdd_ipa->ipa_rx_discard++;
2401 cdf_nbuf_free(skb);
2402 break;
2403 }
2404
2405 if (fw_desc & FW_RX_DESC_FORWARD_M) {
2406 HDD_IPA_LOG(
2407 CDF_TRACE_LEVEL_DEBUG,
2408 "Forward packet to Tx (fw_desc=%d)",
2409 fw_desc);
2410 copy = cdf_nbuf_copy(skb);
2411 if (copy) {
2412 hdd_ipa->ipa_tx_forward++;
2413 ret = hdd_softap_hard_start_xmit(
2414 (struct sk_buff *)copy,
2415 adapter->dev);
2416 if (ret) {
2417 HDD_IPA_LOG(
2418 CDF_TRACE_LEVEL_DEBUG,
2419 "Forward packet tx fail");
2420 hdd_ipa->stats.
2421 num_tx_bcmc_err++;
2422 } else {
2423 hdd_ipa->stats.num_tx_bcmc++;
2424 }
2425 }
2426 }
2427 } else {
2428 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO_HIGH,
2429 "Intra-BSS FWD is disabled-skip forward to Tx");
2430 }
2431
2432 hdd_ipa_send_skb_to_network(skb, adapter);
2433 break;
2434
2435 default:
2436 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2437 "w2i cb wrong event: 0x%x", evt);
2438 return;
2439 }
2440}
2441
2442/**
2443 * hdd_ipa_nbuf_cb() - IPA TX complete callback
2444 * @skb: packet buffer which was transmitted
2445 *
2446 * Return: None
2447 */
2448static void hdd_ipa_nbuf_cb(cdf_nbuf_t skb)
2449{
2450 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2451
2452 HDD_IPA_LOG(CDF_TRACE_LEVEL_DEBUG, "%lx", NBUF_OWNER_PRIV_DATA(skb));
2453 ipa_free_skb((struct ipa_rx_data *)NBUF_OWNER_PRIV_DATA(skb));
2454
2455 hdd_ipa->stats.num_tx_comp_cnt++;
2456
2457 atomic_dec(&hdd_ipa->tx_ref_cnt);
2458
2459 hdd_ipa_rm_try_release(hdd_ipa);
2460}
2461
2462/**
2463 * hdd_ipa_send_pkt_to_tl() - Send an IPA packet to TL
2464 * @iface_context: interface-specific IPA context
2465 * @ipa_tx_desc: packet data descriptor
2466 *
2467 * Return: None
2468 */
2469static void hdd_ipa_send_pkt_to_tl(
2470 struct hdd_ipa_iface_context *iface_context,
2471 struct ipa_rx_data *ipa_tx_desc)
2472{
2473 struct hdd_ipa_priv *hdd_ipa = iface_context->hdd_ipa;
2474 uint8_t interface_id;
2475 hdd_adapter_t *adapter = NULL;
2476 cdf_nbuf_t skb;
2477
2478 cdf_spin_lock_bh(&iface_context->interface_lock);
2479 adapter = iface_context->adapter;
2480 if (!adapter) {
2481 HDD_IPA_LOG(CDF_TRACE_LEVEL_WARN, "Interface Down");
2482 ipa_free_skb(ipa_tx_desc);
2483 iface_context->stats.num_tx_drop++;
2484 cdf_spin_unlock_bh(&iface_context->interface_lock);
2485 hdd_ipa_rm_try_release(hdd_ipa);
2486 return;
2487 }
2488
2489 /*
2490 * During CAC period, data packets shouldn't be sent over the air so
2491 * drop all the packets here
2492 */
2493 if (WLAN_HDD_GET_AP_CTX_PTR(adapter)->dfs_cac_block_tx) {
2494 ipa_free_skb(ipa_tx_desc);
2495 cdf_spin_unlock_bh(&iface_context->interface_lock);
2496 iface_context->stats.num_tx_cac_drop++;
2497 hdd_ipa_rm_try_release(hdd_ipa);
2498 return;
2499 }
2500
2501 interface_id = adapter->sessionId;
2502 ++adapter->stats.tx_packets;
2503
2504 cdf_spin_unlock_bh(&iface_context->interface_lock);
2505
2506 skb = ipa_tx_desc->skb;
2507
2508 cdf_mem_set(skb->cb, sizeof(skb->cb), 0);
2509 NBUF_OWNER_ID(skb) = IPA_NBUF_OWNER_ID;
2510 NBUF_CALLBACK_FN(skb) = hdd_ipa_nbuf_cb;
2511 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
2512 NBUF_MAPPED_PADDR_LO(skb) = ipa_tx_desc->dma_addr
2513 + HDD_IPA_WLAN_FRAG_HEADER
2514 + HDD_IPA_WLAN_IPA_HEADER;
2515 ipa_tx_desc->skb->len -=
2516 HDD_IPA_WLAN_FRAG_HEADER + HDD_IPA_WLAN_IPA_HEADER;
2517 } else
2518 NBUF_MAPPED_PADDR_LO(skb) = ipa_tx_desc->dma_addr;
2519
2520 NBUF_OWNER_PRIV_DATA(skb) = (unsigned long)ipa_tx_desc;
2521
2522 adapter->stats.tx_bytes += ipa_tx_desc->skb->len;
2523
2524 skb = ol_tx_send_ipa_data_frame(iface_context->tl_context,
2525 ipa_tx_desc->skb);
2526 if (skb) {
2527 HDD_IPA_LOG(CDF_TRACE_LEVEL_DEBUG, "TLSHIM tx fail");
2528 ipa_free_skb(ipa_tx_desc);
2529 iface_context->stats.num_tx_err++;
2530 hdd_ipa_rm_try_release(hdd_ipa);
2531 return;
2532 }
2533
2534 atomic_inc(&hdd_ipa->tx_ref_cnt);
2535
2536 iface_context->stats.num_tx++;
2537
2538}
2539
2540/**
2541 * hdd_ipa_pm_send_pkt_to_tl() - Send queued packets to TL
2542 * @work: pointer to the scheduled work
2543 *
2544 * Called during PM resume to send packets to TL which were queued
2545 * while host was in the process of suspending.
2546 *
2547 * Return: None
2548 */
2549static void hdd_ipa_pm_send_pkt_to_tl(struct work_struct *work)
2550{
2551 struct hdd_ipa_priv *hdd_ipa = container_of(work,
2552 struct hdd_ipa_priv,
2553 pm_work);
2554 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
2555 cdf_nbuf_t skb;
2556 uint32_t dequeued = 0;
2557
2558 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
2559
2560 while (((skb = cdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head)) != NULL)) {
2561 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
2562
2563 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2564
2565 dequeued++;
2566
2567 hdd_ipa_send_pkt_to_tl(pm_tx_cb->iface_context,
2568 pm_tx_cb->ipa_tx_desc);
2569
2570 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
2571 }
2572
2573 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
2574
2575 hdd_ipa->stats.num_tx_dequeued += dequeued;
2576 if (dequeued > hdd_ipa->stats.num_max_pm_queue)
2577 hdd_ipa->stats.num_max_pm_queue = dequeued;
2578}
2579
2580/**
2581 * hdd_ipa_i2w_cb() - IPA to WLAN callback
2582 * @priv: pointer to private data registered with IPA (we register a
2583 * pointer to the interface-specific IPA context)
2584 * @evt: the IPA event which triggered the callback
2585 * @data: data associated with the event
2586 *
2587 * Return: None
2588 */
2589static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
2590 unsigned long data)
2591{
2592 struct hdd_ipa_priv *hdd_ipa = NULL;
2593 struct ipa_rx_data *ipa_tx_desc;
2594 struct hdd_ipa_iface_context *iface_context;
2595 cdf_nbuf_t skb;
2596 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
2597 CDF_STATUS status = CDF_STATUS_SUCCESS;
2598
Mukul Sharma81661ae2015-10-30 20:26:02 +05302599 iface_context = (struct hdd_ipa_iface_context *)priv;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002600 if (evt != IPA_RECEIVE) {
2601 skb = (cdf_nbuf_t) data;
2602 dev_kfree_skb_any(skb);
2603 iface_context->stats.num_tx_drop++;
2604 return;
2605 }
2606
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002607 ipa_tx_desc = (struct ipa_rx_data *)data;
2608
2609 hdd_ipa = iface_context->hdd_ipa;
2610
2611 /*
2612 * When SSR is going on or driver is unloading, just drop the packets.
2613 * During SSR, there is no use in queueing the packets as STA has to
2614 * connect back any way
2615 */
2616 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
2617 if (0 != status) {
2618 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "HDD context is not valid");
2619 ipa_free_skb(ipa_tx_desc);
2620 iface_context->stats.num_tx_drop++;
2621 return;
2622 }
2623
2624 skb = ipa_tx_desc->skb;
2625
2626 HDD_IPA_DBG_DUMP(CDF_TRACE_LEVEL_DEBUG, "i2w", skb->data, 8);
2627
2628 /*
2629 * If PROD resource is not requested here then there may be cases where
2630 * IPA hardware may be clocked down because of not having proper
2631 * dependency graph between WLAN CONS and modem PROD pipes. Adding the
2632 * workaround to request PROD resource while data is going over CONS
2633 * pipe to prevent the IPA hardware clockdown.
2634 */
2635 hdd_ipa_rm_request(hdd_ipa);
2636
2637 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
2638 /*
2639 * If host is still suspended then queue the packets and these will be
2640 * drained later when resume completes. When packet is arrived here and
2641 * host is suspended, this means that there is already resume is in
2642 * progress.
2643 */
2644 if (hdd_ipa->suspended) {
2645 cdf_mem_set(skb->cb, sizeof(skb->cb), 0);
2646 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2647 pm_tx_cb->iface_context = iface_context;
2648 pm_tx_cb->ipa_tx_desc = ipa_tx_desc;
2649 cdf_nbuf_queue_add(&hdd_ipa->pm_queue_head, skb);
2650 hdd_ipa->stats.num_tx_queued++;
2651
2652 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
2653 return;
2654 }
2655
2656 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
2657
2658 /*
2659 * If we are here means, host is not suspended, wait for the work queue
2660 * to finish.
2661 */
2662#ifdef WLAN_OPEN_SOURCE
2663 flush_work(&hdd_ipa->pm_work);
2664#endif
2665
2666 return hdd_ipa_send_pkt_to_tl(iface_context, ipa_tx_desc);
2667}
2668
2669/**
2670 * hdd_ipa_suspend() - Suspend IPA
2671 * @hdd_ctx: Global HDD context
2672 *
2673 * Return: 0 on success, negativer errno on error
2674 */
2675int hdd_ipa_suspend(hdd_context_t *hdd_ctx)
2676{
2677 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2678
2679 if (!hdd_ipa_is_enabled(hdd_ctx))
2680 return 0;
2681
2682 /*
2683 * Check if IPA is ready for suspend, If we are here means, there is
2684 * high chance that suspend would go through but just to avoid any race
2685 * condition after suspend started, these checks are conducted before
2686 * allowing to suspend.
2687 */
2688 if (atomic_read(&hdd_ipa->tx_ref_cnt))
2689 return -EAGAIN;
2690
2691 cdf_spin_lock_bh(&hdd_ipa->rm_lock);
2692
2693 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED) {
2694 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
2695 return -EAGAIN;
2696 }
2697 cdf_spin_unlock_bh(&hdd_ipa->rm_lock);
2698
2699 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
2700 hdd_ipa->suspended = true;
2701 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
2702
2703 return 0;
2704}
2705
2706/**
2707 * hdd_ipa_resume() - Resume IPA following suspend
2708 * hdd_ctx: Global HDD context
2709 *
2710 * Return: 0 on success, negative errno on error
2711 */
2712int hdd_ipa_resume(hdd_context_t *hdd_ctx)
2713{
2714 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2715
2716 if (!hdd_ipa_is_enabled(hdd_ctx))
2717 return 0;
2718
2719 schedule_work(&hdd_ipa->pm_work);
2720
2721 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
2722 hdd_ipa->suspended = false;
2723 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
2724
2725 return 0;
2726}
2727
2728/**
2729 * hdd_ipa_setup_sys_pipe() - Setup all IPA Sys pipes
2730 * @hdd_ipa: Global HDD IPA context
2731 *
2732 * Return: 0 on success, negative errno on error
2733 */
2734static int hdd_ipa_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
2735{
2736 int i, ret = 0;
2737 struct ipa_sys_connect_params *ipa;
2738 uint32_t desc_fifo_sz;
2739
2740 /* The maximum number of descriptors that can be provided to a BAM at
2741 * once is one less than the total number of descriptors that the buffer
2742 * can contain.
2743 * If max_num_of_descriptors = (BAM_PIPE_DESCRIPTOR_FIFO_SIZE / sizeof
2744 * (SPS_DESCRIPTOR)), then (max_num_of_descriptors - 1) descriptors can
2745 * be provided at once.
2746 * Because of above requirement, one extra descriptor will be added to
2747 * make sure hardware always has one descriptor.
2748 */
2749 desc_fifo_sz = hdd_ipa->hdd_ctx->config->IpaDescSize
2750 + sizeof(struct sps_iovec);
2751
2752 /*setup TX pipes */
2753 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
2754 ipa = &hdd_ipa->sys_pipe[i].ipa_sys_params;
2755
2756 ipa->client = hdd_ipa_adapter_2_client[i].cons_client;
2757 ipa->desc_fifo_sz = desc_fifo_sz;
2758 ipa->priv = &hdd_ipa->iface_context[i];
2759 ipa->notify = hdd_ipa_i2w_cb;
2760
2761 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
2762 ipa->ipa_ep_cfg.hdr.hdr_len =
2763 HDD_IPA_UC_WLAN_TX_HDR_LEN;
2764 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
2765 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
2766 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
2767 ipa->ipa_ep_cfg.hdr.hdr_additional_const_len =
2768 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
2769 ipa->ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
2770 } else {
2771 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
2772 }
2773 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
2774
2775 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2776 ipa->keep_ipa_awake = 1;
2777
2778 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
2779 if (ret) {
2780 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "Failed for pipe %d"
2781 " ret: %d", i, ret);
2782 goto setup_sys_pipe_fail;
2783 }
2784 hdd_ipa->sys_pipe[i].conn_hdl_valid = 1;
2785 }
2786
2787 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
2788 /*
2789 * Hard code it here, this can be extended if in case
2790 * PROD pipe is also per interface.
2791 * Right now there is no advantage of doing this.
2792 */
2793 hdd_ipa->prod_client = IPA_CLIENT_WLAN1_PROD;
2794
2795 ipa = &hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].ipa_sys_params;
2796
2797 ipa->client = hdd_ipa->prod_client;
2798
2799 ipa->desc_fifo_sz = desc_fifo_sz;
2800 ipa->priv = hdd_ipa;
2801 ipa->notify = hdd_ipa_w2i_cb;
2802
2803 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
2804 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_RX_HDR_LEN;
2805 ipa->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
2806 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
2807
2808 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2809 ipa->keep_ipa_awake = 1;
2810
2811 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
2812 if (ret) {
2813 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
2814 "Failed for RX pipe: %d", ret);
2815 goto setup_sys_pipe_fail;
2816 }
2817 hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].conn_hdl_valid = 1;
2818 }
2819
2820 return ret;
2821
2822setup_sys_pipe_fail:
2823
2824 while (--i >= 0) {
2825 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].conn_hdl);
2826 cdf_mem_zero(&hdd_ipa->sys_pipe[i],
2827 sizeof(struct hdd_ipa_sys_pipe));
2828 }
2829
2830 return ret;
2831}
2832
2833/**
2834 * hdd_ipa_teardown_sys_pipe() - Tear down all IPA Sys pipes
2835 * @hdd_ipa: Global HDD IPA context
2836 *
2837 * Return: None
2838 */
2839static void hdd_ipa_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
2840{
2841 int ret = 0, i;
2842 for (i = 0; i < HDD_IPA_MAX_SYSBAM_PIPE; i++) {
2843 if (hdd_ipa->sys_pipe[i].conn_hdl_valid) {
2844 ret =
2845 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].
2846 conn_hdl);
2847 if (ret)
2848 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "Failed: %d",
2849 ret);
2850
2851 hdd_ipa->sys_pipe[i].conn_hdl_valid = 0;
2852 }
2853 }
2854}
2855
2856/**
2857 * hdd_ipa_register_interface() - register IPA interface
2858 * @hdd_ipa: Global IPA context
2859 * @iface_context: Per-interface IPA context
2860 *
2861 * Return: 0 on success, negative errno on error
2862 */
2863static int hdd_ipa_register_interface(struct hdd_ipa_priv *hdd_ipa,
2864 struct hdd_ipa_iface_context
2865 *iface_context)
2866{
2867 struct ipa_tx_intf tx_intf;
2868 struct ipa_rx_intf rx_intf;
2869 struct ipa_ioc_tx_intf_prop *tx_prop = NULL;
2870 struct ipa_ioc_rx_intf_prop *rx_prop = NULL;
2871 char *ifname = iface_context->adapter->dev->name;
2872
2873 char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX];
2874 char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX];
2875
2876 int num_prop = 1;
2877 int ret = 0;
2878
2879 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx))
2880 num_prop++;
2881
2882 /* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */
2883 tx_prop =
2884 cdf_mem_malloc(sizeof(struct ipa_ioc_tx_intf_prop) * num_prop);
2885 if (!tx_prop) {
2886 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "tx_prop allocation failed");
2887 goto register_interface_fail;
2888 }
2889
2890 /* Allocate RX properties, 1 each for IPv4 & IPv6 */
2891 rx_prop =
2892 cdf_mem_malloc(sizeof(struct ipa_ioc_rx_intf_prop) * num_prop);
2893 if (!rx_prop) {
2894 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "rx_prop allocation failed");
2895 goto register_interface_fail;
2896 }
2897
2898 cdf_mem_zero(&tx_intf, sizeof(tx_intf));
2899 cdf_mem_zero(&rx_intf, sizeof(rx_intf));
2900
2901 snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
2902 ifname, HDD_IPA_IPV4_NAME_EXT);
2903 snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
2904 ifname, HDD_IPA_IPV6_NAME_EXT);
2905
2906 rx_prop[IPA_IP_v4].ip = IPA_IP_v4;
2907 rx_prop[IPA_IP_v4].src_pipe = iface_context->prod_client;
2908 rx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
2909 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
2910
2911 /*
2912 * Interface ID is 3rd byte in the CLD header. Add the meta data and
2913 * mask to identify the interface in IPA hardware
2914 */
2915 rx_prop[IPA_IP_v4].attrib.meta_data =
2916 htonl(iface_context->adapter->sessionId << 16);
2917 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
2918
2919 rx_intf.num_props++;
2920 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
2921 rx_prop[IPA_IP_v6].ip = IPA_IP_v6;
2922 rx_prop[IPA_IP_v6].src_pipe = iface_context->prod_client;
2923 rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
2924 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
2925 rx_prop[IPA_IP_v4].attrib.meta_data =
2926 htonl(iface_context->adapter->sessionId << 16);
2927 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
2928
2929 rx_intf.num_props++;
2930 }
2931
2932 tx_prop[IPA_IP_v4].ip = IPA_IP_v4;
2933 tx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
2934 tx_prop[IPA_IP_v4].dst_pipe = IPA_CLIENT_WLAN1_CONS;
2935 tx_prop[IPA_IP_v4].alt_dst_pipe = iface_context->cons_client;
2936 strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name,
2937 IPA_RESOURCE_NAME_MAX);
2938 tx_intf.num_props++;
2939
2940 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
2941 tx_prop[IPA_IP_v6].ip = IPA_IP_v6;
2942 tx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
2943 tx_prop[IPA_IP_v6].dst_pipe = IPA_CLIENT_WLAN1_CONS;
2944 tx_prop[IPA_IP_v6].alt_dst_pipe = iface_context->cons_client;
2945 strlcpy(tx_prop[IPA_IP_v6].hdr_name, ipv6_hdr_name,
2946 IPA_RESOURCE_NAME_MAX);
2947 tx_intf.num_props++;
2948 }
2949
2950 tx_intf.prop = tx_prop;
2951 rx_intf.prop = rx_prop;
2952
2953 /* Call the ipa api to register interface */
2954 ret = ipa_register_intf(ifname, &tx_intf, &rx_intf);
2955
2956register_interface_fail:
2957 cdf_mem_free(tx_prop);
2958 cdf_mem_free(rx_prop);
2959 return ret;
2960}
2961
2962/**
2963 * hdd_remove_ipa_header() - Remove a specific header from IPA
2964 * @name: Name of the header to be removed
2965 *
2966 * Return: None
2967 */
2968static void hdd_ipa_remove_header(char *name)
2969{
2970 struct ipa_ioc_get_hdr hdrlookup;
2971 int ret = 0, len;
2972 struct ipa_ioc_del_hdr *ipa_hdr;
2973
2974 cdf_mem_zero(&hdrlookup, sizeof(hdrlookup));
2975 strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name));
2976 ret = ipa_get_hdr(&hdrlookup);
2977 if (ret) {
2978 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "Hdr deleted already %s, %d",
2979 name, ret);
2980 return;
2981 }
2982
2983 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "hdl: 0x%x", hdrlookup.hdl);
2984 len = sizeof(struct ipa_ioc_del_hdr) + sizeof(struct ipa_hdr_del) * 1;
2985 ipa_hdr = (struct ipa_ioc_del_hdr *)cdf_mem_malloc(len);
2986 if (ipa_hdr == NULL) {
2987 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "ipa_hdr allocation failed");
2988 return;
2989 }
2990 ipa_hdr->num_hdls = 1;
2991 ipa_hdr->commit = 0;
2992 ipa_hdr->hdl[0].hdl = hdrlookup.hdl;
2993 ipa_hdr->hdl[0].status = -1;
2994 ret = ipa_del_hdr(ipa_hdr);
2995 if (ret != 0)
2996 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "Delete header failed: %d",
2997 ret);
2998
2999 cdf_mem_free(ipa_hdr);
3000}
3001
3002/**
3003 * hdd_ipa_add_header_info() - Add IPA header for a given interface
3004 * @hdd_ipa: Global HDD IPA context
3005 * @iface_context: Interface-specific HDD IPA context
3006 * @mac_addr: Interface MAC address
3007 *
3008 * Return: 0 on success, negativer errno value on error
3009 */
3010static int hdd_ipa_add_header_info(struct hdd_ipa_priv *hdd_ipa,
3011 struct hdd_ipa_iface_context *iface_context,
3012 uint8_t *mac_addr)
3013{
3014 hdd_adapter_t *adapter = iface_context->adapter;
3015 char *ifname;
3016 struct ipa_ioc_add_hdr *ipa_hdr = NULL;
3017 int ret = -EINVAL;
3018 struct hdd_ipa_tx_hdr *tx_hdr = NULL;
3019 struct hdd_ipa_uc_tx_hdr *uc_tx_hdr = NULL;
3020
3021 ifname = adapter->dev->name;
3022
3023 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "Add Partial hdr: %s, %pM",
3024 ifname, mac_addr);
3025
3026 /* dynamically allocate the memory to add the hdrs */
3027 ipa_hdr = cdf_mem_malloc(sizeof(struct ipa_ioc_add_hdr)
3028 + sizeof(struct ipa_hdr_add));
3029 if (!ipa_hdr) {
3030 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3031 "%s: ipa_hdr allocation failed", ifname);
3032 ret = -ENOMEM;
3033 goto end;
3034 }
3035
3036 ipa_hdr->commit = 0;
3037 ipa_hdr->num_hdrs = 1;
3038
3039 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3040 uc_tx_hdr = (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3041 memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN);
3042 memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3043 uc_tx_hdr->ipa_hd.vdev_id = iface_context->adapter->sessionId;
3044 HDD_IPA_LOG(CDF_TRACE_LEVEL_DEBUG,
3045 "ifname=%s, vdev_id=%d",
3046 ifname, uc_tx_hdr->ipa_hd.vdev_id);
3047 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3048 ifname, HDD_IPA_IPV4_NAME_EXT);
3049 ipa_hdr->hdr[0].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
3050 ipa_hdr->hdr[0].type = IPA_HDR_L2_ETHERNET_II;
3051 ipa_hdr->hdr[0].is_partial = 1;
3052 ipa_hdr->hdr[0].hdr_hdl = 0;
3053 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3054 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3055
3056 ret = ipa_add_hdr(ipa_hdr);
3057 } else {
3058 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3059
3060 /* Set the Source MAC */
3061 memcpy(tx_hdr, &ipa_tx_hdr, HDD_IPA_WLAN_TX_HDR_LEN);
3062 memcpy(tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3063
3064 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3065 ifname, HDD_IPA_IPV4_NAME_EXT);
3066 ipa_hdr->hdr[0].hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
3067 ipa_hdr->hdr[0].is_partial = 1;
3068 ipa_hdr->hdr[0].hdr_hdl = 0;
3069 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3070 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3071
3072 /* Set the type to IPV4 in the header */
3073 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IP);
3074
3075 ret = ipa_add_hdr(ipa_hdr);
3076 }
3077 if (ret) {
3078 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "%s IPv4 add hdr failed: %d",
3079 ifname, ret);
3080 goto end;
3081 }
3082
3083 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: IPv4 hdr_hdl: 0x%x",
3084 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3085
3086 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3087 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3088 ifname, HDD_IPA_IPV6_NAME_EXT);
3089
3090 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3091 uc_tx_hdr =
3092 (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3093 uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6);
3094 } else {
3095 /* Set the type to IPV6 in the header */
3096 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3097 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IPV6);
3098 }
3099
3100 ret = ipa_add_hdr(ipa_hdr);
3101 if (ret) {
3102 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3103 "%s: IPv6 add hdr failed: %d", ifname, ret);
3104 goto clean_ipv4_hdr;
3105 }
3106
3107 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: IPv6 hdr_hdl: 0x%x",
3108 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3109 }
3110
3111 cdf_mem_free(ipa_hdr);
3112
3113 return ret;
3114
3115clean_ipv4_hdr:
3116 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3117 ifname, HDD_IPA_IPV4_NAME_EXT);
3118 hdd_ipa_remove_header(ipa_hdr->hdr[0].name);
3119end:
3120 if (ipa_hdr)
3121 cdf_mem_free(ipa_hdr);
3122
3123 return ret;
3124}
3125
3126/**
3127 * hdd_ipa_clean_hdr() - Cleanup IPA on a given adapter
3128 * @adapter: Adapter upon which IPA was previously configured
3129 *
3130 * Return: None
3131 */
3132static void hdd_ipa_clean_hdr(hdd_adapter_t *adapter)
3133{
3134 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3135 int ret;
3136 char name_ipa[IPA_RESOURCE_NAME_MAX];
3137
3138 /* Remove the headers */
3139 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3140 adapter->dev->name, HDD_IPA_IPV4_NAME_EXT);
3141 hdd_ipa_remove_header(name_ipa);
3142
3143 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3144 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3145 adapter->dev->name, HDD_IPA_IPV6_NAME_EXT);
3146 hdd_ipa_remove_header(name_ipa);
3147 }
3148 /* unregister the interface with IPA */
3149 ret = ipa_deregister_intf(adapter->dev->name);
3150 if (ret)
3151 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3152 "%s: ipa_deregister_intf fail: %d",
3153 adapter->dev->name, ret);
3154}
3155
3156/**
3157 * hdd_ipa_cleanup_iface() - Cleanup IPA on a given interface
3158 * @iface_context: interface-specific IPA context
3159 *
3160 * Return: None
3161 */
3162static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context)
3163{
3164 if (iface_context == NULL)
3165 return;
3166
3167 hdd_ipa_clean_hdr(iface_context->adapter);
3168
3169 cdf_spin_lock_bh(&iface_context->interface_lock);
3170 iface_context->adapter->ipa_context = NULL;
3171 iface_context->adapter = NULL;
3172 iface_context->tl_context = NULL;
3173 cdf_spin_unlock_bh(&iface_context->interface_lock);
3174 iface_context->ifa_address = 0;
3175 if (!iface_context->hdd_ipa->num_iface) {
3176 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3177 "NUM INTF 0, Invalid");
3178 CDF_ASSERT(0);
3179 }
3180 iface_context->hdd_ipa->num_iface--;
3181}
3182
3183/**
3184 * hdd_ipa_setup_iface() - Setup IPA on a given interface
3185 * @hdd_ipa: HDD IPA global context
3186 * @adapter: Interface upon which IPA is being setup
3187 * @sta_id: Station ID of the API instance
3188 *
3189 * Return: 0 on success, negative errno value on error
3190 */
3191static int hdd_ipa_setup_iface(struct hdd_ipa_priv *hdd_ipa,
3192 hdd_adapter_t *adapter, uint8_t sta_id)
3193{
3194 struct hdd_ipa_iface_context *iface_context = NULL;
3195 void *tl_context = NULL;
3196 int i, ret = 0;
3197
3198 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
3199 * channel change indication. Since these indications are sent by lower
3200 * layer as SAP updates and IPA doesn't have to do anything for these
3201 * updates so ignoring!
3202 */
3203 if (WLAN_HDD_SOFTAP == adapter->device_mode && adapter->ipa_context)
3204 return 0;
3205
3206 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
3207 if (hdd_ipa->iface_context[i].adapter == NULL) {
3208 iface_context = &(hdd_ipa->iface_context[i]);
3209 break;
3210 }
3211 }
3212
3213 if (iface_context == NULL) {
3214 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3215 "All the IPA interfaces are in use");
3216 ret = -ENOMEM;
3217 goto end;
3218 }
3219
3220 adapter->ipa_context = iface_context;
3221 iface_context->adapter = adapter;
3222 iface_context->sta_id = sta_id;
3223 tl_context = ol_txrx_get_vdev_by_sta_id(sta_id);
3224
3225 if (tl_context == NULL) {
3226 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3227 "Not able to get TL context sta_id: %d", sta_id);
3228 ret = -EINVAL;
3229 goto end;
3230 }
3231
3232 iface_context->tl_context = tl_context;
3233
3234 ret = hdd_ipa_add_header_info(hdd_ipa, iface_context,
3235 adapter->dev->dev_addr);
3236
3237 if (ret)
3238 goto end;
3239
3240 /* Configure the TX and RX pipes filter rules */
3241 ret = hdd_ipa_register_interface(hdd_ipa, iface_context);
3242 if (ret)
3243 goto cleanup_header;
3244
3245 hdd_ipa->num_iface++;
3246 return ret;
3247
3248cleanup_header:
3249
3250 hdd_ipa_clean_hdr(adapter);
3251end:
3252 if (iface_context)
3253 hdd_ipa_cleanup_iface(iface_context);
3254 return ret;
3255}
3256
3257/**
3258 * hdd_ipa_msg_free_fn() - Free an IPA message
3259 * @buff: pointer to the IPA message
3260 * @len: length of the IPA message
3261 * @type: type of IPA message
3262 *
3263 * Return: None
3264 */
3265static void hdd_ipa_msg_free_fn(void *buff, uint32_t len, uint32_t type)
3266{
3267 hddLog(LOG1, "msg type:%d, len:%d", type, len);
3268 ghdd_ipa->stats.num_free_msg++;
3269 cdf_mem_free(buff);
3270}
3271
3272/**
3273 * hdd_ipa_send_mcc_scc_msg() - send IPA WLAN_SWITCH_TO_MCC/SCC message
3274 * @mcc_mode: 0=MCC/1=SCC
3275 *
3276 * Return: 0 on success, negative errno value on error
3277 */
3278int hdd_ipa_send_mcc_scc_msg(hdd_context_t *pHddCtx, bool mcc_mode)
3279{
3280 hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
3281 CDF_STATUS status;
3282 hdd_adapter_t *pAdapter;
3283 struct ipa_msg_meta meta;
3284 struct ipa_wlan_msg *msg;
3285 int ret;
3286
3287 if (!hdd_ipa_uc_sta_is_enabled(pHddCtx))
3288 return -EINVAL;
3289
3290 if (!pHddCtx->mcc_mode) {
3291 /* Flush TxRx queue for each adapter before switch to SCC */
3292 status = hdd_get_front_adapter(pHddCtx, &adapter_node);
3293 while (NULL != adapter_node && CDF_STATUS_SUCCESS == status) {
3294 pAdapter = adapter_node->pAdapter;
3295 if (pAdapter->device_mode == WLAN_HDD_INFRA_STATION ||
3296 pAdapter->device_mode == WLAN_HDD_SOFTAP) {
3297 hddLog(CDF_TRACE_LEVEL_INFO,
3298 "MCC->SCC: Flush TxRx queue(d_mode=%d)",
3299 pAdapter->device_mode);
3300 hdd_deinit_tx_rx(pAdapter);
3301 }
3302 status = hdd_get_next_adapter(
3303 pHddCtx, adapter_node, &next);
3304 adapter_node = next;
3305 }
3306 }
3307
3308 /* Send SCC/MCC Switching event to IPA */
3309 meta.msg_len = sizeof(*msg);
3310 msg = cdf_mem_malloc(meta.msg_len);
3311 if (msg == NULL) {
3312 hddLog(LOGE, "msg allocation failed");
3313 return -ENOMEM;
3314 }
3315
3316 meta.msg_type = mcc_mode ?
3317 WLAN_SWITCH_TO_MCC : WLAN_SWITCH_TO_SCC;
3318 hddLog(LOG1, "ipa_send_msg(Evt:%d)", meta.msg_type);
3319
3320 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3321
3322 if (ret) {
3323 hddLog(LOGE, "ipa_send_msg(Evt:%d) - fail=%d",
3324 meta.msg_type, ret);
3325 cdf_mem_free(msg);
3326 }
3327
3328 return ret;
3329}
3330
3331/**
3332 * hdd_ipa_wlan_event_to_str() - convert IPA WLAN event to string
3333 * @event: IPA WLAN event to be converted to a string
3334 *
3335 * Return: ASCII string representing the IPA WLAN event
3336 */
3337static inline char *hdd_ipa_wlan_event_to_str(enum ipa_wlan_event event)
3338{
3339 switch (event) {
3340 case WLAN_CLIENT_CONNECT:
3341 return "WLAN_CLIENT_CONNECT";
3342 case WLAN_CLIENT_DISCONNECT:
3343 return "WLAN_CLIENT_DISCONNECT";
3344 case WLAN_CLIENT_POWER_SAVE_MODE:
3345 return "WLAN_CLIENT_POWER_SAVE_MODE";
3346 case WLAN_CLIENT_NORMAL_MODE:
3347 return "WLAN_CLIENT_NORMAL_MODE";
3348 case SW_ROUTING_ENABLE:
3349 return "SW_ROUTING_ENABLE";
3350 case SW_ROUTING_DISABLE:
3351 return "SW_ROUTING_DISABLE";
3352 case WLAN_AP_CONNECT:
3353 return "WLAN_AP_CONNECT";
3354 case WLAN_AP_DISCONNECT:
3355 return "WLAN_AP_DISCONNECT";
3356 case WLAN_STA_CONNECT:
3357 return "WLAN_STA_CONNECT";
3358 case WLAN_STA_DISCONNECT:
3359 return "WLAN_STA_DISCONNECT";
3360 case WLAN_CLIENT_CONNECT_EX:
3361 return "WLAN_CLIENT_CONNECT_EX";
3362
3363 case IPA_WLAN_EVENT_MAX:
3364 default:
3365 return "UNKNOWN";
3366 }
3367}
3368
3369/**
3370 * hdd_ipa_wlan_evt() - IPA event handler
3371 * @adapter: adapter upon which the event was received
3372 * @sta_id: station id for the event
3373 * @type: the event type
3374 * @mac_address: MAC address associated with the event
3375 *
3376 * Return: 0 on success, negative errno value on error
3377 */
3378int hdd_ipa_wlan_evt(hdd_adapter_t *adapter, uint8_t sta_id,
3379 enum ipa_wlan_event type, uint8_t *mac_addr)
3380{
3381 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3382 struct ipa_msg_meta meta;
3383 struct ipa_wlan_msg *msg;
3384 struct ipa_wlan_msg_ex *msg_ex = NULL;
3385 int ret;
3386
3387 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: %s evt, MAC: %pM sta_id: %d",
3388 adapter->dev->name, hdd_ipa_wlan_event_to_str(type),
3389 mac_addr, sta_id);
3390
3391 if (type >= IPA_WLAN_EVENT_MAX)
3392 return -EINVAL;
3393
3394 if (WARN_ON(is_zero_ether_addr(mac_addr)))
3395 return -EINVAL;
3396
3397 if (!hdd_ipa || !hdd_ipa_is_enabled(hdd_ipa->hdd_ctx)) {
3398 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "IPA OFFLOAD NOT ENABLED");
3399 return -EINVAL;
3400 }
3401
3402 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3403 !hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
3404 (WLAN_HDD_SOFTAP != adapter->device_mode)) {
3405 return 0;
3406 }
3407
3408 /*
3409 * During IPA UC resource loading/unloading new events can be issued.
3410 * Store the events separately and handle them later.
3411 */
3412 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3413 ((hdd_ipa->resource_loading) ||
3414 (hdd_ipa->resource_unloading))) {
3415 struct ipa_uc_pending_event *pending_evet = NULL;
3416
3417 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3418 "%s, RL/RUL inprogress", __func__);
3419 pending_evet = (struct ipa_uc_pending_event *)cdf_mem_malloc(
3420 sizeof(struct ipa_uc_pending_event));
3421 if (!pending_evet) {
3422 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3423 "Pending event memory alloc fail");
3424 return -ENOMEM;
3425 }
3426 pending_evet->adapter = adapter;
3427 pending_evet->sta_id = sta_id;
3428 pending_evet->type = type;
3429 cdf_mem_copy(pending_evet->mac_addr,
3430 mac_addr,
3431 CDF_MAC_ADDR_SIZE);
3432 cdf_list_insert_back(&hdd_ipa->pending_event,
3433 &pending_evet->node);
3434 return 0;
3435 }
3436
3437 hdd_ipa->stats.event[type]++;
3438
3439 switch (type) {
3440 case WLAN_STA_CONNECT:
3441 /* STA already connected and without disconnect, connect again
3442 * This is Roaming scenario
3443 */
3444 if (hdd_ipa->sta_connected)
3445 hdd_ipa_cleanup_iface(adapter->ipa_context);
3446
3447 if ((hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) &&
3448 (!hdd_ipa->sta_connected))
3449 hdd_ipa_uc_offload_enable_disable(adapter,
3450 SIR_STA_RX_DATA_OFFLOAD, 1);
3451
3452 cdf_mutex_acquire(&hdd_ipa->event_lock);
3453
3454 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3455 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3456 "%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED",
3457 msg_ex->name, meta.msg_type);
3458 } else if ((!hdd_ipa->sap_num_connected_sta) &&
3459 (!hdd_ipa->sta_connected)) {
3460 /* Enable IPA UC TX PIPE when STA connected */
3461 ret = hdd_ipa_uc_handle_first_con(hdd_ipa);
3462 if (!ret) {
3463 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3464 "handle 1st con ret %d", ret);
3465 } else {
3466 cdf_mutex_release(&hdd_ipa->event_lock);
3467 hdd_ipa_uc_offload_enable_disable(adapter,
3468 SIR_STA_RX_DATA_OFFLOAD, 0);
3469 goto end;
3470 }
3471 }
3472 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3473 if (ret) {
3474 cdf_mutex_release(&hdd_ipa->event_lock);
3475 hdd_ipa_uc_offload_enable_disable(adapter,
3476 SIR_STA_RX_DATA_OFFLOAD, 0);
3477 goto end;
3478
3479#ifdef IPA_UC_OFFLOAD
3480 vdev_to_iface[adapter->sessionId] =
3481 ((struct hdd_ipa_iface_context *)
3482 (adapter->ipa_context))->iface_id;
3483#endif /* IPA_UC_OFFLOAD */
3484 }
3485
3486 cdf_mutex_release(&hdd_ipa->event_lock);
3487
3488 hdd_ipa->sta_connected = 1;
3489 break;
3490
3491 case WLAN_AP_CONNECT:
3492 /* For DFS channel we get two start_bss event (before and after
3493 * CAC). Also when ACS range includes both DFS and non DFS
3494 * channels, we could possibly change channel many times due to
3495 * RADAR detection and chosen channel may not be a DFS channels.
3496 * So dont return error here. Just discard the event.
3497 */
3498 if (adapter->ipa_context)
3499 return 0;
3500
3501 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3502 hdd_ipa_uc_offload_enable_disable(adapter,
3503 SIR_AP_RX_DATA_OFFLOAD, 1);
3504 }
3505 cdf_mutex_acquire(&hdd_ipa->event_lock);
3506 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3507 if (ret) {
3508 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3509 "%s: Evt: %d, Interface setup failed",
3510 msg_ex->name, meta.msg_type);
3511 cdf_mutex_release(&hdd_ipa->event_lock);
3512 goto end;
3513
3514#ifdef IPA_UC_OFFLOAD
3515 vdev_to_iface[adapter->sessionId] =
3516 ((struct hdd_ipa_iface_context *)
3517 (adapter->ipa_context))->iface_id;
3518#endif /* IPA_UC_OFFLOAD */
3519 }
3520 cdf_mutex_release(&hdd_ipa->event_lock);
3521 break;
3522
3523 case WLAN_STA_DISCONNECT:
3524 cdf_mutex_acquire(&hdd_ipa->event_lock);
3525 hdd_ipa_cleanup_iface(adapter->ipa_context);
3526
3527 if (!hdd_ipa->sta_connected) {
3528 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3529 "%s: Evt: %d, STA already disconnected",
3530 msg_ex->name, meta.msg_type);
3531 cdf_mutex_release(&hdd_ipa->event_lock);
3532 return -EINVAL;
3533 }
3534 hdd_ipa->sta_connected = 0;
3535 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3536 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3537 "%s: IPA UC OFFLOAD NOT ENABLED",
3538 msg_ex->name);
3539 } else {
3540 /* Disable IPA UC TX PIPE when STA disconnected */
3541 if ((!hdd_ipa->sap_num_connected_sta) ||
3542 ((!hdd_ipa->num_iface) &&
3543 (HDD_IPA_UC_NUM_WDI_PIPE ==
3544 hdd_ipa->activated_fw_pipe))) {
3545 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3546 }
3547 }
3548
3549 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
3550 hdd_ipa_uc_offload_enable_disable(adapter,
3551 SIR_STA_RX_DATA_OFFLOAD, 0);
3552 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3553 }
3554
3555 cdf_mutex_release(&hdd_ipa->event_lock);
3556 break;
3557
3558 case WLAN_AP_DISCONNECT:
3559 if (!adapter->ipa_context) {
3560 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3561 "%s: Evt: %d, SAP already disconnected",
3562 msg_ex->name, meta.msg_type);
3563 return -EINVAL;
3564 }
3565
3566 cdf_mutex_acquire(&hdd_ipa->event_lock);
3567 hdd_ipa_cleanup_iface(adapter->ipa_context);
3568 if ((!hdd_ipa->num_iface) &&
3569 (HDD_IPA_UC_NUM_WDI_PIPE ==
3570 hdd_ipa->activated_fw_pipe)) {
3571 if (hdd_ipa->hdd_ctx->isUnloadInProgress) {
3572 /*
3573 * We disable WDI pipes directly here since
3574 * IPA_OPCODE_TX/RX_SUSPEND message will not be
3575 * processed when unloading WLAN driver is in
3576 * progress
3577 */
3578 hdd_ipa_uc_disable_pipes(hdd_ipa);
3579 } else {
3580 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3581 "NO INTF left but still pipe clean up");
3582 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3583 }
3584 }
3585
3586 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3587 hdd_ipa_uc_offload_enable_disable(adapter,
3588 SIR_AP_RX_DATA_OFFLOAD, 0);
3589 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3590 }
3591 cdf_mutex_release(&hdd_ipa->event_lock);
3592 break;
3593
3594 case WLAN_CLIENT_CONNECT_EX:
3595 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%d %d",
3596 adapter->dev->ifindex, sta_id);
3597
3598 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3599 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3600 "%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED",
3601 adapter->dev->name, meta.msg_type);
3602 return 0;
3603 }
3604
3605 cdf_mutex_acquire(&hdd_ipa->event_lock);
3606 if (hdd_ipa_uc_find_add_assoc_sta(hdd_ipa,
3607 true, sta_id)) {
3608 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3609 "%s: STA ID %d found, not valid",
3610 adapter->dev->name, sta_id);
3611 cdf_mutex_release(&hdd_ipa->event_lock);
3612 return 0;
3613 }
3614 hdd_ipa->sap_num_connected_sta++;
3615 hdd_ipa->pending_cons_req = false;
3616 cdf_mutex_release(&hdd_ipa->event_lock);
3617
3618 meta.msg_type = type;
3619 meta.msg_len = (sizeof(struct ipa_wlan_msg_ex) +
3620 sizeof(struct ipa_wlan_hdr_attrib_val));
3621 msg_ex = cdf_mem_malloc(meta.msg_len);
3622
3623 if (msg_ex == NULL) {
3624 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3625 "msg_ex allocation failed");
3626 return -ENOMEM;
3627 }
3628 strlcpy(msg_ex->name, adapter->dev->name,
3629 IPA_RESOURCE_NAME_MAX);
3630 msg_ex->num_of_attribs = 1;
3631 msg_ex->attribs[0].attrib_type = WLAN_HDR_ATTRIB_MAC_ADDR;
3632 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3633 msg_ex->attribs[0].offset =
3634 HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3635 } else {
3636 msg_ex->attribs[0].offset =
3637 HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3638 }
3639 memcpy(msg_ex->attribs[0].u.mac_addr, mac_addr,
3640 IPA_MAC_ADDR_SIZE);
3641
3642 ret = ipa_send_msg(&meta, msg_ex, hdd_ipa_msg_free_fn);
3643
3644 if (ret) {
3645 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Evt: %d : %d",
3646 msg_ex->name, meta.msg_type, ret);
3647 cdf_mem_free(msg_ex);
3648 return ret;
3649 }
3650 hdd_ipa->stats.num_send_msg++;
3651
3652 cdf_mutex_acquire(&hdd_ipa->event_lock);
3653 /* Enable IPA UC Data PIPEs when first STA connected */
3654 if ((1 == hdd_ipa->sap_num_connected_sta)
3655 && (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)
3656 || !hdd_ipa->sta_connected)) {
3657 ret = hdd_ipa_uc_handle_first_con(hdd_ipa);
3658 if (ret) {
3659 cdf_mutex_release(&hdd_ipa->event_lock);
3660 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3661 "%s: handle 1st con ret %d",
3662 adapter->dev->name, ret);
3663 return ret;
3664 }
3665 }
3666 cdf_mutex_release(&hdd_ipa->event_lock);
3667
3668 return ret;
3669
3670 case WLAN_CLIENT_DISCONNECT:
3671 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3672 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3673 "%s: IPA UC OFFLOAD NOT ENABLED",
3674 msg_ex->name);
3675 return 0;
3676 }
3677
3678 cdf_mutex_acquire(&hdd_ipa->event_lock);
3679 if (!hdd_ipa_uc_find_add_assoc_sta(hdd_ipa, false, sta_id)) {
3680 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3681 "%s: STA ID %d NOT found, not valid",
3682 msg_ex->name, sta_id);
3683 cdf_mutex_release(&hdd_ipa->event_lock);
3684 return 0;
3685 }
3686 hdd_ipa->sap_num_connected_sta--;
3687 /* Disable IPA UC TX PIPE when last STA disconnected */
3688 if (!hdd_ipa->sap_num_connected_sta
3689 && (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) ||
3690 !hdd_ipa->sta_connected)
3691 && (false == hdd_ipa->resource_unloading)
3692 && (HDD_IPA_UC_NUM_WDI_PIPE ==
3693 hdd_ipa->activated_fw_pipe))
3694 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3695 cdf_mutex_release(&hdd_ipa->event_lock);
3696 break;
3697
3698 default:
3699 return 0;
3700 }
3701
3702 meta.msg_len = sizeof(struct ipa_wlan_msg);
3703 msg = cdf_mem_malloc(meta.msg_len);
3704 if (msg == NULL) {
3705 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR, "msg allocation failed");
3706 return -ENOMEM;
3707 }
3708
3709 meta.msg_type = type;
3710 strlcpy(msg->name, adapter->dev->name, IPA_RESOURCE_NAME_MAX);
3711 memcpy(msg->mac_addr, mac_addr, ETH_ALEN);
3712
3713 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Evt: %d",
3714 msg->name, meta.msg_type);
3715
3716 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3717
3718 if (ret) {
3719 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO, "%s: Evt: %d fail:%d",
3720 msg->name, meta.msg_type, ret);
3721 cdf_mem_free(msg);
3722 return ret;
3723 }
3724
3725 hdd_ipa->stats.num_send_msg++;
3726
3727end:
3728 return ret;
3729}
3730
3731/**
3732 * hdd_ipa_rm_state_to_str() - Convert IPA RM state to string
3733 * @state: IPA RM state value
3734 *
3735 * Return: ASCII string representing the IPA RM state
3736 */
3737static inline char *hdd_ipa_rm_state_to_str(enum hdd_ipa_rm_state state)
3738{
3739 switch (state) {
3740 case HDD_IPA_RM_RELEASED:
3741 return "RELEASED";
3742 case HDD_IPA_RM_GRANT_PENDING:
3743 return "GRANT_PENDING";
3744 case HDD_IPA_RM_GRANTED:
3745 return "GRANTED";
3746 }
3747
3748 return "UNKNOWN";
3749}
3750
3751/**
3752 * hdd_ipa_init() - IPA initialization function
3753 * @hdd_ctx: HDD global context
3754 *
3755 * Allocate hdd_ipa resources, ipa pipe resource and register
3756 * wlan interface with IPA module.
3757 *
3758 * Return: CDF_STATUS enumeration
3759 */
3760CDF_STATUS hdd_ipa_init(hdd_context_t *hdd_ctx)
3761{
3762 struct hdd_ipa_priv *hdd_ipa = NULL;
3763 int ret, i;
3764 struct hdd_ipa_iface_context *iface_context = NULL;
3765
3766 if (!hdd_ipa_is_enabled(hdd_ctx))
3767 return CDF_STATUS_SUCCESS;
3768
3769 hdd_ipa = cdf_mem_malloc(sizeof(*hdd_ipa));
3770 if (!hdd_ipa) {
3771 HDD_IPA_LOG(CDF_TRACE_LEVEL_FATAL, "hdd_ipa allocation failed");
3772 goto fail_setup_rm;
3773 }
3774
3775 hdd_ctx->hdd_ipa = hdd_ipa;
3776 ghdd_ipa = hdd_ipa;
3777 hdd_ipa->hdd_ctx = hdd_ctx;
3778 hdd_ipa->num_iface = 0;
3779
3780 /* Create the interface context */
3781 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
3782 iface_context = &hdd_ipa->iface_context[i];
3783 iface_context->hdd_ipa = hdd_ipa;
3784 iface_context->cons_client =
3785 hdd_ipa_adapter_2_client[i].cons_client;
3786 iface_context->prod_client =
3787 hdd_ipa_adapter_2_client[i].prod_client;
3788 iface_context->iface_id = i;
3789 iface_context->adapter = NULL;
3790 cdf_spinlock_init(&iface_context->interface_lock);
3791 }
3792
3793#ifdef CONFIG_CNSS
3794 cnss_init_work(&hdd_ipa->pm_work, hdd_ipa_pm_send_pkt_to_tl);
3795#else
3796 INIT_WORK(&hdd_ipa->pm_work, hdd_ipa_pm_send_pkt_to_tl);
3797#endif
3798 cdf_spinlock_init(&hdd_ipa->pm_lock);
3799 cdf_nbuf_queue_init(&hdd_ipa->pm_queue_head);
3800
3801 ret = hdd_ipa_setup_rm(hdd_ipa);
3802 if (ret)
3803 goto fail_setup_rm;
3804
3805 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3806 hdd_ipa_uc_rt_debug_init(hdd_ctx);
3807 cdf_mem_zero(&hdd_ipa->stats, sizeof(hdd_ipa->stats));
3808 hdd_ipa->sap_num_connected_sta = 0;
3809 hdd_ipa->ipa_tx_packets_diff = 0;
3810 hdd_ipa->ipa_rx_packets_diff = 0;
3811 hdd_ipa->ipa_p_tx_packets = 0;
3812 hdd_ipa->ipa_p_rx_packets = 0;
3813 hdd_ipa->resource_loading = false;
3814 hdd_ipa->resource_unloading = false;
3815 hdd_ipa->sta_connected = 0;
3816
3817 /* Setup IPA sys_pipe for MCC */
3818 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
3819 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
3820 if (ret)
3821 goto fail_create_sys_pipe;
3822 }
3823 hdd_ipa_uc_ol_init(hdd_ctx);
3824 } else {
3825 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
3826 if (ret)
3827 goto fail_create_sys_pipe;
3828 }
3829
3830 return CDF_STATUS_SUCCESS;
3831
3832fail_create_sys_pipe:
3833 hdd_ipa_destroy_rm_resource(hdd_ipa);
3834fail_setup_rm:
3835 if (hdd_ipa)
3836 cdf_mem_free(hdd_ipa);
3837
3838 return CDF_STATUS_E_FAILURE;
3839}
3840
3841/**
3842 * hdd_ipa_cleanup - IPA cleanup function
3843 * @hdd_ctx: HDD global context
3844 *
3845 * Return: CDF_STATUS enumeration
3846 */
3847CDF_STATUS hdd_ipa_cleanup(hdd_context_t *hdd_ctx)
3848{
3849 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
3850 int i;
3851 struct hdd_ipa_iface_context *iface_context = NULL;
3852 cdf_nbuf_t skb;
3853 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
3854
3855 if (!hdd_ipa_is_enabled(hdd_ctx))
3856 return CDF_STATUS_SUCCESS;
3857
3858 if (!hdd_ipa_uc_is_enabled(hdd_ctx)) {
3859 unregister_inetaddr_notifier(&hdd_ipa->ipv4_notifier);
3860 hdd_ipa_teardown_sys_pipe(hdd_ipa);
3861 }
3862
3863 /* Teardown IPA sys_pipe for MCC */
3864 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx))
3865 hdd_ipa_teardown_sys_pipe(hdd_ipa);
3866
3867 hdd_ipa_destroy_rm_resource(hdd_ipa);
3868
3869#ifdef WLAN_OPEN_SOURCE
3870 cancel_work_sync(&hdd_ipa->pm_work);
3871#endif
3872
3873 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
3874
3875 while (((skb = cdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head)) != NULL)) {
3876 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
3877
3878 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
3879 ipa_free_skb(pm_tx_cb->ipa_tx_desc);
3880
3881 cdf_spin_lock_bh(&hdd_ipa->pm_lock);
3882 }
3883 cdf_spin_unlock_bh(&hdd_ipa->pm_lock);
3884
3885 cdf_spinlock_destroy(&hdd_ipa->pm_lock);
3886
3887 /* destory the interface lock */
3888 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
3889 iface_context = &hdd_ipa->iface_context[i];
3890 cdf_spinlock_destroy(&iface_context->interface_lock);
3891 }
3892
3893 /* This should never hit but still make sure that there are no pending
3894 * descriptor in IPA hardware
3895 */
3896 if (hdd_ipa->pending_hw_desc_cnt != 0) {
3897 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3898 "IPA Pending write done: %d Waiting!",
3899 hdd_ipa->pending_hw_desc_cnt);
3900
3901 for (i = 0; hdd_ipa->pending_hw_desc_cnt != 0 && i < 10; i++) {
3902 usleep_range(100, 100);
3903 }
3904
3905 HDD_IPA_LOG(CDF_TRACE_LEVEL_ERROR,
3906 "IPA Pending write done: desc: %d %s(%d)!",
3907 hdd_ipa->pending_hw_desc_cnt,
3908 hdd_ipa->pending_hw_desc_cnt == 0 ? "completed"
3909 : "leak", i);
3910 }
3911 if (hdd_ipa_uc_is_enabled(hdd_ctx)) {
3912 hdd_ipa_uc_rt_debug_deinit(hdd_ctx);
3913 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3914 "%s: Disconnect TX PIPE", __func__);
3915 ipa_disconnect_wdi_pipe(hdd_ipa->tx_pipe_handle);
3916 HDD_IPA_LOG(CDF_TRACE_LEVEL_INFO,
3917 "%s: Disconnect RX PIPE", __func__);
3918 ipa_disconnect_wdi_pipe(hdd_ipa->rx_pipe_handle);
3919 cdf_mutex_destroy(&hdd_ipa->event_lock);
3920 cdf_list_destroy(&hdd_ipa->pending_event);
3921
3922#ifdef WLAN_OPEN_SOURCE
3923 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
3924 cancel_work_sync(&hdd_ipa->uc_op_work[i].work);
3925 hdd_ipa->uc_op_work[i].msg = NULL;
3926 }
3927#endif
3928 }
3929
3930 cdf_mem_free(hdd_ipa);
3931 hdd_ctx->hdd_ipa = NULL;
3932
3933 return CDF_STATUS_SUCCESS;
3934}
3935#endif /* IPA_OFFLOAD */