blob: 209209b2a5ac0b594519a8bba2998622f5a65c18 [file] [log] [blame]
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "icnss: " fmt
14
15#include <asm/dma-iommu.h>
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +053016#include <linux/of_address.h>
Yuanyuan Liu607051c2016-11-28 17:04:13 -080017#include <linux/clk.h>
18#include <linux/iommu.h>
19#include <linux/export.h>
20#include <linux/err.h>
21#include <linux/of.h>
22#include <linux/init.h>
23#include <linux/io.h>
24#include <linux/module.h>
25#include <linux/kernel.h>
26#include <linux/debugfs.h>
27#include <linux/seq_file.h>
28#include <linux/slab.h>
29#include <linux/platform_device.h>
30#include <linux/regulator/consumer.h>
31#include <linux/interrupt.h>
32#include <linux/sched.h>
33#include <linux/delay.h>
34#include <linux/dma-mapping.h>
35#include <linux/qmi_encdec.h>
36#include <linux/ipc_logging.h>
37#include <linux/thread_info.h>
38#include <linux/uaccess.h>
39#include <linux/qpnp/qpnp-adc.h>
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080040#include <linux/etherdevice.h>
Yuanyuan Liu607051c2016-11-28 17:04:13 -080041#include <soc/qcom/memory_dump.h>
42#include <soc/qcom/icnss.h>
43#include <soc/qcom/msm_qmi_interface.h>
44#include <soc/qcom/secure_buffer.h>
45#include <soc/qcom/subsystem_notif.h>
46#include <soc/qcom/subsystem_restart.h>
47#include <soc/qcom/service-locator.h>
48#include <soc/qcom/service-notifier.h>
49#include <soc/qcom/socinfo.h>
50#include <soc/qcom/ramdump.h>
51
52#include "wlan_firmware_service_v01.h"
53
54#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080055unsigned long qmi_timeout = 10000;
Yuanyuan Liu607051c2016-11-28 17:04:13 -080056module_param(qmi_timeout, ulong, 0600);
57
58#define WLFW_TIMEOUT_MS qmi_timeout
59#else
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080060#define WLFW_TIMEOUT_MS 10000
Yuanyuan Liu607051c2016-11-28 17:04:13 -080061#endif
62#define WLFW_SERVICE_INS_ID_V01 0
63#define WLFW_CLIENT_ID 0x4b4e454c
64#define MAX_PROP_SIZE 32
65#define NUM_LOG_PAGES 10
Yuanyuan Liu68939762017-04-04 16:43:03 -070066#define NUM_LOG_LONG_PAGES 4
Yuanyuan Liu607051c2016-11-28 17:04:13 -080067#define ICNSS_MAGIC 0x5abc5abc
68
Yuanyuan Liu607051c2016-11-28 17:04:13 -080069#define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN"
70#define ICNSS_WLAN_SERVICE_NAME "wlan/fw"
71
72#define ICNSS_THRESHOLD_HIGH 3600000
73#define ICNSS_THRESHOLD_LOW 3450000
74#define ICNSS_THRESHOLD_GUARD 20000
75
76#define icnss_ipc_log_string(_x...) do { \
77 if (icnss_ipc_log_context) \
78 ipc_log_string(icnss_ipc_log_context, _x); \
79 } while (0)
80
Yuanyuan Liu607051c2016-11-28 17:04:13 -080081#define icnss_ipc_log_long_string(_x...) do { \
82 if (icnss_ipc_log_long_context) \
83 ipc_log_string(icnss_ipc_log_long_context, _x); \
84 } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -080085
86#define icnss_pr_err(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070087 printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__); \
88 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
89 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080090 } while (0)
91
92#define icnss_pr_warn(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070093 printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__); \
94 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
95 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080096 } while (0)
97
98#define icnss_pr_info(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070099 printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__); \
100 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
101 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800102 } while (0)
103
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700104#if defined(CONFIG_DYNAMIC_DEBUG)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800105#define icnss_pr_dbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700106 pr_debug(_fmt, ##__VA_ARGS__); \
107 icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800108 } while (0)
109
Yuanyuan Liu68939762017-04-04 16:43:03 -0700110#define icnss_pr_vdbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700111 pr_debug(_fmt, ##__VA_ARGS__); \
112 icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800113 } while (0)
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700114#elif defined(DEBUG)
115#define icnss_pr_dbg(_fmt, ...) do { \
116 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
117 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
118 ##__VA_ARGS__); \
119 } while (0)
120
121#define icnss_pr_vdbg(_fmt, ...) do { \
122 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
123 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
124 ##__VA_ARGS__); \
125 } while (0)
126#else
127#define icnss_pr_dbg(_fmt, ...) do { \
128 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
129 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
130 ##__VA_ARGS__); \
131 } while (0)
132
133#define icnss_pr_vdbg(_fmt, ...) do { \
134 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
135 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
136 ##__VA_ARGS__); \
137 } while (0)
138#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800139
140#ifdef CONFIG_ICNSS_DEBUG
141#define ICNSS_ASSERT(_condition) do { \
142 if (!(_condition)) { \
Yuanyuan Liu68939762017-04-04 16:43:03 -0700143 icnss_pr_err("ASSERT at line %d\n", __LINE__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800144 BUG_ON(1); \
145 } \
146 } while (0)
Yuanyuan Liu68939762017-04-04 16:43:03 -0700147
148bool ignore_qmi_timeout;
149#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800150#else
Yuanyuan Liu68939762017-04-04 16:43:03 -0700151#define ICNSS_ASSERT(_condition) do { } while (0)
152#define ICNSS_QMI_ASSERT() do { } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800153#endif
154
155enum icnss_debug_quirks {
156 HW_ALWAYS_ON,
157 HW_DEBUG_ENABLE,
158 SKIP_QMI,
159 HW_ONLY_TOP_LEVEL_RESET,
160 RECOVERY_DISABLE,
161 SSR_ONLY,
162 PDR_ONLY,
163 VBATT_DISABLE,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800164 FW_REJUVENATE_ENABLE,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800165};
166
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800167#define ICNSS_QUIRKS_DEFAULT (BIT(VBATT_DISABLE) | \
168 BIT(FW_REJUVENATE_ENABLE))
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800169
170unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
171module_param(quirks, ulong, 0600);
172
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800173uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01;
174module_param(dynamic_feature_mask, ullong, 0600);
175
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800176void *icnss_ipc_log_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800177void *icnss_ipc_log_long_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800178
179#define ICNSS_EVENT_PENDING 2989
180
181#define ICNSS_EVENT_SYNC BIT(0)
182#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
183#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
184 ICNSS_EVENT_SYNC)
185
186enum icnss_driver_event_type {
187 ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
188 ICNSS_DRIVER_EVENT_SERVER_EXIT,
189 ICNSS_DRIVER_EVENT_FW_READY_IND,
190 ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
191 ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
192 ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
193 ICNSS_DRIVER_EVENT_MAX,
194};
195
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530196enum icnss_msa_perm {
197 ICNSS_MSA_PERM_HLOS_ALL = 0,
198 ICNSS_MSA_PERM_WLAN_HW_RW = 1,
199 ICNSS_MSA_PERM_DUMP_COLLECT = 2,
200 ICNSS_MSA_PERM_MAX,
201};
202
203#define ICNSS_MAX_VMIDS 4
204
205struct icnss_mem_region_info {
206 uint64_t reg_addr;
207 uint32_t size;
208 uint8_t secure_flag;
209 enum icnss_msa_perm perm;
210};
211
212struct icnss_msa_perm_list_t {
213 int vmids[ICNSS_MAX_VMIDS];
214 int perms[ICNSS_MAX_VMIDS];
215 int nelems;
216};
217
218struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
219 [ICNSS_MSA_PERM_HLOS_ALL] = {
220 .vmids = {VMID_HLOS},
221 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
222 .nelems = 1,
223 },
224
225 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
226 .vmids = {VMID_MSS_MSA, VMID_WLAN},
227 .perms = {PERM_READ | PERM_WRITE,
228 PERM_READ | PERM_WRITE},
229 .nelems = 2,
230 },
231
232 [ICNSS_MSA_PERM_DUMP_COLLECT] = {
233 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS},
234 .perms = {PERM_READ | PERM_WRITE,
235 PERM_READ | PERM_WRITE,
236 PERM_READ},
237 .nelems = 3,
238 },
239};
240
241struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
242 [ICNSS_MSA_PERM_HLOS_ALL] = {
243 .vmids = {VMID_HLOS},
244 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
245 .nelems = 1,
246 },
247
248 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
249 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
250 .perms = {PERM_READ | PERM_WRITE,
251 PERM_READ | PERM_WRITE,
252 PERM_READ | PERM_WRITE},
253 .nelems = 3,
254 },
255
256 [ICNSS_MSA_PERM_DUMP_COLLECT] = {
257 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS},
258 .perms = {PERM_READ | PERM_WRITE,
259 PERM_READ | PERM_WRITE,
260 PERM_READ | PERM_WRITE,
261 PERM_READ},
262 .nelems = 4,
263 },
264};
265
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800266struct icnss_event_pd_service_down_data {
267 bool crashed;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800268 bool fw_rejuvenate;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800269};
270
271struct icnss_driver_event {
272 struct list_head list;
273 enum icnss_driver_event_type type;
274 bool sync;
275 struct completion complete;
276 int ret;
277 void *data;
278};
279
280enum icnss_driver_state {
281 ICNSS_WLFW_QMI_CONNECTED,
282 ICNSS_POWER_ON,
283 ICNSS_FW_READY,
284 ICNSS_DRIVER_PROBED,
285 ICNSS_FW_TEST_MODE,
286 ICNSS_PM_SUSPEND,
287 ICNSS_PM_SUSPEND_NOIRQ,
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -0700288 ICNSS_SSR_REGISTERED,
289 ICNSS_PDR_REGISTERED,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800290 ICNSS_PD_RESTART,
291 ICNSS_MSA0_ASSIGNED,
292 ICNSS_WLFW_EXISTS,
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +0530293 ICNSS_SHUTDOWN_DONE,
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700294 ICNSS_HOST_TRIGGERED_PDR,
Sameer Thalappil93c00732017-08-18 13:02:32 -0700295 ICNSS_FW_DOWN,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800296};
297
298struct ce_irq_list {
299 int irq;
300 irqreturn_t (*handler)(int, void *);
301};
302
Yuanyuan Liu68939762017-04-04 16:43:03 -0700303struct icnss_vreg_info {
304 struct regulator *reg;
305 const char *name;
306 u32 min_v;
307 u32 max_v;
308 u32 load_ua;
309 unsigned long settle_delay;
310 bool required;
311};
312
313struct icnss_clk_info {
314 struct clk *handle;
315 const char *name;
316 u32 freq;
317 bool required;
318};
319
320static struct icnss_vreg_info icnss_vreg_info[] = {
321 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
322 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
323 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
324 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
325};
326
327#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
328
329static struct icnss_clk_info icnss_clk_info[] = {
330 {NULL, "cxo_ref_clk_pin", 0, false},
331};
332
333#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
334
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800335struct icnss_stats {
336 struct {
337 uint32_t posted;
338 uint32_t processed;
339 } events[ICNSS_DRIVER_EVENT_MAX];
340
341 struct {
342 uint32_t request;
343 uint32_t free;
344 uint32_t enable;
345 uint32_t disable;
346 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
347
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700348 struct {
349 uint32_t pdr_fw_crash;
350 uint32_t pdr_host_error;
351 uint32_t root_pd_crash;
352 uint32_t root_pd_shutdown;
353 } recovery;
354
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800355 uint32_t pm_suspend;
356 uint32_t pm_suspend_err;
357 uint32_t pm_resume;
358 uint32_t pm_resume_err;
359 uint32_t pm_suspend_noirq;
360 uint32_t pm_suspend_noirq_err;
361 uint32_t pm_resume_noirq;
362 uint32_t pm_resume_noirq_err;
363 uint32_t pm_stay_awake;
364 uint32_t pm_relax;
365
366 uint32_t ind_register_req;
367 uint32_t ind_register_resp;
368 uint32_t ind_register_err;
369 uint32_t msa_info_req;
370 uint32_t msa_info_resp;
371 uint32_t msa_info_err;
372 uint32_t msa_ready_req;
373 uint32_t msa_ready_resp;
374 uint32_t msa_ready_err;
375 uint32_t msa_ready_ind;
376 uint32_t cap_req;
377 uint32_t cap_resp;
378 uint32_t cap_err;
379 uint32_t pin_connect_result;
380 uint32_t cfg_req;
381 uint32_t cfg_resp;
382 uint32_t cfg_req_err;
383 uint32_t mode_req;
384 uint32_t mode_resp;
385 uint32_t mode_req_err;
386 uint32_t ini_req;
387 uint32_t ini_resp;
388 uint32_t ini_req_err;
389 uint32_t vbatt_req;
390 uint32_t vbatt_resp;
391 uint32_t vbatt_req_err;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800392 u32 rejuvenate_ind;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800393 uint32_t rejuvenate_ack_req;
394 uint32_t rejuvenate_ack_resp;
395 uint32_t rejuvenate_ack_err;
396};
397
398#define MAX_NO_OF_MAC_ADDR 4
399struct icnss_wlan_mac_addr {
400 u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
401 uint32_t no_of_mac_addr_set;
402};
403
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700404enum icnss_pdr_cause_index {
405 ICNSS_FW_CRASH,
406 ICNSS_ROOT_PD_CRASH,
407 ICNSS_ROOT_PD_SHUTDOWN,
408 ICNSS_HOST_ERROR,
409};
410
411static const char * const icnss_pdr_cause[] = {
412 [ICNSS_FW_CRASH] = "FW crash",
413 [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
414 [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
415 [ICNSS_HOST_ERROR] = "Host error",
416};
417
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800418struct service_notifier_context {
419 void *handle;
420 uint32_t instance_id;
421 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800422};
423
424static struct icnss_priv {
425 uint32_t magic;
426 struct platform_device *pdev;
427 struct icnss_driver_ops *ops;
428 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700429 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
430 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800431 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
432 phys_addr_t mem_base_pa;
433 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800434 struct dma_iommu_mapping *smmu_mapping;
435 dma_addr_t smmu_iova_start;
436 size_t smmu_iova_len;
437 dma_addr_t smmu_iova_ipa_start;
438 size_t smmu_iova_ipa_len;
439 struct qmi_handle *wlfw_clnt;
440 struct list_head event_list;
441 spinlock_t event_lock;
442 struct work_struct event_work;
443 struct work_struct qmi_recv_msg_work;
444 struct workqueue_struct *event_wq;
445 phys_addr_t msa_pa;
446 uint32_t msa_mem_size;
447 void *msa_va;
448 unsigned long state;
449 struct wlfw_rf_chip_info_s_v01 chip_info;
450 struct wlfw_rf_board_info_s_v01 board_info;
451 struct wlfw_soc_info_s_v01 soc_info;
452 struct wlfw_fw_version_info_s_v01 fw_version_info;
453 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
454 u32 pwr_pin_result;
455 u32 phy_io_pin_result;
456 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700457 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800458 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700459 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800460 struct dentry *root_dentry;
461 spinlock_t on_off_lock;
462 struct icnss_stats stats;
463 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800464 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800465 struct notifier_block service_notifier_nb;
466 int total_domains;
467 struct notifier_block get_service_nb;
468 void *modem_notify_handler;
469 struct notifier_block modem_ssr_nb;
470 uint32_t diag_reg_read_addr;
471 uint32_t diag_reg_read_mem_type;
472 uint32_t diag_reg_read_len;
473 uint8_t *diag_reg_read_buf;
474 struct qpnp_adc_tm_btm_param vph_monitor_params;
475 struct qpnp_adc_tm_chip *adc_tm_dev;
476 struct qpnp_vadc_chip *vadc_dev;
477 uint64_t vph_pwr;
478 atomic_t pm_count;
479 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800480 bool is_wlan_mac_set;
481 struct icnss_wlan_mac_addr wlan_mac_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700482 bool bypass_s1_smmu;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800483 u8 cause_for_rejuvenation;
484 u8 requesting_sub_system;
485 u16 line_number;
486 char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +0530487 struct mutex dev_lock;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800488} *penv;
489
Yuanyuan Liu68939762017-04-04 16:43:03 -0700490#ifdef CONFIG_ICNSS_DEBUG
491static void icnss_ignore_qmi_timeout(bool ignore)
492{
493 ignore_qmi_timeout = ignore;
494}
495#else
496static void icnss_ignore_qmi_timeout(bool ignore) { }
497#endif
498
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530499static int icnss_assign_msa_perm(struct icnss_mem_region_info
500 *mem_region, enum icnss_msa_perm new_perm)
501{
502 int ret = 0;
503 phys_addr_t addr;
504 u32 size;
505 u32 i = 0;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530506 u32 source_vmids[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530507 u32 source_nelems;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530508 u32 dest_vmids[ICNSS_MAX_VMIDS] = {0};
509 u32 dest_perms[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530510 u32 dest_nelems;
511 enum icnss_msa_perm cur_perm = mem_region->perm;
512 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
513
514 addr = mem_region->reg_addr;
515 size = mem_region->size;
516
517 if (mem_region->secure_flag) {
518 new_perm_list = &msa_perm_secure_list[new_perm];
519 old_perm_list = &msa_perm_secure_list[cur_perm];
520 } else {
521 new_perm_list = &msa_perm_list[new_perm];
522 old_perm_list = &msa_perm_list[cur_perm];
523 }
524
525 source_nelems = old_perm_list->nelems;
526 dest_nelems = new_perm_list->nelems;
527
528 for (i = 0; i < source_nelems; ++i)
529 source_vmids[i] = old_perm_list->vmids[i];
530
531 for (i = 0; i < dest_nelems; ++i) {
532 dest_vmids[i] = new_perm_list->vmids[i];
533 dest_perms[i] = new_perm_list->perms[i];
534 }
535
536 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
537 dest_vmids, dest_perms, dest_nelems);
538 if (ret) {
539 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
540 &addr, size, ret);
541 goto out;
542 }
543
544 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
545 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
546 source_nelems, source_vmids[0], source_vmids[1],
547 source_vmids[2], source_vmids[3], dest_nelems,
548 dest_vmids[0], dest_vmids[1], dest_vmids[2],
549 dest_vmids[3]);
550out:
551 return ret;
552}
553
554static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
555 enum icnss_msa_perm new_perm)
556{
557 int ret;
558 int i;
559 enum icnss_msa_perm old_perm;
560
Yuanyuan Liu26ec2212017-09-01 10:34:25 -0700561 if (priv->nr_mem_region > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
562 icnss_pr_err("Invalid memory region len %d\n",
563 priv->nr_mem_region);
564 return -EINVAL;
565 }
566
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530567 for (i = 0; i < priv->nr_mem_region; i++) {
568 old_perm = priv->mem_region[i].perm;
569 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
570 if (ret)
571 goto err_unmap;
572 priv->mem_region[i].perm = new_perm;
573 }
574 return 0;
575
576err_unmap:
577 for (i--; i >= 0; i--) {
578 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
579 }
580 return ret;
581}
582
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800583static void icnss_pm_stay_awake(struct icnss_priv *priv)
584{
585 if (atomic_inc_return(&priv->pm_count) != 1)
586 return;
587
Yuanyuan Liu68939762017-04-04 16:43:03 -0700588 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800589 atomic_read(&priv->pm_count));
590
591 pm_stay_awake(&priv->pdev->dev);
592
593 priv->stats.pm_stay_awake++;
594}
595
596static void icnss_pm_relax(struct icnss_priv *priv)
597{
598 int r = atomic_dec_return(&priv->pm_count);
599
600 WARN_ON(r < 0);
601
602 if (r != 0)
603 return;
604
Yuanyuan Liu68939762017-04-04 16:43:03 -0700605 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800606 atomic_read(&priv->pm_count));
607
608 pm_relax(&priv->pdev->dev);
609 priv->stats.pm_relax++;
610}
611
612static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
613{
614 switch (type) {
615 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
616 return "SERVER_ARRIVE";
617 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
618 return "SERVER_EXIT";
619 case ICNSS_DRIVER_EVENT_FW_READY_IND:
620 return "FW_READY";
621 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
622 return "REGISTER_DRIVER";
623 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
624 return "UNREGISTER_DRIVER";
625 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
626 return "PD_SERVICE_DOWN";
627 case ICNSS_DRIVER_EVENT_MAX:
628 return "EVENT_MAX";
629 }
630
631 return "UNKNOWN";
632};
633
634static int icnss_driver_event_post(enum icnss_driver_event_type type,
635 u32 flags, void *data)
636{
637 struct icnss_driver_event *event;
638 unsigned long irq_flags;
639 int gfp = GFP_KERNEL;
640 int ret = 0;
641
642 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
643 icnss_driver_event_to_str(type), type, current->comm,
644 flags, penv->state);
645
646 if (type >= ICNSS_DRIVER_EVENT_MAX) {
647 icnss_pr_err("Invalid Event type: %d, can't post", type);
648 return -EINVAL;
649 }
650
651 if (in_interrupt() || irqs_disabled())
652 gfp = GFP_ATOMIC;
653
654 event = kzalloc(sizeof(*event), gfp);
655 if (event == NULL)
656 return -ENOMEM;
657
658 icnss_pm_stay_awake(penv);
659
660 event->type = type;
661 event->data = data;
662 init_completion(&event->complete);
663 event->ret = ICNSS_EVENT_PENDING;
664 event->sync = !!(flags & ICNSS_EVENT_SYNC);
665
666 spin_lock_irqsave(&penv->event_lock, irq_flags);
667 list_add_tail(&event->list, &penv->event_list);
668 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
669
670 penv->stats.events[type].posted++;
671 queue_work(penv->event_wq, &penv->event_work);
672
673 if (!(flags & ICNSS_EVENT_SYNC))
674 goto out;
675
676 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
677 wait_for_completion(&event->complete);
678 else
679 ret = wait_for_completion_interruptible(&event->complete);
680
681 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
682 icnss_driver_event_to_str(type), type, penv->state, ret,
683 event->ret);
684
685 spin_lock_irqsave(&penv->event_lock, irq_flags);
686 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
687 event->sync = false;
688 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
689 ret = -EINTR;
690 goto out;
691 }
692 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
693
694 ret = event->ret;
695 kfree(event);
696
697out:
698 icnss_pm_relax(penv);
699 return ret;
700}
701
702static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
703 uint64_t voltage_uv)
704{
705 int ret;
706 struct wlfw_vbatt_req_msg_v01 req;
707 struct wlfw_vbatt_resp_msg_v01 resp;
708 struct msg_desc req_desc, resp_desc;
709
710 if (!priv->wlfw_clnt) {
711 ret = -ENODEV;
712 goto out;
713 }
714
715 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
716 penv->state);
717
718 memset(&req, 0, sizeof(req));
719 memset(&resp, 0, sizeof(resp));
720
721 req.voltage_uv = voltage_uv;
722
723 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
724 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
725 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
726
727 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
728 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
729 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
730
731 priv->stats.vbatt_req++;
732
733 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
734 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
735 if (ret < 0) {
736 icnss_pr_err("Send vbatt req failed %d\n", ret);
737 goto out;
738 }
739
740 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
741 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
742 resp.resp.result, resp.resp.error);
743 ret = resp.resp.result;
744 goto out;
745 }
746 priv->stats.vbatt_resp++;
747
748out:
749 priv->stats.vbatt_req_err++;
750 return ret;
751}
752
753static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
754{
755 int ret = 0;
756 struct qpnp_vadc_result adc_result;
757
758 if (!priv->vadc_dev) {
759 icnss_pr_err("VADC dev doesn't exists\n");
760 ret = -EINVAL;
761 goto out;
762 }
763
764 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
765 if (ret) {
766 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
767 VADC_VPH_PWR, ret);
768 goto out;
769 }
770
771 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
772 adc_result.physical, adc_result.measurement);
773
774 *result_uv = adc_result.physical;
775out:
776 return ret;
777}
778
779static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
780{
781 struct icnss_priv *priv = ctx;
782 uint64_t vph_pwr = 0;
783 uint64_t vph_pwr_prev;
784 int ret = 0;
785 bool update = true;
786
787 if (!priv) {
788 icnss_pr_err("Priv pointer is NULL\n");
789 return;
790 }
791
792 vph_pwr_prev = priv->vph_pwr;
793
794 ret = icnss_get_phone_power(priv, &vph_pwr);
795 if (ret)
796 return;
797
798 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
799 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
800 update = false;
801 priv->vph_monitor_params.state_request =
802 ADC_TM_HIGH_THR_ENABLE;
803 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
804 ICNSS_THRESHOLD_GUARD;
805 priv->vph_monitor_params.low_thr = 0;
806 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
807 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
808 update = false;
809 priv->vph_monitor_params.state_request =
810 ADC_TM_LOW_THR_ENABLE;
811 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
812 ICNSS_THRESHOLD_GUARD;
813 priv->vph_monitor_params.high_thr = 0;
814 } else {
815 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
816 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
817 update = false;
818 priv->vph_monitor_params.state_request =
819 ADC_TM_HIGH_LOW_THR_ENABLE;
820 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
821 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
822 }
823
824 priv->vph_pwr = vph_pwr;
825
826 if (update)
827 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
828
829 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
830 priv->vph_monitor_params.low_thr,
831 priv->vph_monitor_params.high_thr);
832 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
833 &priv->vph_monitor_params);
834 if (ret)
835 icnss_pr_err("TM channel setup failed %d\n", ret);
836}
837
838static int icnss_setup_vph_monitor(struct icnss_priv *priv)
839{
840 int ret = 0;
841
842 if (!priv->adc_tm_dev) {
843 icnss_pr_err("ADC TM handler is NULL\n");
844 ret = -EINVAL;
845 goto out;
846 }
847
848 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
849 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
850 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
851 priv->vph_monitor_params.channel = VADC_VPH_PWR;
852 priv->vph_monitor_params.btm_ctx = priv;
853 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
854 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
855 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
856 priv->vph_monitor_params.low_thr,
857 priv->vph_monitor_params.high_thr);
858
859 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
860 &priv->vph_monitor_params);
861 if (ret)
862 icnss_pr_err("TM channel setup failed %d\n", ret);
863out:
864 return ret;
865}
866
867static int icnss_init_vph_monitor(struct icnss_priv *priv)
868{
869 int ret = 0;
870
871 if (test_bit(VBATT_DISABLE, &quirks))
872 goto out;
873
874 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
875 if (ret)
876 goto out;
877
878 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
879
880 ret = icnss_setup_vph_monitor(priv);
881 if (ret)
882 goto out;
883out:
884 return ret;
885}
886
887
888static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
889{
890 struct msg_desc ind_desc;
891 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
892 int ret = 0;
893
894 if (!penv || !penv->wlfw_clnt) {
895 ret = -ENODEV;
896 goto out;
897 }
898
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530899 memset(&ind_msg, 0, sizeof(ind_msg));
900
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800901 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
902 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
903 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
904
905 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
906 if (ret < 0) {
907 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
908 ret, msg_len);
909 goto out;
910 }
911
912 /* store pin result locally */
913 if (ind_msg.pwr_pin_result_valid)
914 penv->pwr_pin_result = ind_msg.pwr_pin_result;
915 if (ind_msg.phy_io_pin_result_valid)
916 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
917 if (ind_msg.rf_pin_result_valid)
918 penv->rf_pin_result = ind_msg.rf_pin_result;
919
920 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
921 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
922 ind_msg.rf_pin_result);
923
924 penv->stats.pin_connect_result++;
925out:
926 return ret;
927}
928
Yuanyuan Liu68939762017-04-04 16:43:03 -0700929static int icnss_vreg_on(struct icnss_priv *priv)
930{
931 int ret = 0;
932 struct icnss_vreg_info *vreg_info;
933 int i;
934
935 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
936 vreg_info = &priv->vreg_info[i];
937
938 if (!vreg_info->reg)
939 continue;
940
941 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
942
943 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
944 vreg_info->max_v);
945 if (ret) {
946 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
947 vreg_info->name, vreg_info->min_v,
948 vreg_info->max_v, ret);
949 break;
950 }
951
952 if (vreg_info->load_ua) {
953 ret = regulator_set_load(vreg_info->reg,
954 vreg_info->load_ua);
955 if (ret < 0) {
956 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
957 vreg_info->name,
958 vreg_info->load_ua, ret);
959 break;
960 }
961 }
962
963 ret = regulator_enable(vreg_info->reg);
964 if (ret) {
965 icnss_pr_err("Regulator %s, can't enable: %d\n",
966 vreg_info->name, ret);
967 break;
968 }
969
970 if (vreg_info->settle_delay)
971 udelay(vreg_info->settle_delay);
972 }
973
974 if (!ret)
975 return 0;
976
977 for (; i >= 0; i--) {
978 vreg_info = &priv->vreg_info[i];
979
980 if (!vreg_info->reg)
981 continue;
982
983 regulator_disable(vreg_info->reg);
984 regulator_set_load(vreg_info->reg, 0);
985 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
986 }
987
988 return ret;
989}
990
991static int icnss_vreg_off(struct icnss_priv *priv)
992{
993 int ret = 0;
994 struct icnss_vreg_info *vreg_info;
995 int i;
996
997 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
998 vreg_info = &priv->vreg_info[i];
999
1000 if (!vreg_info->reg)
1001 continue;
1002
1003 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
1004
1005 ret = regulator_disable(vreg_info->reg);
1006 if (ret)
1007 icnss_pr_err("Regulator %s, can't disable: %d\n",
1008 vreg_info->name, ret);
1009
1010 ret = regulator_set_load(vreg_info->reg, 0);
1011 if (ret < 0)
1012 icnss_pr_err("Regulator %s, can't set load: %d\n",
1013 vreg_info->name, ret);
1014
1015 ret = regulator_set_voltage(vreg_info->reg, 0,
1016 vreg_info->max_v);
1017 if (ret)
1018 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
1019 vreg_info->name, ret);
1020 }
1021
1022 return ret;
1023}
1024
1025static int icnss_clk_init(struct icnss_priv *priv)
1026{
1027 struct icnss_clk_info *clk_info;
1028 int i;
1029 int ret = 0;
1030
1031 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1032 clk_info = &priv->clk_info[i];
1033
1034 if (!clk_info->handle)
1035 continue;
1036
1037 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1038
1039 if (clk_info->freq) {
1040 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1041
1042 if (ret) {
1043 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1044 clk_info->name, clk_info->freq,
1045 ret);
1046 break;
1047 }
1048 }
1049
1050 ret = clk_prepare_enable(clk_info->handle);
1051 if (ret) {
1052 icnss_pr_err("Clock %s, can't enable: %d\n",
1053 clk_info->name, ret);
1054 break;
1055 }
1056 }
1057
1058 if (ret == 0)
1059 return 0;
1060
1061 for (; i >= 0; i--) {
1062 clk_info = &priv->clk_info[i];
1063
1064 if (!clk_info->handle)
1065 continue;
1066
1067 clk_disable_unprepare(clk_info->handle);
1068 }
1069
1070 return ret;
1071}
1072
1073static int icnss_clk_deinit(struct icnss_priv *priv)
1074{
1075 struct icnss_clk_info *clk_info;
1076 int i;
1077
1078 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1079 clk_info = &priv->clk_info[i];
1080
1081 if (!clk_info->handle)
1082 continue;
1083
1084 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1085
1086 clk_disable_unprepare(clk_info->handle);
1087 }
1088
1089 return 0;
1090}
1091
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001092static int icnss_hw_power_on(struct icnss_priv *priv)
1093{
1094 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001095
1096 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1097
Yuanyuan Liu68939762017-04-04 16:43:03 -07001098 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001099 if (test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001100 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001101 return ret;
1102 }
1103 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001104 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001105
Yuanyuan Liu68939762017-04-04 16:43:03 -07001106 ret = icnss_vreg_on(priv);
1107 if (ret)
1108 goto out;
1109
1110 ret = icnss_clk_init(priv);
1111 if (ret)
1112 goto vreg_off;
1113
1114 return ret;
1115
1116vreg_off:
1117 icnss_vreg_off(priv);
1118out:
1119 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001120 return ret;
1121}
1122
1123static int icnss_hw_power_off(struct icnss_priv *priv)
1124{
1125 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001126
1127 if (test_bit(HW_ALWAYS_ON, &quirks))
1128 return 0;
1129
1130 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1131
Yuanyuan Liu68939762017-04-04 16:43:03 -07001132 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001133 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001134 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001135 return ret;
1136 }
1137 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001138 spin_unlock(&priv->on_off_lock);
1139
1140 icnss_clk_deinit(priv);
1141
1142 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001143
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001144 return ret;
1145}
1146
1147int icnss_power_on(struct device *dev)
1148{
1149 struct icnss_priv *priv = dev_get_drvdata(dev);
1150
1151 if (!priv) {
1152 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1153 dev, priv);
1154 return -EINVAL;
1155 }
1156
1157 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1158
1159 return icnss_hw_power_on(priv);
1160}
1161EXPORT_SYMBOL(icnss_power_on);
1162
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001163bool icnss_is_fw_ready(void)
1164{
1165 if (!penv)
1166 return false;
1167 else
1168 return test_bit(ICNSS_FW_READY, &penv->state);
1169}
1170EXPORT_SYMBOL(icnss_is_fw_ready);
1171
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001172int icnss_power_off(struct device *dev)
1173{
1174 struct icnss_priv *priv = dev_get_drvdata(dev);
1175
1176 if (!priv) {
1177 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1178 dev, priv);
1179 return -EINVAL;
1180 }
1181
1182 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1183
1184 return icnss_hw_power_off(priv);
1185}
1186EXPORT_SYMBOL(icnss_power_off);
1187
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001188static int wlfw_msa_mem_info_send_sync_msg(void)
1189{
1190 int ret;
1191 int i;
1192 struct wlfw_msa_info_req_msg_v01 req;
1193 struct wlfw_msa_info_resp_msg_v01 resp;
1194 struct msg_desc req_desc, resp_desc;
1195
1196 if (!penv || !penv->wlfw_clnt)
1197 return -ENODEV;
1198
1199 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1200
1201 memset(&req, 0, sizeof(req));
1202 memset(&resp, 0, sizeof(resp));
1203
1204 req.msa_addr = penv->msa_pa;
1205 req.size = penv->msa_mem_size;
1206
1207 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1208 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1209 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1210
1211 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1212 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1213 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1214
1215 penv->stats.msa_info_req++;
1216
1217 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1218 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1219 if (ret < 0) {
1220 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1221 goto out;
1222 }
1223
1224 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1225 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1226 resp.resp.result, resp.resp.error);
1227 ret = resp.resp.result;
1228 goto out;
1229 }
1230
1231 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1232 resp.mem_region_info_len);
1233
Yuanyuan Liu68939762017-04-04 16:43:03 -07001234 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001235 icnss_pr_err("Invalid memory region length received: %d\n",
1236 resp.mem_region_info_len);
1237 ret = -EINVAL;
1238 goto out;
1239 }
1240
1241 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001242 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001243 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001244 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001245 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001246 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001247 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001248 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001249 resp.mem_region_info[i].secure_flag;
1250 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001251 i, penv->mem_region[i].reg_addr,
1252 penv->mem_region[i].size,
1253 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001254 }
1255
1256 return 0;
1257
1258out:
1259 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001260 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001261 return ret;
1262}
1263
1264static int wlfw_msa_ready_send_sync_msg(void)
1265{
1266 int ret;
1267 struct wlfw_msa_ready_req_msg_v01 req;
1268 struct wlfw_msa_ready_resp_msg_v01 resp;
1269 struct msg_desc req_desc, resp_desc;
1270
1271 if (!penv || !penv->wlfw_clnt)
1272 return -ENODEV;
1273
1274 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1275 penv->state);
1276
1277 memset(&req, 0, sizeof(req));
1278 memset(&resp, 0, sizeof(resp));
1279
1280 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1281 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1282 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1283
1284 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1285 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1286 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1287
1288 penv->stats.msa_ready_req++;
1289 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1290 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1291 if (ret < 0) {
1292 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1293 goto out;
1294 }
1295
1296 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1297 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1298 resp.resp.result, resp.resp.error);
1299 ret = resp.resp.result;
1300 goto out;
1301 }
1302 penv->stats.msa_ready_resp++;
1303
1304 return 0;
1305
1306out:
1307 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001308 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001309 return ret;
1310}
1311
1312static int wlfw_ind_register_send_sync_msg(void)
1313{
1314 int ret;
1315 struct wlfw_ind_register_req_msg_v01 req;
1316 struct wlfw_ind_register_resp_msg_v01 resp;
1317 struct msg_desc req_desc, resp_desc;
1318
1319 if (!penv || !penv->wlfw_clnt)
1320 return -ENODEV;
1321
1322 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1323 penv->state);
1324
1325 memset(&req, 0, sizeof(req));
1326 memset(&resp, 0, sizeof(resp));
1327
1328 req.client_id_valid = 1;
1329 req.client_id = WLFW_CLIENT_ID;
1330 req.fw_ready_enable_valid = 1;
1331 req.fw_ready_enable = 1;
1332 req.msa_ready_enable_valid = 1;
1333 req.msa_ready_enable = 1;
1334 req.pin_connect_result_enable_valid = 1;
1335 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001336 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1337 req.rejuvenate_enable_valid = 1;
1338 req.rejuvenate_enable = 1;
1339 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001340
1341 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1342 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1343 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1344
1345 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1346 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1347 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1348
1349 penv->stats.ind_register_req++;
1350
1351 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1352 &resp_desc, &resp, sizeof(resp),
1353 WLFW_TIMEOUT_MS);
1354 if (ret < 0) {
1355 icnss_pr_err("Send indication register req failed %d\n", ret);
1356 goto out;
1357 }
1358
1359 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1360 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1361 resp.resp.result, resp.resp.error);
1362 ret = resp.resp.result;
1363 goto out;
1364 }
1365 penv->stats.ind_register_resp++;
1366
1367 return 0;
1368
1369out:
1370 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001371 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001372 return ret;
1373}
1374
1375static int wlfw_cap_send_sync_msg(void)
1376{
1377 int ret;
1378 struct wlfw_cap_req_msg_v01 req;
1379 struct wlfw_cap_resp_msg_v01 resp;
1380 struct msg_desc req_desc, resp_desc;
1381
1382 if (!penv || !penv->wlfw_clnt)
1383 return -ENODEV;
1384
1385 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1386
1387 memset(&resp, 0, sizeof(resp));
1388
1389 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1390 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1391 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1392
1393 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1394 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1395 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1396
1397 penv->stats.cap_req++;
1398 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1399 &resp_desc, &resp, sizeof(resp),
1400 WLFW_TIMEOUT_MS);
1401 if (ret < 0) {
1402 icnss_pr_err("Send capability req failed %d\n", ret);
1403 goto out;
1404 }
1405
1406 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1407 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1408 resp.resp.result, resp.resp.error);
1409 ret = resp.resp.result;
1410 goto out;
1411 }
1412
1413 penv->stats.cap_resp++;
1414 /* store cap locally */
1415 if (resp.chip_info_valid)
1416 penv->chip_info = resp.chip_info;
1417 if (resp.board_info_valid)
1418 penv->board_info = resp.board_info;
1419 else
1420 penv->board_info.board_id = 0xFF;
1421 if (resp.soc_info_valid)
1422 penv->soc_info = resp.soc_info;
1423 if (resp.fw_version_info_valid)
1424 penv->fw_version_info = resp.fw_version_info;
1425 if (resp.fw_build_id_valid)
1426 strlcpy(penv->fw_build_id, resp.fw_build_id,
1427 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1428
1429 icnss_pr_dbg("Capability, chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, fw_version: 0x%x, fw_build_timestamp: %s, fw_build_id: %s",
1430 penv->chip_info.chip_id, penv->chip_info.chip_family,
1431 penv->board_info.board_id, penv->soc_info.soc_id,
1432 penv->fw_version_info.fw_version,
1433 penv->fw_version_info.fw_build_timestamp,
1434 penv->fw_build_id);
1435
1436 return 0;
1437
1438out:
1439 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001440 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001441 return ret;
1442}
1443
1444static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1445{
1446 int ret;
1447 struct wlfw_wlan_mode_req_msg_v01 req;
1448 struct wlfw_wlan_mode_resp_msg_v01 resp;
1449 struct msg_desc req_desc, resp_desc;
1450
1451 if (!penv || !penv->wlfw_clnt)
1452 return -ENODEV;
1453
1454 /* During recovery do not send mode request for WLAN OFF as
1455 * FW not able to process it.
1456 */
1457 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1458 mode == QMI_WLFW_OFF_V01)
1459 return 0;
1460
1461 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1462 penv->state, mode);
1463
1464 memset(&req, 0, sizeof(req));
1465 memset(&resp, 0, sizeof(resp));
1466
1467 req.mode = mode;
1468 req.hw_debug_valid = 1;
1469 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1470
1471 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1472 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1473 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1474
1475 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1476 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1477 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1478
1479 penv->stats.mode_req++;
1480 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1481 &resp_desc, &resp, sizeof(resp),
1482 WLFW_TIMEOUT_MS);
1483 if (ret < 0) {
1484 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1485 mode, ret);
1486 goto out;
1487 }
1488
1489 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1490 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1491 mode, resp.resp.result, resp.resp.error);
1492 ret = resp.resp.result;
1493 goto out;
1494 }
1495 penv->stats.mode_resp++;
1496
1497 return 0;
1498
1499out:
1500 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001501 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001502 return ret;
1503}
1504
1505static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1506{
1507 int ret;
1508 struct wlfw_wlan_cfg_req_msg_v01 req;
1509 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1510 struct msg_desc req_desc, resp_desc;
1511
1512 if (!penv || !penv->wlfw_clnt)
1513 return -ENODEV;
1514
1515 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1516
1517 memset(&req, 0, sizeof(req));
1518 memset(&resp, 0, sizeof(resp));
1519
1520 memcpy(&req, data, sizeof(req));
1521
1522 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1523 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1524 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1525
1526 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1527 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1528 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1529
1530 penv->stats.cfg_req++;
1531 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1532 &resp_desc, &resp, sizeof(resp),
1533 WLFW_TIMEOUT_MS);
1534 if (ret < 0) {
1535 icnss_pr_err("Send config req failed %d\n", ret);
1536 goto out;
1537 }
1538
1539 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1540 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1541 resp.resp.result, resp.resp.error);
1542 ret = resp.resp.result;
1543 goto out;
1544 }
1545 penv->stats.cfg_resp++;
1546
1547 return 0;
1548
1549out:
1550 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001551 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001552 return ret;
1553}
1554
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001555static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001556{
1557 int ret;
1558 struct wlfw_ini_req_msg_v01 req;
1559 struct wlfw_ini_resp_msg_v01 resp;
1560 struct msg_desc req_desc, resp_desc;
1561
1562 if (!penv || !penv->wlfw_clnt)
1563 return -ENODEV;
1564
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001565 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1566 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001567
1568 memset(&req, 0, sizeof(req));
1569 memset(&resp, 0, sizeof(resp));
1570
1571 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001572 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001573
1574 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1575 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1576 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1577
1578 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1579 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1580 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1581
1582 penv->stats.ini_req++;
1583
1584 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1585 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1586 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001587 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1588 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001589 goto out;
1590 }
1591
1592 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001593 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1594 fw_log_mode, resp.resp.result, resp.resp.error);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001595 ret = resp.resp.result;
1596 goto out;
1597 }
1598 penv->stats.ini_resp++;
1599
1600 return 0;
1601
1602out:
1603 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001604 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001605 return ret;
1606}
1607
1608static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1609 uint32_t offset, uint32_t mem_type,
1610 uint32_t data_len, uint8_t *data)
1611{
1612 int ret;
1613 struct wlfw_athdiag_read_req_msg_v01 req;
1614 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1615 struct msg_desc req_desc, resp_desc;
1616
1617 if (!priv->wlfw_clnt) {
1618 ret = -ENODEV;
1619 goto out;
1620 }
1621
1622 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1623 priv->state, offset, mem_type, data_len);
1624
1625 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1626 if (!resp) {
1627 ret = -ENOMEM;
1628 goto out;
1629 }
1630 memset(&req, 0, sizeof(req));
1631
1632 req.offset = offset;
1633 req.mem_type = mem_type;
1634 req.data_len = data_len;
1635
1636 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1637 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1638 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1639
1640 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1641 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1642 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1643
1644 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1645 &resp_desc, resp, sizeof(*resp),
1646 WLFW_TIMEOUT_MS);
1647 if (ret < 0) {
1648 icnss_pr_err("send athdiag read req failed %d\n", ret);
1649 goto out;
1650 }
1651
1652 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1653 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1654 resp->resp.result, resp->resp.error);
1655 ret = resp->resp.result;
1656 goto out;
1657 }
1658
Yuanyuan Liu68939762017-04-04 16:43:03 -07001659 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001660 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1661 resp->data_valid, resp->data_len);
1662 ret = -EINVAL;
1663 goto out;
1664 }
1665
1666 memcpy(data, resp->data, resp->data_len);
1667
1668out:
1669 kfree(resp);
1670 return ret;
1671}
1672
1673static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1674 uint32_t offset, uint32_t mem_type,
1675 uint32_t data_len, uint8_t *data)
1676{
1677 int ret;
1678 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1679 struct wlfw_athdiag_write_resp_msg_v01 resp;
1680 struct msg_desc req_desc, resp_desc;
1681
1682 if (!priv->wlfw_clnt) {
1683 ret = -ENODEV;
1684 goto out;
1685 }
1686
1687 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1688 priv->state, offset, mem_type, data_len, data);
1689
1690 req = kzalloc(sizeof(*req), GFP_KERNEL);
1691 if (!req) {
1692 ret = -ENOMEM;
1693 goto out;
1694 }
1695 memset(&resp, 0, sizeof(resp));
1696
1697 req->offset = offset;
1698 req->mem_type = mem_type;
1699 req->data_len = data_len;
1700 memcpy(req->data, data, data_len);
1701
1702 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1703 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1704 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1705
1706 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1707 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1708 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1709
1710 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1711 &resp_desc, &resp, sizeof(resp),
1712 WLFW_TIMEOUT_MS);
1713 if (ret < 0) {
1714 icnss_pr_err("send athdiag write req failed %d\n", ret);
1715 goto out;
1716 }
1717
1718 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1719 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1720 resp.resp.result, resp.resp.error);
1721 ret = resp.resp.result;
1722 goto out;
1723 }
1724out:
1725 kfree(req);
1726 return ret;
1727}
1728
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001729static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1730{
1731 struct msg_desc ind_desc;
1732 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1733 int ret = 0;
1734
1735 if (!penv || !penv->wlfw_clnt) {
1736 ret = -ENODEV;
1737 goto out;
1738 }
1739
1740 memset(&ind_msg, 0, sizeof(ind_msg));
1741
1742 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1743 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1744 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1745
1746 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1747 if (ret < 0) {
1748 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1749 ret, msg_len);
1750 goto out;
1751 }
1752
1753 if (ind_msg.cause_for_rejuvenation_valid)
1754 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1755 else
1756 penv->cause_for_rejuvenation = 0;
1757 if (ind_msg.requesting_sub_system_valid)
1758 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1759 else
1760 penv->requesting_sub_system = 0;
1761 if (ind_msg.line_number_valid)
1762 penv->line_number = ind_msg.line_number;
1763 else
1764 penv->line_number = 0;
1765 if (ind_msg.function_name_valid)
1766 memcpy(penv->function_name, ind_msg.function_name,
1767 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1768 else
1769 memset(penv->function_name, 0,
1770 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1771
1772 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1773 penv->cause_for_rejuvenation,
1774 penv->requesting_sub_system,
1775 penv->line_number,
1776 penv->function_name);
1777
1778 penv->stats.rejuvenate_ind++;
1779out:
1780 return ret;
1781}
1782
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001783static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1784{
1785 int ret;
1786 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1787 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1788 struct msg_desc req_desc, resp_desc;
1789
1790 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1791 priv->state);
1792
1793 memset(&req, 0, sizeof(req));
1794 memset(&resp, 0, sizeof(resp));
1795
1796 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1797 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1798 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1799
1800 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1801 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1802 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1803
1804 priv->stats.rejuvenate_ack_req++;
1805 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1806 &resp_desc, &resp, sizeof(resp),
1807 WLFW_TIMEOUT_MS);
1808 if (ret < 0) {
1809 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1810 goto out;
1811 }
1812
1813 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1814 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1815 resp.resp.result, resp.resp.error);
1816 ret = resp.resp.result;
1817 goto out;
1818 }
1819 priv->stats.rejuvenate_ack_resp++;
1820 return 0;
1821
1822out:
1823 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001824 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001825 return ret;
1826}
1827
1828static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1829 uint64_t dynamic_feature_mask)
1830{
1831 int ret;
1832 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1833 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1834 struct msg_desc req_desc, resp_desc;
1835
1836 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1837 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1838 priv->state);
1839 return -EINVAL;
1840 }
1841
1842 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1843 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1844 return 0;
1845 }
1846
1847 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1848 dynamic_feature_mask, priv->state);
1849
1850 memset(&req, 0, sizeof(req));
1851 memset(&resp, 0, sizeof(resp));
1852
1853 req.mask_valid = 1;
1854 req.mask = dynamic_feature_mask;
1855
1856 req_desc.max_msg_len =
1857 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1858 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1859 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1860
1861 resp_desc.max_msg_len =
1862 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1863 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1864 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1865
1866 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1867 &resp_desc, &resp, sizeof(resp),
1868 WLFW_TIMEOUT_MS);
1869 if (ret < 0) {
1870 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1871 goto out;
1872 }
1873
1874 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1875 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1876 resp.resp.result, resp.resp.error);
1877 ret = resp.resp.result;
1878 goto out;
1879 }
1880
1881 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1882 resp.prev_mask_valid, resp.prev_mask,
1883 resp.curr_mask_valid, resp.curr_mask);
1884
1885 return 0;
1886
1887out:
1888 return ret;
1889}
1890
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001891static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1892{
1893 int ret;
1894
1895 if (!penv || !penv->wlfw_clnt)
1896 return;
1897
Yuanyuan Liu68939762017-04-04 16:43:03 -07001898 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001899
1900 do {
1901 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1902
1903 if (ret != -ENOMSG)
1904 icnss_pr_err("Error receiving message: %d\n", ret);
1905
Yuanyuan Liu68939762017-04-04 16:43:03 -07001906 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001907}
1908
1909static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1910 enum qmi_event_type event, void *notify_priv)
1911{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001912 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001913
1914 if (!penv || !penv->wlfw_clnt)
1915 return;
1916
1917 switch (event) {
1918 case QMI_RECV_MSG:
1919 schedule_work(&penv->qmi_recv_msg_work);
1920 break;
1921 default:
1922 icnss_pr_dbg("Unknown Event: %d\n", event);
1923 break;
1924 }
1925}
1926
Yuanyuan Liu68939762017-04-04 16:43:03 -07001927static int icnss_call_driver_uevent(struct icnss_priv *priv,
1928 enum icnss_uevent uevent, void *data)
1929{
1930 struct icnss_uevent_data uevent_data;
1931
1932 if (!priv->ops || !priv->ops->uevent)
1933 return 0;
1934
1935 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
1936 priv->state, uevent);
1937
1938 uevent_data.uevent = uevent;
1939 uevent_data.data = data;
1940
1941 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
1942}
1943
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001944static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
1945 unsigned int msg_id, void *msg,
1946 unsigned int msg_len, void *ind_cb_priv)
1947{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001948 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001949 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001950
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001951 if (!penv)
1952 return;
1953
1954 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
1955
Sameer Thalappil93c00732017-08-18 13:02:32 -07001956 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
1957 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
1958 msg_id, penv->state);
1959 return;
1960 }
1961
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001962 switch (msg_id) {
1963 case QMI_WLFW_FW_READY_IND_V01:
1964 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
1965 0, NULL);
1966 break;
1967 case QMI_WLFW_MSA_READY_IND_V01:
1968 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
1969 msg_id);
1970 penv->stats.msa_ready_ind++;
1971 break;
1972 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
1973 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
1974 msg_id);
1975 icnss_qmi_pin_connect_result_ind(msg, msg_len);
1976 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001977 case QMI_WLFW_REJUVENATE_IND_V01:
1978 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
1979 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001980
1981 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001982 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001983 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
1984 if (event_data == NULL)
1985 return;
1986 event_data->crashed = true;
1987 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001988 fw_down_data.crashed = true;
1989 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
1990 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001991 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
1992 0, event_data);
1993 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001994 default:
1995 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
1996 break;
1997 }
1998}
1999
2000static int icnss_driver_event_server_arrive(void *data)
2001{
2002 int ret = 0;
2003
2004 if (!penv)
2005 return -ENODEV;
2006
2007 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07002008 clear_bit(ICNSS_FW_DOWN, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002009
2010 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
2011 if (!penv->wlfw_clnt) {
2012 icnss_pr_err("QMI client handle create failed\n");
2013 ret = -ENOMEM;
2014 goto out;
2015 }
2016
2017 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2018 WLFW_SERVICE_VERS_V01,
2019 WLFW_SERVICE_INS_ID_V01);
2020 if (ret < 0) {
2021 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2022 goto fail;
2023 }
2024
2025 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2026 icnss_qmi_wlfw_clnt_ind, penv);
2027 if (ret < 0) {
2028 icnss_pr_err("Failed to register indication callback: %d\n",
2029 ret);
2030 goto fail;
2031 }
2032
2033 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2034
2035 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2036
2037 ret = icnss_hw_power_on(penv);
2038 if (ret)
2039 goto fail;
2040
2041 ret = wlfw_ind_register_send_sync_msg();
2042 if (ret < 0)
2043 goto err_power_on;
2044
2045 if (!penv->msa_va) {
2046 icnss_pr_err("Invalid MSA address\n");
2047 ret = -EINVAL;
2048 goto err_power_on;
2049 }
2050
2051 ret = wlfw_msa_mem_info_send_sync_msg();
2052 if (ret < 0)
2053 goto err_power_on;
2054
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302055 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2056 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2057 if (ret < 0)
2058 goto err_power_on;
2059 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2060 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002061
2062 ret = wlfw_msa_ready_send_sync_msg();
2063 if (ret < 0)
2064 goto err_setup_msa;
2065
2066 ret = wlfw_cap_send_sync_msg();
2067 if (ret < 0)
2068 goto err_setup_msa;
2069
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002070 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2071 dynamic_feature_mask);
2072
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002073 icnss_init_vph_monitor(penv);
2074
2075 return ret;
2076
2077err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302078 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002079err_power_on:
2080 icnss_hw_power_off(penv);
2081fail:
2082 qmi_handle_destroy(penv->wlfw_clnt);
2083 penv->wlfw_clnt = NULL;
2084out:
2085 ICNSS_ASSERT(0);
2086 return ret;
2087}
2088
2089static int icnss_driver_event_server_exit(void *data)
2090{
2091 if (!penv || !penv->wlfw_clnt)
2092 return -ENODEV;
2093
2094 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2095
2096 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2097 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2098 &penv->vph_monitor_params);
2099
2100 qmi_handle_destroy(penv->wlfw_clnt);
2101
2102 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2103 penv->wlfw_clnt = NULL;
2104
2105 return 0;
2106}
2107
2108static int icnss_call_driver_probe(struct icnss_priv *priv)
2109{
2110 int ret;
2111
2112 if (!priv->ops || !priv->ops->probe)
2113 return 0;
2114
Yuanyuan Liu68939762017-04-04 16:43:03 -07002115 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2116 return -EINVAL;
2117
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002118 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2119
2120 icnss_hw_power_on(priv);
2121
2122 ret = priv->ops->probe(&priv->pdev->dev);
2123 if (ret < 0) {
2124 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
2125 ret, priv->state);
2126 goto out;
2127 }
2128
2129 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2130
2131 return 0;
2132
2133out:
2134 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002135 return ret;
2136}
2137
Yuanyuan Liu68939762017-04-04 16:43:03 -07002138static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2139{
2140 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2141 goto out;
2142
2143 if (!priv->ops || !priv->ops->shutdown)
2144 goto out;
2145
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302146 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2147 goto out;
2148
Yuanyuan Liu68939762017-04-04 16:43:03 -07002149 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2150
2151 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302152 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002153
2154out:
2155 return 0;
2156}
2157
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002158static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002159{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002160 int ret;
2161
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002162 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002163
Anurag Chouhan9de21192017-08-08 15:30:02 +05302164 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002165
2166 clear_bit(ICNSS_PD_RESTART, &priv->state);
2167
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002168 if (!priv->ops || !priv->ops->reinit)
2169 goto out;
2170
Yuanyuan Liu68939762017-04-04 16:43:03 -07002171 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002172 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002173
2174 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2175
2176 icnss_hw_power_on(priv);
2177
2178 ret = priv->ops->reinit(&priv->pdev->dev);
2179 if (ret < 0) {
2180 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2181 ret, priv->state);
2182 ICNSS_ASSERT(false);
2183 goto out_power_off;
2184 }
2185
2186out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302187 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002188 return 0;
2189
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002190call_probe:
2191 return icnss_call_driver_probe(priv);
2192
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002193out_power_off:
2194 icnss_hw_power_off(priv);
2195
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002196 return ret;
2197}
2198
2199
2200static int icnss_driver_event_fw_ready_ind(void *data)
2201{
2202 int ret = 0;
2203
2204 if (!penv)
2205 return -ENODEV;
2206
2207 set_bit(ICNSS_FW_READY, &penv->state);
2208
Yuanyuan Liu68939762017-04-04 16:43:03 -07002209 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
2210
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002211 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2212
2213 icnss_hw_power_off(penv);
2214
2215 if (!penv->pdev) {
2216 icnss_pr_err("Device is not ready\n");
2217 ret = -ENODEV;
2218 goto out;
2219 }
2220
2221 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002222 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002223 else
2224 ret = icnss_call_driver_probe(penv);
2225
2226out:
2227 return ret;
2228}
2229
2230static int icnss_driver_event_register_driver(void *data)
2231{
2232 int ret = 0;
2233
2234 if (penv->ops)
2235 return -EEXIST;
2236
2237 penv->ops = data;
2238
2239 if (test_bit(SKIP_QMI, &quirks))
2240 set_bit(ICNSS_FW_READY, &penv->state);
2241
2242 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2243 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2244 penv->state);
2245 goto out;
2246 }
2247
2248 ret = icnss_hw_power_on(penv);
2249 if (ret)
2250 goto out;
2251
2252 ret = penv->ops->probe(&penv->pdev->dev);
2253
2254 if (ret) {
2255 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
2256 ret, penv->state);
2257 goto power_off;
2258 }
2259
2260 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2261
2262 return 0;
2263
2264power_off:
2265 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002266out:
2267 return ret;
2268}
2269
2270static int icnss_driver_event_unregister_driver(void *data)
2271{
2272 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2273 penv->ops = NULL;
2274 goto out;
2275 }
2276
2277 if (penv->ops)
2278 penv->ops->remove(&penv->pdev->dev);
2279
2280 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2281
2282 penv->ops = NULL;
2283
2284 icnss_hw_power_off(penv);
2285
2286out:
2287 return 0;
2288}
2289
2290static int icnss_call_driver_remove(struct icnss_priv *priv)
2291{
2292 icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
2293
2294 clear_bit(ICNSS_FW_READY, &priv->state);
2295
2296 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2297 return 0;
2298
2299 if (!priv->ops || !priv->ops->remove)
2300 return 0;
2301
2302 penv->ops->remove(&priv->pdev->dev);
2303
2304 clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002305
2306 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002307
2308 return 0;
2309}
2310
Yuanyuan Liu68939762017-04-04 16:43:03 -07002311static int icnss_fw_crashed(struct icnss_priv *priv,
2312 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002313{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302314 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002315
2316 set_bit(ICNSS_PD_RESTART, &priv->state);
2317 clear_bit(ICNSS_FW_READY, &priv->state);
2318
2319 icnss_pm_stay_awake(priv);
2320
Yuanyuan Liu68939762017-04-04 16:43:03 -07002321 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2322 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002323
Yuanyuan Liu68939762017-04-04 16:43:03 -07002324 if (event_data->fw_rejuvenate)
2325 wlfw_rejuvenate_ack_send_sync_msg(priv);
2326
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002327 return 0;
2328}
2329
2330static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2331 void *data)
2332{
2333 int ret = 0;
2334 struct icnss_event_pd_service_down_data *event_data = data;
2335
2336 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002337 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002338
2339 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
2340 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2341 event_data->crashed, priv->state);
2342 ICNSS_ASSERT(0);
2343 goto out;
2344 }
2345
2346 if (event_data->crashed)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002347 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002348 else
2349 icnss_call_driver_remove(priv);
2350
2351out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002352 kfree(data);
2353
Yuanyuan Liu68939762017-04-04 16:43:03 -07002354 icnss_ignore_qmi_timeout(false);
2355
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002356 return ret;
2357}
2358
2359static void icnss_driver_event_work(struct work_struct *work)
2360{
2361 struct icnss_driver_event *event;
2362 unsigned long flags;
2363 int ret;
2364
2365 icnss_pm_stay_awake(penv);
2366
2367 spin_lock_irqsave(&penv->event_lock, flags);
2368
2369 while (!list_empty(&penv->event_list)) {
2370 event = list_first_entry(&penv->event_list,
2371 struct icnss_driver_event, list);
2372 list_del(&event->list);
2373 spin_unlock_irqrestore(&penv->event_lock, flags);
2374
2375 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2376 icnss_driver_event_to_str(event->type),
2377 event->sync ? "-sync" : "", event->type,
2378 penv->state);
2379
2380 switch (event->type) {
2381 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2382 ret = icnss_driver_event_server_arrive(event->data);
2383 break;
2384 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2385 ret = icnss_driver_event_server_exit(event->data);
2386 break;
2387 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2388 ret = icnss_driver_event_fw_ready_ind(event->data);
2389 break;
2390 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2391 ret = icnss_driver_event_register_driver(event->data);
2392 break;
2393 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2394 ret = icnss_driver_event_unregister_driver(event->data);
2395 break;
2396 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2397 ret = icnss_driver_event_pd_service_down(penv,
2398 event->data);
2399 break;
2400 default:
2401 icnss_pr_err("Invalid Event type: %d", event->type);
2402 kfree(event);
2403 continue;
2404 }
2405
2406 penv->stats.events[event->type].processed++;
2407
2408 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2409 icnss_driver_event_to_str(event->type),
2410 event->sync ? "-sync" : "", event->type, ret,
2411 penv->state);
2412
2413 spin_lock_irqsave(&penv->event_lock, flags);
2414 if (event->sync) {
2415 event->ret = ret;
2416 complete(&event->complete);
2417 continue;
2418 }
2419 spin_unlock_irqrestore(&penv->event_lock, flags);
2420
2421 kfree(event);
2422
2423 spin_lock_irqsave(&penv->event_lock, flags);
2424 }
2425 spin_unlock_irqrestore(&penv->event_lock, flags);
2426
2427 icnss_pm_relax(penv);
2428}
2429
2430static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2431 unsigned long code,
2432 void *_cmd)
2433{
2434 int ret = 0;
2435
2436 if (!penv)
2437 return -ENODEV;
2438
2439 icnss_pr_dbg("Event Notify: code: %ld", code);
2440
2441 switch (code) {
2442 case QMI_SERVER_ARRIVE:
2443 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2444 0, NULL);
2445 break;
2446
2447 case QMI_SERVER_EXIT:
2448 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2449 0, NULL);
2450 break;
2451 default:
2452 icnss_pr_dbg("Invalid code: %ld", code);
2453 break;
2454 }
2455 return ret;
2456}
2457
2458static int icnss_msa0_ramdump(struct icnss_priv *priv)
2459{
2460 struct ramdump_segment segment;
2461
2462 memset(&segment, 0, sizeof(segment));
2463 segment.v_address = priv->msa_va;
2464 segment.size = priv->msa_mem_size;
2465 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2466}
2467
2468static struct notifier_block wlfw_clnt_nb = {
2469 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2470};
2471
2472static int icnss_modem_notifier_nb(struct notifier_block *nb,
2473 unsigned long code,
2474 void *data)
2475{
2476 struct icnss_event_pd_service_down_data *event_data;
2477 struct notif_data *notif = data;
2478 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2479 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002480 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302481 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002482
Yuanyuan Liu68939762017-04-04 16:43:03 -07002483 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002484
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302485 if (code == SUBSYS_AFTER_SHUTDOWN) {
2486 ret = icnss_assign_msa_perm_all(priv,
2487 ICNSS_MSA_PERM_DUMP_COLLECT);
2488 if (!ret) {
2489 icnss_pr_info("Collecting msa0 segment dump\n");
2490 icnss_msa0_ramdump(priv);
2491 icnss_assign_msa_perm_all(priv,
2492 ICNSS_MSA_PERM_WLAN_HW_RW);
2493 } else {
2494 icnss_pr_err("Not able to Collect msa0 segment dump"
2495 "Apps permissions not assigned %d\n", ret);
2496 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002497 return NOTIFY_OK;
2498 }
2499
2500 if (code != SUBSYS_BEFORE_SHUTDOWN)
2501 return NOTIFY_OK;
2502
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002503 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002504 return NOTIFY_OK;
2505
Yuanyuan Liu68939762017-04-04 16:43:03 -07002506 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2507 priv->state, notif->crashed);
2508
Sameer Thalappil93c00732017-08-18 13:02:32 -07002509 set_bit(ICNSS_FW_DOWN, &priv->state);
2510
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002511 if (notif->crashed)
2512 priv->stats.recovery.root_pd_crash++;
2513 else
2514 priv->stats.recovery.root_pd_shutdown++;
2515
Yuanyuan Liu68939762017-04-04 16:43:03 -07002516 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002517
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002518 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002519
2520 if (event_data == NULL)
2521 return notifier_from_errno(-ENOMEM);
2522
2523 event_data->crashed = notif->crashed;
2524
Yuanyuan Liu68939762017-04-04 16:43:03 -07002525 fw_down_data.crashed = !!notif->crashed;
2526 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2527
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002528 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2529 ICNSS_EVENT_SYNC, event_data);
2530
2531 return NOTIFY_OK;
2532}
2533
2534static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2535{
2536 int ret = 0;
2537
2538 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2539
2540 priv->modem_notify_handler =
2541 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2542
2543 if (IS_ERR(priv->modem_notify_handler)) {
2544 ret = PTR_ERR(priv->modem_notify_handler);
2545 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2546 }
2547
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002548 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002549
2550 return ret;
2551}
2552
2553static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2554{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002555 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002556 return 0;
2557
2558 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2559 &priv->modem_ssr_nb);
2560 priv->modem_notify_handler = NULL;
2561
2562 return 0;
2563}
2564
2565static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2566{
2567 int i;
2568
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002569 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002570 return 0;
2571
2572 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002573 service_notif_unregister_notifier(
2574 priv->service_notifier[i].handle,
2575 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002576
2577 kfree(priv->service_notifier);
2578
2579 priv->service_notifier = NULL;
2580
2581 return 0;
2582}
2583
2584static int icnss_service_notifier_notify(struct notifier_block *nb,
2585 unsigned long notification, void *data)
2586{
2587 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2588 service_notifier_nb);
2589 enum pd_subsys_state *state = data;
2590 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002591 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002592 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002593
Yuanyuan Liu68939762017-04-04 16:43:03 -07002594 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2595 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002596
Yuanyuan Liu68939762017-04-04 16:43:03 -07002597 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2598 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002599
Yuanyuan Liu68939762017-04-04 16:43:03 -07002600 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002601
Yuanyuan Liu68939762017-04-04 16:43:03 -07002602 if (event_data == NULL)
2603 return notifier_from_errno(-ENOMEM);
2604
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002605 event_data->crashed = true;
2606
Yuanyuan Liu68939762017-04-04 16:43:03 -07002607 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002608 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002609 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002610 }
2611
Yuanyuan Liu68939762017-04-04 16:43:03 -07002612 switch (*state) {
2613 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002614 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002615 break;
2616 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002617 cause = ICNSS_ROOT_PD_SHUTDOWN;
2618 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002619 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002620 break;
2621 case USER_PD_STATE_CHANGE:
2622 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2623 cause = ICNSS_HOST_ERROR;
2624 priv->stats.recovery.pdr_host_error++;
2625 } else {
2626 cause = ICNSS_FW_CRASH;
2627 priv->stats.recovery.pdr_fw_crash++;
2628 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002629 break;
2630 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002631 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002632 break;
2633 }
2634
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002635 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2636 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002637event_post:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002638 set_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002639 icnss_ignore_qmi_timeout(true);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002640 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002641
2642 fw_down_data.crashed = event_data->crashed;
2643 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2644 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2645 ICNSS_EVENT_SYNC, event_data);
2646done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002647 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2648 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002649 return NOTIFY_OK;
2650}
2651
2652static int icnss_get_service_location_notify(struct notifier_block *nb,
2653 unsigned long opcode, void *data)
2654{
2655 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2656 get_service_nb);
2657 struct pd_qmi_client_data *pd = data;
2658 int curr_state;
2659 int ret;
2660 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002661 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002662
2663 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2664 priv->state);
2665
2666 if (opcode != LOCATOR_UP)
2667 return NOTIFY_DONE;
2668
2669 if (pd->total_domains == 0) {
2670 icnss_pr_err("Did not find any domains\n");
2671 ret = -ENOENT;
2672 goto out;
2673 }
2674
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002675 notifier = kcalloc(pd->total_domains,
2676 sizeof(struct service_notifier_context),
2677 GFP_KERNEL);
2678 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002679 ret = -ENOMEM;
2680 goto out;
2681 }
2682
2683 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2684
2685 for (i = 0; i < pd->total_domains; i++) {
2686 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2687 pd->domain_list[i].name,
2688 pd->domain_list[i].instance_id);
2689
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002690 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002691 service_notif_register_notifier(pd->domain_list[i].name,
2692 pd->domain_list[i].instance_id,
2693 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002694 notifier[i].instance_id = pd->domain_list[i].instance_id;
2695 strlcpy(notifier[i].name, pd->domain_list[i].name,
2696 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002697
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002698 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002699 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2700 i, pd->domain_list->name,
2701 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002702 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002703 goto free_handle;
2704 }
2705 }
2706
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002707 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002708 priv->total_domains = pd->total_domains;
2709
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002710 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002711
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002712 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2713 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002714
2715 return NOTIFY_OK;
2716
2717free_handle:
2718 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002719 if (notifier[i].handle)
2720 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002721 &priv->service_notifier_nb);
2722 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002723 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002724
2725out:
2726 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2727 priv->state);
2728
2729 return NOTIFY_OK;
2730}
2731
2732
2733static int icnss_pd_restart_enable(struct icnss_priv *priv)
2734{
2735 int ret;
2736
2737 if (test_bit(SSR_ONLY, &quirks)) {
2738 icnss_pr_dbg("PDR disabled through module parameter\n");
2739 return 0;
2740 }
2741
2742 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2743
2744 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2745 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2746 ICNSS_WLAN_SERVICE_NAME,
2747 &priv->get_service_nb);
2748 if (ret) {
2749 icnss_pr_err("Get service location failed: %d\n", ret);
2750 goto out;
2751 }
2752
2753 return 0;
2754out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002755 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002756 return ret;
2757
2758}
2759
2760
2761static int icnss_enable_recovery(struct icnss_priv *priv)
2762{
2763 int ret;
2764
2765 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2766 icnss_pr_dbg("Recovery disabled through module parameter\n");
2767 return 0;
2768 }
2769
2770 if (test_bit(PDR_ONLY, &quirks)) {
2771 icnss_pr_dbg("SSR disabled through module parameter\n");
2772 goto enable_pdr;
2773 }
2774
2775 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2776 &priv->pdev->dev);
2777 if (!priv->msa0_dump_dev)
2778 return -ENOMEM;
2779
2780 icnss_modem_ssr_register_notifier(priv);
2781 if (test_bit(SSR_ONLY, &quirks)) {
2782 icnss_pr_dbg("PDR disabled through module parameter\n");
2783 return 0;
2784 }
2785
2786enable_pdr:
2787 ret = icnss_pd_restart_enable(priv);
2788
2789 if (ret)
2790 return ret;
2791
2792 return 0;
2793}
2794
2795int icnss_register_driver(struct icnss_driver_ops *ops)
2796{
2797 int ret = 0;
2798
2799 if (!penv || !penv->pdev) {
2800 ret = -ENODEV;
2801 goto out;
2802 }
2803
2804 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2805
2806 if (penv->ops) {
2807 icnss_pr_err("Driver already registered\n");
2808 ret = -EEXIST;
2809 goto out;
2810 }
2811
2812 if (!ops->probe || !ops->remove) {
2813 ret = -EINVAL;
2814 goto out;
2815 }
2816
2817 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302818 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002819
2820 if (ret == -EINTR)
2821 ret = 0;
2822
2823out:
2824 return ret;
2825}
2826EXPORT_SYMBOL(icnss_register_driver);
2827
2828int icnss_unregister_driver(struct icnss_driver_ops *ops)
2829{
2830 int ret;
2831
2832 if (!penv || !penv->pdev) {
2833 ret = -ENODEV;
2834 goto out;
2835 }
2836
2837 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2838
2839 if (!penv->ops) {
2840 icnss_pr_err("Driver not registered\n");
2841 ret = -ENOENT;
2842 goto out;
2843 }
2844
2845 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2846 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2847out:
2848 return ret;
2849}
2850EXPORT_SYMBOL(icnss_unregister_driver);
2851
2852int icnss_ce_request_irq(unsigned int ce_id,
2853 irqreturn_t (*handler)(int, void *),
2854 unsigned long flags, const char *name, void *ctx)
2855{
2856 int ret = 0;
2857 unsigned int irq;
2858 struct ce_irq_list *irq_entry;
2859
2860 if (!penv || !penv->pdev) {
2861 ret = -ENODEV;
2862 goto out;
2863 }
2864
Yuanyuan Liu68939762017-04-04 16:43:03 -07002865 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002866
2867 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2868 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
2869 ret = -EINVAL;
2870 goto out;
2871 }
2872 irq = penv->ce_irqs[ce_id];
2873 irq_entry = &penv->ce_irq_list[ce_id];
2874
2875 if (irq_entry->handler || irq_entry->irq) {
2876 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
2877 irq, ce_id);
2878 ret = -EEXIST;
2879 goto out;
2880 }
2881
2882 ret = request_irq(irq, handler, flags, name, ctx);
2883 if (ret) {
2884 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
2885 irq, ce_id, ret);
2886 goto out;
2887 }
2888 irq_entry->irq = irq;
2889 irq_entry->handler = handler;
2890
Yuanyuan Liu68939762017-04-04 16:43:03 -07002891 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002892
2893 penv->stats.ce_irqs[ce_id].request++;
2894out:
2895 return ret;
2896}
2897EXPORT_SYMBOL(icnss_ce_request_irq);
2898
2899int icnss_ce_free_irq(unsigned int ce_id, void *ctx)
2900{
2901 int ret = 0;
2902 unsigned int irq;
2903 struct ce_irq_list *irq_entry;
2904
2905 if (!penv || !penv->pdev) {
2906 ret = -ENODEV;
2907 goto out;
2908 }
2909
Yuanyuan Liu68939762017-04-04 16:43:03 -07002910 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002911
2912 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2913 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
2914 ret = -EINVAL;
2915 goto out;
2916 }
2917
2918 irq = penv->ce_irqs[ce_id];
2919 irq_entry = &penv->ce_irq_list[ce_id];
2920 if (!irq_entry->handler || !irq_entry->irq) {
2921 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
2922 ret = -EEXIST;
2923 goto out;
2924 }
2925 free_irq(irq, ctx);
2926 irq_entry->irq = 0;
2927 irq_entry->handler = NULL;
2928
2929 penv->stats.ce_irqs[ce_id].free++;
2930out:
2931 return ret;
2932}
2933EXPORT_SYMBOL(icnss_ce_free_irq);
2934
2935void icnss_enable_irq(unsigned int ce_id)
2936{
2937 unsigned int irq;
2938
2939 if (!penv || !penv->pdev) {
2940 icnss_pr_err("Platform driver not initialized\n");
2941 return;
2942 }
2943
Yuanyuan Liu68939762017-04-04 16:43:03 -07002944 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002945 penv->state);
2946
2947 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2948 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
2949 return;
2950 }
2951
2952 penv->stats.ce_irqs[ce_id].enable++;
2953
2954 irq = penv->ce_irqs[ce_id];
2955 enable_irq(irq);
2956}
2957EXPORT_SYMBOL(icnss_enable_irq);
2958
2959void icnss_disable_irq(unsigned int ce_id)
2960{
2961 unsigned int irq;
2962
2963 if (!penv || !penv->pdev) {
2964 icnss_pr_err("Platform driver not initialized\n");
2965 return;
2966 }
2967
Yuanyuan Liu68939762017-04-04 16:43:03 -07002968 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002969 penv->state);
2970
2971 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2972 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
2973 ce_id);
2974 return;
2975 }
2976
2977 irq = penv->ce_irqs[ce_id];
2978 disable_irq(irq);
2979
2980 penv->stats.ce_irqs[ce_id].disable++;
2981}
2982EXPORT_SYMBOL(icnss_disable_irq);
2983
2984int icnss_get_soc_info(struct icnss_soc_info *info)
2985{
2986 if (!penv) {
2987 icnss_pr_err("Platform driver not initialized\n");
2988 return -EINVAL;
2989 }
2990
2991 info->v_addr = penv->mem_base_va;
2992 info->p_addr = penv->mem_base_pa;
2993 info->chip_id = penv->chip_info.chip_id;
2994 info->chip_family = penv->chip_info.chip_family;
2995 info->board_id = penv->board_info.board_id;
2996 info->soc_id = penv->soc_info.soc_id;
2997 info->fw_version = penv->fw_version_info.fw_version;
2998 strlcpy(info->fw_build_timestamp,
2999 penv->fw_version_info.fw_build_timestamp,
3000 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
3001
3002 return 0;
3003}
3004EXPORT_SYMBOL(icnss_get_soc_info);
3005
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003006int icnss_set_fw_log_mode(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003007{
3008 int ret;
3009
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003010 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003011
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003012 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003013 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003014 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3015 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003016 return ret;
3017}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003018EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003019
3020int icnss_athdiag_read(struct device *dev, uint32_t offset,
3021 uint32_t mem_type, uint32_t data_len,
3022 uint8_t *output)
3023{
3024 int ret = 0;
3025 struct icnss_priv *priv = dev_get_drvdata(dev);
3026
3027 if (priv->magic != ICNSS_MAGIC) {
3028 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3029 dev, priv, priv->magic);
3030 return -EINVAL;
3031 }
3032
3033 if (!output || data_len == 0
3034 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3035 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3036 output, data_len);
3037 ret = -EINVAL;
3038 goto out;
3039 }
3040
3041 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3042 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3043 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3044 priv->state);
3045 ret = -EINVAL;
3046 goto out;
3047 }
3048
3049 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3050 data_len, output);
3051out:
3052 return ret;
3053}
3054EXPORT_SYMBOL(icnss_athdiag_read);
3055
3056int icnss_athdiag_write(struct device *dev, uint32_t offset,
3057 uint32_t mem_type, uint32_t data_len,
3058 uint8_t *input)
3059{
3060 int ret = 0;
3061 struct icnss_priv *priv = dev_get_drvdata(dev);
3062
3063 if (priv->magic != ICNSS_MAGIC) {
3064 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3065 dev, priv, priv->magic);
3066 return -EINVAL;
3067 }
3068
3069 if (!input || data_len == 0
3070 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3071 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3072 input, data_len);
3073 ret = -EINVAL;
3074 goto out;
3075 }
3076
3077 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3078 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3079 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3080 priv->state);
3081 ret = -EINVAL;
3082 goto out;
3083 }
3084
3085 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3086 data_len, input);
3087out:
3088 return ret;
3089}
3090EXPORT_SYMBOL(icnss_athdiag_write);
3091
3092int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
3093 enum icnss_driver_mode mode,
3094 const char *host_version)
3095{
3096 struct wlfw_wlan_cfg_req_msg_v01 req;
3097 u32 i;
3098 int ret;
3099
3100 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3101 mode, config, host_version);
3102
3103 memset(&req, 0, sizeof(req));
3104
3105 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3106 goto skip;
3107
3108 if (!config || !host_version) {
3109 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3110 config, host_version);
3111 ret = -EINVAL;
3112 goto out;
3113 }
3114
3115 req.host_version_valid = 1;
3116 strlcpy(req.host_version, host_version,
3117 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3118
3119 req.tgt_cfg_valid = 1;
3120 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3121 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3122 else
3123 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3124 for (i = 0; i < req.tgt_cfg_len; i++) {
3125 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3126 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3127 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3128 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3129 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3130 }
3131
3132 req.svc_cfg_valid = 1;
3133 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3134 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3135 else
3136 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3137 for (i = 0; i < req.svc_cfg_len; i++) {
3138 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3139 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3140 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3141 }
3142
3143 req.shadow_reg_valid = 1;
3144 if (config->num_shadow_reg_cfg >
3145 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3146 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3147 else
3148 req.shadow_reg_len = config->num_shadow_reg_cfg;
3149
3150 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3151 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3152
3153 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3154 if (ret)
3155 goto out;
3156skip:
3157 ret = wlfw_wlan_mode_send_sync_msg(mode);
3158out:
3159 if (test_bit(SKIP_QMI, &quirks))
3160 ret = 0;
3161
3162 return ret;
3163}
3164EXPORT_SYMBOL(icnss_wlan_enable);
3165
3166int icnss_wlan_disable(enum icnss_driver_mode mode)
3167{
3168 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3169}
3170EXPORT_SYMBOL(icnss_wlan_disable);
3171
3172bool icnss_is_qmi_disable(void)
3173{
3174 return test_bit(SKIP_QMI, &quirks) ? true : false;
3175}
3176EXPORT_SYMBOL(icnss_is_qmi_disable);
3177
3178int icnss_get_ce_id(int irq)
3179{
3180 int i;
3181
3182 if (!penv || !penv->pdev)
3183 return -ENODEV;
3184
3185 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3186 if (penv->ce_irqs[i] == irq)
3187 return i;
3188 }
3189
3190 icnss_pr_err("No matching CE id for irq %d\n", irq);
3191
3192 return -EINVAL;
3193}
3194EXPORT_SYMBOL(icnss_get_ce_id);
3195
3196int icnss_get_irq(int ce_id)
3197{
3198 int irq;
3199
3200 if (!penv || !penv->pdev)
3201 return -ENODEV;
3202
3203 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3204 return -EINVAL;
3205
3206 irq = penv->ce_irqs[ce_id];
3207
3208 return irq;
3209}
3210EXPORT_SYMBOL(icnss_get_irq);
3211
3212struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3213{
3214 struct icnss_priv *priv = dev_get_drvdata(dev);
3215
3216 if (!priv) {
3217 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3218 dev, priv);
3219 return NULL;
3220 }
3221
3222 return priv->smmu_mapping;
3223}
3224EXPORT_SYMBOL(icnss_smmu_get_mapping);
3225
3226int icnss_smmu_map(struct device *dev,
3227 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3228{
3229 struct icnss_priv *priv = dev_get_drvdata(dev);
3230 unsigned long iova;
3231 size_t len;
3232 int ret = 0;
3233
3234 if (!priv) {
3235 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3236 dev, priv);
3237 return -EINVAL;
3238 }
3239
3240 if (!iova_addr) {
3241 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3242 &paddr, size);
3243 return -EINVAL;
3244 }
3245
3246 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3247 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3248
3249 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3250 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3251 iova,
3252 &priv->smmu_iova_ipa_start,
3253 priv->smmu_iova_ipa_len);
3254 return -ENOMEM;
3255 }
3256
3257 ret = iommu_map(priv->smmu_mapping->domain, iova,
3258 rounddown(paddr, PAGE_SIZE), len,
3259 IOMMU_READ | IOMMU_WRITE);
3260 if (ret) {
3261 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3262 return ret;
3263 }
3264
3265 priv->smmu_iova_ipa_start = iova + len;
3266 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3267
3268 return 0;
3269}
3270EXPORT_SYMBOL(icnss_smmu_map);
3271
3272unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3273{
3274 return socinfo_get_serial_number();
3275}
3276EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3277
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003278int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len)
3279{
3280 struct icnss_priv *priv = penv;
3281 uint32_t no_of_mac_addr;
3282 struct icnss_wlan_mac_addr *addr = NULL;
3283 int iter;
3284 u8 *temp = NULL;
3285
3286 if (!priv) {
3287 icnss_pr_err("Priv data is NULL\n");
3288 return -EINVAL;
3289 }
3290
3291 if (priv->is_wlan_mac_set) {
3292 icnss_pr_dbg("WLAN MAC address is already set\n");
3293 return 0;
3294 }
3295
3296 if (len == 0 || (len % ETH_ALEN) != 0) {
3297 icnss_pr_err("Invalid length %d\n", len);
3298 return -EINVAL;
3299 }
3300
3301 no_of_mac_addr = len / ETH_ALEN;
3302 if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
3303 icnss_pr_err("Exceed maxinum supported MAC address %u %u\n",
3304 MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
3305 return -EINVAL;
3306 }
3307
3308 priv->is_wlan_mac_set = true;
3309 addr = &priv->wlan_mac_addr;
3310 addr->no_of_mac_addr_set = no_of_mac_addr;
3311 temp = &addr->mac_addr[0][0];
3312
3313 for (iter = 0; iter < no_of_mac_addr;
3314 ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
3315 ether_addr_copy(temp, in);
3316 icnss_pr_dbg("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
3317 temp[0], temp[1], temp[2],
3318 temp[3], temp[4], temp[5]);
3319 }
3320
3321 return 0;
3322}
3323EXPORT_SYMBOL(icnss_set_wlan_mac_address);
3324
3325u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num)
3326{
3327 struct icnss_priv *priv = dev_get_drvdata(dev);
3328 struct icnss_wlan_mac_addr *addr = NULL;
3329
3330 if (priv->magic != ICNSS_MAGIC) {
3331 icnss_pr_err("Invalid drvdata: dev %p, data %p, magic 0x%x\n",
3332 dev, priv, priv->magic);
3333 goto out;
3334 }
3335
3336 if (!priv->is_wlan_mac_set) {
3337 icnss_pr_dbg("WLAN MAC address is not set\n");
3338 goto out;
3339 }
3340
3341 addr = &priv->wlan_mac_addr;
3342 *num = addr->no_of_mac_addr_set;
3343 return &addr->mac_addr[0][0];
3344out:
3345 *num = 0;
3346 return NULL;
3347}
3348EXPORT_SYMBOL(icnss_get_wlan_mac_address);
3349
3350int icnss_trigger_recovery(struct device *dev)
3351{
3352 int ret = 0;
3353 struct icnss_priv *priv = dev_get_drvdata(dev);
3354
3355 if (priv->magic != ICNSS_MAGIC) {
3356 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3357 ret = -EINVAL;
3358 goto out;
3359 }
3360
3361 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3362 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3363 priv->state);
3364 ret = -EPERM;
3365 goto out;
3366 }
3367
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003368 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003369 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3370 priv->state);
3371 ret = -EOPNOTSUPP;
3372 goto out;
3373 }
3374
3375 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3376 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3377 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003378 ret = -EINVAL;
3379 goto out;
3380 }
3381
Yuanyuan Liu68939762017-04-04 16:43:03 -07003382 WARN_ON(1);
3383 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3384 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003385
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003386 /*
3387 * Initiate PDR, required only for the first instance
3388 */
3389 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3390 priv->service_notifier[0].instance_id);
3391
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003392 if (!ret)
3393 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3394
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003395out:
3396 return ret;
3397}
3398EXPORT_SYMBOL(icnss_trigger_recovery);
3399
3400
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003401static int icnss_smmu_init(struct icnss_priv *priv)
3402{
3403 struct dma_iommu_mapping *mapping;
3404 int atomic_ctx = 1;
3405 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303406 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003407 int ret = 0;
3408
3409 icnss_pr_dbg("Initializing SMMU\n");
3410
3411 mapping = arm_iommu_create_mapping(&platform_bus_type,
3412 priv->smmu_iova_start,
3413 priv->smmu_iova_len);
3414 if (IS_ERR(mapping)) {
3415 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3416 ret = PTR_ERR(mapping);
3417 goto map_fail;
3418 }
3419
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303420 if (priv->bypass_s1_smmu) {
3421 ret = iommu_domain_set_attr(mapping->domain,
3422 DOMAIN_ATTR_S1_BYPASS,
3423 &s1_bypass);
3424 if (ret < 0) {
3425 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3426 ret);
3427 goto set_attr_fail;
3428 }
3429 icnss_pr_dbg("SMMU S1 BYPASS\n");
3430 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003431 ret = iommu_domain_set_attr(mapping->domain,
3432 DOMAIN_ATTR_ATOMIC,
3433 &atomic_ctx);
3434 if (ret < 0) {
3435 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3436 ret);
3437 goto set_attr_fail;
3438 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303439 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003440
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303441 ret = iommu_domain_set_attr(mapping->domain,
3442 DOMAIN_ATTR_FAST,
3443 &fast);
3444 if (ret < 0) {
3445 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3446 ret);
3447 goto set_attr_fail;
3448 }
3449 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003450 }
3451
3452 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3453 if (ret < 0) {
3454 icnss_pr_err("Attach device failed, err = %d\n", ret);
3455 goto attach_fail;
3456 }
3457
3458 priv->smmu_mapping = mapping;
3459
3460 return ret;
3461
3462attach_fail:
3463set_attr_fail:
3464 arm_iommu_release_mapping(mapping);
3465map_fail:
3466 return ret;
3467}
3468
3469static void icnss_smmu_deinit(struct icnss_priv *priv)
3470{
3471 if (!priv->smmu_mapping)
3472 return;
3473
3474 arm_iommu_detach_device(&priv->pdev->dev);
3475 arm_iommu_release_mapping(priv->smmu_mapping);
3476
3477 priv->smmu_mapping = NULL;
3478}
3479
Yuanyuan Liu68939762017-04-04 16:43:03 -07003480static int icnss_get_vreg_info(struct device *dev,
3481 struct icnss_vreg_info *vreg_info)
3482{
3483 int ret = 0;
3484 char prop_name[MAX_PROP_SIZE];
3485 struct regulator *reg;
3486 const __be32 *prop;
3487 int len = 0;
3488 int i;
3489
3490 reg = devm_regulator_get_optional(dev, vreg_info->name);
3491 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3492 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3493 vreg_info->name);
3494 ret = PTR_ERR(reg);
3495 goto out;
3496 }
3497
3498 if (IS_ERR(reg)) {
3499 ret = PTR_ERR(reg);
3500
3501 if (vreg_info->required) {
3502 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3503 vreg_info->name, ret);
3504 goto out;
3505 } else {
3506 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3507 vreg_info->name, ret);
3508 goto done;
3509 }
3510 }
3511
3512 vreg_info->reg = reg;
3513
3514 snprintf(prop_name, MAX_PROP_SIZE,
3515 "qcom,%s-config", vreg_info->name);
3516
3517 prop = of_get_property(dev->of_node, prop_name, &len);
3518
3519 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3520 prop_name, len);
3521
3522 if (!prop || len < (2 * sizeof(__be32))) {
3523 icnss_pr_dbg("Property %s %s\n", prop_name,
3524 prop ? "invalid format" : "doesn't exist");
3525 goto done;
3526 }
3527
3528 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3529 switch (i) {
3530 case 0:
3531 vreg_info->min_v = be32_to_cpup(&prop[0]);
3532 break;
3533 case 1:
3534 vreg_info->max_v = be32_to_cpup(&prop[1]);
3535 break;
3536 case 2:
3537 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3538 break;
3539 case 3:
3540 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3541 break;
3542 default:
3543 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3544 prop_name, i);
3545 break;
3546 }
3547 }
3548
3549done:
3550 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3551 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3552 vreg_info->load_ua, vreg_info->settle_delay);
3553
3554 return 0;
3555
3556out:
3557 return ret;
3558}
3559
3560static int icnss_get_clk_info(struct device *dev,
3561 struct icnss_clk_info *clk_info)
3562{
3563 struct clk *handle;
3564 int ret = 0;
3565
3566 handle = devm_clk_get(dev, clk_info->name);
3567 if (IS_ERR(handle)) {
3568 ret = PTR_ERR(handle);
3569 if (clk_info->required) {
3570 icnss_pr_err("Clock %s isn't available: %d\n",
3571 clk_info->name, ret);
3572 goto out;
3573 } else {
3574 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3575 ret);
3576 ret = 0;
3577 goto out;
3578 }
3579 }
3580
3581 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3582
3583 clk_info->handle = handle;
3584out:
3585 return ret;
3586}
3587
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003588static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003589{
3590 struct icnss_priv *priv = s->private;
3591
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003592 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003593
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003594 seq_puts(s, "\nCMD: test_mode\n");
3595 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3596 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3597 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003598 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003599
3600 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3601 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003602
3603 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003604 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003605 goto out;
3606 }
3607
3608 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003609 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003610 goto out;
3611 }
3612
3613 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003614 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003615 goto out;
3616 }
3617
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003618out:
3619 seq_puts(s, "\n");
3620 return 0;
3621}
3622
3623static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3624{
3625 int ret;
3626
3627 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3628 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3629 priv->state);
3630 ret = -ENODEV;
3631 goto out;
3632 }
3633
3634 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3635 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3636 priv->state);
3637 ret = -EINVAL;
3638 goto out;
3639 }
3640
3641 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3642 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3643 priv->state);
3644 ret = -EINVAL;
3645 goto out;
3646 }
3647
3648 icnss_wlan_disable(ICNSS_OFF);
3649
3650 ret = icnss_hw_power_off(priv);
3651
3652 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3653
3654out:
3655 return ret;
3656}
3657static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3658 enum icnss_driver_mode mode)
3659{
3660 int ret;
3661
3662 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3663 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3664 priv->state);
3665 ret = -ENODEV;
3666 goto out;
3667 }
3668
3669 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3670 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3671 priv->state);
3672 ret = -EINVAL;
3673 goto out;
3674 }
3675
3676 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3677 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3678 priv->state);
3679 ret = -EBUSY;
3680 goto out;
3681 }
3682
3683 ret = icnss_hw_power_on(priv);
3684 if (ret)
3685 goto out;
3686
3687 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3688
3689 ret = icnss_wlan_enable(NULL, mode, NULL);
3690 if (ret)
3691 goto power_off;
3692
3693 return 0;
3694
3695power_off:
3696 icnss_hw_power_off(priv);
3697 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3698
3699out:
3700 return ret;
3701}
3702
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003703static ssize_t icnss_fw_debug_write(struct file *fp,
3704 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003705 size_t count, loff_t *off)
3706{
3707 struct icnss_priv *priv =
3708 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003709 char buf[64];
3710 char *sptr, *token;
3711 unsigned int len = 0;
3712 char *cmd;
3713 uint64_t val;
3714 const char *delim = " ";
3715 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003716
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003717 len = min(count, sizeof(buf) - 1);
3718 if (copy_from_user(buf, user_buf, len))
3719 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003720
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003721 buf[len] = '\0';
3722 sptr = buf;
3723
3724 token = strsep(&sptr, delim);
3725 if (!token)
3726 return -EINVAL;
3727 if (!sptr)
3728 return -EINVAL;
3729 cmd = token;
3730
3731 token = strsep(&sptr, delim);
3732 if (!token)
3733 return -EINVAL;
3734 if (kstrtou64(token, 0, &val))
3735 return -EINVAL;
3736
3737 if (strcmp(cmd, "test_mode") == 0) {
3738 switch (val) {
3739 case 0:
3740 ret = icnss_test_mode_fw_test_off(priv);
3741 break;
3742 case 1:
3743 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3744 break;
3745 case 2:
3746 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3747 break;
3748 case 3:
3749 ret = icnss_trigger_recovery(&priv->pdev->dev);
3750 break;
3751 default:
3752 return -EINVAL;
3753 }
3754 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3755 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3756 } else {
3757 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003758 }
3759
3760 if (ret)
3761 return ret;
3762
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003763 return count;
3764}
3765
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003766static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003767{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003768 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003769}
3770
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003771static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003772 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003773 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003774 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003775 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003776 .owner = THIS_MODULE,
3777 .llseek = seq_lseek,
3778};
3779
3780static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3781 size_t count, loff_t *off)
3782{
3783 struct icnss_priv *priv =
3784 ((struct seq_file *)fp->private_data)->private;
3785 int ret;
3786 u32 val;
3787
3788 ret = kstrtou32_from_user(buf, count, 0, &val);
3789 if (ret)
3790 return ret;
3791
3792 if (ret == 0)
3793 memset(&priv->stats, 0, sizeof(priv->stats));
3794
3795 return count;
3796}
3797
3798static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3799{
3800 enum icnss_driver_state i;
3801 int skip = 0;
3802 unsigned long state;
3803
3804 seq_printf(s, "\nState: 0x%lx(", priv->state);
3805 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3806
3807 if (!(state & 0x1))
3808 continue;
3809
3810 if (skip++)
3811 seq_puts(s, " | ");
3812
3813 switch (i) {
3814 case ICNSS_WLFW_QMI_CONNECTED:
3815 seq_puts(s, "QMI CONN");
3816 continue;
3817 case ICNSS_POWER_ON:
3818 seq_puts(s, "POWER ON");
3819 continue;
3820 case ICNSS_FW_READY:
3821 seq_puts(s, "FW READY");
3822 continue;
3823 case ICNSS_DRIVER_PROBED:
3824 seq_puts(s, "DRIVER PROBED");
3825 continue;
3826 case ICNSS_FW_TEST_MODE:
3827 seq_puts(s, "FW TEST MODE");
3828 continue;
3829 case ICNSS_PM_SUSPEND:
3830 seq_puts(s, "PM SUSPEND");
3831 continue;
3832 case ICNSS_PM_SUSPEND_NOIRQ:
3833 seq_puts(s, "PM SUSPEND NOIRQ");
3834 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003835 case ICNSS_SSR_REGISTERED:
3836 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003837 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003838 case ICNSS_PDR_REGISTERED:
3839 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003840 continue;
3841 case ICNSS_PD_RESTART:
3842 seq_puts(s, "PD RESTART");
3843 continue;
3844 case ICNSS_MSA0_ASSIGNED:
3845 seq_puts(s, "MSA0 ASSIGNED");
3846 continue;
3847 case ICNSS_WLFW_EXISTS:
3848 seq_puts(s, "WLAN FW EXISTS");
3849 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303850 case ICNSS_SHUTDOWN_DONE:
3851 seq_puts(s, "SHUTDOWN DONE");
3852 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003853 case ICNSS_HOST_TRIGGERED_PDR:
3854 seq_puts(s, "HOST TRIGGERED PDR");
3855 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07003856 case ICNSS_FW_DOWN:
3857 seq_puts(s, "FW DOWN");
3858 continue;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003859 }
3860
3861 seq_printf(s, "UNKNOWN-%d", i);
3862 }
3863 seq_puts(s, ")\n");
3864
3865 return 0;
3866}
3867
3868static int icnss_stats_show_capability(struct seq_file *s,
3869 struct icnss_priv *priv)
3870{
3871 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3872 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3873 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3874 seq_printf(s, "Chip family: 0x%x\n",
3875 priv->chip_info.chip_family);
3876 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3877 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3878 seq_printf(s, "Firmware Version: 0x%x\n",
3879 priv->fw_version_info.fw_version);
3880 seq_printf(s, "Firmware Build Timestamp: %s\n",
3881 priv->fw_version_info.fw_build_timestamp);
3882 seq_printf(s, "Firmware Build ID: %s\n",
3883 priv->fw_build_id);
3884 }
3885
3886 return 0;
3887}
3888
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003889static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
3890 struct icnss_priv *priv)
3891{
3892 if (priv->stats.rejuvenate_ind) {
3893 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
3894 seq_printf(s, "Number of Rejuvenations: %u\n",
3895 priv->stats.rejuvenate_ind);
3896 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
3897 priv->cause_for_rejuvenation);
3898 seq_printf(s, "Requesting Sub-System: 0x%x\n",
3899 priv->requesting_sub_system);
3900 seq_printf(s, "Line Number: %u\n",
3901 priv->line_number);
3902 seq_printf(s, "Function Name: %s\n",
3903 priv->function_name);
3904 }
3905
3906 return 0;
3907}
3908
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003909static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
3910{
3911 int i;
3912
3913 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
3914 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
3915 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
3916 seq_printf(s, "%24s %16u %16u\n",
3917 icnss_driver_event_to_str(i),
3918 priv->stats.events[i].posted,
3919 priv->stats.events[i].processed);
3920
3921 return 0;
3922}
3923
3924static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
3925{
3926 int i;
3927
3928 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
3929 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
3930 "Free", "Enable", "Disable");
3931 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
3932 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
3933 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
3934 priv->stats.ce_irqs[i].free,
3935 priv->stats.ce_irqs[i].enable,
3936 priv->stats.ce_irqs[i].disable);
3937
3938 return 0;
3939}
3940
3941static int icnss_stats_show(struct seq_file *s, void *data)
3942{
3943#define ICNSS_STATS_DUMP(_s, _priv, _x) \
3944 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
3945
3946 struct icnss_priv *priv = s->private;
3947
3948 ICNSS_STATS_DUMP(s, priv, ind_register_req);
3949 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
3950 ICNSS_STATS_DUMP(s, priv, ind_register_err);
3951 ICNSS_STATS_DUMP(s, priv, msa_info_req);
3952 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
3953 ICNSS_STATS_DUMP(s, priv, msa_info_err);
3954 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
3955 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
3956 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
3957 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
3958 ICNSS_STATS_DUMP(s, priv, cap_req);
3959 ICNSS_STATS_DUMP(s, priv, cap_resp);
3960 ICNSS_STATS_DUMP(s, priv, cap_err);
3961 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
3962 ICNSS_STATS_DUMP(s, priv, cfg_req);
3963 ICNSS_STATS_DUMP(s, priv, cfg_resp);
3964 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
3965 ICNSS_STATS_DUMP(s, priv, mode_req);
3966 ICNSS_STATS_DUMP(s, priv, mode_resp);
3967 ICNSS_STATS_DUMP(s, priv, mode_req_err);
3968 ICNSS_STATS_DUMP(s, priv, ini_req);
3969 ICNSS_STATS_DUMP(s, priv, ini_resp);
3970 ICNSS_STATS_DUMP(s, priv, ini_req_err);
3971 ICNSS_STATS_DUMP(s, priv, vbatt_req);
3972 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
3973 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003974 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003975 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
3976 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
3977 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003978 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
3979 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
3980 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
3981 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003982
3983 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
3984 ICNSS_STATS_DUMP(s, priv, pm_suspend);
3985 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
3986 ICNSS_STATS_DUMP(s, priv, pm_resume);
3987 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
3988 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
3989 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
3990 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
3991 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
3992 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
3993 ICNSS_STATS_DUMP(s, priv, pm_relax);
3994
3995 icnss_stats_show_irqs(s, priv);
3996
3997 icnss_stats_show_capability(s, priv);
3998
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003999 icnss_stats_show_rejuvenate_info(s, priv);
4000
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004001 icnss_stats_show_events(s, priv);
4002
4003 icnss_stats_show_state(s, priv);
4004
4005 return 0;
4006#undef ICNSS_STATS_DUMP
4007}
4008
4009static int icnss_stats_open(struct inode *inode, struct file *file)
4010{
4011 return single_open(file, icnss_stats_show, inode->i_private);
4012}
4013
4014static const struct file_operations icnss_stats_fops = {
4015 .read = seq_read,
4016 .write = icnss_stats_write,
4017 .release = single_release,
4018 .open = icnss_stats_open,
4019 .owner = THIS_MODULE,
4020 .llseek = seq_lseek,
4021};
4022
4023static int icnss_regwrite_show(struct seq_file *s, void *data)
4024{
4025 struct icnss_priv *priv = s->private;
4026
4027 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4028
4029 if (!test_bit(ICNSS_FW_READY, &priv->state))
4030 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4031
4032 return 0;
4033}
4034
4035static ssize_t icnss_regwrite_write(struct file *fp,
4036 const char __user *user_buf,
4037 size_t count, loff_t *off)
4038{
4039 struct icnss_priv *priv =
4040 ((struct seq_file *)fp->private_data)->private;
4041 char buf[64];
4042 char *sptr, *token;
4043 unsigned int len = 0;
4044 uint32_t reg_offset, mem_type, reg_val;
4045 const char *delim = " ";
4046 int ret = 0;
4047
4048 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4049 !test_bit(ICNSS_POWER_ON, &priv->state))
4050 return -EINVAL;
4051
4052 len = min(count, sizeof(buf) - 1);
4053 if (copy_from_user(buf, user_buf, len))
4054 return -EFAULT;
4055
4056 buf[len] = '\0';
4057 sptr = buf;
4058
4059 token = strsep(&sptr, delim);
4060 if (!token)
4061 return -EINVAL;
4062
4063 if (!sptr)
4064 return -EINVAL;
4065
4066 if (kstrtou32(token, 0, &mem_type))
4067 return -EINVAL;
4068
4069 token = strsep(&sptr, delim);
4070 if (!token)
4071 return -EINVAL;
4072
4073 if (!sptr)
4074 return -EINVAL;
4075
4076 if (kstrtou32(token, 0, &reg_offset))
4077 return -EINVAL;
4078
4079 token = strsep(&sptr, delim);
4080 if (!token)
4081 return -EINVAL;
4082
4083 if (kstrtou32(token, 0, &reg_val))
4084 return -EINVAL;
4085
4086 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4087 sizeof(uint32_t),
4088 (uint8_t *)&reg_val);
4089 if (ret)
4090 return ret;
4091
4092 return count;
4093}
4094
4095static int icnss_regwrite_open(struct inode *inode, struct file *file)
4096{
4097 return single_open(file, icnss_regwrite_show, inode->i_private);
4098}
4099
4100static const struct file_operations icnss_regwrite_fops = {
4101 .read = seq_read,
4102 .write = icnss_regwrite_write,
4103 .open = icnss_regwrite_open,
4104 .owner = THIS_MODULE,
4105 .llseek = seq_lseek,
4106};
4107
4108static int icnss_regread_show(struct seq_file *s, void *data)
4109{
4110 struct icnss_priv *priv = s->private;
4111
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304112 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004113 if (!priv->diag_reg_read_buf) {
4114 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4115
4116 if (!test_bit(ICNSS_FW_READY, &priv->state))
4117 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4118
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304119 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004120 return 0;
4121 }
4122
4123 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4124 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4125 priv->diag_reg_read_len);
4126
4127 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4128 priv->diag_reg_read_len, false);
4129
4130 priv->diag_reg_read_len = 0;
4131 kfree(priv->diag_reg_read_buf);
4132 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304133 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004134
4135 return 0;
4136}
4137
4138static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4139 size_t count, loff_t *off)
4140{
4141 struct icnss_priv *priv =
4142 ((struct seq_file *)fp->private_data)->private;
4143 char buf[64];
4144 char *sptr, *token;
4145 unsigned int len = 0;
4146 uint32_t reg_offset, mem_type;
4147 uint32_t data_len = 0;
4148 uint8_t *reg_buf = NULL;
4149 const char *delim = " ";
4150 int ret = 0;
4151
4152 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4153 !test_bit(ICNSS_POWER_ON, &priv->state))
4154 return -EINVAL;
4155
4156 len = min(count, sizeof(buf) - 1);
4157 if (copy_from_user(buf, user_buf, len))
4158 return -EFAULT;
4159
4160 buf[len] = '\0';
4161 sptr = buf;
4162
4163 token = strsep(&sptr, delim);
4164 if (!token)
4165 return -EINVAL;
4166
4167 if (!sptr)
4168 return -EINVAL;
4169
4170 if (kstrtou32(token, 0, &mem_type))
4171 return -EINVAL;
4172
4173 token = strsep(&sptr, delim);
4174 if (!token)
4175 return -EINVAL;
4176
4177 if (!sptr)
4178 return -EINVAL;
4179
4180 if (kstrtou32(token, 0, &reg_offset))
4181 return -EINVAL;
4182
4183 token = strsep(&sptr, delim);
4184 if (!token)
4185 return -EINVAL;
4186
4187 if (kstrtou32(token, 0, &data_len))
4188 return -EINVAL;
4189
4190 if (data_len == 0 ||
4191 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4192 return -EINVAL;
4193
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304194 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004195 kfree(priv->diag_reg_read_buf);
4196 priv->diag_reg_read_buf = NULL;
4197
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004198 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304199 if (!reg_buf) {
4200 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004201 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304202 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004203
4204 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4205 mem_type, data_len,
4206 reg_buf);
4207 if (ret) {
4208 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304209 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004210 return ret;
4211 }
4212
4213 priv->diag_reg_read_addr = reg_offset;
4214 priv->diag_reg_read_mem_type = mem_type;
4215 priv->diag_reg_read_len = data_len;
4216 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304217 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004218
4219 return count;
4220}
4221
4222static int icnss_regread_open(struct inode *inode, struct file *file)
4223{
4224 return single_open(file, icnss_regread_show, inode->i_private);
4225}
4226
4227static const struct file_operations icnss_regread_fops = {
4228 .read = seq_read,
4229 .write = icnss_regread_write,
4230 .open = icnss_regread_open,
4231 .owner = THIS_MODULE,
4232 .llseek = seq_lseek,
4233};
4234
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004235#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004236static int icnss_debugfs_create(struct icnss_priv *priv)
4237{
4238 int ret = 0;
4239 struct dentry *root_dentry;
4240
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004241 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004242
4243 if (IS_ERR(root_dentry)) {
4244 ret = PTR_ERR(root_dentry);
4245 icnss_pr_err("Unable to create debugfs %d\n", ret);
4246 goto out;
4247 }
4248
4249 priv->root_dentry = root_dentry;
4250
Yuanyuan Liu84132752017-05-24 12:07:12 -07004251 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004252 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004253
Yuanyuan Liu84132752017-05-24 12:07:12 -07004254 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004255 &icnss_stats_fops);
4256 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4257 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004258 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004259 &icnss_regwrite_fops);
4260
4261out:
4262 return ret;
4263}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004264#else
4265static int icnss_debugfs_create(struct icnss_priv *priv)
4266{
4267 int ret = 0;
4268 struct dentry *root_dentry;
4269
4270 root_dentry = debugfs_create_dir("icnss", NULL);
4271
4272 if (IS_ERR(root_dentry)) {
4273 ret = PTR_ERR(root_dentry);
4274 icnss_pr_err("Unable to create debugfs %d\n", ret);
4275 return ret;
4276 }
4277
4278 priv->root_dentry = root_dentry;
4279
4280 debugfs_create_file("stats", 0600, root_dentry, priv,
4281 &icnss_stats_fops);
4282 return 0;
4283}
4284#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004285
4286static void icnss_debugfs_destroy(struct icnss_priv *priv)
4287{
4288 debugfs_remove_recursive(priv->root_dentry);
4289}
4290
4291static int icnss_get_vbatt_info(struct icnss_priv *priv)
4292{
4293 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4294 struct qpnp_vadc_chip *vadc_dev = NULL;
4295 int ret = 0;
4296
4297 if (test_bit(VBATT_DISABLE, &quirks)) {
4298 icnss_pr_dbg("VBATT feature is disabled\n");
4299 return ret;
4300 }
4301
4302 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4303 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4304 icnss_pr_err("adc_tm_dev probe defer\n");
4305 return -EPROBE_DEFER;
4306 }
4307
4308 if (IS_ERR(adc_tm_dev)) {
4309 ret = PTR_ERR(adc_tm_dev);
4310 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4311 ret);
4312 return ret;
4313 }
4314
4315 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4316 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4317 icnss_pr_err("vadc_dev probe defer\n");
4318 return -EPROBE_DEFER;
4319 }
4320
4321 if (IS_ERR(vadc_dev)) {
4322 ret = PTR_ERR(vadc_dev);
4323 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4324 ret);
4325 return ret;
4326 }
4327
4328 priv->adc_tm_dev = adc_tm_dev;
4329 priv->vadc_dev = vadc_dev;
4330
4331 return 0;
4332}
4333
4334static int icnss_probe(struct platform_device *pdev)
4335{
4336 int ret = 0;
4337 struct resource *res;
4338 int i;
4339 struct device *dev = &pdev->dev;
4340 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304341 const __be32 *addrp;
4342 u64 prop_size = 0;
4343 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004344
4345 if (penv) {
4346 icnss_pr_err("Driver is already initialized\n");
4347 return -EEXIST;
4348 }
4349
4350 icnss_pr_dbg("Platform driver probe\n");
4351
4352 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4353 if (!priv)
4354 return -ENOMEM;
4355
4356 priv->magic = ICNSS_MAGIC;
4357 dev_set_drvdata(dev, priv);
4358
4359 priv->pdev = pdev;
4360
4361 ret = icnss_get_vbatt_info(priv);
4362 if (ret == -EPROBE_DEFER)
4363 goto out;
4364
Yuanyuan Liu68939762017-04-04 16:43:03 -07004365 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4366 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4367 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4368
4369 if (ret)
4370 goto out;
4371 }
4372
4373 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4374 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4375 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4376 if (ret)
4377 goto out;
4378 }
4379
4380 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4381 priv->bypass_s1_smmu = true;
4382
4383 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4384
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004385 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4386 if (!res) {
4387 icnss_pr_err("Memory base not found in DT\n");
4388 ret = -EINVAL;
4389 goto out;
4390 }
4391
4392 priv->mem_base_pa = res->start;
4393 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4394 resource_size(res));
4395 if (!priv->mem_base_va) {
4396 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4397 &priv->mem_base_pa);
4398 ret = -EINVAL;
4399 goto out;
4400 }
4401 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4402 priv->mem_base_va);
4403
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004404 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4405 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4406 if (!res) {
4407 icnss_pr_err("Fail to get IRQ-%d\n", i);
4408 ret = -ENODEV;
4409 goto out;
4410 } else {
4411 priv->ce_irqs[i] = res->start;
4412 }
4413 }
4414
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304415 np = of_parse_phandle(dev->of_node,
4416 "qcom,wlan-msa-fixed-region", 0);
4417 if (np) {
4418 addrp = of_get_address(np, 0, &prop_size, NULL);
4419 if (!addrp) {
4420 icnss_pr_err("Failed to get assigned-addresses or property\n");
4421 ret = -EINVAL;
4422 goto out;
4423 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004424
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304425 priv->msa_pa = of_translate_address(np, addrp);
4426 if (priv->msa_pa == OF_BAD_ADDR) {
4427 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4428 ret = -EINVAL;
4429 goto out;
4430 }
4431
4432 priv->msa_va = memremap(priv->msa_pa,
4433 (unsigned long)prop_size, MEMREMAP_WT);
4434 if (!priv->msa_va) {
4435 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4436 &priv->msa_pa);
4437 ret = -EINVAL;
4438 goto out;
4439 }
4440 priv->msa_mem_size = prop_size;
4441 } else {
4442 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4443 &priv->msa_mem_size);
4444 if (ret || priv->msa_mem_size == 0) {
4445 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4446 priv->msa_mem_size, ret);
4447 goto out;
4448 }
4449
4450 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4451 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4452
4453 if (!priv->msa_va) {
4454 icnss_pr_err("DMA alloc failed for MSA\n");
4455 ret = -ENOMEM;
4456 goto out;
4457 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004458 }
4459
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304460 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4461 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004462
4463 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4464 "smmu_iova_base");
4465 if (!res) {
4466 icnss_pr_err("SMMU IOVA base not found\n");
4467 } else {
4468 priv->smmu_iova_start = res->start;
4469 priv->smmu_iova_len = resource_size(res);
4470 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4471 &priv->smmu_iova_start, priv->smmu_iova_len);
4472
4473 res = platform_get_resource_byname(pdev,
4474 IORESOURCE_MEM,
4475 "smmu_iova_ipa");
4476 if (!res) {
4477 icnss_pr_err("SMMU IOVA IPA not found\n");
4478 } else {
4479 priv->smmu_iova_ipa_start = res->start;
4480 priv->smmu_iova_ipa_len = resource_size(res);
4481 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4482 &priv->smmu_iova_ipa_start,
4483 priv->smmu_iova_ipa_len);
4484 }
4485
4486 ret = icnss_smmu_init(priv);
4487 if (ret < 0) {
4488 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4489 ret, &priv->smmu_iova_start,
4490 priv->smmu_iova_len);
4491 goto out;
4492 }
4493 }
4494
4495 spin_lock_init(&priv->event_lock);
4496 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304497 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004498
4499 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4500 if (!priv->event_wq) {
4501 icnss_pr_err("Workqueue creation failed\n");
4502 ret = -EFAULT;
4503 goto out_smmu_deinit;
4504 }
4505
4506 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4507 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4508 INIT_LIST_HEAD(&priv->event_list);
4509
4510 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4511 WLFW_SERVICE_VERS_V01,
4512 WLFW_SERVICE_INS_ID_V01,
4513 &wlfw_clnt_nb);
4514 if (ret < 0) {
4515 icnss_pr_err("Notifier register failed: %d\n", ret);
4516 goto out_destroy_wq;
4517 }
4518
4519 icnss_enable_recovery(priv);
4520
4521 icnss_debugfs_create(priv);
4522
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004523 ret = device_init_wakeup(&priv->pdev->dev, true);
4524 if (ret)
4525 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4526 ret);
4527
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004528 penv = priv;
4529
4530 icnss_pr_info("Platform driver probed successfully\n");
4531
4532 return 0;
4533
4534out_destroy_wq:
4535 destroy_workqueue(priv->event_wq);
4536out_smmu_deinit:
4537 icnss_smmu_deinit(priv);
4538out:
4539 dev_set_drvdata(dev, NULL);
4540
4541 return ret;
4542}
4543
4544static int icnss_remove(struct platform_device *pdev)
4545{
4546 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4547
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004548 device_init_wakeup(&penv->pdev->dev, false);
4549
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004550 icnss_debugfs_destroy(penv);
4551
4552 icnss_modem_ssr_unregister_notifier(penv);
4553
4554 destroy_ramdump_device(penv->msa0_dump_dev);
4555
4556 icnss_pdr_unregister_notifier(penv);
4557
4558 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4559 WLFW_SERVICE_VERS_V01,
4560 WLFW_SERVICE_INS_ID_V01,
4561 &wlfw_clnt_nb);
4562 if (penv->event_wq)
4563 destroy_workqueue(penv->event_wq);
4564
4565 icnss_hw_power_off(penv);
4566
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304567 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4568 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004569
4570 dev_set_drvdata(&pdev->dev, NULL);
4571
4572 return 0;
4573}
4574
4575#ifdef CONFIG_PM_SLEEP
4576static int icnss_pm_suspend(struct device *dev)
4577{
4578 struct icnss_priv *priv = dev_get_drvdata(dev);
4579 int ret = 0;
4580
4581 if (priv->magic != ICNSS_MAGIC) {
4582 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4583 dev, priv, priv->magic);
4584 return -EINVAL;
4585 }
4586
Yuanyuan Liu68939762017-04-04 16:43:03 -07004587 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004588
4589 if (!priv->ops || !priv->ops->pm_suspend ||
4590 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4591 goto out;
4592
4593 ret = priv->ops->pm_suspend(dev);
4594
4595out:
4596 if (ret == 0) {
4597 priv->stats.pm_suspend++;
4598 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4599 } else {
4600 priv->stats.pm_suspend_err++;
4601 }
4602 return ret;
4603}
4604
4605static int icnss_pm_resume(struct device *dev)
4606{
4607 struct icnss_priv *priv = dev_get_drvdata(dev);
4608 int ret = 0;
4609
4610 if (priv->magic != ICNSS_MAGIC) {
4611 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4612 dev, priv, priv->magic);
4613 return -EINVAL;
4614 }
4615
Yuanyuan Liu68939762017-04-04 16:43:03 -07004616 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004617
4618 if (!priv->ops || !priv->ops->pm_resume ||
4619 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4620 goto out;
4621
4622 ret = priv->ops->pm_resume(dev);
4623
4624out:
4625 if (ret == 0) {
4626 priv->stats.pm_resume++;
4627 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4628 } else {
4629 priv->stats.pm_resume_err++;
4630 }
4631 return ret;
4632}
4633
4634static int icnss_pm_suspend_noirq(struct device *dev)
4635{
4636 struct icnss_priv *priv = dev_get_drvdata(dev);
4637 int ret = 0;
4638
4639 if (priv->magic != ICNSS_MAGIC) {
4640 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4641 dev, priv, priv->magic);
4642 return -EINVAL;
4643 }
4644
Yuanyuan Liu68939762017-04-04 16:43:03 -07004645 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004646
4647 if (!priv->ops || !priv->ops->suspend_noirq ||
4648 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4649 goto out;
4650
4651 ret = priv->ops->suspend_noirq(dev);
4652
4653out:
4654 if (ret == 0) {
4655 priv->stats.pm_suspend_noirq++;
4656 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4657 } else {
4658 priv->stats.pm_suspend_noirq_err++;
4659 }
4660 return ret;
4661}
4662
4663static int icnss_pm_resume_noirq(struct device *dev)
4664{
4665 struct icnss_priv *priv = dev_get_drvdata(dev);
4666 int ret = 0;
4667
4668 if (priv->magic != ICNSS_MAGIC) {
4669 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4670 dev, priv, priv->magic);
4671 return -EINVAL;
4672 }
4673
Yuanyuan Liu68939762017-04-04 16:43:03 -07004674 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004675
4676 if (!priv->ops || !priv->ops->resume_noirq ||
4677 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4678 goto out;
4679
4680 ret = priv->ops->resume_noirq(dev);
4681
4682out:
4683 if (ret == 0) {
4684 priv->stats.pm_resume_noirq++;
4685 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4686 } else {
4687 priv->stats.pm_resume_noirq_err++;
4688 }
4689 return ret;
4690}
4691#endif
4692
4693static const struct dev_pm_ops icnss_pm_ops = {
4694 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4695 icnss_pm_resume)
4696 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4697 icnss_pm_resume_noirq)
4698};
4699
4700static const struct of_device_id icnss_dt_match[] = {
4701 {.compatible = "qcom,icnss"},
4702 {}
4703};
4704
4705MODULE_DEVICE_TABLE(of, icnss_dt_match);
4706
4707static struct platform_driver icnss_driver = {
4708 .probe = icnss_probe,
4709 .remove = icnss_remove,
4710 .driver = {
4711 .name = "icnss",
4712 .pm = &icnss_pm_ops,
4713 .owner = THIS_MODULE,
4714 .of_match_table = icnss_dt_match,
4715 },
4716};
4717
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004718static int __init icnss_initialize(void)
4719{
4720 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4721 "icnss", 0);
4722 if (!icnss_ipc_log_context)
4723 icnss_pr_err("Unable to create log context\n");
4724
Yuanyuan Liu68939762017-04-04 16:43:03 -07004725 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4726 "icnss_long", 0);
4727 if (!icnss_ipc_log_long_context)
4728 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004729
4730 return platform_driver_register(&icnss_driver);
4731}
4732
4733static void __exit icnss_exit(void)
4734{
4735 platform_driver_unregister(&icnss_driver);
4736 ipc_log_context_destroy(icnss_ipc_log_context);
4737 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004738 ipc_log_context_destroy(icnss_ipc_log_long_context);
4739 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004740}
4741
4742
4743module_init(icnss_initialize);
4744module_exit(icnss_exit);
4745
4746MODULE_LICENSE("GPL v2");
4747MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");