blob: 6f0c38d069c0fb1ce9acabe2a933e2da449747c7 [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
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -070076#define ICNSS_MAX_PROBE_CNT 2
77
Yuanyuan Liu607051c2016-11-28 17:04:13 -080078#define icnss_ipc_log_string(_x...) do { \
79 if (icnss_ipc_log_context) \
80 ipc_log_string(icnss_ipc_log_context, _x); \
81 } while (0)
82
Yuanyuan Liu607051c2016-11-28 17:04:13 -080083#define icnss_ipc_log_long_string(_x...) do { \
84 if (icnss_ipc_log_long_context) \
85 ipc_log_string(icnss_ipc_log_long_context, _x); \
86 } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -080087
88#define icnss_pr_err(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070089 printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__); \
90 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
91 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080092 } while (0)
93
94#define icnss_pr_warn(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070095 printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__); \
96 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
97 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080098 } while (0)
99
100#define icnss_pr_info(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700101 printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__); \
102 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
103 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800104 } while (0)
105
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700106#if defined(CONFIG_DYNAMIC_DEBUG)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800107#define icnss_pr_dbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700108 pr_debug(_fmt, ##__VA_ARGS__); \
109 icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800110 } while (0)
111
Yuanyuan Liu68939762017-04-04 16:43:03 -0700112#define icnss_pr_vdbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700113 pr_debug(_fmt, ##__VA_ARGS__); \
114 icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800115 } while (0)
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700116#elif defined(DEBUG)
117#define icnss_pr_dbg(_fmt, ...) do { \
118 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
119 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
120 ##__VA_ARGS__); \
121 } while (0)
122
123#define icnss_pr_vdbg(_fmt, ...) do { \
124 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
125 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
126 ##__VA_ARGS__); \
127 } while (0)
128#else
129#define icnss_pr_dbg(_fmt, ...) do { \
130 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
131 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
132 ##__VA_ARGS__); \
133 } while (0)
134
135#define icnss_pr_vdbg(_fmt, ...) do { \
136 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
137 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
138 ##__VA_ARGS__); \
139 } while (0)
140#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800141
142#ifdef CONFIG_ICNSS_DEBUG
143#define ICNSS_ASSERT(_condition) do { \
144 if (!(_condition)) { \
Yuanyuan Liu68939762017-04-04 16:43:03 -0700145 icnss_pr_err("ASSERT at line %d\n", __LINE__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800146 BUG_ON(1); \
147 } \
148 } while (0)
Yuanyuan Liu68939762017-04-04 16:43:03 -0700149
150bool ignore_qmi_timeout;
151#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800152#else
Yuanyuan Liu68939762017-04-04 16:43:03 -0700153#define ICNSS_ASSERT(_condition) do { } while (0)
154#define ICNSS_QMI_ASSERT() do { } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800155#endif
156
Anurag Chouhan52a48a12017-09-08 18:40:14 +0530157#define QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED 0x77
158
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800159enum icnss_debug_quirks {
160 HW_ALWAYS_ON,
161 HW_DEBUG_ENABLE,
162 SKIP_QMI,
163 HW_ONLY_TOP_LEVEL_RESET,
164 RECOVERY_DISABLE,
165 SSR_ONLY,
166 PDR_ONLY,
167 VBATT_DISABLE,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800168 FW_REJUVENATE_ENABLE,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800169};
170
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800171#define ICNSS_QUIRKS_DEFAULT (BIT(VBATT_DISABLE) | \
172 BIT(FW_REJUVENATE_ENABLE))
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800173
174unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
175module_param(quirks, ulong, 0600);
176
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800177uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01;
178module_param(dynamic_feature_mask, ullong, 0600);
179
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800180void *icnss_ipc_log_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800181void *icnss_ipc_log_long_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800182
183#define ICNSS_EVENT_PENDING 2989
184
185#define ICNSS_EVENT_SYNC BIT(0)
186#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
187#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
188 ICNSS_EVENT_SYNC)
189
190enum icnss_driver_event_type {
191 ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
192 ICNSS_DRIVER_EVENT_SERVER_EXIT,
193 ICNSS_DRIVER_EVENT_FW_READY_IND,
194 ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
195 ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
196 ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
197 ICNSS_DRIVER_EVENT_MAX,
198};
199
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530200enum icnss_msa_perm {
201 ICNSS_MSA_PERM_HLOS_ALL = 0,
202 ICNSS_MSA_PERM_WLAN_HW_RW = 1,
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530203 ICNSS_MSA_PERM_MAX,
204};
205
206#define ICNSS_MAX_VMIDS 4
207
208struct icnss_mem_region_info {
209 uint64_t reg_addr;
210 uint32_t size;
211 uint8_t secure_flag;
212 enum icnss_msa_perm perm;
213};
214
215struct icnss_msa_perm_list_t {
216 int vmids[ICNSS_MAX_VMIDS];
217 int perms[ICNSS_MAX_VMIDS];
218 int nelems;
219};
220
221struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
222 [ICNSS_MSA_PERM_HLOS_ALL] = {
223 .vmids = {VMID_HLOS},
224 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
225 .nelems = 1,
226 },
227
228 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
229 .vmids = {VMID_MSS_MSA, VMID_WLAN},
230 .perms = {PERM_READ | PERM_WRITE,
231 PERM_READ | PERM_WRITE},
232 .nelems = 2,
233 },
234
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530235};
236
237struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
238 [ICNSS_MSA_PERM_HLOS_ALL] = {
239 .vmids = {VMID_HLOS},
240 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
241 .nelems = 1,
242 },
243
244 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
245 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
246 .perms = {PERM_READ | PERM_WRITE,
247 PERM_READ | PERM_WRITE,
248 PERM_READ | PERM_WRITE},
249 .nelems = 3,
250 },
251
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530252};
253
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800254struct icnss_event_pd_service_down_data {
255 bool crashed;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800256 bool fw_rejuvenate;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800257};
258
259struct icnss_driver_event {
260 struct list_head list;
261 enum icnss_driver_event_type type;
262 bool sync;
263 struct completion complete;
264 int ret;
265 void *data;
266};
267
268enum icnss_driver_state {
269 ICNSS_WLFW_QMI_CONNECTED,
270 ICNSS_POWER_ON,
271 ICNSS_FW_READY,
272 ICNSS_DRIVER_PROBED,
273 ICNSS_FW_TEST_MODE,
274 ICNSS_PM_SUSPEND,
275 ICNSS_PM_SUSPEND_NOIRQ,
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -0700276 ICNSS_SSR_REGISTERED,
277 ICNSS_PDR_REGISTERED,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800278 ICNSS_PD_RESTART,
279 ICNSS_MSA0_ASSIGNED,
280 ICNSS_WLFW_EXISTS,
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +0530281 ICNSS_SHUTDOWN_DONE,
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700282 ICNSS_HOST_TRIGGERED_PDR,
Sameer Thalappil93c00732017-08-18 13:02:32 -0700283 ICNSS_FW_DOWN,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800284};
285
286struct ce_irq_list {
287 int irq;
288 irqreturn_t (*handler)(int, void *);
289};
290
Yuanyuan Liu68939762017-04-04 16:43:03 -0700291struct icnss_vreg_info {
292 struct regulator *reg;
293 const char *name;
294 u32 min_v;
295 u32 max_v;
296 u32 load_ua;
297 unsigned long settle_delay;
298 bool required;
299};
300
301struct icnss_clk_info {
302 struct clk *handle;
303 const char *name;
304 u32 freq;
305 bool required;
306};
307
308static struct icnss_vreg_info icnss_vreg_info[] = {
309 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
310 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
311 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
312 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
313};
314
315#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
316
317static struct icnss_clk_info icnss_clk_info[] = {
318 {NULL, "cxo_ref_clk_pin", 0, false},
319};
320
321#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
322
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800323struct icnss_stats {
324 struct {
325 uint32_t posted;
326 uint32_t processed;
327 } events[ICNSS_DRIVER_EVENT_MAX];
328
329 struct {
330 uint32_t request;
331 uint32_t free;
332 uint32_t enable;
333 uint32_t disable;
334 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
335
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700336 struct {
337 uint32_t pdr_fw_crash;
338 uint32_t pdr_host_error;
339 uint32_t root_pd_crash;
340 uint32_t root_pd_shutdown;
341 } recovery;
342
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800343 uint32_t pm_suspend;
344 uint32_t pm_suspend_err;
345 uint32_t pm_resume;
346 uint32_t pm_resume_err;
347 uint32_t pm_suspend_noirq;
348 uint32_t pm_suspend_noirq_err;
349 uint32_t pm_resume_noirq;
350 uint32_t pm_resume_noirq_err;
351 uint32_t pm_stay_awake;
352 uint32_t pm_relax;
353
354 uint32_t ind_register_req;
355 uint32_t ind_register_resp;
356 uint32_t ind_register_err;
357 uint32_t msa_info_req;
358 uint32_t msa_info_resp;
359 uint32_t msa_info_err;
360 uint32_t msa_ready_req;
361 uint32_t msa_ready_resp;
362 uint32_t msa_ready_err;
363 uint32_t msa_ready_ind;
364 uint32_t cap_req;
365 uint32_t cap_resp;
366 uint32_t cap_err;
367 uint32_t pin_connect_result;
368 uint32_t cfg_req;
369 uint32_t cfg_resp;
370 uint32_t cfg_req_err;
371 uint32_t mode_req;
372 uint32_t mode_resp;
373 uint32_t mode_req_err;
374 uint32_t ini_req;
375 uint32_t ini_resp;
376 uint32_t ini_req_err;
377 uint32_t vbatt_req;
378 uint32_t vbatt_resp;
379 uint32_t vbatt_req_err;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800380 u32 rejuvenate_ind;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800381 uint32_t rejuvenate_ack_req;
382 uint32_t rejuvenate_ack_resp;
383 uint32_t rejuvenate_ack_err;
384};
385
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700386enum icnss_pdr_cause_index {
387 ICNSS_FW_CRASH,
388 ICNSS_ROOT_PD_CRASH,
389 ICNSS_ROOT_PD_SHUTDOWN,
390 ICNSS_HOST_ERROR,
391};
392
393static const char * const icnss_pdr_cause[] = {
394 [ICNSS_FW_CRASH] = "FW crash",
395 [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
396 [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
397 [ICNSS_HOST_ERROR] = "Host error",
398};
399
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800400struct service_notifier_context {
401 void *handle;
402 uint32_t instance_id;
403 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800404};
405
406static struct icnss_priv {
407 uint32_t magic;
408 struct platform_device *pdev;
409 struct icnss_driver_ops *ops;
410 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700411 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
412 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800413 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
414 phys_addr_t mem_base_pa;
415 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800416 struct dma_iommu_mapping *smmu_mapping;
417 dma_addr_t smmu_iova_start;
418 size_t smmu_iova_len;
419 dma_addr_t smmu_iova_ipa_start;
420 size_t smmu_iova_ipa_len;
421 struct qmi_handle *wlfw_clnt;
422 struct list_head event_list;
423 spinlock_t event_lock;
424 struct work_struct event_work;
425 struct work_struct qmi_recv_msg_work;
426 struct workqueue_struct *event_wq;
427 phys_addr_t msa_pa;
428 uint32_t msa_mem_size;
429 void *msa_va;
430 unsigned long state;
431 struct wlfw_rf_chip_info_s_v01 chip_info;
432 struct wlfw_rf_board_info_s_v01 board_info;
433 struct wlfw_soc_info_s_v01 soc_info;
434 struct wlfw_fw_version_info_s_v01 fw_version_info;
435 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
436 u32 pwr_pin_result;
437 u32 phy_io_pin_result;
438 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700439 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800440 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700441 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800442 struct dentry *root_dentry;
443 spinlock_t on_off_lock;
444 struct icnss_stats stats;
445 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800446 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800447 struct notifier_block service_notifier_nb;
448 int total_domains;
449 struct notifier_block get_service_nb;
450 void *modem_notify_handler;
451 struct notifier_block modem_ssr_nb;
452 uint32_t diag_reg_read_addr;
453 uint32_t diag_reg_read_mem_type;
454 uint32_t diag_reg_read_len;
455 uint8_t *diag_reg_read_buf;
456 struct qpnp_adc_tm_btm_param vph_monitor_params;
457 struct qpnp_adc_tm_chip *adc_tm_dev;
458 struct qpnp_vadc_chip *vadc_dev;
459 uint64_t vph_pwr;
460 atomic_t pm_count;
461 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700462 bool bypass_s1_smmu;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800463 u8 cause_for_rejuvenation;
464 u8 requesting_sub_system;
465 u16 line_number;
466 char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +0530467 struct mutex dev_lock;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800468} *penv;
469
Yuanyuan Liu68939762017-04-04 16:43:03 -0700470#ifdef CONFIG_ICNSS_DEBUG
471static void icnss_ignore_qmi_timeout(bool ignore)
472{
473 ignore_qmi_timeout = ignore;
474}
475#else
476static void icnss_ignore_qmi_timeout(bool ignore) { }
477#endif
478
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530479static int icnss_assign_msa_perm(struct icnss_mem_region_info
480 *mem_region, enum icnss_msa_perm new_perm)
481{
482 int ret = 0;
483 phys_addr_t addr;
484 u32 size;
485 u32 i = 0;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530486 u32 source_vmids[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530487 u32 source_nelems;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530488 u32 dest_vmids[ICNSS_MAX_VMIDS] = {0};
489 u32 dest_perms[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530490 u32 dest_nelems;
491 enum icnss_msa_perm cur_perm = mem_region->perm;
492 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
493
494 addr = mem_region->reg_addr;
495 size = mem_region->size;
496
497 if (mem_region->secure_flag) {
498 new_perm_list = &msa_perm_secure_list[new_perm];
499 old_perm_list = &msa_perm_secure_list[cur_perm];
500 } else {
501 new_perm_list = &msa_perm_list[new_perm];
502 old_perm_list = &msa_perm_list[cur_perm];
503 }
504
505 source_nelems = old_perm_list->nelems;
506 dest_nelems = new_perm_list->nelems;
507
508 for (i = 0; i < source_nelems; ++i)
509 source_vmids[i] = old_perm_list->vmids[i];
510
511 for (i = 0; i < dest_nelems; ++i) {
512 dest_vmids[i] = new_perm_list->vmids[i];
513 dest_perms[i] = new_perm_list->perms[i];
514 }
515
516 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
517 dest_vmids, dest_perms, dest_nelems);
518 if (ret) {
519 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
520 &addr, size, ret);
521 goto out;
522 }
523
524 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
525 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
526 source_nelems, source_vmids[0], source_vmids[1],
527 source_vmids[2], source_vmids[3], dest_nelems,
528 dest_vmids[0], dest_vmids[1], dest_vmids[2],
529 dest_vmids[3]);
530out:
531 return ret;
532}
533
534static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
535 enum icnss_msa_perm new_perm)
536{
537 int ret;
538 int i;
539 enum icnss_msa_perm old_perm;
540
Yuanyuan Liu26ec2212017-09-01 10:34:25 -0700541 if (priv->nr_mem_region > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
542 icnss_pr_err("Invalid memory region len %d\n",
543 priv->nr_mem_region);
544 return -EINVAL;
545 }
546
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530547 for (i = 0; i < priv->nr_mem_region; i++) {
548 old_perm = priv->mem_region[i].perm;
549 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
550 if (ret)
551 goto err_unmap;
552 priv->mem_region[i].perm = new_perm;
553 }
554 return 0;
555
556err_unmap:
557 for (i--; i >= 0; i--) {
558 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
559 }
560 return ret;
561}
562
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800563static void icnss_pm_stay_awake(struct icnss_priv *priv)
564{
565 if (atomic_inc_return(&priv->pm_count) != 1)
566 return;
567
Yuanyuan Liu68939762017-04-04 16:43:03 -0700568 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800569 atomic_read(&priv->pm_count));
570
571 pm_stay_awake(&priv->pdev->dev);
572
573 priv->stats.pm_stay_awake++;
574}
575
576static void icnss_pm_relax(struct icnss_priv *priv)
577{
578 int r = atomic_dec_return(&priv->pm_count);
579
580 WARN_ON(r < 0);
581
582 if (r != 0)
583 return;
584
Yuanyuan Liu68939762017-04-04 16:43:03 -0700585 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800586 atomic_read(&priv->pm_count));
587
588 pm_relax(&priv->pdev->dev);
589 priv->stats.pm_relax++;
590}
591
592static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
593{
594 switch (type) {
595 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
596 return "SERVER_ARRIVE";
597 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
598 return "SERVER_EXIT";
599 case ICNSS_DRIVER_EVENT_FW_READY_IND:
600 return "FW_READY";
601 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
602 return "REGISTER_DRIVER";
603 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
604 return "UNREGISTER_DRIVER";
605 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
606 return "PD_SERVICE_DOWN";
607 case ICNSS_DRIVER_EVENT_MAX:
608 return "EVENT_MAX";
609 }
610
611 return "UNKNOWN";
612};
613
614static int icnss_driver_event_post(enum icnss_driver_event_type type,
615 u32 flags, void *data)
616{
617 struct icnss_driver_event *event;
618 unsigned long irq_flags;
619 int gfp = GFP_KERNEL;
620 int ret = 0;
621
622 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
623 icnss_driver_event_to_str(type), type, current->comm,
624 flags, penv->state);
625
626 if (type >= ICNSS_DRIVER_EVENT_MAX) {
627 icnss_pr_err("Invalid Event type: %d, can't post", type);
628 return -EINVAL;
629 }
630
631 if (in_interrupt() || irqs_disabled())
632 gfp = GFP_ATOMIC;
633
634 event = kzalloc(sizeof(*event), gfp);
635 if (event == NULL)
636 return -ENOMEM;
637
638 icnss_pm_stay_awake(penv);
639
640 event->type = type;
641 event->data = data;
642 init_completion(&event->complete);
643 event->ret = ICNSS_EVENT_PENDING;
644 event->sync = !!(flags & ICNSS_EVENT_SYNC);
645
646 spin_lock_irqsave(&penv->event_lock, irq_flags);
647 list_add_tail(&event->list, &penv->event_list);
648 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
649
650 penv->stats.events[type].posted++;
651 queue_work(penv->event_wq, &penv->event_work);
652
653 if (!(flags & ICNSS_EVENT_SYNC))
654 goto out;
655
656 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
657 wait_for_completion(&event->complete);
658 else
659 ret = wait_for_completion_interruptible(&event->complete);
660
661 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
662 icnss_driver_event_to_str(type), type, penv->state, ret,
663 event->ret);
664
665 spin_lock_irqsave(&penv->event_lock, irq_flags);
666 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
667 event->sync = false;
668 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
669 ret = -EINTR;
670 goto out;
671 }
672 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
673
674 ret = event->ret;
675 kfree(event);
676
677out:
678 icnss_pm_relax(penv);
679 return ret;
680}
681
682static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
683 uint64_t voltage_uv)
684{
685 int ret;
686 struct wlfw_vbatt_req_msg_v01 req;
687 struct wlfw_vbatt_resp_msg_v01 resp;
688 struct msg_desc req_desc, resp_desc;
689
690 if (!priv->wlfw_clnt) {
691 ret = -ENODEV;
692 goto out;
693 }
694
695 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
696 penv->state);
697
698 memset(&req, 0, sizeof(req));
699 memset(&resp, 0, sizeof(resp));
700
701 req.voltage_uv = voltage_uv;
702
703 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
704 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
705 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
706
707 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
708 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
709 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
710
711 priv->stats.vbatt_req++;
712
713 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
714 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
715 if (ret < 0) {
716 icnss_pr_err("Send vbatt req failed %d\n", ret);
717 goto out;
718 }
719
720 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
721 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
722 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +0530723 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800724 goto out;
725 }
726 priv->stats.vbatt_resp++;
727
728out:
729 priv->stats.vbatt_req_err++;
730 return ret;
731}
732
733static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
734{
735 int ret = 0;
736 struct qpnp_vadc_result adc_result;
737
738 if (!priv->vadc_dev) {
739 icnss_pr_err("VADC dev doesn't exists\n");
740 ret = -EINVAL;
741 goto out;
742 }
743
744 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
745 if (ret) {
746 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
747 VADC_VPH_PWR, ret);
748 goto out;
749 }
750
751 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
752 adc_result.physical, adc_result.measurement);
753
754 *result_uv = adc_result.physical;
755out:
756 return ret;
757}
758
759static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
760{
761 struct icnss_priv *priv = ctx;
762 uint64_t vph_pwr = 0;
763 uint64_t vph_pwr_prev;
764 int ret = 0;
765 bool update = true;
766
767 if (!priv) {
768 icnss_pr_err("Priv pointer is NULL\n");
769 return;
770 }
771
772 vph_pwr_prev = priv->vph_pwr;
773
774 ret = icnss_get_phone_power(priv, &vph_pwr);
775 if (ret)
776 return;
777
778 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
779 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
780 update = false;
781 priv->vph_monitor_params.state_request =
782 ADC_TM_HIGH_THR_ENABLE;
783 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
784 ICNSS_THRESHOLD_GUARD;
785 priv->vph_monitor_params.low_thr = 0;
786 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
787 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
788 update = false;
789 priv->vph_monitor_params.state_request =
790 ADC_TM_LOW_THR_ENABLE;
791 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
792 ICNSS_THRESHOLD_GUARD;
793 priv->vph_monitor_params.high_thr = 0;
794 } else {
795 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
796 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
797 update = false;
798 priv->vph_monitor_params.state_request =
799 ADC_TM_HIGH_LOW_THR_ENABLE;
800 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
801 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
802 }
803
804 priv->vph_pwr = vph_pwr;
805
806 if (update)
807 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
808
809 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
810 priv->vph_monitor_params.low_thr,
811 priv->vph_monitor_params.high_thr);
812 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
813 &priv->vph_monitor_params);
814 if (ret)
815 icnss_pr_err("TM channel setup failed %d\n", ret);
816}
817
818static int icnss_setup_vph_monitor(struct icnss_priv *priv)
819{
820 int ret = 0;
821
822 if (!priv->adc_tm_dev) {
823 icnss_pr_err("ADC TM handler is NULL\n");
824 ret = -EINVAL;
825 goto out;
826 }
827
828 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
829 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
830 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
831 priv->vph_monitor_params.channel = VADC_VPH_PWR;
832 priv->vph_monitor_params.btm_ctx = priv;
833 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
834 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
835 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
836 priv->vph_monitor_params.low_thr,
837 priv->vph_monitor_params.high_thr);
838
839 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
840 &priv->vph_monitor_params);
841 if (ret)
842 icnss_pr_err("TM channel setup failed %d\n", ret);
843out:
844 return ret;
845}
846
847static int icnss_init_vph_monitor(struct icnss_priv *priv)
848{
849 int ret = 0;
850
851 if (test_bit(VBATT_DISABLE, &quirks))
852 goto out;
853
854 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
855 if (ret)
856 goto out;
857
858 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
859
860 ret = icnss_setup_vph_monitor(priv);
861 if (ret)
862 goto out;
863out:
864 return ret;
865}
866
867
868static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
869{
870 struct msg_desc ind_desc;
871 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
872 int ret = 0;
873
874 if (!penv || !penv->wlfw_clnt) {
875 ret = -ENODEV;
876 goto out;
877 }
878
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530879 memset(&ind_msg, 0, sizeof(ind_msg));
880
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800881 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
882 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
883 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
884
885 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
886 if (ret < 0) {
887 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
888 ret, msg_len);
889 goto out;
890 }
891
892 /* store pin result locally */
893 if (ind_msg.pwr_pin_result_valid)
894 penv->pwr_pin_result = ind_msg.pwr_pin_result;
895 if (ind_msg.phy_io_pin_result_valid)
896 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
897 if (ind_msg.rf_pin_result_valid)
898 penv->rf_pin_result = ind_msg.rf_pin_result;
899
900 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
901 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
902 ind_msg.rf_pin_result);
903
904 penv->stats.pin_connect_result++;
905out:
906 return ret;
907}
908
Yuanyuan Liu68939762017-04-04 16:43:03 -0700909static int icnss_vreg_on(struct icnss_priv *priv)
910{
911 int ret = 0;
912 struct icnss_vreg_info *vreg_info;
913 int i;
914
915 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
916 vreg_info = &priv->vreg_info[i];
917
918 if (!vreg_info->reg)
919 continue;
920
921 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
922
923 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
924 vreg_info->max_v);
925 if (ret) {
926 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
927 vreg_info->name, vreg_info->min_v,
928 vreg_info->max_v, ret);
929 break;
930 }
931
932 if (vreg_info->load_ua) {
933 ret = regulator_set_load(vreg_info->reg,
934 vreg_info->load_ua);
935 if (ret < 0) {
936 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
937 vreg_info->name,
938 vreg_info->load_ua, ret);
939 break;
940 }
941 }
942
943 ret = regulator_enable(vreg_info->reg);
944 if (ret) {
945 icnss_pr_err("Regulator %s, can't enable: %d\n",
946 vreg_info->name, ret);
947 break;
948 }
949
950 if (vreg_info->settle_delay)
951 udelay(vreg_info->settle_delay);
952 }
953
954 if (!ret)
955 return 0;
956
957 for (; i >= 0; i--) {
958 vreg_info = &priv->vreg_info[i];
959
960 if (!vreg_info->reg)
961 continue;
962
963 regulator_disable(vreg_info->reg);
964 regulator_set_load(vreg_info->reg, 0);
965 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
966 }
967
968 return ret;
969}
970
971static int icnss_vreg_off(struct icnss_priv *priv)
972{
973 int ret = 0;
974 struct icnss_vreg_info *vreg_info;
975 int i;
976
977 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
978 vreg_info = &priv->vreg_info[i];
979
980 if (!vreg_info->reg)
981 continue;
982
983 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
984
985 ret = regulator_disable(vreg_info->reg);
986 if (ret)
987 icnss_pr_err("Regulator %s, can't disable: %d\n",
988 vreg_info->name, ret);
989
990 ret = regulator_set_load(vreg_info->reg, 0);
991 if (ret < 0)
992 icnss_pr_err("Regulator %s, can't set load: %d\n",
993 vreg_info->name, ret);
994
995 ret = regulator_set_voltage(vreg_info->reg, 0,
996 vreg_info->max_v);
997 if (ret)
998 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
999 vreg_info->name, ret);
1000 }
1001
1002 return ret;
1003}
1004
1005static int icnss_clk_init(struct icnss_priv *priv)
1006{
1007 struct icnss_clk_info *clk_info;
1008 int i;
1009 int ret = 0;
1010
1011 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1012 clk_info = &priv->clk_info[i];
1013
1014 if (!clk_info->handle)
1015 continue;
1016
1017 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1018
1019 if (clk_info->freq) {
1020 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1021
1022 if (ret) {
1023 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1024 clk_info->name, clk_info->freq,
1025 ret);
1026 break;
1027 }
1028 }
1029
1030 ret = clk_prepare_enable(clk_info->handle);
1031 if (ret) {
1032 icnss_pr_err("Clock %s, can't enable: %d\n",
1033 clk_info->name, ret);
1034 break;
1035 }
1036 }
1037
1038 if (ret == 0)
1039 return 0;
1040
1041 for (; i >= 0; i--) {
1042 clk_info = &priv->clk_info[i];
1043
1044 if (!clk_info->handle)
1045 continue;
1046
1047 clk_disable_unprepare(clk_info->handle);
1048 }
1049
1050 return ret;
1051}
1052
1053static int icnss_clk_deinit(struct icnss_priv *priv)
1054{
1055 struct icnss_clk_info *clk_info;
1056 int i;
1057
1058 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1059 clk_info = &priv->clk_info[i];
1060
1061 if (!clk_info->handle)
1062 continue;
1063
1064 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1065
1066 clk_disable_unprepare(clk_info->handle);
1067 }
1068
1069 return 0;
1070}
1071
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001072static int icnss_hw_power_on(struct icnss_priv *priv)
1073{
1074 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001075
1076 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1077
Yuanyuan Liu68939762017-04-04 16:43:03 -07001078 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001079 if (test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001080 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001081 return ret;
1082 }
1083 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001084 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001085
Yuanyuan Liu68939762017-04-04 16:43:03 -07001086 ret = icnss_vreg_on(priv);
1087 if (ret)
1088 goto out;
1089
1090 ret = icnss_clk_init(priv);
1091 if (ret)
1092 goto vreg_off;
1093
1094 return ret;
1095
1096vreg_off:
1097 icnss_vreg_off(priv);
1098out:
1099 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001100 return ret;
1101}
1102
1103static int icnss_hw_power_off(struct icnss_priv *priv)
1104{
1105 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001106
1107 if (test_bit(HW_ALWAYS_ON, &quirks))
1108 return 0;
1109
1110 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1111
Yuanyuan Liu68939762017-04-04 16:43:03 -07001112 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001113 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001114 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001115 return ret;
1116 }
1117 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001118 spin_unlock(&priv->on_off_lock);
1119
1120 icnss_clk_deinit(priv);
1121
1122 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001123
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001124 return ret;
1125}
1126
1127int icnss_power_on(struct device *dev)
1128{
1129 struct icnss_priv *priv = dev_get_drvdata(dev);
1130
1131 if (!priv) {
1132 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1133 dev, priv);
1134 return -EINVAL;
1135 }
1136
1137 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1138
1139 return icnss_hw_power_on(priv);
1140}
1141EXPORT_SYMBOL(icnss_power_on);
1142
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001143bool icnss_is_fw_ready(void)
1144{
1145 if (!penv)
1146 return false;
1147 else
1148 return test_bit(ICNSS_FW_READY, &penv->state);
1149}
1150EXPORT_SYMBOL(icnss_is_fw_ready);
1151
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001152int icnss_power_off(struct device *dev)
1153{
1154 struct icnss_priv *priv = dev_get_drvdata(dev);
1155
1156 if (!priv) {
1157 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1158 dev, priv);
1159 return -EINVAL;
1160 }
1161
1162 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1163
1164 return icnss_hw_power_off(priv);
1165}
1166EXPORT_SYMBOL(icnss_power_off);
1167
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001168static int wlfw_msa_mem_info_send_sync_msg(void)
1169{
1170 int ret;
1171 int i;
1172 struct wlfw_msa_info_req_msg_v01 req;
1173 struct wlfw_msa_info_resp_msg_v01 resp;
1174 struct msg_desc req_desc, resp_desc;
1175
1176 if (!penv || !penv->wlfw_clnt)
1177 return -ENODEV;
1178
1179 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1180
1181 memset(&req, 0, sizeof(req));
1182 memset(&resp, 0, sizeof(resp));
1183
1184 req.msa_addr = penv->msa_pa;
1185 req.size = penv->msa_mem_size;
1186
1187 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1188 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1189 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1190
1191 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1192 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1193 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1194
1195 penv->stats.msa_info_req++;
1196
1197 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1198 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1199 if (ret < 0) {
1200 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1201 goto out;
1202 }
1203
1204 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1205 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1206 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301207 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001208 goto out;
1209 }
1210
1211 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1212 resp.mem_region_info_len);
1213
Yuanyuan Liu68939762017-04-04 16:43:03 -07001214 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001215 icnss_pr_err("Invalid memory region length received: %d\n",
1216 resp.mem_region_info_len);
1217 ret = -EINVAL;
1218 goto out;
1219 }
1220
1221 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001222 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001223 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001224 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001225 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001226 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001227 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001228 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001229 resp.mem_region_info[i].secure_flag;
1230 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001231 i, penv->mem_region[i].reg_addr,
1232 penv->mem_region[i].size,
1233 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001234 }
1235
1236 return 0;
1237
1238out:
1239 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001240 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001241 return ret;
1242}
1243
1244static int wlfw_msa_ready_send_sync_msg(void)
1245{
1246 int ret;
1247 struct wlfw_msa_ready_req_msg_v01 req;
1248 struct wlfw_msa_ready_resp_msg_v01 resp;
1249 struct msg_desc req_desc, resp_desc;
1250
1251 if (!penv || !penv->wlfw_clnt)
1252 return -ENODEV;
1253
1254 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1255 penv->state);
1256
1257 memset(&req, 0, sizeof(req));
1258 memset(&resp, 0, sizeof(resp));
1259
1260 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1261 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1262 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1263
1264 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1265 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1266 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1267
1268 penv->stats.msa_ready_req++;
1269 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1270 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1271 if (ret < 0) {
1272 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1273 goto out;
1274 }
1275
1276 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1277 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1278 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301279 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001280 goto out;
1281 }
1282 penv->stats.msa_ready_resp++;
1283
1284 return 0;
1285
1286out:
1287 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001288 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001289 return ret;
1290}
1291
1292static int wlfw_ind_register_send_sync_msg(void)
1293{
1294 int ret;
1295 struct wlfw_ind_register_req_msg_v01 req;
1296 struct wlfw_ind_register_resp_msg_v01 resp;
1297 struct msg_desc req_desc, resp_desc;
1298
1299 if (!penv || !penv->wlfw_clnt)
1300 return -ENODEV;
1301
1302 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1303 penv->state);
1304
1305 memset(&req, 0, sizeof(req));
1306 memset(&resp, 0, sizeof(resp));
1307
1308 req.client_id_valid = 1;
1309 req.client_id = WLFW_CLIENT_ID;
1310 req.fw_ready_enable_valid = 1;
1311 req.fw_ready_enable = 1;
1312 req.msa_ready_enable_valid = 1;
1313 req.msa_ready_enable = 1;
1314 req.pin_connect_result_enable_valid = 1;
1315 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001316 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1317 req.rejuvenate_enable_valid = 1;
1318 req.rejuvenate_enable = 1;
1319 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001320
1321 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1322 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1323 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1324
1325 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1326 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1327 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1328
1329 penv->stats.ind_register_req++;
1330
1331 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1332 &resp_desc, &resp, sizeof(resp),
1333 WLFW_TIMEOUT_MS);
1334 if (ret < 0) {
1335 icnss_pr_err("Send indication register req failed %d\n", ret);
1336 goto out;
1337 }
1338
1339 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1340 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1341 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301342 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001343 goto out;
1344 }
1345 penv->stats.ind_register_resp++;
1346
1347 return 0;
1348
1349out:
1350 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001351 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001352 return ret;
1353}
1354
1355static int wlfw_cap_send_sync_msg(void)
1356{
1357 int ret;
1358 struct wlfw_cap_req_msg_v01 req;
1359 struct wlfw_cap_resp_msg_v01 resp;
1360 struct msg_desc req_desc, resp_desc;
1361
1362 if (!penv || !penv->wlfw_clnt)
1363 return -ENODEV;
1364
1365 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1366
1367 memset(&resp, 0, sizeof(resp));
1368
1369 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1370 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1371 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1372
1373 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1374 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1375 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1376
1377 penv->stats.cap_req++;
1378 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1379 &resp_desc, &resp, sizeof(resp),
1380 WLFW_TIMEOUT_MS);
1381 if (ret < 0) {
1382 icnss_pr_err("Send capability req failed %d\n", ret);
1383 goto out;
1384 }
1385
1386 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1387 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1388 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301389 ret = -resp.resp.result;
1390 if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
1391 icnss_pr_err("RF card Not present");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001392 goto out;
1393 }
1394
1395 penv->stats.cap_resp++;
1396 /* store cap locally */
1397 if (resp.chip_info_valid)
1398 penv->chip_info = resp.chip_info;
1399 if (resp.board_info_valid)
1400 penv->board_info = resp.board_info;
1401 else
1402 penv->board_info.board_id = 0xFF;
1403 if (resp.soc_info_valid)
1404 penv->soc_info = resp.soc_info;
1405 if (resp.fw_version_info_valid)
1406 penv->fw_version_info = resp.fw_version_info;
1407 if (resp.fw_build_id_valid)
1408 strlcpy(penv->fw_build_id, resp.fw_build_id,
1409 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1410
1411 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",
1412 penv->chip_info.chip_id, penv->chip_info.chip_family,
1413 penv->board_info.board_id, penv->soc_info.soc_id,
1414 penv->fw_version_info.fw_version,
1415 penv->fw_version_info.fw_build_timestamp,
1416 penv->fw_build_id);
1417
1418 return 0;
1419
1420out:
1421 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001422 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001423 return ret;
1424}
1425
1426static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1427{
1428 int ret;
1429 struct wlfw_wlan_mode_req_msg_v01 req;
1430 struct wlfw_wlan_mode_resp_msg_v01 resp;
1431 struct msg_desc req_desc, resp_desc;
1432
1433 if (!penv || !penv->wlfw_clnt)
1434 return -ENODEV;
1435
1436 /* During recovery do not send mode request for WLAN OFF as
1437 * FW not able to process it.
1438 */
1439 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1440 mode == QMI_WLFW_OFF_V01)
1441 return 0;
1442
1443 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1444 penv->state, mode);
1445
1446 memset(&req, 0, sizeof(req));
1447 memset(&resp, 0, sizeof(resp));
1448
1449 req.mode = mode;
1450 req.hw_debug_valid = 1;
1451 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1452
1453 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1454 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1455 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1456
1457 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1458 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1459 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1460
1461 penv->stats.mode_req++;
1462 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1463 &resp_desc, &resp, sizeof(resp),
1464 WLFW_TIMEOUT_MS);
1465 if (ret < 0) {
1466 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1467 mode, ret);
1468 goto out;
1469 }
1470
1471 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1472 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1473 mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301474 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001475 goto out;
1476 }
1477 penv->stats.mode_resp++;
1478
1479 return 0;
1480
1481out:
1482 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001483 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001484 return ret;
1485}
1486
1487static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1488{
1489 int ret;
1490 struct wlfw_wlan_cfg_req_msg_v01 req;
1491 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1492 struct msg_desc req_desc, resp_desc;
1493
1494 if (!penv || !penv->wlfw_clnt)
1495 return -ENODEV;
1496
1497 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1498
1499 memset(&req, 0, sizeof(req));
1500 memset(&resp, 0, sizeof(resp));
1501
1502 memcpy(&req, data, sizeof(req));
1503
1504 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1505 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1506 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1507
1508 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1509 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1510 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1511
1512 penv->stats.cfg_req++;
1513 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1514 &resp_desc, &resp, sizeof(resp),
1515 WLFW_TIMEOUT_MS);
1516 if (ret < 0) {
1517 icnss_pr_err("Send config req failed %d\n", ret);
1518 goto out;
1519 }
1520
1521 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1522 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1523 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301524 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001525 goto out;
1526 }
1527 penv->stats.cfg_resp++;
1528
1529 return 0;
1530
1531out:
1532 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001533 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001534 return ret;
1535}
1536
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001537static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001538{
1539 int ret;
1540 struct wlfw_ini_req_msg_v01 req;
1541 struct wlfw_ini_resp_msg_v01 resp;
1542 struct msg_desc req_desc, resp_desc;
1543
1544 if (!penv || !penv->wlfw_clnt)
1545 return -ENODEV;
1546
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001547 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1548 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001549
1550 memset(&req, 0, sizeof(req));
1551 memset(&resp, 0, sizeof(resp));
1552
1553 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001554 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001555
1556 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1557 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1558 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1559
1560 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1561 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1562 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1563
1564 penv->stats.ini_req++;
1565
1566 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1567 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1568 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001569 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1570 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001571 goto out;
1572 }
1573
1574 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001575 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1576 fw_log_mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301577 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001578 goto out;
1579 }
1580 penv->stats.ini_resp++;
1581
1582 return 0;
1583
1584out:
1585 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001586 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001587 return ret;
1588}
1589
1590static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1591 uint32_t offset, uint32_t mem_type,
1592 uint32_t data_len, uint8_t *data)
1593{
1594 int ret;
1595 struct wlfw_athdiag_read_req_msg_v01 req;
1596 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1597 struct msg_desc req_desc, resp_desc;
1598
1599 if (!priv->wlfw_clnt) {
1600 ret = -ENODEV;
1601 goto out;
1602 }
1603
1604 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1605 priv->state, offset, mem_type, data_len);
1606
1607 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1608 if (!resp) {
1609 ret = -ENOMEM;
1610 goto out;
1611 }
1612 memset(&req, 0, sizeof(req));
1613
1614 req.offset = offset;
1615 req.mem_type = mem_type;
1616 req.data_len = data_len;
1617
1618 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1619 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1620 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1621
1622 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1623 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1624 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1625
1626 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1627 &resp_desc, resp, sizeof(*resp),
1628 WLFW_TIMEOUT_MS);
1629 if (ret < 0) {
1630 icnss_pr_err("send athdiag read req failed %d\n", ret);
1631 goto out;
1632 }
1633
1634 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1635 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1636 resp->resp.result, resp->resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301637 ret = -resp->resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001638 goto out;
1639 }
1640
Yuanyuan Liu68939762017-04-04 16:43:03 -07001641 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001642 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1643 resp->data_valid, resp->data_len);
1644 ret = -EINVAL;
1645 goto out;
1646 }
1647
1648 memcpy(data, resp->data, resp->data_len);
1649
1650out:
1651 kfree(resp);
1652 return ret;
1653}
1654
1655static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1656 uint32_t offset, uint32_t mem_type,
1657 uint32_t data_len, uint8_t *data)
1658{
1659 int ret;
1660 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1661 struct wlfw_athdiag_write_resp_msg_v01 resp;
1662 struct msg_desc req_desc, resp_desc;
1663
1664 if (!priv->wlfw_clnt) {
1665 ret = -ENODEV;
1666 goto out;
1667 }
1668
1669 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1670 priv->state, offset, mem_type, data_len, data);
1671
1672 req = kzalloc(sizeof(*req), GFP_KERNEL);
1673 if (!req) {
1674 ret = -ENOMEM;
1675 goto out;
1676 }
1677 memset(&resp, 0, sizeof(resp));
1678
1679 req->offset = offset;
1680 req->mem_type = mem_type;
1681 req->data_len = data_len;
1682 memcpy(req->data, data, data_len);
1683
1684 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1685 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1686 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1687
1688 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1689 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1690 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1691
1692 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1693 &resp_desc, &resp, sizeof(resp),
1694 WLFW_TIMEOUT_MS);
1695 if (ret < 0) {
1696 icnss_pr_err("send athdiag write req failed %d\n", ret);
1697 goto out;
1698 }
1699
1700 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1701 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1702 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301703 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001704 goto out;
1705 }
1706out:
1707 kfree(req);
1708 return ret;
1709}
1710
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001711static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1712{
1713 struct msg_desc ind_desc;
1714 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1715 int ret = 0;
1716
1717 if (!penv || !penv->wlfw_clnt) {
1718 ret = -ENODEV;
1719 goto out;
1720 }
1721
1722 memset(&ind_msg, 0, sizeof(ind_msg));
1723
1724 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1725 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1726 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1727
1728 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1729 if (ret < 0) {
1730 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1731 ret, msg_len);
1732 goto out;
1733 }
1734
1735 if (ind_msg.cause_for_rejuvenation_valid)
1736 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1737 else
1738 penv->cause_for_rejuvenation = 0;
1739 if (ind_msg.requesting_sub_system_valid)
1740 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1741 else
1742 penv->requesting_sub_system = 0;
1743 if (ind_msg.line_number_valid)
1744 penv->line_number = ind_msg.line_number;
1745 else
1746 penv->line_number = 0;
1747 if (ind_msg.function_name_valid)
1748 memcpy(penv->function_name, ind_msg.function_name,
1749 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1750 else
1751 memset(penv->function_name, 0,
1752 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1753
1754 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1755 penv->cause_for_rejuvenation,
1756 penv->requesting_sub_system,
1757 penv->line_number,
1758 penv->function_name);
1759
1760 penv->stats.rejuvenate_ind++;
1761out:
1762 return ret;
1763}
1764
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001765static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1766{
1767 int ret;
1768 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1769 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1770 struct msg_desc req_desc, resp_desc;
1771
1772 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1773 priv->state);
1774
1775 memset(&req, 0, sizeof(req));
1776 memset(&resp, 0, sizeof(resp));
1777
1778 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1779 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1780 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1781
1782 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1783 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1784 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1785
1786 priv->stats.rejuvenate_ack_req++;
1787 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1788 &resp_desc, &resp, sizeof(resp),
1789 WLFW_TIMEOUT_MS);
1790 if (ret < 0) {
1791 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1792 goto out;
1793 }
1794
1795 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1796 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1797 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301798 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001799 goto out;
1800 }
1801 priv->stats.rejuvenate_ack_resp++;
1802 return 0;
1803
1804out:
1805 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001806 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001807 return ret;
1808}
1809
1810static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1811 uint64_t dynamic_feature_mask)
1812{
1813 int ret;
1814 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1815 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1816 struct msg_desc req_desc, resp_desc;
1817
1818 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1819 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1820 priv->state);
1821 return -EINVAL;
1822 }
1823
1824 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1825 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1826 return 0;
1827 }
1828
1829 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1830 dynamic_feature_mask, priv->state);
1831
1832 memset(&req, 0, sizeof(req));
1833 memset(&resp, 0, sizeof(resp));
1834
1835 req.mask_valid = 1;
1836 req.mask = dynamic_feature_mask;
1837
1838 req_desc.max_msg_len =
1839 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1840 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1841 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1842
1843 resp_desc.max_msg_len =
1844 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1845 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1846 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1847
1848 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1849 &resp_desc, &resp, sizeof(resp),
1850 WLFW_TIMEOUT_MS);
1851 if (ret < 0) {
1852 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1853 goto out;
1854 }
1855
1856 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1857 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1858 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301859 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001860 goto out;
1861 }
1862
1863 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1864 resp.prev_mask_valid, resp.prev_mask,
1865 resp.curr_mask_valid, resp.curr_mask);
1866
1867 return 0;
1868
1869out:
1870 return ret;
1871}
1872
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001873static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1874{
1875 int ret;
1876
1877 if (!penv || !penv->wlfw_clnt)
1878 return;
1879
Yuanyuan Liu68939762017-04-04 16:43:03 -07001880 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001881
1882 do {
1883 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1884
1885 if (ret != -ENOMSG)
1886 icnss_pr_err("Error receiving message: %d\n", ret);
1887
Yuanyuan Liu68939762017-04-04 16:43:03 -07001888 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001889}
1890
1891static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1892 enum qmi_event_type event, void *notify_priv)
1893{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001894 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001895
1896 if (!penv || !penv->wlfw_clnt)
1897 return;
1898
1899 switch (event) {
1900 case QMI_RECV_MSG:
1901 schedule_work(&penv->qmi_recv_msg_work);
1902 break;
1903 default:
1904 icnss_pr_dbg("Unknown Event: %d\n", event);
1905 break;
1906 }
1907}
1908
Yuanyuan Liu68939762017-04-04 16:43:03 -07001909static int icnss_call_driver_uevent(struct icnss_priv *priv,
1910 enum icnss_uevent uevent, void *data)
1911{
1912 struct icnss_uevent_data uevent_data;
1913
1914 if (!priv->ops || !priv->ops->uevent)
1915 return 0;
1916
1917 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
1918 priv->state, uevent);
1919
1920 uevent_data.uevent = uevent;
1921 uevent_data.data = data;
1922
1923 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
1924}
1925
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001926static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
1927 unsigned int msg_id, void *msg,
1928 unsigned int msg_len, void *ind_cb_priv)
1929{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001930 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001931 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001932
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001933 if (!penv)
1934 return;
1935
1936 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
1937
Sameer Thalappil93c00732017-08-18 13:02:32 -07001938 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
1939 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
1940 msg_id, penv->state);
1941 return;
1942 }
1943
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001944 switch (msg_id) {
1945 case QMI_WLFW_FW_READY_IND_V01:
1946 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
1947 0, NULL);
1948 break;
1949 case QMI_WLFW_MSA_READY_IND_V01:
1950 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
1951 msg_id);
1952 penv->stats.msa_ready_ind++;
1953 break;
1954 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
1955 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
1956 msg_id);
1957 icnss_qmi_pin_connect_result_ind(msg, msg_len);
1958 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001959 case QMI_WLFW_REJUVENATE_IND_V01:
1960 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
1961 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001962
1963 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001964 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001965 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
1966 if (event_data == NULL)
1967 return;
1968 event_data->crashed = true;
1969 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001970 fw_down_data.crashed = true;
1971 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
1972 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001973 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
1974 0, event_data);
1975 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001976 default:
1977 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
1978 break;
1979 }
1980}
1981
1982static int icnss_driver_event_server_arrive(void *data)
1983{
1984 int ret = 0;
1985
1986 if (!penv)
1987 return -ENODEV;
1988
1989 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07001990 clear_bit(ICNSS_FW_DOWN, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001991
1992 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
1993 if (!penv->wlfw_clnt) {
1994 icnss_pr_err("QMI client handle create failed\n");
1995 ret = -ENOMEM;
1996 goto out;
1997 }
1998
1999 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2000 WLFW_SERVICE_VERS_V01,
2001 WLFW_SERVICE_INS_ID_V01);
2002 if (ret < 0) {
2003 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2004 goto fail;
2005 }
2006
2007 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2008 icnss_qmi_wlfw_clnt_ind, penv);
2009 if (ret < 0) {
2010 icnss_pr_err("Failed to register indication callback: %d\n",
2011 ret);
2012 goto fail;
2013 }
2014
2015 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2016
2017 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2018
2019 ret = icnss_hw_power_on(penv);
2020 if (ret)
2021 goto fail;
2022
2023 ret = wlfw_ind_register_send_sync_msg();
2024 if (ret < 0)
2025 goto err_power_on;
2026
2027 if (!penv->msa_va) {
2028 icnss_pr_err("Invalid MSA address\n");
2029 ret = -EINVAL;
2030 goto err_power_on;
2031 }
2032
2033 ret = wlfw_msa_mem_info_send_sync_msg();
2034 if (ret < 0)
2035 goto err_power_on;
2036
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302037 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2038 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2039 if (ret < 0)
2040 goto err_power_on;
2041 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2042 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002043
2044 ret = wlfw_msa_ready_send_sync_msg();
2045 if (ret < 0)
2046 goto err_setup_msa;
2047
2048 ret = wlfw_cap_send_sync_msg();
2049 if (ret < 0)
2050 goto err_setup_msa;
2051
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002052 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2053 dynamic_feature_mask);
2054
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002055 icnss_init_vph_monitor(penv);
2056
2057 return ret;
2058
2059err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302060 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002061err_power_on:
2062 icnss_hw_power_off(penv);
2063fail:
2064 qmi_handle_destroy(penv->wlfw_clnt);
2065 penv->wlfw_clnt = NULL;
2066out:
2067 ICNSS_ASSERT(0);
2068 return ret;
2069}
2070
2071static int icnss_driver_event_server_exit(void *data)
2072{
2073 if (!penv || !penv->wlfw_clnt)
2074 return -ENODEV;
2075
2076 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2077
2078 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2079 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2080 &penv->vph_monitor_params);
2081
2082 qmi_handle_destroy(penv->wlfw_clnt);
2083
2084 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2085 penv->wlfw_clnt = NULL;
2086
2087 return 0;
2088}
2089
2090static int icnss_call_driver_probe(struct icnss_priv *priv)
2091{
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002092 int ret = 0;
2093 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002094
2095 if (!priv->ops || !priv->ops->probe)
2096 return 0;
2097
Yuanyuan Liu68939762017-04-04 16:43:03 -07002098 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2099 return -EINVAL;
2100
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002101 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2102
2103 icnss_hw_power_on(priv);
2104
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002105 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2106 ret = priv->ops->probe(&priv->pdev->dev);
2107 probe_cnt++;
2108 if (ret != -EPROBE_DEFER)
2109 break;
2110 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002111 if (ret < 0) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002112 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2113 ret, priv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002114 goto out;
2115 }
2116
2117 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2118
2119 return 0;
2120
2121out:
2122 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002123 return ret;
2124}
2125
Yuanyuan Liu68939762017-04-04 16:43:03 -07002126static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2127{
2128 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2129 goto out;
2130
2131 if (!priv->ops || !priv->ops->shutdown)
2132 goto out;
2133
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302134 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2135 goto out;
2136
Yuanyuan Liu68939762017-04-04 16:43:03 -07002137 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2138
2139 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302140 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002141
2142out:
2143 return 0;
2144}
2145
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002146static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002147{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002148 int ret;
2149
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002150 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002151
Anurag Chouhan9de21192017-08-08 15:30:02 +05302152 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002153
2154 clear_bit(ICNSS_PD_RESTART, &priv->state);
2155
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002156 if (!priv->ops || !priv->ops->reinit)
2157 goto out;
2158
Yuanyuan Liu68939762017-04-04 16:43:03 -07002159 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002160 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002161
2162 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2163
2164 icnss_hw_power_on(priv);
2165
2166 ret = priv->ops->reinit(&priv->pdev->dev);
2167 if (ret < 0) {
2168 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2169 ret, priv->state);
2170 ICNSS_ASSERT(false);
2171 goto out_power_off;
2172 }
2173
2174out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302175 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002176 return 0;
2177
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002178call_probe:
2179 return icnss_call_driver_probe(priv);
2180
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002181out_power_off:
2182 icnss_hw_power_off(priv);
2183
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002184 return ret;
2185}
2186
2187
2188static int icnss_driver_event_fw_ready_ind(void *data)
2189{
2190 int ret = 0;
2191
2192 if (!penv)
2193 return -ENODEV;
2194
2195 set_bit(ICNSS_FW_READY, &penv->state);
2196
Yuanyuan Liu68939762017-04-04 16:43:03 -07002197 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
2198
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002199 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2200
2201 icnss_hw_power_off(penv);
2202
2203 if (!penv->pdev) {
2204 icnss_pr_err("Device is not ready\n");
2205 ret = -ENODEV;
2206 goto out;
2207 }
2208
2209 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002210 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002211 else
2212 ret = icnss_call_driver_probe(penv);
2213
2214out:
2215 return ret;
2216}
2217
2218static int icnss_driver_event_register_driver(void *data)
2219{
2220 int ret = 0;
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002221 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002222
2223 if (penv->ops)
2224 return -EEXIST;
2225
2226 penv->ops = data;
2227
2228 if (test_bit(SKIP_QMI, &quirks))
2229 set_bit(ICNSS_FW_READY, &penv->state);
2230
Yuanyuan Liu5fa7ec52017-11-30 11:11:42 -08002231 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2232 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2233 penv->state);
2234 return -ENODEV;
2235 }
2236
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002237 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2238 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2239 penv->state);
2240 goto out;
2241 }
2242
2243 ret = icnss_hw_power_on(penv);
2244 if (ret)
2245 goto out;
2246
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002247 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2248 ret = penv->ops->probe(&penv->pdev->dev);
2249 probe_cnt++;
2250 if (ret != -EPROBE_DEFER)
2251 break;
2252 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002253 if (ret) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002254 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2255 ret, penv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002256 goto power_off;
2257 }
2258
2259 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2260
2261 return 0;
2262
2263power_off:
2264 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002265out:
2266 return ret;
2267}
2268
2269static int icnss_driver_event_unregister_driver(void *data)
2270{
2271 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2272 penv->ops = NULL;
2273 goto out;
2274 }
2275
2276 if (penv->ops)
2277 penv->ops->remove(&penv->pdev->dev);
2278
2279 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2280
2281 penv->ops = NULL;
2282
2283 icnss_hw_power_off(penv);
2284
2285out:
2286 return 0;
2287}
2288
2289static int icnss_call_driver_remove(struct icnss_priv *priv)
2290{
2291 icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
2292
2293 clear_bit(ICNSS_FW_READY, &priv->state);
2294
2295 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2296 return 0;
2297
2298 if (!priv->ops || !priv->ops->remove)
2299 return 0;
2300
2301 penv->ops->remove(&priv->pdev->dev);
2302
2303 clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002304
2305 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002306
2307 return 0;
2308}
2309
Yuanyuan Liu68939762017-04-04 16:43:03 -07002310static int icnss_fw_crashed(struct icnss_priv *priv,
2311 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002312{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302313 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002314
2315 set_bit(ICNSS_PD_RESTART, &priv->state);
2316 clear_bit(ICNSS_FW_READY, &priv->state);
2317
2318 icnss_pm_stay_awake(priv);
2319
Yuanyuan Liu68939762017-04-04 16:43:03 -07002320 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2321 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002322
Yuanyuan Liu68939762017-04-04 16:43:03 -07002323 if (event_data->fw_rejuvenate)
2324 wlfw_rejuvenate_ack_send_sync_msg(priv);
2325
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002326 return 0;
2327}
2328
2329static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2330 void *data)
2331{
2332 int ret = 0;
2333 struct icnss_event_pd_service_down_data *event_data = data;
2334
2335 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002336 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002337
Sameer Thalappil42ed2cc2017-10-30 11:17:01 -07002338 if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002339 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2340 event_data->crashed, priv->state);
2341 ICNSS_ASSERT(0);
2342 goto out;
2343 }
2344
2345 if (event_data->crashed)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002346 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002347 else
2348 icnss_call_driver_remove(priv);
2349
2350out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002351 kfree(data);
2352
Yuanyuan Liu68939762017-04-04 16:43:03 -07002353 icnss_ignore_qmi_timeout(false);
2354
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002355 return ret;
2356}
2357
2358static void icnss_driver_event_work(struct work_struct *work)
2359{
2360 struct icnss_driver_event *event;
2361 unsigned long flags;
2362 int ret;
2363
2364 icnss_pm_stay_awake(penv);
2365
2366 spin_lock_irqsave(&penv->event_lock, flags);
2367
2368 while (!list_empty(&penv->event_list)) {
2369 event = list_first_entry(&penv->event_list,
2370 struct icnss_driver_event, list);
2371 list_del(&event->list);
2372 spin_unlock_irqrestore(&penv->event_lock, flags);
2373
2374 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2375 icnss_driver_event_to_str(event->type),
2376 event->sync ? "-sync" : "", event->type,
2377 penv->state);
2378
2379 switch (event->type) {
2380 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2381 ret = icnss_driver_event_server_arrive(event->data);
2382 break;
2383 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2384 ret = icnss_driver_event_server_exit(event->data);
2385 break;
2386 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2387 ret = icnss_driver_event_fw_ready_ind(event->data);
2388 break;
2389 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2390 ret = icnss_driver_event_register_driver(event->data);
2391 break;
2392 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2393 ret = icnss_driver_event_unregister_driver(event->data);
2394 break;
2395 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2396 ret = icnss_driver_event_pd_service_down(penv,
2397 event->data);
2398 break;
2399 default:
2400 icnss_pr_err("Invalid Event type: %d", event->type);
2401 kfree(event);
2402 continue;
2403 }
2404
2405 penv->stats.events[event->type].processed++;
2406
2407 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2408 icnss_driver_event_to_str(event->type),
2409 event->sync ? "-sync" : "", event->type, ret,
2410 penv->state);
2411
2412 spin_lock_irqsave(&penv->event_lock, flags);
2413 if (event->sync) {
2414 event->ret = ret;
2415 complete(&event->complete);
2416 continue;
2417 }
2418 spin_unlock_irqrestore(&penv->event_lock, flags);
2419
2420 kfree(event);
2421
2422 spin_lock_irqsave(&penv->event_lock, flags);
2423 }
2424 spin_unlock_irqrestore(&penv->event_lock, flags);
2425
2426 icnss_pm_relax(penv);
2427}
2428
2429static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2430 unsigned long code,
2431 void *_cmd)
2432{
2433 int ret = 0;
2434
2435 if (!penv)
2436 return -ENODEV;
2437
2438 icnss_pr_dbg("Event Notify: code: %ld", code);
2439
2440 switch (code) {
2441 case QMI_SERVER_ARRIVE:
2442 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2443 0, NULL);
2444 break;
2445
2446 case QMI_SERVER_EXIT:
2447 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2448 0, NULL);
2449 break;
2450 default:
2451 icnss_pr_dbg("Invalid code: %ld", code);
2452 break;
2453 }
2454 return ret;
2455}
2456
2457static int icnss_msa0_ramdump(struct icnss_priv *priv)
2458{
2459 struct ramdump_segment segment;
2460
2461 memset(&segment, 0, sizeof(segment));
2462 segment.v_address = priv->msa_va;
2463 segment.size = priv->msa_mem_size;
2464 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2465}
2466
2467static struct notifier_block wlfw_clnt_nb = {
2468 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2469};
2470
2471static int icnss_modem_notifier_nb(struct notifier_block *nb,
2472 unsigned long code,
2473 void *data)
2474{
2475 struct icnss_event_pd_service_down_data *event_data;
2476 struct notif_data *notif = data;
2477 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2478 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002479 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302480 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002481
Yuanyuan Liu68939762017-04-04 16:43:03 -07002482 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002483
Sameer Thalappil765cb492017-10-04 17:57:07 -07002484 if (code == SUBSYS_AFTER_SHUTDOWN &&
Yuanyuan Liu52a484b2017-11-15 11:23:45 -08002485 notif->crashed == CRASH_STATUS_ERR_FATAL) {
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302486 ret = icnss_assign_msa_perm_all(priv,
Sameer Thalappil765cb492017-10-04 17:57:07 -07002487 ICNSS_MSA_PERM_HLOS_ALL);
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302488 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 Liu08ab5692017-11-10 12:12:17 -08002503 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
2504 set_bit(ICNSS_FW_DOWN, &priv->state);
2505 icnss_ignore_qmi_timeout(true);
2506
2507 fw_down_data.crashed = !!notif->crashed;
2508 if (test_bit(ICNSS_FW_READY, &priv->state))
2509 icnss_call_driver_uevent(priv,
2510 ICNSS_UEVENT_FW_DOWN,
2511 &fw_down_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002512 return NOTIFY_OK;
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002513 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002514
Yuanyuan Liu68939762017-04-04 16:43:03 -07002515 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2516 priv->state, notif->crashed);
2517
Sameer Thalappil93c00732017-08-18 13:02:32 -07002518 set_bit(ICNSS_FW_DOWN, &priv->state);
2519
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002520 if (notif->crashed)
2521 priv->stats.recovery.root_pd_crash++;
2522 else
2523 priv->stats.recovery.root_pd_shutdown++;
2524
Yuanyuan Liu68939762017-04-04 16:43:03 -07002525 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002526
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002527 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002528
2529 if (event_data == NULL)
2530 return notifier_from_errno(-ENOMEM);
2531
2532 event_data->crashed = notif->crashed;
2533
Yuanyuan Liu68939762017-04-04 16:43:03 -07002534 fw_down_data.crashed = !!notif->crashed;
2535 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2536
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002537 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2538 ICNSS_EVENT_SYNC, event_data);
2539
2540 return NOTIFY_OK;
2541}
2542
2543static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2544{
2545 int ret = 0;
2546
2547 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2548
2549 priv->modem_notify_handler =
2550 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2551
2552 if (IS_ERR(priv->modem_notify_handler)) {
2553 ret = PTR_ERR(priv->modem_notify_handler);
2554 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2555 }
2556
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002557 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002558
2559 return ret;
2560}
2561
2562static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2563{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002564 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002565 return 0;
2566
2567 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2568 &priv->modem_ssr_nb);
2569 priv->modem_notify_handler = NULL;
2570
2571 return 0;
2572}
2573
2574static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2575{
2576 int i;
2577
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002578 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002579 return 0;
2580
2581 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002582 service_notif_unregister_notifier(
2583 priv->service_notifier[i].handle,
2584 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002585
2586 kfree(priv->service_notifier);
2587
2588 priv->service_notifier = NULL;
2589
2590 return 0;
2591}
2592
2593static int icnss_service_notifier_notify(struct notifier_block *nb,
2594 unsigned long notification, void *data)
2595{
2596 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2597 service_notifier_nb);
2598 enum pd_subsys_state *state = data;
2599 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002600 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002601 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002602
Yuanyuan Liu68939762017-04-04 16:43:03 -07002603 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2604 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002605
Yuanyuan Liu68939762017-04-04 16:43:03 -07002606 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2607 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002608
Yuanyuan Liu68939762017-04-04 16:43:03 -07002609 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002610
Yuanyuan Liu68939762017-04-04 16:43:03 -07002611 if (event_data == NULL)
2612 return notifier_from_errno(-ENOMEM);
2613
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002614 event_data->crashed = true;
2615
Yuanyuan Liu68939762017-04-04 16:43:03 -07002616 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002617 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002618 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002619 }
2620
Yuanyuan Liu68939762017-04-04 16:43:03 -07002621 switch (*state) {
2622 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002623 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002624 break;
2625 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002626 cause = ICNSS_ROOT_PD_SHUTDOWN;
2627 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002628 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002629 break;
2630 case USER_PD_STATE_CHANGE:
2631 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2632 cause = ICNSS_HOST_ERROR;
2633 priv->stats.recovery.pdr_host_error++;
2634 } else {
2635 cause = ICNSS_FW_CRASH;
2636 priv->stats.recovery.pdr_fw_crash++;
2637 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002638 break;
2639 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002640 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002641 break;
2642 }
2643
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002644 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2645 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002646event_post:
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002647 if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
2648 set_bit(ICNSS_FW_DOWN, &priv->state);
2649 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002650
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002651 fw_down_data.crashed = event_data->crashed;
2652 if (test_bit(ICNSS_FW_READY, &priv->state))
2653 icnss_call_driver_uevent(priv,
2654 ICNSS_UEVENT_FW_DOWN,
2655 &fw_down_data);
2656 }
2657
2658 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002659 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2660 ICNSS_EVENT_SYNC, event_data);
2661done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002662 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2663 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002664 return NOTIFY_OK;
2665}
2666
2667static int icnss_get_service_location_notify(struct notifier_block *nb,
2668 unsigned long opcode, void *data)
2669{
2670 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2671 get_service_nb);
2672 struct pd_qmi_client_data *pd = data;
2673 int curr_state;
2674 int ret;
2675 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002676 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002677
2678 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2679 priv->state);
2680
2681 if (opcode != LOCATOR_UP)
2682 return NOTIFY_DONE;
2683
2684 if (pd->total_domains == 0) {
2685 icnss_pr_err("Did not find any domains\n");
2686 ret = -ENOENT;
2687 goto out;
2688 }
2689
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002690 notifier = kcalloc(pd->total_domains,
2691 sizeof(struct service_notifier_context),
2692 GFP_KERNEL);
2693 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002694 ret = -ENOMEM;
2695 goto out;
2696 }
2697
2698 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2699
2700 for (i = 0; i < pd->total_domains; i++) {
2701 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2702 pd->domain_list[i].name,
2703 pd->domain_list[i].instance_id);
2704
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002705 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002706 service_notif_register_notifier(pd->domain_list[i].name,
2707 pd->domain_list[i].instance_id,
2708 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002709 notifier[i].instance_id = pd->domain_list[i].instance_id;
2710 strlcpy(notifier[i].name, pd->domain_list[i].name,
2711 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002712
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002713 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002714 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2715 i, pd->domain_list->name,
2716 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002717 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002718 goto free_handle;
2719 }
2720 }
2721
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002722 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002723 priv->total_domains = pd->total_domains;
2724
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002725 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002726
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002727 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2728 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002729
2730 return NOTIFY_OK;
2731
2732free_handle:
2733 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002734 if (notifier[i].handle)
2735 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002736 &priv->service_notifier_nb);
2737 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002738 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002739
2740out:
2741 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2742 priv->state);
2743
2744 return NOTIFY_OK;
2745}
2746
2747
2748static int icnss_pd_restart_enable(struct icnss_priv *priv)
2749{
2750 int ret;
2751
2752 if (test_bit(SSR_ONLY, &quirks)) {
2753 icnss_pr_dbg("PDR disabled through module parameter\n");
2754 return 0;
2755 }
2756
2757 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2758
2759 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2760 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2761 ICNSS_WLAN_SERVICE_NAME,
2762 &priv->get_service_nb);
2763 if (ret) {
2764 icnss_pr_err("Get service location failed: %d\n", ret);
2765 goto out;
2766 }
2767
2768 return 0;
2769out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002770 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002771 return ret;
2772
2773}
2774
2775
2776static int icnss_enable_recovery(struct icnss_priv *priv)
2777{
2778 int ret;
2779
2780 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2781 icnss_pr_dbg("Recovery disabled through module parameter\n");
2782 return 0;
2783 }
2784
2785 if (test_bit(PDR_ONLY, &quirks)) {
2786 icnss_pr_dbg("SSR disabled through module parameter\n");
2787 goto enable_pdr;
2788 }
2789
2790 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2791 &priv->pdev->dev);
2792 if (!priv->msa0_dump_dev)
2793 return -ENOMEM;
2794
2795 icnss_modem_ssr_register_notifier(priv);
2796 if (test_bit(SSR_ONLY, &quirks)) {
2797 icnss_pr_dbg("PDR disabled through module parameter\n");
2798 return 0;
2799 }
2800
2801enable_pdr:
2802 ret = icnss_pd_restart_enable(priv);
2803
2804 if (ret)
2805 return ret;
2806
2807 return 0;
2808}
2809
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302810int __icnss_register_driver(struct icnss_driver_ops *ops,
2811 struct module *owner, const char *mod_name)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002812{
2813 int ret = 0;
2814
2815 if (!penv || !penv->pdev) {
2816 ret = -ENODEV;
2817 goto out;
2818 }
2819
2820 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2821
2822 if (penv->ops) {
2823 icnss_pr_err("Driver already registered\n");
2824 ret = -EEXIST;
2825 goto out;
2826 }
2827
2828 if (!ops->probe || !ops->remove) {
2829 ret = -EINVAL;
2830 goto out;
2831 }
2832
2833 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302834 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002835
2836 if (ret == -EINTR)
2837 ret = 0;
2838
2839out:
2840 return ret;
2841}
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302842EXPORT_SYMBOL(__icnss_register_driver);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002843
2844int icnss_unregister_driver(struct icnss_driver_ops *ops)
2845{
2846 int ret;
2847
2848 if (!penv || !penv->pdev) {
2849 ret = -ENODEV;
2850 goto out;
2851 }
2852
2853 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2854
2855 if (!penv->ops) {
2856 icnss_pr_err("Driver not registered\n");
2857 ret = -ENOENT;
2858 goto out;
2859 }
2860
2861 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2862 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2863out:
2864 return ret;
2865}
2866EXPORT_SYMBOL(icnss_unregister_driver);
2867
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302868int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002869 irqreturn_t (*handler)(int, void *),
2870 unsigned long flags, const char *name, void *ctx)
2871{
2872 int ret = 0;
2873 unsigned int irq;
2874 struct ce_irq_list *irq_entry;
2875
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302876 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002877 ret = -ENODEV;
2878 goto out;
2879 }
2880
Yuanyuan Liu68939762017-04-04 16:43:03 -07002881 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002882
2883 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2884 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
2885 ret = -EINVAL;
2886 goto out;
2887 }
2888 irq = penv->ce_irqs[ce_id];
2889 irq_entry = &penv->ce_irq_list[ce_id];
2890
2891 if (irq_entry->handler || irq_entry->irq) {
2892 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
2893 irq, ce_id);
2894 ret = -EEXIST;
2895 goto out;
2896 }
2897
2898 ret = request_irq(irq, handler, flags, name, ctx);
2899 if (ret) {
2900 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
2901 irq, ce_id, ret);
2902 goto out;
2903 }
2904 irq_entry->irq = irq;
2905 irq_entry->handler = handler;
2906
Yuanyuan Liu68939762017-04-04 16:43:03 -07002907 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002908
2909 penv->stats.ce_irqs[ce_id].request++;
2910out:
2911 return ret;
2912}
2913EXPORT_SYMBOL(icnss_ce_request_irq);
2914
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302915int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002916{
2917 int ret = 0;
2918 unsigned int irq;
2919 struct ce_irq_list *irq_entry;
2920
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302921 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002922 ret = -ENODEV;
2923 goto out;
2924 }
2925
Yuanyuan Liu68939762017-04-04 16:43:03 -07002926 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002927
2928 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2929 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
2930 ret = -EINVAL;
2931 goto out;
2932 }
2933
2934 irq = penv->ce_irqs[ce_id];
2935 irq_entry = &penv->ce_irq_list[ce_id];
2936 if (!irq_entry->handler || !irq_entry->irq) {
2937 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
2938 ret = -EEXIST;
2939 goto out;
2940 }
2941 free_irq(irq, ctx);
2942 irq_entry->irq = 0;
2943 irq_entry->handler = NULL;
2944
2945 penv->stats.ce_irqs[ce_id].free++;
2946out:
2947 return ret;
2948}
2949EXPORT_SYMBOL(icnss_ce_free_irq);
2950
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302951void icnss_enable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002952{
2953 unsigned int irq;
2954
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302955 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002956 icnss_pr_err("Platform driver not initialized\n");
2957 return;
2958 }
2959
Yuanyuan Liu68939762017-04-04 16:43:03 -07002960 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002961 penv->state);
2962
2963 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2964 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
2965 return;
2966 }
2967
2968 penv->stats.ce_irqs[ce_id].enable++;
2969
2970 irq = penv->ce_irqs[ce_id];
2971 enable_irq(irq);
2972}
2973EXPORT_SYMBOL(icnss_enable_irq);
2974
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302975void icnss_disable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002976{
2977 unsigned int irq;
2978
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302979 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002980 icnss_pr_err("Platform driver not initialized\n");
2981 return;
2982 }
2983
Yuanyuan Liu68939762017-04-04 16:43:03 -07002984 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002985 penv->state);
2986
2987 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2988 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
2989 ce_id);
2990 return;
2991 }
2992
2993 irq = penv->ce_irqs[ce_id];
2994 disable_irq(irq);
2995
2996 penv->stats.ce_irqs[ce_id].disable++;
2997}
2998EXPORT_SYMBOL(icnss_disable_irq);
2999
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303000int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003001{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303002 if (!penv || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003003 icnss_pr_err("Platform driver not initialized\n");
3004 return -EINVAL;
3005 }
3006
3007 info->v_addr = penv->mem_base_va;
3008 info->p_addr = penv->mem_base_pa;
3009 info->chip_id = penv->chip_info.chip_id;
3010 info->chip_family = penv->chip_info.chip_family;
3011 info->board_id = penv->board_info.board_id;
3012 info->soc_id = penv->soc_info.soc_id;
3013 info->fw_version = penv->fw_version_info.fw_version;
3014 strlcpy(info->fw_build_timestamp,
3015 penv->fw_version_info.fw_build_timestamp,
3016 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
3017
3018 return 0;
3019}
3020EXPORT_SYMBOL(icnss_get_soc_info);
3021
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303022int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003023{
3024 int ret;
3025
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303026 if (!dev)
3027 return -ENODEV;
3028
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003029 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003030
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003031 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003032 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003033 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3034 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003035 return ret;
3036}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003037EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003038
3039int icnss_athdiag_read(struct device *dev, uint32_t offset,
3040 uint32_t mem_type, uint32_t data_len,
3041 uint8_t *output)
3042{
3043 int ret = 0;
3044 struct icnss_priv *priv = dev_get_drvdata(dev);
3045
3046 if (priv->magic != ICNSS_MAGIC) {
3047 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3048 dev, priv, priv->magic);
3049 return -EINVAL;
3050 }
3051
3052 if (!output || data_len == 0
3053 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3054 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3055 output, data_len);
3056 ret = -EINVAL;
3057 goto out;
3058 }
3059
3060 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3061 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3062 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3063 priv->state);
3064 ret = -EINVAL;
3065 goto out;
3066 }
3067
3068 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3069 data_len, output);
3070out:
3071 return ret;
3072}
3073EXPORT_SYMBOL(icnss_athdiag_read);
3074
3075int icnss_athdiag_write(struct device *dev, uint32_t offset,
3076 uint32_t mem_type, uint32_t data_len,
3077 uint8_t *input)
3078{
3079 int ret = 0;
3080 struct icnss_priv *priv = dev_get_drvdata(dev);
3081
3082 if (priv->magic != ICNSS_MAGIC) {
3083 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3084 dev, priv, priv->magic);
3085 return -EINVAL;
3086 }
3087
3088 if (!input || data_len == 0
3089 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3090 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3091 input, data_len);
3092 ret = -EINVAL;
3093 goto out;
3094 }
3095
3096 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3097 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3098 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3099 priv->state);
3100 ret = -EINVAL;
3101 goto out;
3102 }
3103
3104 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3105 data_len, input);
3106out:
3107 return ret;
3108}
3109EXPORT_SYMBOL(icnss_athdiag_write);
3110
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303111int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003112 enum icnss_driver_mode mode,
3113 const char *host_version)
3114{
3115 struct wlfw_wlan_cfg_req_msg_v01 req;
3116 u32 i;
3117 int ret;
3118
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303119 if (!dev)
3120 return -ENODEV;
3121
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003122 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3123 mode, config, host_version);
3124
3125 memset(&req, 0, sizeof(req));
3126
3127 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3128 goto skip;
3129
3130 if (!config || !host_version) {
3131 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3132 config, host_version);
3133 ret = -EINVAL;
3134 goto out;
3135 }
3136
3137 req.host_version_valid = 1;
3138 strlcpy(req.host_version, host_version,
3139 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3140
3141 req.tgt_cfg_valid = 1;
3142 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3143 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3144 else
3145 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3146 for (i = 0; i < req.tgt_cfg_len; i++) {
3147 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3148 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3149 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3150 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3151 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3152 }
3153
3154 req.svc_cfg_valid = 1;
3155 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3156 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3157 else
3158 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3159 for (i = 0; i < req.svc_cfg_len; i++) {
3160 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3161 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3162 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3163 }
3164
3165 req.shadow_reg_valid = 1;
3166 if (config->num_shadow_reg_cfg >
3167 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3168 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3169 else
3170 req.shadow_reg_len = config->num_shadow_reg_cfg;
3171
3172 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3173 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3174
3175 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3176 if (ret)
3177 goto out;
3178skip:
3179 ret = wlfw_wlan_mode_send_sync_msg(mode);
3180out:
3181 if (test_bit(SKIP_QMI, &quirks))
3182 ret = 0;
3183
3184 return ret;
3185}
3186EXPORT_SYMBOL(icnss_wlan_enable);
3187
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303188int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003189{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303190 if (!dev)
3191 return -ENODEV;
3192
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003193 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3194}
3195EXPORT_SYMBOL(icnss_wlan_disable);
3196
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303197bool icnss_is_qmi_disable(struct device *dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003198{
3199 return test_bit(SKIP_QMI, &quirks) ? true : false;
3200}
3201EXPORT_SYMBOL(icnss_is_qmi_disable);
3202
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303203int icnss_get_ce_id(struct device *dev, int irq)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003204{
3205 int i;
3206
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303207 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003208 return -ENODEV;
3209
3210 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3211 if (penv->ce_irqs[i] == irq)
3212 return i;
3213 }
3214
3215 icnss_pr_err("No matching CE id for irq %d\n", irq);
3216
3217 return -EINVAL;
3218}
3219EXPORT_SYMBOL(icnss_get_ce_id);
3220
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303221int icnss_get_irq(struct device *dev, int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003222{
3223 int irq;
3224
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303225 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003226 return -ENODEV;
3227
3228 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3229 return -EINVAL;
3230
3231 irq = penv->ce_irqs[ce_id];
3232
3233 return irq;
3234}
3235EXPORT_SYMBOL(icnss_get_irq);
3236
3237struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3238{
3239 struct icnss_priv *priv = dev_get_drvdata(dev);
3240
3241 if (!priv) {
3242 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3243 dev, priv);
3244 return NULL;
3245 }
3246
3247 return priv->smmu_mapping;
3248}
3249EXPORT_SYMBOL(icnss_smmu_get_mapping);
3250
3251int icnss_smmu_map(struct device *dev,
3252 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3253{
3254 struct icnss_priv *priv = dev_get_drvdata(dev);
3255 unsigned long iova;
3256 size_t len;
3257 int ret = 0;
3258
3259 if (!priv) {
3260 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3261 dev, priv);
3262 return -EINVAL;
3263 }
3264
3265 if (!iova_addr) {
3266 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3267 &paddr, size);
3268 return -EINVAL;
3269 }
3270
3271 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3272 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3273
3274 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3275 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3276 iova,
3277 &priv->smmu_iova_ipa_start,
3278 priv->smmu_iova_ipa_len);
3279 return -ENOMEM;
3280 }
3281
3282 ret = iommu_map(priv->smmu_mapping->domain, iova,
3283 rounddown(paddr, PAGE_SIZE), len,
3284 IOMMU_READ | IOMMU_WRITE);
3285 if (ret) {
3286 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3287 return ret;
3288 }
3289
3290 priv->smmu_iova_ipa_start = iova + len;
3291 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3292
3293 return 0;
3294}
3295EXPORT_SYMBOL(icnss_smmu_map);
3296
3297unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3298{
3299 return socinfo_get_serial_number();
3300}
3301EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3302
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003303int icnss_trigger_recovery(struct device *dev)
3304{
3305 int ret = 0;
3306 struct icnss_priv *priv = dev_get_drvdata(dev);
3307
3308 if (priv->magic != ICNSS_MAGIC) {
3309 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3310 ret = -EINVAL;
3311 goto out;
3312 }
3313
3314 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3315 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3316 priv->state);
3317 ret = -EPERM;
3318 goto out;
3319 }
3320
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003321 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003322 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3323 priv->state);
3324 ret = -EOPNOTSUPP;
3325 goto out;
3326 }
3327
3328 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3329 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3330 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003331 ret = -EINVAL;
3332 goto out;
3333 }
3334
Yuanyuan Liu68939762017-04-04 16:43:03 -07003335 WARN_ON(1);
3336 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3337 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003338
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003339 /*
3340 * Initiate PDR, required only for the first instance
3341 */
3342 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3343 priv->service_notifier[0].instance_id);
3344
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003345 if (!ret)
3346 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3347
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003348out:
3349 return ret;
3350}
3351EXPORT_SYMBOL(icnss_trigger_recovery);
3352
3353
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003354static int icnss_smmu_init(struct icnss_priv *priv)
3355{
3356 struct dma_iommu_mapping *mapping;
3357 int atomic_ctx = 1;
3358 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303359 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003360 int ret = 0;
3361
3362 icnss_pr_dbg("Initializing SMMU\n");
3363
3364 mapping = arm_iommu_create_mapping(&platform_bus_type,
3365 priv->smmu_iova_start,
3366 priv->smmu_iova_len);
3367 if (IS_ERR(mapping)) {
3368 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3369 ret = PTR_ERR(mapping);
3370 goto map_fail;
3371 }
3372
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303373 if (priv->bypass_s1_smmu) {
3374 ret = iommu_domain_set_attr(mapping->domain,
3375 DOMAIN_ATTR_S1_BYPASS,
3376 &s1_bypass);
3377 if (ret < 0) {
3378 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3379 ret);
3380 goto set_attr_fail;
3381 }
3382 icnss_pr_dbg("SMMU S1 BYPASS\n");
3383 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003384 ret = iommu_domain_set_attr(mapping->domain,
3385 DOMAIN_ATTR_ATOMIC,
3386 &atomic_ctx);
3387 if (ret < 0) {
3388 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3389 ret);
3390 goto set_attr_fail;
3391 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303392 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003393
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303394 ret = iommu_domain_set_attr(mapping->domain,
3395 DOMAIN_ATTR_FAST,
3396 &fast);
3397 if (ret < 0) {
3398 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3399 ret);
3400 goto set_attr_fail;
3401 }
3402 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003403 }
3404
3405 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3406 if (ret < 0) {
3407 icnss_pr_err("Attach device failed, err = %d\n", ret);
3408 goto attach_fail;
3409 }
3410
3411 priv->smmu_mapping = mapping;
3412
3413 return ret;
3414
3415attach_fail:
3416set_attr_fail:
3417 arm_iommu_release_mapping(mapping);
3418map_fail:
3419 return ret;
3420}
3421
3422static void icnss_smmu_deinit(struct icnss_priv *priv)
3423{
3424 if (!priv->smmu_mapping)
3425 return;
3426
3427 arm_iommu_detach_device(&priv->pdev->dev);
3428 arm_iommu_release_mapping(priv->smmu_mapping);
3429
3430 priv->smmu_mapping = NULL;
3431}
3432
Yuanyuan Liu68939762017-04-04 16:43:03 -07003433static int icnss_get_vreg_info(struct device *dev,
3434 struct icnss_vreg_info *vreg_info)
3435{
3436 int ret = 0;
3437 char prop_name[MAX_PROP_SIZE];
3438 struct regulator *reg;
3439 const __be32 *prop;
3440 int len = 0;
3441 int i;
3442
3443 reg = devm_regulator_get_optional(dev, vreg_info->name);
3444 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3445 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3446 vreg_info->name);
3447 ret = PTR_ERR(reg);
3448 goto out;
3449 }
3450
3451 if (IS_ERR(reg)) {
3452 ret = PTR_ERR(reg);
3453
3454 if (vreg_info->required) {
3455 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3456 vreg_info->name, ret);
3457 goto out;
3458 } else {
3459 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3460 vreg_info->name, ret);
3461 goto done;
3462 }
3463 }
3464
3465 vreg_info->reg = reg;
3466
3467 snprintf(prop_name, MAX_PROP_SIZE,
3468 "qcom,%s-config", vreg_info->name);
3469
3470 prop = of_get_property(dev->of_node, prop_name, &len);
3471
3472 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3473 prop_name, len);
3474
3475 if (!prop || len < (2 * sizeof(__be32))) {
3476 icnss_pr_dbg("Property %s %s\n", prop_name,
3477 prop ? "invalid format" : "doesn't exist");
3478 goto done;
3479 }
3480
3481 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3482 switch (i) {
3483 case 0:
3484 vreg_info->min_v = be32_to_cpup(&prop[0]);
3485 break;
3486 case 1:
3487 vreg_info->max_v = be32_to_cpup(&prop[1]);
3488 break;
3489 case 2:
3490 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3491 break;
3492 case 3:
3493 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3494 break;
3495 default:
3496 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3497 prop_name, i);
3498 break;
3499 }
3500 }
3501
3502done:
3503 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3504 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3505 vreg_info->load_ua, vreg_info->settle_delay);
3506
3507 return 0;
3508
3509out:
3510 return ret;
3511}
3512
3513static int icnss_get_clk_info(struct device *dev,
3514 struct icnss_clk_info *clk_info)
3515{
3516 struct clk *handle;
3517 int ret = 0;
3518
3519 handle = devm_clk_get(dev, clk_info->name);
3520 if (IS_ERR(handle)) {
3521 ret = PTR_ERR(handle);
3522 if (clk_info->required) {
3523 icnss_pr_err("Clock %s isn't available: %d\n",
3524 clk_info->name, ret);
3525 goto out;
3526 } else {
3527 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3528 ret);
3529 ret = 0;
3530 goto out;
3531 }
3532 }
3533
3534 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3535
3536 clk_info->handle = handle;
3537out:
3538 return ret;
3539}
3540
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003541static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003542{
3543 struct icnss_priv *priv = s->private;
3544
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003545 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003546
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003547 seq_puts(s, "\nCMD: test_mode\n");
3548 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3549 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3550 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003551 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003552
3553 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3554 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003555
3556 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003557 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003558 goto out;
3559 }
3560
3561 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003562 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003563 goto out;
3564 }
3565
3566 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003567 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003568 goto out;
3569 }
3570
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003571out:
3572 seq_puts(s, "\n");
3573 return 0;
3574}
3575
3576static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3577{
3578 int ret;
3579
3580 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3581 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3582 priv->state);
3583 ret = -ENODEV;
3584 goto out;
3585 }
3586
3587 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3588 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3589 priv->state);
3590 ret = -EINVAL;
3591 goto out;
3592 }
3593
3594 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3595 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3596 priv->state);
3597 ret = -EINVAL;
3598 goto out;
3599 }
3600
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303601 icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003602
3603 ret = icnss_hw_power_off(priv);
3604
3605 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3606
3607out:
3608 return ret;
3609}
3610static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3611 enum icnss_driver_mode mode)
3612{
3613 int ret;
3614
3615 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3616 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3617 priv->state);
3618 ret = -ENODEV;
3619 goto out;
3620 }
3621
3622 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3623 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3624 priv->state);
3625 ret = -EINVAL;
3626 goto out;
3627 }
3628
3629 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3630 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3631 priv->state);
3632 ret = -EBUSY;
3633 goto out;
3634 }
3635
3636 ret = icnss_hw_power_on(priv);
3637 if (ret)
3638 goto out;
3639
3640 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3641
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303642 ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003643 if (ret)
3644 goto power_off;
3645
3646 return 0;
3647
3648power_off:
3649 icnss_hw_power_off(priv);
3650 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3651
3652out:
3653 return ret;
3654}
3655
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003656static ssize_t icnss_fw_debug_write(struct file *fp,
3657 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003658 size_t count, loff_t *off)
3659{
3660 struct icnss_priv *priv =
3661 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003662 char buf[64];
3663 char *sptr, *token;
3664 unsigned int len = 0;
3665 char *cmd;
3666 uint64_t val;
3667 const char *delim = " ";
3668 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003669
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003670 len = min(count, sizeof(buf) - 1);
3671 if (copy_from_user(buf, user_buf, len))
3672 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003673
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003674 buf[len] = '\0';
3675 sptr = buf;
3676
3677 token = strsep(&sptr, delim);
3678 if (!token)
3679 return -EINVAL;
3680 if (!sptr)
3681 return -EINVAL;
3682 cmd = token;
3683
3684 token = strsep(&sptr, delim);
3685 if (!token)
3686 return -EINVAL;
3687 if (kstrtou64(token, 0, &val))
3688 return -EINVAL;
3689
3690 if (strcmp(cmd, "test_mode") == 0) {
3691 switch (val) {
3692 case 0:
3693 ret = icnss_test_mode_fw_test_off(priv);
3694 break;
3695 case 1:
3696 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3697 break;
3698 case 2:
3699 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3700 break;
3701 case 3:
3702 ret = icnss_trigger_recovery(&priv->pdev->dev);
3703 break;
3704 default:
3705 return -EINVAL;
3706 }
3707 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3708 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3709 } else {
3710 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003711 }
3712
3713 if (ret)
3714 return ret;
3715
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003716 return count;
3717}
3718
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003719static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003720{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003721 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003722}
3723
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003724static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003725 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003726 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003727 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003728 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003729 .owner = THIS_MODULE,
3730 .llseek = seq_lseek,
3731};
3732
3733static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3734 size_t count, loff_t *off)
3735{
3736 struct icnss_priv *priv =
3737 ((struct seq_file *)fp->private_data)->private;
3738 int ret;
3739 u32 val;
3740
3741 ret = kstrtou32_from_user(buf, count, 0, &val);
3742 if (ret)
3743 return ret;
3744
3745 if (ret == 0)
3746 memset(&priv->stats, 0, sizeof(priv->stats));
3747
3748 return count;
3749}
3750
3751static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3752{
3753 enum icnss_driver_state i;
3754 int skip = 0;
3755 unsigned long state;
3756
3757 seq_printf(s, "\nState: 0x%lx(", priv->state);
3758 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3759
3760 if (!(state & 0x1))
3761 continue;
3762
3763 if (skip++)
3764 seq_puts(s, " | ");
3765
3766 switch (i) {
3767 case ICNSS_WLFW_QMI_CONNECTED:
3768 seq_puts(s, "QMI CONN");
3769 continue;
3770 case ICNSS_POWER_ON:
3771 seq_puts(s, "POWER ON");
3772 continue;
3773 case ICNSS_FW_READY:
3774 seq_puts(s, "FW READY");
3775 continue;
3776 case ICNSS_DRIVER_PROBED:
3777 seq_puts(s, "DRIVER PROBED");
3778 continue;
3779 case ICNSS_FW_TEST_MODE:
3780 seq_puts(s, "FW TEST MODE");
3781 continue;
3782 case ICNSS_PM_SUSPEND:
3783 seq_puts(s, "PM SUSPEND");
3784 continue;
3785 case ICNSS_PM_SUSPEND_NOIRQ:
3786 seq_puts(s, "PM SUSPEND NOIRQ");
3787 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003788 case ICNSS_SSR_REGISTERED:
3789 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003790 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003791 case ICNSS_PDR_REGISTERED:
3792 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003793 continue;
3794 case ICNSS_PD_RESTART:
3795 seq_puts(s, "PD RESTART");
3796 continue;
3797 case ICNSS_MSA0_ASSIGNED:
3798 seq_puts(s, "MSA0 ASSIGNED");
3799 continue;
3800 case ICNSS_WLFW_EXISTS:
3801 seq_puts(s, "WLAN FW EXISTS");
3802 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303803 case ICNSS_SHUTDOWN_DONE:
3804 seq_puts(s, "SHUTDOWN DONE");
3805 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003806 case ICNSS_HOST_TRIGGERED_PDR:
3807 seq_puts(s, "HOST TRIGGERED PDR");
3808 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07003809 case ICNSS_FW_DOWN:
3810 seq_puts(s, "FW DOWN");
3811 continue;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003812 }
3813
3814 seq_printf(s, "UNKNOWN-%d", i);
3815 }
3816 seq_puts(s, ")\n");
3817
3818 return 0;
3819}
3820
3821static int icnss_stats_show_capability(struct seq_file *s,
3822 struct icnss_priv *priv)
3823{
3824 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3825 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3826 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3827 seq_printf(s, "Chip family: 0x%x\n",
3828 priv->chip_info.chip_family);
3829 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3830 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3831 seq_printf(s, "Firmware Version: 0x%x\n",
3832 priv->fw_version_info.fw_version);
3833 seq_printf(s, "Firmware Build Timestamp: %s\n",
3834 priv->fw_version_info.fw_build_timestamp);
3835 seq_printf(s, "Firmware Build ID: %s\n",
3836 priv->fw_build_id);
3837 }
3838
3839 return 0;
3840}
3841
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003842static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
3843 struct icnss_priv *priv)
3844{
3845 if (priv->stats.rejuvenate_ind) {
3846 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
3847 seq_printf(s, "Number of Rejuvenations: %u\n",
3848 priv->stats.rejuvenate_ind);
3849 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
3850 priv->cause_for_rejuvenation);
3851 seq_printf(s, "Requesting Sub-System: 0x%x\n",
3852 priv->requesting_sub_system);
3853 seq_printf(s, "Line Number: %u\n",
3854 priv->line_number);
3855 seq_printf(s, "Function Name: %s\n",
3856 priv->function_name);
3857 }
3858
3859 return 0;
3860}
3861
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003862static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
3863{
3864 int i;
3865
3866 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
3867 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
3868 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
3869 seq_printf(s, "%24s %16u %16u\n",
3870 icnss_driver_event_to_str(i),
3871 priv->stats.events[i].posted,
3872 priv->stats.events[i].processed);
3873
3874 return 0;
3875}
3876
3877static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
3878{
3879 int i;
3880
3881 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
3882 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
3883 "Free", "Enable", "Disable");
3884 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
3885 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
3886 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
3887 priv->stats.ce_irqs[i].free,
3888 priv->stats.ce_irqs[i].enable,
3889 priv->stats.ce_irqs[i].disable);
3890
3891 return 0;
3892}
3893
3894static int icnss_stats_show(struct seq_file *s, void *data)
3895{
3896#define ICNSS_STATS_DUMP(_s, _priv, _x) \
3897 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
3898
3899 struct icnss_priv *priv = s->private;
3900
3901 ICNSS_STATS_DUMP(s, priv, ind_register_req);
3902 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
3903 ICNSS_STATS_DUMP(s, priv, ind_register_err);
3904 ICNSS_STATS_DUMP(s, priv, msa_info_req);
3905 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
3906 ICNSS_STATS_DUMP(s, priv, msa_info_err);
3907 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
3908 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
3909 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
3910 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
3911 ICNSS_STATS_DUMP(s, priv, cap_req);
3912 ICNSS_STATS_DUMP(s, priv, cap_resp);
3913 ICNSS_STATS_DUMP(s, priv, cap_err);
3914 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
3915 ICNSS_STATS_DUMP(s, priv, cfg_req);
3916 ICNSS_STATS_DUMP(s, priv, cfg_resp);
3917 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
3918 ICNSS_STATS_DUMP(s, priv, mode_req);
3919 ICNSS_STATS_DUMP(s, priv, mode_resp);
3920 ICNSS_STATS_DUMP(s, priv, mode_req_err);
3921 ICNSS_STATS_DUMP(s, priv, ini_req);
3922 ICNSS_STATS_DUMP(s, priv, ini_resp);
3923 ICNSS_STATS_DUMP(s, priv, ini_req_err);
3924 ICNSS_STATS_DUMP(s, priv, vbatt_req);
3925 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
3926 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003927 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003928 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
3929 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
3930 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003931 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
3932 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
3933 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
3934 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003935
3936 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
3937 ICNSS_STATS_DUMP(s, priv, pm_suspend);
3938 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
3939 ICNSS_STATS_DUMP(s, priv, pm_resume);
3940 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
3941 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
3942 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
3943 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
3944 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
3945 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
3946 ICNSS_STATS_DUMP(s, priv, pm_relax);
3947
3948 icnss_stats_show_irqs(s, priv);
3949
3950 icnss_stats_show_capability(s, priv);
3951
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003952 icnss_stats_show_rejuvenate_info(s, priv);
3953
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003954 icnss_stats_show_events(s, priv);
3955
3956 icnss_stats_show_state(s, priv);
3957
3958 return 0;
3959#undef ICNSS_STATS_DUMP
3960}
3961
3962static int icnss_stats_open(struct inode *inode, struct file *file)
3963{
3964 return single_open(file, icnss_stats_show, inode->i_private);
3965}
3966
3967static const struct file_operations icnss_stats_fops = {
3968 .read = seq_read,
3969 .write = icnss_stats_write,
3970 .release = single_release,
3971 .open = icnss_stats_open,
3972 .owner = THIS_MODULE,
3973 .llseek = seq_lseek,
3974};
3975
3976static int icnss_regwrite_show(struct seq_file *s, void *data)
3977{
3978 struct icnss_priv *priv = s->private;
3979
3980 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
3981
3982 if (!test_bit(ICNSS_FW_READY, &priv->state))
3983 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
3984
3985 return 0;
3986}
3987
3988static ssize_t icnss_regwrite_write(struct file *fp,
3989 const char __user *user_buf,
3990 size_t count, loff_t *off)
3991{
3992 struct icnss_priv *priv =
3993 ((struct seq_file *)fp->private_data)->private;
3994 char buf[64];
3995 char *sptr, *token;
3996 unsigned int len = 0;
3997 uint32_t reg_offset, mem_type, reg_val;
3998 const char *delim = " ";
3999 int ret = 0;
4000
4001 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4002 !test_bit(ICNSS_POWER_ON, &priv->state))
4003 return -EINVAL;
4004
4005 len = min(count, sizeof(buf) - 1);
4006 if (copy_from_user(buf, user_buf, len))
4007 return -EFAULT;
4008
4009 buf[len] = '\0';
4010 sptr = buf;
4011
4012 token = strsep(&sptr, delim);
4013 if (!token)
4014 return -EINVAL;
4015
4016 if (!sptr)
4017 return -EINVAL;
4018
4019 if (kstrtou32(token, 0, &mem_type))
4020 return -EINVAL;
4021
4022 token = strsep(&sptr, delim);
4023 if (!token)
4024 return -EINVAL;
4025
4026 if (!sptr)
4027 return -EINVAL;
4028
4029 if (kstrtou32(token, 0, &reg_offset))
4030 return -EINVAL;
4031
4032 token = strsep(&sptr, delim);
4033 if (!token)
4034 return -EINVAL;
4035
4036 if (kstrtou32(token, 0, &reg_val))
4037 return -EINVAL;
4038
4039 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4040 sizeof(uint32_t),
4041 (uint8_t *)&reg_val);
4042 if (ret)
4043 return ret;
4044
4045 return count;
4046}
4047
4048static int icnss_regwrite_open(struct inode *inode, struct file *file)
4049{
4050 return single_open(file, icnss_regwrite_show, inode->i_private);
4051}
4052
4053static const struct file_operations icnss_regwrite_fops = {
4054 .read = seq_read,
4055 .write = icnss_regwrite_write,
4056 .open = icnss_regwrite_open,
4057 .owner = THIS_MODULE,
4058 .llseek = seq_lseek,
4059};
4060
4061static int icnss_regread_show(struct seq_file *s, void *data)
4062{
4063 struct icnss_priv *priv = s->private;
4064
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304065 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004066 if (!priv->diag_reg_read_buf) {
4067 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4068
4069 if (!test_bit(ICNSS_FW_READY, &priv->state))
4070 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4071
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304072 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004073 return 0;
4074 }
4075
4076 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4077 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4078 priv->diag_reg_read_len);
4079
4080 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4081 priv->diag_reg_read_len, false);
4082
4083 priv->diag_reg_read_len = 0;
4084 kfree(priv->diag_reg_read_buf);
4085 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304086 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004087
4088 return 0;
4089}
4090
4091static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4092 size_t count, loff_t *off)
4093{
4094 struct icnss_priv *priv =
4095 ((struct seq_file *)fp->private_data)->private;
4096 char buf[64];
4097 char *sptr, *token;
4098 unsigned int len = 0;
4099 uint32_t reg_offset, mem_type;
4100 uint32_t data_len = 0;
4101 uint8_t *reg_buf = NULL;
4102 const char *delim = " ";
4103 int ret = 0;
4104
4105 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4106 !test_bit(ICNSS_POWER_ON, &priv->state))
4107 return -EINVAL;
4108
4109 len = min(count, sizeof(buf) - 1);
4110 if (copy_from_user(buf, user_buf, len))
4111 return -EFAULT;
4112
4113 buf[len] = '\0';
4114 sptr = buf;
4115
4116 token = strsep(&sptr, delim);
4117 if (!token)
4118 return -EINVAL;
4119
4120 if (!sptr)
4121 return -EINVAL;
4122
4123 if (kstrtou32(token, 0, &mem_type))
4124 return -EINVAL;
4125
4126 token = strsep(&sptr, delim);
4127 if (!token)
4128 return -EINVAL;
4129
4130 if (!sptr)
4131 return -EINVAL;
4132
4133 if (kstrtou32(token, 0, &reg_offset))
4134 return -EINVAL;
4135
4136 token = strsep(&sptr, delim);
4137 if (!token)
4138 return -EINVAL;
4139
4140 if (kstrtou32(token, 0, &data_len))
4141 return -EINVAL;
4142
4143 if (data_len == 0 ||
4144 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4145 return -EINVAL;
4146
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304147 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004148 kfree(priv->diag_reg_read_buf);
4149 priv->diag_reg_read_buf = NULL;
4150
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004151 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304152 if (!reg_buf) {
4153 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004154 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304155 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004156
4157 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4158 mem_type, data_len,
4159 reg_buf);
4160 if (ret) {
4161 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304162 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004163 return ret;
4164 }
4165
4166 priv->diag_reg_read_addr = reg_offset;
4167 priv->diag_reg_read_mem_type = mem_type;
4168 priv->diag_reg_read_len = data_len;
4169 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304170 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004171
4172 return count;
4173}
4174
4175static int icnss_regread_open(struct inode *inode, struct file *file)
4176{
4177 return single_open(file, icnss_regread_show, inode->i_private);
4178}
4179
4180static const struct file_operations icnss_regread_fops = {
4181 .read = seq_read,
4182 .write = icnss_regread_write,
4183 .open = icnss_regread_open,
4184 .owner = THIS_MODULE,
4185 .llseek = seq_lseek,
4186};
4187
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004188#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004189static int icnss_debugfs_create(struct icnss_priv *priv)
4190{
4191 int ret = 0;
4192 struct dentry *root_dentry;
4193
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004194 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004195
4196 if (IS_ERR(root_dentry)) {
4197 ret = PTR_ERR(root_dentry);
4198 icnss_pr_err("Unable to create debugfs %d\n", ret);
4199 goto out;
4200 }
4201
4202 priv->root_dentry = root_dentry;
4203
Yuanyuan Liu84132752017-05-24 12:07:12 -07004204 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004205 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004206
Yuanyuan Liu84132752017-05-24 12:07:12 -07004207 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004208 &icnss_stats_fops);
4209 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4210 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004211 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004212 &icnss_regwrite_fops);
4213
4214out:
4215 return ret;
4216}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004217#else
4218static int icnss_debugfs_create(struct icnss_priv *priv)
4219{
4220 int ret = 0;
4221 struct dentry *root_dentry;
4222
4223 root_dentry = debugfs_create_dir("icnss", NULL);
4224
4225 if (IS_ERR(root_dentry)) {
4226 ret = PTR_ERR(root_dentry);
4227 icnss_pr_err("Unable to create debugfs %d\n", ret);
4228 return ret;
4229 }
4230
4231 priv->root_dentry = root_dentry;
4232
4233 debugfs_create_file("stats", 0600, root_dentry, priv,
4234 &icnss_stats_fops);
4235 return 0;
4236}
4237#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004238
4239static void icnss_debugfs_destroy(struct icnss_priv *priv)
4240{
4241 debugfs_remove_recursive(priv->root_dentry);
4242}
4243
4244static int icnss_get_vbatt_info(struct icnss_priv *priv)
4245{
4246 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4247 struct qpnp_vadc_chip *vadc_dev = NULL;
4248 int ret = 0;
4249
4250 if (test_bit(VBATT_DISABLE, &quirks)) {
4251 icnss_pr_dbg("VBATT feature is disabled\n");
4252 return ret;
4253 }
4254
4255 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4256 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4257 icnss_pr_err("adc_tm_dev probe defer\n");
4258 return -EPROBE_DEFER;
4259 }
4260
4261 if (IS_ERR(adc_tm_dev)) {
4262 ret = PTR_ERR(adc_tm_dev);
4263 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4264 ret);
4265 return ret;
4266 }
4267
4268 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4269 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4270 icnss_pr_err("vadc_dev probe defer\n");
4271 return -EPROBE_DEFER;
4272 }
4273
4274 if (IS_ERR(vadc_dev)) {
4275 ret = PTR_ERR(vadc_dev);
4276 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4277 ret);
4278 return ret;
4279 }
4280
4281 priv->adc_tm_dev = adc_tm_dev;
4282 priv->vadc_dev = vadc_dev;
4283
4284 return 0;
4285}
4286
4287static int icnss_probe(struct platform_device *pdev)
4288{
4289 int ret = 0;
4290 struct resource *res;
4291 int i;
4292 struct device *dev = &pdev->dev;
4293 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304294 const __be32 *addrp;
4295 u64 prop_size = 0;
4296 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004297
4298 if (penv) {
4299 icnss_pr_err("Driver is already initialized\n");
4300 return -EEXIST;
4301 }
4302
4303 icnss_pr_dbg("Platform driver probe\n");
4304
4305 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4306 if (!priv)
4307 return -ENOMEM;
4308
4309 priv->magic = ICNSS_MAGIC;
4310 dev_set_drvdata(dev, priv);
4311
4312 priv->pdev = pdev;
4313
4314 ret = icnss_get_vbatt_info(priv);
4315 if (ret == -EPROBE_DEFER)
4316 goto out;
4317
Yuanyuan Liu68939762017-04-04 16:43:03 -07004318 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4319 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4320 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4321
4322 if (ret)
4323 goto out;
4324 }
4325
4326 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4327 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4328 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4329 if (ret)
4330 goto out;
4331 }
4332
4333 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4334 priv->bypass_s1_smmu = true;
4335
4336 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4337
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004338 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4339 if (!res) {
4340 icnss_pr_err("Memory base not found in DT\n");
4341 ret = -EINVAL;
4342 goto out;
4343 }
4344
4345 priv->mem_base_pa = res->start;
4346 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4347 resource_size(res));
4348 if (!priv->mem_base_va) {
4349 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4350 &priv->mem_base_pa);
4351 ret = -EINVAL;
4352 goto out;
4353 }
4354 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4355 priv->mem_base_va);
4356
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004357 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4358 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4359 if (!res) {
4360 icnss_pr_err("Fail to get IRQ-%d\n", i);
4361 ret = -ENODEV;
4362 goto out;
4363 } else {
4364 priv->ce_irqs[i] = res->start;
4365 }
4366 }
4367
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304368 np = of_parse_phandle(dev->of_node,
4369 "qcom,wlan-msa-fixed-region", 0);
4370 if (np) {
4371 addrp = of_get_address(np, 0, &prop_size, NULL);
4372 if (!addrp) {
4373 icnss_pr_err("Failed to get assigned-addresses or property\n");
4374 ret = -EINVAL;
4375 goto out;
4376 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004377
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304378 priv->msa_pa = of_translate_address(np, addrp);
4379 if (priv->msa_pa == OF_BAD_ADDR) {
4380 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4381 ret = -EINVAL;
4382 goto out;
4383 }
4384
4385 priv->msa_va = memremap(priv->msa_pa,
4386 (unsigned long)prop_size, MEMREMAP_WT);
4387 if (!priv->msa_va) {
4388 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4389 &priv->msa_pa);
4390 ret = -EINVAL;
4391 goto out;
4392 }
4393 priv->msa_mem_size = prop_size;
4394 } else {
4395 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4396 &priv->msa_mem_size);
4397 if (ret || priv->msa_mem_size == 0) {
4398 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4399 priv->msa_mem_size, ret);
4400 goto out;
4401 }
4402
4403 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4404 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4405
4406 if (!priv->msa_va) {
4407 icnss_pr_err("DMA alloc failed for MSA\n");
4408 ret = -ENOMEM;
4409 goto out;
4410 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004411 }
4412
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304413 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4414 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004415
4416 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4417 "smmu_iova_base");
4418 if (!res) {
4419 icnss_pr_err("SMMU IOVA base not found\n");
4420 } else {
4421 priv->smmu_iova_start = res->start;
4422 priv->smmu_iova_len = resource_size(res);
4423 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4424 &priv->smmu_iova_start, priv->smmu_iova_len);
4425
4426 res = platform_get_resource_byname(pdev,
4427 IORESOURCE_MEM,
4428 "smmu_iova_ipa");
4429 if (!res) {
4430 icnss_pr_err("SMMU IOVA IPA not found\n");
4431 } else {
4432 priv->smmu_iova_ipa_start = res->start;
4433 priv->smmu_iova_ipa_len = resource_size(res);
4434 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4435 &priv->smmu_iova_ipa_start,
4436 priv->smmu_iova_ipa_len);
4437 }
4438
4439 ret = icnss_smmu_init(priv);
4440 if (ret < 0) {
4441 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4442 ret, &priv->smmu_iova_start,
4443 priv->smmu_iova_len);
4444 goto out;
4445 }
4446 }
4447
4448 spin_lock_init(&priv->event_lock);
4449 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304450 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004451
4452 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4453 if (!priv->event_wq) {
4454 icnss_pr_err("Workqueue creation failed\n");
4455 ret = -EFAULT;
4456 goto out_smmu_deinit;
4457 }
4458
4459 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4460 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4461 INIT_LIST_HEAD(&priv->event_list);
4462
4463 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4464 WLFW_SERVICE_VERS_V01,
4465 WLFW_SERVICE_INS_ID_V01,
4466 &wlfw_clnt_nb);
4467 if (ret < 0) {
4468 icnss_pr_err("Notifier register failed: %d\n", ret);
4469 goto out_destroy_wq;
4470 }
4471
4472 icnss_enable_recovery(priv);
4473
4474 icnss_debugfs_create(priv);
4475
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004476 ret = device_init_wakeup(&priv->pdev->dev, true);
4477 if (ret)
4478 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4479 ret);
4480
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004481 penv = priv;
4482
4483 icnss_pr_info("Platform driver probed successfully\n");
4484
4485 return 0;
4486
4487out_destroy_wq:
4488 destroy_workqueue(priv->event_wq);
4489out_smmu_deinit:
4490 icnss_smmu_deinit(priv);
4491out:
4492 dev_set_drvdata(dev, NULL);
4493
4494 return ret;
4495}
4496
4497static int icnss_remove(struct platform_device *pdev)
4498{
4499 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4500
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004501 device_init_wakeup(&penv->pdev->dev, false);
4502
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004503 icnss_debugfs_destroy(penv);
4504
4505 icnss_modem_ssr_unregister_notifier(penv);
4506
4507 destroy_ramdump_device(penv->msa0_dump_dev);
4508
4509 icnss_pdr_unregister_notifier(penv);
4510
4511 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4512 WLFW_SERVICE_VERS_V01,
4513 WLFW_SERVICE_INS_ID_V01,
4514 &wlfw_clnt_nb);
4515 if (penv->event_wq)
4516 destroy_workqueue(penv->event_wq);
4517
4518 icnss_hw_power_off(penv);
4519
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304520 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4521 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004522
4523 dev_set_drvdata(&pdev->dev, NULL);
4524
4525 return 0;
4526}
4527
4528#ifdef CONFIG_PM_SLEEP
4529static int icnss_pm_suspend(struct device *dev)
4530{
4531 struct icnss_priv *priv = dev_get_drvdata(dev);
4532 int ret = 0;
4533
4534 if (priv->magic != ICNSS_MAGIC) {
4535 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4536 dev, priv, priv->magic);
4537 return -EINVAL;
4538 }
4539
Yuanyuan Liu68939762017-04-04 16:43:03 -07004540 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004541
4542 if (!priv->ops || !priv->ops->pm_suspend ||
4543 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4544 goto out;
4545
4546 ret = priv->ops->pm_suspend(dev);
4547
4548out:
4549 if (ret == 0) {
4550 priv->stats.pm_suspend++;
4551 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4552 } else {
4553 priv->stats.pm_suspend_err++;
4554 }
4555 return ret;
4556}
4557
4558static int icnss_pm_resume(struct device *dev)
4559{
4560 struct icnss_priv *priv = dev_get_drvdata(dev);
4561 int ret = 0;
4562
4563 if (priv->magic != ICNSS_MAGIC) {
4564 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4565 dev, priv, priv->magic);
4566 return -EINVAL;
4567 }
4568
Yuanyuan Liu68939762017-04-04 16:43:03 -07004569 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004570
4571 if (!priv->ops || !priv->ops->pm_resume ||
4572 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4573 goto out;
4574
4575 ret = priv->ops->pm_resume(dev);
4576
4577out:
4578 if (ret == 0) {
4579 priv->stats.pm_resume++;
4580 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4581 } else {
4582 priv->stats.pm_resume_err++;
4583 }
4584 return ret;
4585}
4586
4587static int icnss_pm_suspend_noirq(struct device *dev)
4588{
4589 struct icnss_priv *priv = dev_get_drvdata(dev);
4590 int ret = 0;
4591
4592 if (priv->magic != ICNSS_MAGIC) {
4593 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4594 dev, priv, priv->magic);
4595 return -EINVAL;
4596 }
4597
Yuanyuan Liu68939762017-04-04 16:43:03 -07004598 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004599
4600 if (!priv->ops || !priv->ops->suspend_noirq ||
4601 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4602 goto out;
4603
4604 ret = priv->ops->suspend_noirq(dev);
4605
4606out:
4607 if (ret == 0) {
4608 priv->stats.pm_suspend_noirq++;
4609 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4610 } else {
4611 priv->stats.pm_suspend_noirq_err++;
4612 }
4613 return ret;
4614}
4615
4616static int icnss_pm_resume_noirq(struct device *dev)
4617{
4618 struct icnss_priv *priv = dev_get_drvdata(dev);
4619 int ret = 0;
4620
4621 if (priv->magic != ICNSS_MAGIC) {
4622 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4623 dev, priv, priv->magic);
4624 return -EINVAL;
4625 }
4626
Yuanyuan Liu68939762017-04-04 16:43:03 -07004627 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004628
4629 if (!priv->ops || !priv->ops->resume_noirq ||
4630 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4631 goto out;
4632
4633 ret = priv->ops->resume_noirq(dev);
4634
4635out:
4636 if (ret == 0) {
4637 priv->stats.pm_resume_noirq++;
4638 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4639 } else {
4640 priv->stats.pm_resume_noirq_err++;
4641 }
4642 return ret;
4643}
4644#endif
4645
4646static const struct dev_pm_ops icnss_pm_ops = {
4647 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4648 icnss_pm_resume)
4649 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4650 icnss_pm_resume_noirq)
4651};
4652
4653static const struct of_device_id icnss_dt_match[] = {
4654 {.compatible = "qcom,icnss"},
4655 {}
4656};
4657
4658MODULE_DEVICE_TABLE(of, icnss_dt_match);
4659
4660static struct platform_driver icnss_driver = {
4661 .probe = icnss_probe,
4662 .remove = icnss_remove,
4663 .driver = {
4664 .name = "icnss",
4665 .pm = &icnss_pm_ops,
4666 .owner = THIS_MODULE,
4667 .of_match_table = icnss_dt_match,
4668 },
4669};
4670
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004671static int __init icnss_initialize(void)
4672{
4673 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4674 "icnss", 0);
4675 if (!icnss_ipc_log_context)
4676 icnss_pr_err("Unable to create log context\n");
4677
Yuanyuan Liu68939762017-04-04 16:43:03 -07004678 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4679 "icnss_long", 0);
4680 if (!icnss_ipc_log_long_context)
4681 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004682
4683 return platform_driver_register(&icnss_driver);
4684}
4685
4686static void __exit icnss_exit(void)
4687{
4688 platform_driver_unregister(&icnss_driver);
4689 ipc_log_context_destroy(icnss_ipc_log_context);
4690 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004691 ipc_log_context_destroy(icnss_ipc_log_long_context);
4692 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004693}
4694
4695
4696module_init(icnss_initialize);
4697module_exit(icnss_exit);
4698
4699MODULE_LICENSE("GPL v2");
4700MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");