blob: e3a50e34292af41ef3585373407dd91e902c21fc [file] [log] [blame]
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08001/* Copyright (c) 2015-2018, 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>
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -080041#include <linux/of_gpio.h>
Yuanyuan Liu607051c2016-11-28 17:04:13 -080042#include <soc/qcom/memory_dump.h>
43#include <soc/qcom/icnss.h>
44#include <soc/qcom/msm_qmi_interface.h>
45#include <soc/qcom/secure_buffer.h>
46#include <soc/qcom/subsystem_notif.h>
47#include <soc/qcom/subsystem_restart.h>
48#include <soc/qcom/service-locator.h>
49#include <soc/qcom/service-notifier.h>
50#include <soc/qcom/socinfo.h>
51#include <soc/qcom/ramdump.h>
52
53#include "wlan_firmware_service_v01.h"
54
55#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080056unsigned long qmi_timeout = 10000;
Yuanyuan Liu607051c2016-11-28 17:04:13 -080057module_param(qmi_timeout, ulong, 0600);
58
59#define WLFW_TIMEOUT_MS qmi_timeout
60#else
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080061#define WLFW_TIMEOUT_MS 10000
Yuanyuan Liu607051c2016-11-28 17:04:13 -080062#endif
63#define WLFW_SERVICE_INS_ID_V01 0
64#define WLFW_CLIENT_ID 0x4b4e454c
65#define MAX_PROP_SIZE 32
66#define NUM_LOG_PAGES 10
Yuanyuan Liu68939762017-04-04 16:43:03 -070067#define NUM_LOG_LONG_PAGES 4
Yuanyuan Liu607051c2016-11-28 17:04:13 -080068#define ICNSS_MAGIC 0x5abc5abc
69
Yuanyuan Liu607051c2016-11-28 17:04:13 -080070#define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN"
71#define ICNSS_WLAN_SERVICE_NAME "wlan/fw"
72
73#define ICNSS_THRESHOLD_HIGH 3600000
74#define ICNSS_THRESHOLD_LOW 3450000
75#define ICNSS_THRESHOLD_GUARD 20000
76
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -070077#define ICNSS_MAX_PROBE_CNT 2
78
Yuanyuan Liu607051c2016-11-28 17:04:13 -080079#define icnss_ipc_log_string(_x...) do { \
80 if (icnss_ipc_log_context) \
81 ipc_log_string(icnss_ipc_log_context, _x); \
82 } while (0)
83
Yuanyuan Liu607051c2016-11-28 17:04:13 -080084#define icnss_ipc_log_long_string(_x...) do { \
85 if (icnss_ipc_log_long_context) \
86 ipc_log_string(icnss_ipc_log_long_context, _x); \
87 } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -080088
89#define icnss_pr_err(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070090 printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__); \
91 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
92 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080093 } while (0)
94
95#define icnss_pr_warn(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070096 printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__); \
97 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
98 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080099 } while (0)
100
101#define icnss_pr_info(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700102 printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__); \
103 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
104 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800105 } while (0)
106
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700107#if defined(CONFIG_DYNAMIC_DEBUG)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800108#define icnss_pr_dbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700109 pr_debug(_fmt, ##__VA_ARGS__); \
110 icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800111 } while (0)
112
Yuanyuan Liu68939762017-04-04 16:43:03 -0700113#define icnss_pr_vdbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700114 pr_debug(_fmt, ##__VA_ARGS__); \
115 icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800116 } while (0)
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700117#elif defined(DEBUG)
118#define icnss_pr_dbg(_fmt, ...) do { \
119 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
120 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
121 ##__VA_ARGS__); \
122 } while (0)
123
124#define icnss_pr_vdbg(_fmt, ...) do { \
125 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
126 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
127 ##__VA_ARGS__); \
128 } while (0)
129#else
130#define icnss_pr_dbg(_fmt, ...) do { \
131 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
132 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
133 ##__VA_ARGS__); \
134 } while (0)
135
136#define icnss_pr_vdbg(_fmt, ...) do { \
137 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
138 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
139 ##__VA_ARGS__); \
140 } while (0)
141#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800142
143#ifdef CONFIG_ICNSS_DEBUG
144#define ICNSS_ASSERT(_condition) do { \
145 if (!(_condition)) { \
Yuanyuan Liu68939762017-04-04 16:43:03 -0700146 icnss_pr_err("ASSERT at line %d\n", __LINE__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800147 BUG_ON(1); \
148 } \
149 } while (0)
Yuanyuan Liu68939762017-04-04 16:43:03 -0700150
151bool ignore_qmi_timeout;
152#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800153#else
Yuanyuan Liu68939762017-04-04 16:43:03 -0700154#define ICNSS_ASSERT(_condition) do { } while (0)
155#define ICNSS_QMI_ASSERT() do { } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800156#endif
157
Anurag Chouhan52a48a12017-09-08 18:40:14 +0530158#define QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED 0x77
159
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800160enum icnss_debug_quirks {
161 HW_ALWAYS_ON,
162 HW_DEBUG_ENABLE,
163 SKIP_QMI,
164 HW_ONLY_TOP_LEVEL_RESET,
165 RECOVERY_DISABLE,
166 SSR_ONLY,
167 PDR_ONLY,
168 VBATT_DISABLE,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800169 FW_REJUVENATE_ENABLE,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800170};
171
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800172#define ICNSS_QUIRKS_DEFAULT (BIT(VBATT_DISABLE) | \
173 BIT(FW_REJUVENATE_ENABLE))
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800174
175unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
176module_param(quirks, ulong, 0600);
177
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800178uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01;
179module_param(dynamic_feature_mask, ullong, 0600);
180
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800181void *icnss_ipc_log_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800182void *icnss_ipc_log_long_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800183
184#define ICNSS_EVENT_PENDING 2989
185
186#define ICNSS_EVENT_SYNC BIT(0)
187#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
188#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
189 ICNSS_EVENT_SYNC)
190
191enum icnss_driver_event_type {
192 ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
193 ICNSS_DRIVER_EVENT_SERVER_EXIT,
194 ICNSS_DRIVER_EVENT_FW_READY_IND,
195 ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
196 ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
197 ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
198 ICNSS_DRIVER_EVENT_MAX,
199};
200
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530201enum icnss_msa_perm {
202 ICNSS_MSA_PERM_HLOS_ALL = 0,
203 ICNSS_MSA_PERM_WLAN_HW_RW = 1,
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530204 ICNSS_MSA_PERM_MAX,
205};
206
207#define ICNSS_MAX_VMIDS 4
208
209struct icnss_mem_region_info {
210 uint64_t reg_addr;
211 uint32_t size;
212 uint8_t secure_flag;
213 enum icnss_msa_perm perm;
214};
215
216struct icnss_msa_perm_list_t {
217 int vmids[ICNSS_MAX_VMIDS];
218 int perms[ICNSS_MAX_VMIDS];
219 int nelems;
220};
221
222struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
223 [ICNSS_MSA_PERM_HLOS_ALL] = {
224 .vmids = {VMID_HLOS},
225 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
226 .nelems = 1,
227 },
228
229 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
230 .vmids = {VMID_MSS_MSA, VMID_WLAN},
231 .perms = {PERM_READ | PERM_WRITE,
232 PERM_READ | PERM_WRITE},
233 .nelems = 2,
234 },
235
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530236};
237
238struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
239 [ICNSS_MSA_PERM_HLOS_ALL] = {
240 .vmids = {VMID_HLOS},
241 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
242 .nelems = 1,
243 },
244
245 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
246 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
247 .perms = {PERM_READ | PERM_WRITE,
248 PERM_READ | PERM_WRITE,
249 PERM_READ | PERM_WRITE},
250 .nelems = 3,
251 },
252
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530253};
254
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800255struct icnss_event_pd_service_down_data {
256 bool crashed;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800257 bool fw_rejuvenate;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800258};
259
260struct icnss_driver_event {
261 struct list_head list;
262 enum icnss_driver_event_type type;
263 bool sync;
264 struct completion complete;
265 int ret;
266 void *data;
267};
268
269enum icnss_driver_state {
270 ICNSS_WLFW_QMI_CONNECTED,
271 ICNSS_POWER_ON,
272 ICNSS_FW_READY,
273 ICNSS_DRIVER_PROBED,
274 ICNSS_FW_TEST_MODE,
275 ICNSS_PM_SUSPEND,
276 ICNSS_PM_SUSPEND_NOIRQ,
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -0700277 ICNSS_SSR_REGISTERED,
278 ICNSS_PDR_REGISTERED,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800279 ICNSS_PD_RESTART,
280 ICNSS_MSA0_ASSIGNED,
281 ICNSS_WLFW_EXISTS,
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +0530282 ICNSS_SHUTDOWN_DONE,
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700283 ICNSS_HOST_TRIGGERED_PDR,
Sameer Thalappil93c00732017-08-18 13:02:32 -0700284 ICNSS_FW_DOWN,
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -0800285 ICNSS_DRIVER_UNLOADING,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800286};
287
288struct ce_irq_list {
289 int irq;
290 irqreturn_t (*handler)(int, void *);
291};
292
Yuanyuan Liu68939762017-04-04 16:43:03 -0700293struct icnss_vreg_info {
294 struct regulator *reg;
295 const char *name;
296 u32 min_v;
297 u32 max_v;
298 u32 load_ua;
299 unsigned long settle_delay;
300 bool required;
301};
302
303struct icnss_clk_info {
304 struct clk *handle;
305 const char *name;
306 u32 freq;
307 bool required;
308};
309
310static struct icnss_vreg_info icnss_vreg_info[] = {
311 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
312 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
313 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
314 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
315};
316
317#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
318
319static struct icnss_clk_info icnss_clk_info[] = {
320 {NULL, "cxo_ref_clk_pin", 0, false},
321};
322
323#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
324
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800325struct icnss_stats {
326 struct {
327 uint32_t posted;
328 uint32_t processed;
329 } events[ICNSS_DRIVER_EVENT_MAX];
330
331 struct {
332 uint32_t request;
333 uint32_t free;
334 uint32_t enable;
335 uint32_t disable;
336 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
337
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700338 struct {
339 uint32_t pdr_fw_crash;
340 uint32_t pdr_host_error;
341 uint32_t root_pd_crash;
342 uint32_t root_pd_shutdown;
343 } recovery;
344
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800345 uint32_t pm_suspend;
346 uint32_t pm_suspend_err;
347 uint32_t pm_resume;
348 uint32_t pm_resume_err;
349 uint32_t pm_suspend_noirq;
350 uint32_t pm_suspend_noirq_err;
351 uint32_t pm_resume_noirq;
352 uint32_t pm_resume_noirq_err;
353 uint32_t pm_stay_awake;
354 uint32_t pm_relax;
355
356 uint32_t ind_register_req;
357 uint32_t ind_register_resp;
358 uint32_t ind_register_err;
359 uint32_t msa_info_req;
360 uint32_t msa_info_resp;
361 uint32_t msa_info_err;
362 uint32_t msa_ready_req;
363 uint32_t msa_ready_resp;
364 uint32_t msa_ready_err;
365 uint32_t msa_ready_ind;
366 uint32_t cap_req;
367 uint32_t cap_resp;
368 uint32_t cap_err;
369 uint32_t pin_connect_result;
370 uint32_t cfg_req;
371 uint32_t cfg_resp;
372 uint32_t cfg_req_err;
373 uint32_t mode_req;
374 uint32_t mode_resp;
375 uint32_t mode_req_err;
376 uint32_t ini_req;
377 uint32_t ini_resp;
378 uint32_t ini_req_err;
379 uint32_t vbatt_req;
380 uint32_t vbatt_resp;
381 uint32_t vbatt_req_err;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800382 u32 rejuvenate_ind;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800383 uint32_t rejuvenate_ack_req;
384 uint32_t rejuvenate_ack_resp;
385 uint32_t rejuvenate_ack_err;
386};
387
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700388enum icnss_pdr_cause_index {
389 ICNSS_FW_CRASH,
390 ICNSS_ROOT_PD_CRASH,
391 ICNSS_ROOT_PD_SHUTDOWN,
392 ICNSS_HOST_ERROR,
393};
394
395static const char * const icnss_pdr_cause[] = {
396 [ICNSS_FW_CRASH] = "FW crash",
397 [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
398 [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
399 [ICNSS_HOST_ERROR] = "Host error",
400};
401
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800402struct service_notifier_context {
403 void *handle;
404 uint32_t instance_id;
405 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800406};
407
408static struct icnss_priv {
409 uint32_t magic;
410 struct platform_device *pdev;
411 struct icnss_driver_ops *ops;
412 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700413 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
414 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800415 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
416 phys_addr_t mem_base_pa;
417 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800418 struct dma_iommu_mapping *smmu_mapping;
419 dma_addr_t smmu_iova_start;
420 size_t smmu_iova_len;
421 dma_addr_t smmu_iova_ipa_start;
422 size_t smmu_iova_ipa_len;
423 struct qmi_handle *wlfw_clnt;
424 struct list_head event_list;
425 spinlock_t event_lock;
426 struct work_struct event_work;
427 struct work_struct qmi_recv_msg_work;
428 struct workqueue_struct *event_wq;
429 phys_addr_t msa_pa;
430 uint32_t msa_mem_size;
431 void *msa_va;
432 unsigned long state;
433 struct wlfw_rf_chip_info_s_v01 chip_info;
434 struct wlfw_rf_board_info_s_v01 board_info;
435 struct wlfw_soc_info_s_v01 soc_info;
436 struct wlfw_fw_version_info_s_v01 fw_version_info;
437 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
438 u32 pwr_pin_result;
439 u32 phy_io_pin_result;
440 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700441 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800442 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700443 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800444 struct dentry *root_dentry;
445 spinlock_t on_off_lock;
446 struct icnss_stats stats;
447 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800448 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800449 struct notifier_block service_notifier_nb;
450 int total_domains;
451 struct notifier_block get_service_nb;
452 void *modem_notify_handler;
453 struct notifier_block modem_ssr_nb;
454 uint32_t diag_reg_read_addr;
455 uint32_t diag_reg_read_mem_type;
456 uint32_t diag_reg_read_len;
457 uint8_t *diag_reg_read_buf;
458 struct qpnp_adc_tm_btm_param vph_monitor_params;
459 struct qpnp_adc_tm_chip *adc_tm_dev;
460 struct qpnp_vadc_chip *vadc_dev;
461 uint64_t vph_pwr;
462 atomic_t pm_count;
463 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700464 bool bypass_s1_smmu;
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -0800465 bool force_err_fatal;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -0800466 bool allow_recursive_recovery;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800467 u8 cause_for_rejuvenation;
468 u8 requesting_sub_system;
469 u16 line_number;
470 char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +0530471 struct mutex dev_lock;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800472} *penv;
473
Yuanyuan Liu68939762017-04-04 16:43:03 -0700474#ifdef CONFIG_ICNSS_DEBUG
475static void icnss_ignore_qmi_timeout(bool ignore)
476{
477 ignore_qmi_timeout = ignore;
478}
479#else
480static void icnss_ignore_qmi_timeout(bool ignore) { }
481#endif
482
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530483static int icnss_assign_msa_perm(struct icnss_mem_region_info
484 *mem_region, enum icnss_msa_perm new_perm)
485{
486 int ret = 0;
487 phys_addr_t addr;
488 u32 size;
489 u32 i = 0;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530490 u32 source_vmids[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530491 u32 source_nelems;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530492 u32 dest_vmids[ICNSS_MAX_VMIDS] = {0};
493 u32 dest_perms[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530494 u32 dest_nelems;
495 enum icnss_msa_perm cur_perm = mem_region->perm;
496 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
497
498 addr = mem_region->reg_addr;
499 size = mem_region->size;
500
501 if (mem_region->secure_flag) {
502 new_perm_list = &msa_perm_secure_list[new_perm];
503 old_perm_list = &msa_perm_secure_list[cur_perm];
504 } else {
505 new_perm_list = &msa_perm_list[new_perm];
506 old_perm_list = &msa_perm_list[cur_perm];
507 }
508
509 source_nelems = old_perm_list->nelems;
510 dest_nelems = new_perm_list->nelems;
511
512 for (i = 0; i < source_nelems; ++i)
513 source_vmids[i] = old_perm_list->vmids[i];
514
515 for (i = 0; i < dest_nelems; ++i) {
516 dest_vmids[i] = new_perm_list->vmids[i];
517 dest_perms[i] = new_perm_list->perms[i];
518 }
519
520 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
521 dest_vmids, dest_perms, dest_nelems);
522 if (ret) {
523 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
524 &addr, size, ret);
525 goto out;
526 }
527
528 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
529 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
530 source_nelems, source_vmids[0], source_vmids[1],
531 source_vmids[2], source_vmids[3], dest_nelems,
532 dest_vmids[0], dest_vmids[1], dest_vmids[2],
533 dest_vmids[3]);
534out:
535 return ret;
536}
537
538static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
539 enum icnss_msa_perm new_perm)
540{
541 int ret;
542 int i;
543 enum icnss_msa_perm old_perm;
544
Yuanyuan Liu26ec2212017-09-01 10:34:25 -0700545 if (priv->nr_mem_region > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
546 icnss_pr_err("Invalid memory region len %d\n",
547 priv->nr_mem_region);
548 return -EINVAL;
549 }
550
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530551 for (i = 0; i < priv->nr_mem_region; i++) {
552 old_perm = priv->mem_region[i].perm;
553 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
554 if (ret)
555 goto err_unmap;
556 priv->mem_region[i].perm = new_perm;
557 }
558 return 0;
559
560err_unmap:
561 for (i--; i >= 0; i--) {
562 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
563 }
564 return ret;
565}
566
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800567static void icnss_pm_stay_awake(struct icnss_priv *priv)
568{
569 if (atomic_inc_return(&priv->pm_count) != 1)
570 return;
571
Yuanyuan Liu68939762017-04-04 16:43:03 -0700572 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800573 atomic_read(&priv->pm_count));
574
575 pm_stay_awake(&priv->pdev->dev);
576
577 priv->stats.pm_stay_awake++;
578}
579
580static void icnss_pm_relax(struct icnss_priv *priv)
581{
582 int r = atomic_dec_return(&priv->pm_count);
583
584 WARN_ON(r < 0);
585
586 if (r != 0)
587 return;
588
Yuanyuan Liu68939762017-04-04 16:43:03 -0700589 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800590 atomic_read(&priv->pm_count));
591
592 pm_relax(&priv->pdev->dev);
593 priv->stats.pm_relax++;
594}
595
596static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
597{
598 switch (type) {
599 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
600 return "SERVER_ARRIVE";
601 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
602 return "SERVER_EXIT";
603 case ICNSS_DRIVER_EVENT_FW_READY_IND:
604 return "FW_READY";
605 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
606 return "REGISTER_DRIVER";
607 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
608 return "UNREGISTER_DRIVER";
609 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
610 return "PD_SERVICE_DOWN";
611 case ICNSS_DRIVER_EVENT_MAX:
612 return "EVENT_MAX";
613 }
614
615 return "UNKNOWN";
616};
617
618static int icnss_driver_event_post(enum icnss_driver_event_type type,
619 u32 flags, void *data)
620{
621 struct icnss_driver_event *event;
622 unsigned long irq_flags;
623 int gfp = GFP_KERNEL;
624 int ret = 0;
625
626 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
627 icnss_driver_event_to_str(type), type, current->comm,
628 flags, penv->state);
629
630 if (type >= ICNSS_DRIVER_EVENT_MAX) {
631 icnss_pr_err("Invalid Event type: %d, can't post", type);
632 return -EINVAL;
633 }
634
635 if (in_interrupt() || irqs_disabled())
636 gfp = GFP_ATOMIC;
637
638 event = kzalloc(sizeof(*event), gfp);
639 if (event == NULL)
640 return -ENOMEM;
641
642 icnss_pm_stay_awake(penv);
643
644 event->type = type;
645 event->data = data;
646 init_completion(&event->complete);
647 event->ret = ICNSS_EVENT_PENDING;
648 event->sync = !!(flags & ICNSS_EVENT_SYNC);
649
650 spin_lock_irqsave(&penv->event_lock, irq_flags);
651 list_add_tail(&event->list, &penv->event_list);
652 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
653
654 penv->stats.events[type].posted++;
655 queue_work(penv->event_wq, &penv->event_work);
656
657 if (!(flags & ICNSS_EVENT_SYNC))
658 goto out;
659
660 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
661 wait_for_completion(&event->complete);
662 else
663 ret = wait_for_completion_interruptible(&event->complete);
664
665 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
666 icnss_driver_event_to_str(type), type, penv->state, ret,
667 event->ret);
668
669 spin_lock_irqsave(&penv->event_lock, irq_flags);
670 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
671 event->sync = false;
672 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
673 ret = -EINTR;
674 goto out;
675 }
676 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
677
678 ret = event->ret;
679 kfree(event);
680
681out:
682 icnss_pm_relax(penv);
683 return ret;
684}
685
686static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
687 uint64_t voltage_uv)
688{
689 int ret;
690 struct wlfw_vbatt_req_msg_v01 req;
691 struct wlfw_vbatt_resp_msg_v01 resp;
692 struct msg_desc req_desc, resp_desc;
693
694 if (!priv->wlfw_clnt) {
695 ret = -ENODEV;
696 goto out;
697 }
698
699 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
700 penv->state);
701
702 memset(&req, 0, sizeof(req));
703 memset(&resp, 0, sizeof(resp));
704
705 req.voltage_uv = voltage_uv;
706
707 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
708 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
709 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
710
711 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
712 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
713 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
714
715 priv->stats.vbatt_req++;
716
717 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
718 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
719 if (ret < 0) {
720 icnss_pr_err("Send vbatt req failed %d\n", ret);
721 goto out;
722 }
723
724 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
725 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
726 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +0530727 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800728 goto out;
729 }
730 priv->stats.vbatt_resp++;
731
732out:
733 priv->stats.vbatt_req_err++;
734 return ret;
735}
736
737static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
738{
739 int ret = 0;
740 struct qpnp_vadc_result adc_result;
741
742 if (!priv->vadc_dev) {
743 icnss_pr_err("VADC dev doesn't exists\n");
744 ret = -EINVAL;
745 goto out;
746 }
747
748 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
749 if (ret) {
750 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
751 VADC_VPH_PWR, ret);
752 goto out;
753 }
754
755 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
756 adc_result.physical, adc_result.measurement);
757
758 *result_uv = adc_result.physical;
759out:
760 return ret;
761}
762
763static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
764{
765 struct icnss_priv *priv = ctx;
766 uint64_t vph_pwr = 0;
767 uint64_t vph_pwr_prev;
768 int ret = 0;
769 bool update = true;
770
771 if (!priv) {
772 icnss_pr_err("Priv pointer is NULL\n");
773 return;
774 }
775
776 vph_pwr_prev = priv->vph_pwr;
777
778 ret = icnss_get_phone_power(priv, &vph_pwr);
779 if (ret)
780 return;
781
782 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
783 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
784 update = false;
785 priv->vph_monitor_params.state_request =
786 ADC_TM_HIGH_THR_ENABLE;
787 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
788 ICNSS_THRESHOLD_GUARD;
789 priv->vph_monitor_params.low_thr = 0;
790 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
791 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
792 update = false;
793 priv->vph_monitor_params.state_request =
794 ADC_TM_LOW_THR_ENABLE;
795 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
796 ICNSS_THRESHOLD_GUARD;
797 priv->vph_monitor_params.high_thr = 0;
798 } else {
799 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
800 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
801 update = false;
802 priv->vph_monitor_params.state_request =
803 ADC_TM_HIGH_LOW_THR_ENABLE;
804 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
805 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
806 }
807
808 priv->vph_pwr = vph_pwr;
809
810 if (update)
811 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
812
813 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
814 priv->vph_monitor_params.low_thr,
815 priv->vph_monitor_params.high_thr);
816 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
817 &priv->vph_monitor_params);
818 if (ret)
819 icnss_pr_err("TM channel setup failed %d\n", ret);
820}
821
822static int icnss_setup_vph_monitor(struct icnss_priv *priv)
823{
824 int ret = 0;
825
826 if (!priv->adc_tm_dev) {
827 icnss_pr_err("ADC TM handler is NULL\n");
828 ret = -EINVAL;
829 goto out;
830 }
831
832 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
833 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
834 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
835 priv->vph_monitor_params.channel = VADC_VPH_PWR;
836 priv->vph_monitor_params.btm_ctx = priv;
837 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
838 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
839 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
840 priv->vph_monitor_params.low_thr,
841 priv->vph_monitor_params.high_thr);
842
843 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
844 &priv->vph_monitor_params);
845 if (ret)
846 icnss_pr_err("TM channel setup failed %d\n", ret);
847out:
848 return ret;
849}
850
851static int icnss_init_vph_monitor(struct icnss_priv *priv)
852{
853 int ret = 0;
854
855 if (test_bit(VBATT_DISABLE, &quirks))
856 goto out;
857
858 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
859 if (ret)
860 goto out;
861
862 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
863
864 ret = icnss_setup_vph_monitor(priv);
865 if (ret)
866 goto out;
867out:
868 return ret;
869}
870
871
872static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
873{
874 struct msg_desc ind_desc;
875 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
876 int ret = 0;
877
878 if (!penv || !penv->wlfw_clnt) {
879 ret = -ENODEV;
880 goto out;
881 }
882
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530883 memset(&ind_msg, 0, sizeof(ind_msg));
884
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800885 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
886 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
887 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
888
889 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
890 if (ret < 0) {
891 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
892 ret, msg_len);
893 goto out;
894 }
895
896 /* store pin result locally */
897 if (ind_msg.pwr_pin_result_valid)
898 penv->pwr_pin_result = ind_msg.pwr_pin_result;
899 if (ind_msg.phy_io_pin_result_valid)
900 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
901 if (ind_msg.rf_pin_result_valid)
902 penv->rf_pin_result = ind_msg.rf_pin_result;
903
904 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
905 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
906 ind_msg.rf_pin_result);
907
908 penv->stats.pin_connect_result++;
909out:
910 return ret;
911}
912
Yuanyuan Liu68939762017-04-04 16:43:03 -0700913static int icnss_vreg_on(struct icnss_priv *priv)
914{
915 int ret = 0;
916 struct icnss_vreg_info *vreg_info;
917 int i;
918
919 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
920 vreg_info = &priv->vreg_info[i];
921
922 if (!vreg_info->reg)
923 continue;
924
925 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
926
927 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
928 vreg_info->max_v);
929 if (ret) {
930 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
931 vreg_info->name, vreg_info->min_v,
932 vreg_info->max_v, ret);
933 break;
934 }
935
936 if (vreg_info->load_ua) {
937 ret = regulator_set_load(vreg_info->reg,
938 vreg_info->load_ua);
939 if (ret < 0) {
940 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
941 vreg_info->name,
942 vreg_info->load_ua, ret);
943 break;
944 }
945 }
946
947 ret = regulator_enable(vreg_info->reg);
948 if (ret) {
949 icnss_pr_err("Regulator %s, can't enable: %d\n",
950 vreg_info->name, ret);
951 break;
952 }
953
954 if (vreg_info->settle_delay)
955 udelay(vreg_info->settle_delay);
956 }
957
958 if (!ret)
959 return 0;
960
961 for (; i >= 0; i--) {
962 vreg_info = &priv->vreg_info[i];
963
964 if (!vreg_info->reg)
965 continue;
966
967 regulator_disable(vreg_info->reg);
968 regulator_set_load(vreg_info->reg, 0);
969 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
970 }
971
972 return ret;
973}
974
975static int icnss_vreg_off(struct icnss_priv *priv)
976{
977 int ret = 0;
978 struct icnss_vreg_info *vreg_info;
979 int i;
980
981 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
982 vreg_info = &priv->vreg_info[i];
983
984 if (!vreg_info->reg)
985 continue;
986
987 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
988
989 ret = regulator_disable(vreg_info->reg);
990 if (ret)
991 icnss_pr_err("Regulator %s, can't disable: %d\n",
992 vreg_info->name, ret);
993
994 ret = regulator_set_load(vreg_info->reg, 0);
995 if (ret < 0)
996 icnss_pr_err("Regulator %s, can't set load: %d\n",
997 vreg_info->name, ret);
998
999 ret = regulator_set_voltage(vreg_info->reg, 0,
1000 vreg_info->max_v);
1001 if (ret)
1002 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
1003 vreg_info->name, ret);
1004 }
1005
1006 return ret;
1007}
1008
1009static int icnss_clk_init(struct icnss_priv *priv)
1010{
1011 struct icnss_clk_info *clk_info;
1012 int i;
1013 int ret = 0;
1014
1015 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1016 clk_info = &priv->clk_info[i];
1017
1018 if (!clk_info->handle)
1019 continue;
1020
1021 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1022
1023 if (clk_info->freq) {
1024 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1025
1026 if (ret) {
1027 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1028 clk_info->name, clk_info->freq,
1029 ret);
1030 break;
1031 }
1032 }
1033
1034 ret = clk_prepare_enable(clk_info->handle);
1035 if (ret) {
1036 icnss_pr_err("Clock %s, can't enable: %d\n",
1037 clk_info->name, ret);
1038 break;
1039 }
1040 }
1041
1042 if (ret == 0)
1043 return 0;
1044
1045 for (; i >= 0; i--) {
1046 clk_info = &priv->clk_info[i];
1047
1048 if (!clk_info->handle)
1049 continue;
1050
1051 clk_disable_unprepare(clk_info->handle);
1052 }
1053
1054 return ret;
1055}
1056
1057static int icnss_clk_deinit(struct icnss_priv *priv)
1058{
1059 struct icnss_clk_info *clk_info;
1060 int i;
1061
1062 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1063 clk_info = &priv->clk_info[i];
1064
1065 if (!clk_info->handle)
1066 continue;
1067
1068 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1069
1070 clk_disable_unprepare(clk_info->handle);
1071 }
1072
1073 return 0;
1074}
1075
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001076static int icnss_hw_power_on(struct icnss_priv *priv)
1077{
1078 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001079
1080 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1081
Yuanyuan Liu68939762017-04-04 16:43:03 -07001082 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001083 if (test_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 return ret;
1086 }
1087 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001088 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001089
Yuanyuan Liu68939762017-04-04 16:43:03 -07001090 ret = icnss_vreg_on(priv);
1091 if (ret)
1092 goto out;
1093
1094 ret = icnss_clk_init(priv);
1095 if (ret)
1096 goto vreg_off;
1097
1098 return ret;
1099
1100vreg_off:
1101 icnss_vreg_off(priv);
1102out:
1103 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001104 return ret;
1105}
1106
1107static int icnss_hw_power_off(struct icnss_priv *priv)
1108{
1109 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001110
1111 if (test_bit(HW_ALWAYS_ON, &quirks))
1112 return 0;
1113
Yuanyuan Liu16c89c32018-01-10 15:52:06 -08001114 if (test_bit(ICNSS_FW_DOWN, &priv->state))
1115 return 0;
1116
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001117 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1118
Yuanyuan Liu68939762017-04-04 16:43:03 -07001119 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001120 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001121 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001122 return ret;
1123 }
1124 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001125 spin_unlock(&priv->on_off_lock);
1126
1127 icnss_clk_deinit(priv);
1128
1129 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001130
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001131 return ret;
1132}
1133
1134int icnss_power_on(struct device *dev)
1135{
1136 struct icnss_priv *priv = dev_get_drvdata(dev);
1137
1138 if (!priv) {
1139 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1140 dev, priv);
1141 return -EINVAL;
1142 }
1143
1144 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1145
1146 return icnss_hw_power_on(priv);
1147}
1148EXPORT_SYMBOL(icnss_power_on);
1149
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001150bool icnss_is_fw_ready(void)
1151{
1152 if (!penv)
1153 return false;
1154 else
1155 return test_bit(ICNSS_FW_READY, &penv->state);
1156}
1157EXPORT_SYMBOL(icnss_is_fw_ready);
1158
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001159bool icnss_is_fw_down(void)
1160{
1161 if (!penv)
1162 return false;
1163 else
1164 return test_bit(ICNSS_FW_DOWN, &penv->state);
1165}
1166EXPORT_SYMBOL(icnss_is_fw_down);
1167
1168
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001169int icnss_power_off(struct device *dev)
1170{
1171 struct icnss_priv *priv = dev_get_drvdata(dev);
1172
1173 if (!priv) {
1174 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1175 dev, priv);
1176 return -EINVAL;
1177 }
1178
1179 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1180
1181 return icnss_hw_power_off(priv);
1182}
1183EXPORT_SYMBOL(icnss_power_off);
1184
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001185static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
1186{
1187 struct icnss_priv *priv = ctx;
1188
1189 if (priv)
1190 priv->force_err_fatal = true;
1191
1192 icnss_pr_err("Received force error fatal request from FW\n");
1193
1194 return IRQ_HANDLED;
1195}
1196
1197static void icnss_register_force_error_fatal(struct icnss_priv *priv)
1198{
1199 int gpio, irq, ret;
1200
1201 if (!of_find_property(priv->pdev->dev.of_node,
1202 "qcom,gpio-force-fatal-error", NULL)) {
1203 icnss_pr_dbg("Error fatal smp2p handler not registered\n");
1204 return;
1205 }
1206 gpio = of_get_named_gpio(priv->pdev->dev.of_node,
1207 "qcom,gpio-force-fatal-error", 0);
1208 if (!gpio_is_valid(gpio)) {
1209 icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio);
1210 return;
1211 }
1212 irq = gpio_to_irq(gpio);
1213 if (irq < 0) {
1214 icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq);
1215 return;
1216 }
1217 ret = request_irq(irq, fw_error_fatal_handler,
1218 IRQF_TRIGGER_RISING, "wlanfw-err", priv);
1219 if (ret < 0) {
1220 icnss_pr_err("Unable to regiser for error fatal IRQ handler %d",
1221 irq);
1222 return;
1223 }
1224 icnss_pr_dbg("FW force error fatal handler registered\n");
1225}
1226
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001227static int wlfw_msa_mem_info_send_sync_msg(void)
1228{
1229 int ret;
1230 int i;
1231 struct wlfw_msa_info_req_msg_v01 req;
1232 struct wlfw_msa_info_resp_msg_v01 resp;
1233 struct msg_desc req_desc, resp_desc;
1234
1235 if (!penv || !penv->wlfw_clnt)
1236 return -ENODEV;
1237
1238 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1239
1240 memset(&req, 0, sizeof(req));
1241 memset(&resp, 0, sizeof(resp));
1242
1243 req.msa_addr = penv->msa_pa;
1244 req.size = penv->msa_mem_size;
1245
1246 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1247 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1248 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1249
1250 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1251 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1252 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1253
1254 penv->stats.msa_info_req++;
1255
1256 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1257 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1258 if (ret < 0) {
1259 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1260 goto out;
1261 }
1262
1263 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1264 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1265 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301266 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001267 goto out;
1268 }
1269
1270 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1271 resp.mem_region_info_len);
1272
Yuanyuan Liu68939762017-04-04 16:43:03 -07001273 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001274 icnss_pr_err("Invalid memory region length received: %d\n",
1275 resp.mem_region_info_len);
1276 ret = -EINVAL;
1277 goto out;
1278 }
1279
1280 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001281 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001282 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001283 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001284 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001285 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001286 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001287 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001288 resp.mem_region_info[i].secure_flag;
1289 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001290 i, penv->mem_region[i].reg_addr,
1291 penv->mem_region[i].size,
1292 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001293 }
1294
1295 return 0;
1296
1297out:
1298 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001299 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001300 return ret;
1301}
1302
1303static int wlfw_msa_ready_send_sync_msg(void)
1304{
1305 int ret;
1306 struct wlfw_msa_ready_req_msg_v01 req;
1307 struct wlfw_msa_ready_resp_msg_v01 resp;
1308 struct msg_desc req_desc, resp_desc;
1309
1310 if (!penv || !penv->wlfw_clnt)
1311 return -ENODEV;
1312
1313 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1314 penv->state);
1315
1316 memset(&req, 0, sizeof(req));
1317 memset(&resp, 0, sizeof(resp));
1318
1319 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1320 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1321 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1322
1323 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1324 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1325 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1326
1327 penv->stats.msa_ready_req++;
1328 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1329 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1330 if (ret < 0) {
1331 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1332 goto out;
1333 }
1334
1335 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1336 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1337 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301338 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001339 goto out;
1340 }
1341 penv->stats.msa_ready_resp++;
1342
1343 return 0;
1344
1345out:
1346 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001347 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001348 return ret;
1349}
1350
1351static int wlfw_ind_register_send_sync_msg(void)
1352{
1353 int ret;
1354 struct wlfw_ind_register_req_msg_v01 req;
1355 struct wlfw_ind_register_resp_msg_v01 resp;
1356 struct msg_desc req_desc, resp_desc;
1357
1358 if (!penv || !penv->wlfw_clnt)
1359 return -ENODEV;
1360
1361 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1362 penv->state);
1363
1364 memset(&req, 0, sizeof(req));
1365 memset(&resp, 0, sizeof(resp));
1366
1367 req.client_id_valid = 1;
1368 req.client_id = WLFW_CLIENT_ID;
1369 req.fw_ready_enable_valid = 1;
1370 req.fw_ready_enable = 1;
1371 req.msa_ready_enable_valid = 1;
1372 req.msa_ready_enable = 1;
1373 req.pin_connect_result_enable_valid = 1;
1374 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001375 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1376 req.rejuvenate_enable_valid = 1;
1377 req.rejuvenate_enable = 1;
1378 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001379
1380 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1381 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1382 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1383
1384 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1385 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1386 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1387
1388 penv->stats.ind_register_req++;
1389
1390 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1391 &resp_desc, &resp, sizeof(resp),
1392 WLFW_TIMEOUT_MS);
1393 if (ret < 0) {
1394 icnss_pr_err("Send indication register req failed %d\n", ret);
1395 goto out;
1396 }
1397
1398 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1399 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1400 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301401 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001402 goto out;
1403 }
1404 penv->stats.ind_register_resp++;
1405
1406 return 0;
1407
1408out:
1409 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001410 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001411 return ret;
1412}
1413
1414static int wlfw_cap_send_sync_msg(void)
1415{
1416 int ret;
1417 struct wlfw_cap_req_msg_v01 req;
1418 struct wlfw_cap_resp_msg_v01 resp;
1419 struct msg_desc req_desc, resp_desc;
1420
1421 if (!penv || !penv->wlfw_clnt)
1422 return -ENODEV;
1423
1424 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1425
1426 memset(&resp, 0, sizeof(resp));
1427
1428 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1429 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1430 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1431
1432 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1433 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1434 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1435
1436 penv->stats.cap_req++;
1437 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1438 &resp_desc, &resp, sizeof(resp),
1439 WLFW_TIMEOUT_MS);
1440 if (ret < 0) {
1441 icnss_pr_err("Send capability req failed %d\n", ret);
1442 goto out;
1443 }
1444
1445 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1446 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1447 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301448 ret = -resp.resp.result;
1449 if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
1450 icnss_pr_err("RF card Not present");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001451 goto out;
1452 }
1453
1454 penv->stats.cap_resp++;
1455 /* store cap locally */
1456 if (resp.chip_info_valid)
1457 penv->chip_info = resp.chip_info;
1458 if (resp.board_info_valid)
1459 penv->board_info = resp.board_info;
1460 else
1461 penv->board_info.board_id = 0xFF;
1462 if (resp.soc_info_valid)
1463 penv->soc_info = resp.soc_info;
1464 if (resp.fw_version_info_valid)
1465 penv->fw_version_info = resp.fw_version_info;
1466 if (resp.fw_build_id_valid)
1467 strlcpy(penv->fw_build_id, resp.fw_build_id,
1468 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1469
1470 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",
1471 penv->chip_info.chip_id, penv->chip_info.chip_family,
1472 penv->board_info.board_id, penv->soc_info.soc_id,
1473 penv->fw_version_info.fw_version,
1474 penv->fw_version_info.fw_build_timestamp,
1475 penv->fw_build_id);
1476
1477 return 0;
1478
1479out:
1480 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001481 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001482 return ret;
1483}
1484
1485static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1486{
1487 int ret;
1488 struct wlfw_wlan_mode_req_msg_v01 req;
1489 struct wlfw_wlan_mode_resp_msg_v01 resp;
1490 struct msg_desc req_desc, resp_desc;
1491
1492 if (!penv || !penv->wlfw_clnt)
1493 return -ENODEV;
1494
1495 /* During recovery do not send mode request for WLAN OFF as
1496 * FW not able to process it.
1497 */
1498 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1499 mode == QMI_WLFW_OFF_V01)
1500 return 0;
1501
1502 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1503 penv->state, mode);
1504
1505 memset(&req, 0, sizeof(req));
1506 memset(&resp, 0, sizeof(resp));
1507
1508 req.mode = mode;
1509 req.hw_debug_valid = 1;
1510 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1511
1512 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1513 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1514 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1515
1516 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1517 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1518 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1519
1520 penv->stats.mode_req++;
1521 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1522 &resp_desc, &resp, sizeof(resp),
1523 WLFW_TIMEOUT_MS);
1524 if (ret < 0) {
1525 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1526 mode, ret);
1527 goto out;
1528 }
1529
1530 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1531 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1532 mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301533 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001534 goto out;
1535 }
1536 penv->stats.mode_resp++;
1537
1538 return 0;
1539
1540out:
1541 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001542 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001543 return ret;
1544}
1545
1546static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1547{
1548 int ret;
1549 struct wlfw_wlan_cfg_req_msg_v01 req;
1550 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1551 struct msg_desc req_desc, resp_desc;
1552
1553 if (!penv || !penv->wlfw_clnt)
1554 return -ENODEV;
1555
1556 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1557
1558 memset(&req, 0, sizeof(req));
1559 memset(&resp, 0, sizeof(resp));
1560
1561 memcpy(&req, data, sizeof(req));
1562
1563 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1564 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1565 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1566
1567 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1568 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1569 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1570
1571 penv->stats.cfg_req++;
1572 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1573 &resp_desc, &resp, sizeof(resp),
1574 WLFW_TIMEOUT_MS);
1575 if (ret < 0) {
1576 icnss_pr_err("Send config req failed %d\n", ret);
1577 goto out;
1578 }
1579
1580 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1581 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1582 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301583 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001584 goto out;
1585 }
1586 penv->stats.cfg_resp++;
1587
1588 return 0;
1589
1590out:
1591 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001592 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001593 return ret;
1594}
1595
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001596static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001597{
1598 int ret;
1599 struct wlfw_ini_req_msg_v01 req;
1600 struct wlfw_ini_resp_msg_v01 resp;
1601 struct msg_desc req_desc, resp_desc;
1602
1603 if (!penv || !penv->wlfw_clnt)
1604 return -ENODEV;
1605
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001606 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1607 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001608
1609 memset(&req, 0, sizeof(req));
1610 memset(&resp, 0, sizeof(resp));
1611
1612 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001613 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001614
1615 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1616 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1617 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1618
1619 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1620 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1621 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1622
1623 penv->stats.ini_req++;
1624
1625 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1626 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1627 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001628 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1629 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001630 goto out;
1631 }
1632
1633 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001634 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1635 fw_log_mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301636 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001637 goto out;
1638 }
1639 penv->stats.ini_resp++;
1640
1641 return 0;
1642
1643out:
1644 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001645 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001646 return ret;
1647}
1648
1649static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1650 uint32_t offset, uint32_t mem_type,
1651 uint32_t data_len, uint8_t *data)
1652{
1653 int ret;
1654 struct wlfw_athdiag_read_req_msg_v01 req;
1655 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1656 struct msg_desc req_desc, resp_desc;
1657
1658 if (!priv->wlfw_clnt) {
1659 ret = -ENODEV;
1660 goto out;
1661 }
1662
1663 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1664 priv->state, offset, mem_type, data_len);
1665
1666 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1667 if (!resp) {
1668 ret = -ENOMEM;
1669 goto out;
1670 }
1671 memset(&req, 0, sizeof(req));
1672
1673 req.offset = offset;
1674 req.mem_type = mem_type;
1675 req.data_len = data_len;
1676
1677 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1678 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1679 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1680
1681 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1682 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1683 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1684
1685 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1686 &resp_desc, resp, sizeof(*resp),
1687 WLFW_TIMEOUT_MS);
1688 if (ret < 0) {
1689 icnss_pr_err("send athdiag read req failed %d\n", ret);
1690 goto out;
1691 }
1692
1693 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1694 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1695 resp->resp.result, resp->resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301696 ret = -resp->resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001697 goto out;
1698 }
1699
Yuanyuan Liu68939762017-04-04 16:43:03 -07001700 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001701 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1702 resp->data_valid, resp->data_len);
1703 ret = -EINVAL;
1704 goto out;
1705 }
1706
1707 memcpy(data, resp->data, resp->data_len);
1708
1709out:
1710 kfree(resp);
1711 return ret;
1712}
1713
1714static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1715 uint32_t offset, uint32_t mem_type,
1716 uint32_t data_len, uint8_t *data)
1717{
1718 int ret;
1719 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1720 struct wlfw_athdiag_write_resp_msg_v01 resp;
1721 struct msg_desc req_desc, resp_desc;
1722
1723 if (!priv->wlfw_clnt) {
1724 ret = -ENODEV;
1725 goto out;
1726 }
1727
1728 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1729 priv->state, offset, mem_type, data_len, data);
1730
1731 req = kzalloc(sizeof(*req), GFP_KERNEL);
1732 if (!req) {
1733 ret = -ENOMEM;
1734 goto out;
1735 }
1736 memset(&resp, 0, sizeof(resp));
1737
1738 req->offset = offset;
1739 req->mem_type = mem_type;
1740 req->data_len = data_len;
1741 memcpy(req->data, data, data_len);
1742
1743 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1744 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1745 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1746
1747 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1748 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1749 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1750
1751 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1752 &resp_desc, &resp, sizeof(resp),
1753 WLFW_TIMEOUT_MS);
1754 if (ret < 0) {
1755 icnss_pr_err("send athdiag write req failed %d\n", ret);
1756 goto out;
1757 }
1758
1759 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1760 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1761 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301762 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001763 goto out;
1764 }
1765out:
1766 kfree(req);
1767 return ret;
1768}
1769
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001770static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1771{
1772 struct msg_desc ind_desc;
1773 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1774 int ret = 0;
1775
1776 if (!penv || !penv->wlfw_clnt) {
1777 ret = -ENODEV;
1778 goto out;
1779 }
1780
1781 memset(&ind_msg, 0, sizeof(ind_msg));
1782
1783 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1784 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1785 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1786
1787 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1788 if (ret < 0) {
1789 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1790 ret, msg_len);
1791 goto out;
1792 }
1793
1794 if (ind_msg.cause_for_rejuvenation_valid)
1795 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1796 else
1797 penv->cause_for_rejuvenation = 0;
1798 if (ind_msg.requesting_sub_system_valid)
1799 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1800 else
1801 penv->requesting_sub_system = 0;
1802 if (ind_msg.line_number_valid)
1803 penv->line_number = ind_msg.line_number;
1804 else
1805 penv->line_number = 0;
1806 if (ind_msg.function_name_valid)
1807 memcpy(penv->function_name, ind_msg.function_name,
1808 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1809 else
1810 memset(penv->function_name, 0,
1811 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1812
1813 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1814 penv->cause_for_rejuvenation,
1815 penv->requesting_sub_system,
1816 penv->line_number,
1817 penv->function_name);
1818
1819 penv->stats.rejuvenate_ind++;
1820out:
1821 return ret;
1822}
1823
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001824static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1825{
1826 int ret;
1827 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1828 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1829 struct msg_desc req_desc, resp_desc;
1830
1831 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1832 priv->state);
1833
1834 memset(&req, 0, sizeof(req));
1835 memset(&resp, 0, sizeof(resp));
1836
1837 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1838 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1839 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1840
1841 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1842 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1843 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1844
1845 priv->stats.rejuvenate_ack_req++;
1846 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1847 &resp_desc, &resp, sizeof(resp),
1848 WLFW_TIMEOUT_MS);
1849 if (ret < 0) {
1850 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1851 goto out;
1852 }
1853
1854 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1855 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1856 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301857 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001858 goto out;
1859 }
1860 priv->stats.rejuvenate_ack_resp++;
1861 return 0;
1862
1863out:
1864 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001865 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001866 return ret;
1867}
1868
1869static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1870 uint64_t dynamic_feature_mask)
1871{
1872 int ret;
1873 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1874 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1875 struct msg_desc req_desc, resp_desc;
1876
1877 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1878 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1879 priv->state);
1880 return -EINVAL;
1881 }
1882
1883 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1884 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1885 return 0;
1886 }
1887
1888 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1889 dynamic_feature_mask, priv->state);
1890
1891 memset(&req, 0, sizeof(req));
1892 memset(&resp, 0, sizeof(resp));
1893
1894 req.mask_valid = 1;
1895 req.mask = dynamic_feature_mask;
1896
1897 req_desc.max_msg_len =
1898 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1899 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1900 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1901
1902 resp_desc.max_msg_len =
1903 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1904 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1905 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1906
1907 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1908 &resp_desc, &resp, sizeof(resp),
1909 WLFW_TIMEOUT_MS);
1910 if (ret < 0) {
1911 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1912 goto out;
1913 }
1914
1915 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1916 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1917 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301918 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001919 goto out;
1920 }
1921
1922 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1923 resp.prev_mask_valid, resp.prev_mask,
1924 resp.curr_mask_valid, resp.curr_mask);
1925
1926 return 0;
1927
1928out:
1929 return ret;
1930}
1931
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001932static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1933{
1934 int ret;
1935
1936 if (!penv || !penv->wlfw_clnt)
1937 return;
1938
Yuanyuan Liu68939762017-04-04 16:43:03 -07001939 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001940
1941 do {
1942 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1943
1944 if (ret != -ENOMSG)
1945 icnss_pr_err("Error receiving message: %d\n", ret);
1946
Yuanyuan Liu68939762017-04-04 16:43:03 -07001947 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001948}
1949
1950static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1951 enum qmi_event_type event, void *notify_priv)
1952{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001953 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001954
1955 if (!penv || !penv->wlfw_clnt)
1956 return;
1957
1958 switch (event) {
1959 case QMI_RECV_MSG:
1960 schedule_work(&penv->qmi_recv_msg_work);
1961 break;
1962 default:
1963 icnss_pr_dbg("Unknown Event: %d\n", event);
1964 break;
1965 }
1966}
1967
Yuanyuan Liu68939762017-04-04 16:43:03 -07001968static int icnss_call_driver_uevent(struct icnss_priv *priv,
1969 enum icnss_uevent uevent, void *data)
1970{
1971 struct icnss_uevent_data uevent_data;
1972
1973 if (!priv->ops || !priv->ops->uevent)
1974 return 0;
1975
1976 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
1977 priv->state, uevent);
1978
1979 uevent_data.uevent = uevent;
1980 uevent_data.data = data;
1981
1982 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
1983}
1984
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001985static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
1986 unsigned int msg_id, void *msg,
1987 unsigned int msg_len, void *ind_cb_priv)
1988{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001989 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001990 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001991
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001992 if (!penv)
1993 return;
1994
1995 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
1996
Sameer Thalappil93c00732017-08-18 13:02:32 -07001997 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
1998 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
1999 msg_id, penv->state);
2000 return;
2001 }
2002
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002003 switch (msg_id) {
2004 case QMI_WLFW_FW_READY_IND_V01:
2005 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
2006 0, NULL);
2007 break;
2008 case QMI_WLFW_MSA_READY_IND_V01:
2009 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
2010 msg_id);
2011 penv->stats.msa_ready_ind++;
2012 break;
2013 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
2014 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
2015 msg_id);
2016 icnss_qmi_pin_connect_result_ind(msg, msg_len);
2017 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002018 case QMI_WLFW_REJUVENATE_IND_V01:
2019 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
2020 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002021
2022 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08002023 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002024 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
2025 if (event_data == NULL)
2026 return;
2027 event_data->crashed = true;
2028 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002029 fw_down_data.crashed = true;
2030 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
2031 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002032 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2033 0, event_data);
2034 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002035 default:
2036 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
2037 break;
2038 }
2039}
2040
2041static int icnss_driver_event_server_arrive(void *data)
2042{
2043 int ret = 0;
2044
2045 if (!penv)
2046 return -ENODEV;
2047
2048 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07002049 clear_bit(ICNSS_FW_DOWN, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002050
2051 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
2052 if (!penv->wlfw_clnt) {
2053 icnss_pr_err("QMI client handle create failed\n");
2054 ret = -ENOMEM;
2055 goto out;
2056 }
2057
2058 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2059 WLFW_SERVICE_VERS_V01,
2060 WLFW_SERVICE_INS_ID_V01);
2061 if (ret < 0) {
2062 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2063 goto fail;
2064 }
2065
2066 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2067 icnss_qmi_wlfw_clnt_ind, penv);
2068 if (ret < 0) {
2069 icnss_pr_err("Failed to register indication callback: %d\n",
2070 ret);
2071 goto fail;
2072 }
2073
2074 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2075
2076 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2077
2078 ret = icnss_hw_power_on(penv);
2079 if (ret)
2080 goto fail;
2081
2082 ret = wlfw_ind_register_send_sync_msg();
2083 if (ret < 0)
2084 goto err_power_on;
2085
2086 if (!penv->msa_va) {
2087 icnss_pr_err("Invalid MSA address\n");
2088 ret = -EINVAL;
2089 goto err_power_on;
2090 }
2091
2092 ret = wlfw_msa_mem_info_send_sync_msg();
2093 if (ret < 0)
2094 goto err_power_on;
2095
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302096 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2097 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2098 if (ret < 0)
2099 goto err_power_on;
2100 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2101 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002102
2103 ret = wlfw_msa_ready_send_sync_msg();
2104 if (ret < 0)
2105 goto err_setup_msa;
2106
2107 ret = wlfw_cap_send_sync_msg();
2108 if (ret < 0)
2109 goto err_setup_msa;
2110
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002111 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2112 dynamic_feature_mask);
2113
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002114 icnss_init_vph_monitor(penv);
2115
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002116 icnss_register_force_error_fatal(penv);
2117
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002118 return ret;
2119
2120err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302121 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002122err_power_on:
2123 icnss_hw_power_off(penv);
2124fail:
2125 qmi_handle_destroy(penv->wlfw_clnt);
2126 penv->wlfw_clnt = NULL;
2127out:
2128 ICNSS_ASSERT(0);
2129 return ret;
2130}
2131
2132static int icnss_driver_event_server_exit(void *data)
2133{
2134 if (!penv || !penv->wlfw_clnt)
2135 return -ENODEV;
2136
2137 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2138
2139 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2140 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2141 &penv->vph_monitor_params);
2142
2143 qmi_handle_destroy(penv->wlfw_clnt);
2144
2145 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2146 penv->wlfw_clnt = NULL;
2147
2148 return 0;
2149}
2150
2151static int icnss_call_driver_probe(struct icnss_priv *priv)
2152{
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002153 int ret = 0;
2154 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002155
2156 if (!priv->ops || !priv->ops->probe)
2157 return 0;
2158
Yuanyuan Liu68939762017-04-04 16:43:03 -07002159 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2160 return -EINVAL;
2161
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002162 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2163
2164 icnss_hw_power_on(priv);
2165
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002166 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2167 ret = priv->ops->probe(&priv->pdev->dev);
2168 probe_cnt++;
2169 if (ret != -EPROBE_DEFER)
2170 break;
2171 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002172 if (ret < 0) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002173 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2174 ret, priv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002175 goto out;
2176 }
2177
2178 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2179
2180 return 0;
2181
2182out:
2183 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002184 return ret;
2185}
2186
Yuanyuan Liu68939762017-04-04 16:43:03 -07002187static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2188{
2189 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2190 goto out;
2191
2192 if (!priv->ops || !priv->ops->shutdown)
2193 goto out;
2194
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302195 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2196 goto out;
2197
Yuanyuan Liu68939762017-04-04 16:43:03 -07002198 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2199
2200 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302201 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002202
2203out:
2204 return 0;
2205}
2206
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002207static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002208{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002209 int ret;
2210
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002211 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002212
Anurag Chouhan9de21192017-08-08 15:30:02 +05302213 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002214
2215 clear_bit(ICNSS_PD_RESTART, &priv->state);
2216
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002217 if (!priv->ops || !priv->ops->reinit)
2218 goto out;
2219
Yuanyuan Liu48a51e42017-12-04 16:01:31 -08002220 if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
2221 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2222 priv->state);
2223 goto out;
2224 }
2225
Yuanyuan Liu68939762017-04-04 16:43:03 -07002226 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002227 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002228
2229 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2230
2231 icnss_hw_power_on(priv);
2232
2233 ret = priv->ops->reinit(&priv->pdev->dev);
2234 if (ret < 0) {
2235 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2236 ret, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002237 if (!priv->allow_recursive_recovery)
2238 ICNSS_ASSERT(false);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002239 goto out_power_off;
2240 }
2241
2242out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302243 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002244 return 0;
2245
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002246call_probe:
2247 return icnss_call_driver_probe(priv);
2248
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002249out_power_off:
2250 icnss_hw_power_off(priv);
2251
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002252 return ret;
2253}
2254
2255
2256static int icnss_driver_event_fw_ready_ind(void *data)
2257{
2258 int ret = 0;
2259
2260 if (!penv)
2261 return -ENODEV;
2262
2263 set_bit(ICNSS_FW_READY, &penv->state);
2264
2265 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2266
2267 icnss_hw_power_off(penv);
2268
2269 if (!penv->pdev) {
2270 icnss_pr_err("Device is not ready\n");
2271 ret = -ENODEV;
2272 goto out;
2273 }
2274
2275 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002276 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002277 else
2278 ret = icnss_call_driver_probe(penv);
2279
2280out:
2281 return ret;
2282}
2283
2284static int icnss_driver_event_register_driver(void *data)
2285{
2286 int ret = 0;
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002287 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002288
2289 if (penv->ops)
2290 return -EEXIST;
2291
2292 penv->ops = data;
2293
2294 if (test_bit(SKIP_QMI, &quirks))
2295 set_bit(ICNSS_FW_READY, &penv->state);
2296
Yuanyuan Liu5fa7ec52017-11-30 11:11:42 -08002297 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2298 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2299 penv->state);
2300 return -ENODEV;
2301 }
2302
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002303 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2304 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2305 penv->state);
2306 goto out;
2307 }
2308
2309 ret = icnss_hw_power_on(penv);
2310 if (ret)
2311 goto out;
2312
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002313 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2314 ret = penv->ops->probe(&penv->pdev->dev);
2315 probe_cnt++;
2316 if (ret != -EPROBE_DEFER)
2317 break;
2318 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002319 if (ret) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002320 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2321 ret, penv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002322 goto power_off;
2323 }
2324
2325 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2326
2327 return 0;
2328
2329power_off:
2330 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002331out:
2332 return ret;
2333}
2334
2335static int icnss_driver_event_unregister_driver(void *data)
2336{
2337 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2338 penv->ops = NULL;
2339 goto out;
2340 }
2341
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002342 set_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002343 if (penv->ops)
2344 penv->ops->remove(&penv->pdev->dev);
2345
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002346 clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002347 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2348
2349 penv->ops = NULL;
2350
2351 icnss_hw_power_off(penv);
2352
2353out:
2354 return 0;
2355}
2356
Yuanyuan Liu68939762017-04-04 16:43:03 -07002357static int icnss_fw_crashed(struct icnss_priv *priv,
2358 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002359{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302360 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002361
2362 set_bit(ICNSS_PD_RESTART, &priv->state);
2363 clear_bit(ICNSS_FW_READY, &priv->state);
2364
2365 icnss_pm_stay_awake(priv);
2366
Yuanyuan Liu68939762017-04-04 16:43:03 -07002367 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2368 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002369
Yuanyuan Liu68939762017-04-04 16:43:03 -07002370 if (event_data->fw_rejuvenate)
2371 wlfw_rejuvenate_ack_send_sync_msg(priv);
2372
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002373 return 0;
2374}
2375
2376static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2377 void *data)
2378{
2379 int ret = 0;
2380 struct icnss_event_pd_service_down_data *event_data = data;
2381
2382 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002383 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002384
Sameer Thalappil42ed2cc2017-10-30 11:17:01 -07002385 if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002386 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2387 event_data->crashed, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002388 if (!priv->allow_recursive_recovery)
2389 ICNSS_ASSERT(0);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002390 goto out;
2391 }
2392
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002393 if (priv->force_err_fatal)
2394 ICNSS_ASSERT(0);
2395
Hardik Kantilal Patel3b934072018-01-12 18:02:10 +05302396 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002397
2398out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002399 kfree(data);
2400
Yuanyuan Liu68939762017-04-04 16:43:03 -07002401 icnss_ignore_qmi_timeout(false);
2402
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002403 return ret;
2404}
2405
2406static void icnss_driver_event_work(struct work_struct *work)
2407{
2408 struct icnss_driver_event *event;
2409 unsigned long flags;
2410 int ret;
2411
2412 icnss_pm_stay_awake(penv);
2413
2414 spin_lock_irqsave(&penv->event_lock, flags);
2415
2416 while (!list_empty(&penv->event_list)) {
2417 event = list_first_entry(&penv->event_list,
2418 struct icnss_driver_event, list);
2419 list_del(&event->list);
2420 spin_unlock_irqrestore(&penv->event_lock, flags);
2421
2422 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2423 icnss_driver_event_to_str(event->type),
2424 event->sync ? "-sync" : "", event->type,
2425 penv->state);
2426
2427 switch (event->type) {
2428 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2429 ret = icnss_driver_event_server_arrive(event->data);
2430 break;
2431 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2432 ret = icnss_driver_event_server_exit(event->data);
2433 break;
2434 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2435 ret = icnss_driver_event_fw_ready_ind(event->data);
2436 break;
2437 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2438 ret = icnss_driver_event_register_driver(event->data);
2439 break;
2440 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2441 ret = icnss_driver_event_unregister_driver(event->data);
2442 break;
2443 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2444 ret = icnss_driver_event_pd_service_down(penv,
2445 event->data);
2446 break;
2447 default:
2448 icnss_pr_err("Invalid Event type: %d", event->type);
2449 kfree(event);
2450 continue;
2451 }
2452
2453 penv->stats.events[event->type].processed++;
2454
2455 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2456 icnss_driver_event_to_str(event->type),
2457 event->sync ? "-sync" : "", event->type, ret,
2458 penv->state);
2459
2460 spin_lock_irqsave(&penv->event_lock, flags);
2461 if (event->sync) {
2462 event->ret = ret;
2463 complete(&event->complete);
2464 continue;
2465 }
2466 spin_unlock_irqrestore(&penv->event_lock, flags);
2467
2468 kfree(event);
2469
2470 spin_lock_irqsave(&penv->event_lock, flags);
2471 }
2472 spin_unlock_irqrestore(&penv->event_lock, flags);
2473
2474 icnss_pm_relax(penv);
2475}
2476
2477static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2478 unsigned long code,
2479 void *_cmd)
2480{
2481 int ret = 0;
2482
2483 if (!penv)
2484 return -ENODEV;
2485
2486 icnss_pr_dbg("Event Notify: code: %ld", code);
2487
2488 switch (code) {
2489 case QMI_SERVER_ARRIVE:
2490 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2491 0, NULL);
2492 break;
2493
2494 case QMI_SERVER_EXIT:
2495 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2496 0, NULL);
2497 break;
2498 default:
2499 icnss_pr_dbg("Invalid code: %ld", code);
2500 break;
2501 }
2502 return ret;
2503}
2504
2505static int icnss_msa0_ramdump(struct icnss_priv *priv)
2506{
2507 struct ramdump_segment segment;
2508
2509 memset(&segment, 0, sizeof(segment));
2510 segment.v_address = priv->msa_va;
2511 segment.size = priv->msa_mem_size;
2512 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2513}
2514
2515static struct notifier_block wlfw_clnt_nb = {
2516 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2517};
2518
2519static int icnss_modem_notifier_nb(struct notifier_block *nb,
2520 unsigned long code,
2521 void *data)
2522{
2523 struct icnss_event_pd_service_down_data *event_data;
2524 struct notif_data *notif = data;
2525 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2526 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002527 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302528 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002529
Yuanyuan Liu68939762017-04-04 16:43:03 -07002530 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002531
Sameer Thalappil765cb492017-10-04 17:57:07 -07002532 if (code == SUBSYS_AFTER_SHUTDOWN &&
Yuanyuan Liu52a484b2017-11-15 11:23:45 -08002533 notif->crashed == CRASH_STATUS_ERR_FATAL) {
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302534 ret = icnss_assign_msa_perm_all(priv,
Sameer Thalappil765cb492017-10-04 17:57:07 -07002535 ICNSS_MSA_PERM_HLOS_ALL);
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302536 if (!ret) {
2537 icnss_pr_info("Collecting msa0 segment dump\n");
2538 icnss_msa0_ramdump(priv);
2539 icnss_assign_msa_perm_all(priv,
2540 ICNSS_MSA_PERM_WLAN_HW_RW);
2541 } else {
2542 icnss_pr_err("Not able to Collect msa0 segment dump"
2543 "Apps permissions not assigned %d\n", ret);
2544 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002545 return NOTIFY_OK;
2546 }
2547
2548 if (code != SUBSYS_BEFORE_SHUTDOWN)
2549 return NOTIFY_OK;
2550
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002551 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
2552 set_bit(ICNSS_FW_DOWN, &priv->state);
2553 icnss_ignore_qmi_timeout(true);
2554
2555 fw_down_data.crashed = !!notif->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002556 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2557 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002558 icnss_call_driver_uevent(priv,
2559 ICNSS_UEVENT_FW_DOWN,
2560 &fw_down_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002561 return NOTIFY_OK;
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002562 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002563
Yuanyuan Liu68939762017-04-04 16:43:03 -07002564 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2565 priv->state, notif->crashed);
2566
Sameer Thalappil93c00732017-08-18 13:02:32 -07002567 set_bit(ICNSS_FW_DOWN, &priv->state);
2568
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002569 if (notif->crashed)
2570 priv->stats.recovery.root_pd_crash++;
2571 else
2572 priv->stats.recovery.root_pd_shutdown++;
2573
Yuanyuan Liu68939762017-04-04 16:43:03 -07002574 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002575
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002576 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002577
2578 if (event_data == NULL)
2579 return notifier_from_errno(-ENOMEM);
2580
2581 event_data->crashed = notif->crashed;
2582
Yuanyuan Liu68939762017-04-04 16:43:03 -07002583 fw_down_data.crashed = !!notif->crashed;
2584 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2585
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002586 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2587 ICNSS_EVENT_SYNC, event_data);
2588
2589 return NOTIFY_OK;
2590}
2591
2592static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2593{
2594 int ret = 0;
2595
2596 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2597
2598 priv->modem_notify_handler =
2599 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2600
2601 if (IS_ERR(priv->modem_notify_handler)) {
2602 ret = PTR_ERR(priv->modem_notify_handler);
2603 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2604 }
2605
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002606 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002607
2608 return ret;
2609}
2610
2611static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2612{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002613 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002614 return 0;
2615
2616 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2617 &priv->modem_ssr_nb);
2618 priv->modem_notify_handler = NULL;
2619
2620 return 0;
2621}
2622
2623static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2624{
2625 int i;
2626
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002627 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002628 return 0;
2629
2630 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002631 service_notif_unregister_notifier(
2632 priv->service_notifier[i].handle,
2633 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002634
2635 kfree(priv->service_notifier);
2636
2637 priv->service_notifier = NULL;
2638
2639 return 0;
2640}
2641
2642static int icnss_service_notifier_notify(struct notifier_block *nb,
2643 unsigned long notification, void *data)
2644{
2645 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2646 service_notifier_nb);
2647 enum pd_subsys_state *state = data;
2648 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002649 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002650 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002651
Yuanyuan Liu68939762017-04-04 16:43:03 -07002652 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2653 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002654
Yuanyuan Liu68939762017-04-04 16:43:03 -07002655 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2656 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002657
Yuanyuan Liu68939762017-04-04 16:43:03 -07002658 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002659
Yuanyuan Liu68939762017-04-04 16:43:03 -07002660 if (event_data == NULL)
2661 return notifier_from_errno(-ENOMEM);
2662
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002663 event_data->crashed = true;
2664
Yuanyuan Liu68939762017-04-04 16:43:03 -07002665 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002666 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002667 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002668 }
2669
Yuanyuan Liu68939762017-04-04 16:43:03 -07002670 switch (*state) {
2671 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002672 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002673 break;
2674 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002675 cause = ICNSS_ROOT_PD_SHUTDOWN;
2676 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002677 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002678 break;
2679 case USER_PD_STATE_CHANGE:
2680 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2681 cause = ICNSS_HOST_ERROR;
2682 priv->stats.recovery.pdr_host_error++;
2683 } else {
2684 cause = ICNSS_FW_CRASH;
2685 priv->stats.recovery.pdr_fw_crash++;
2686 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002687 break;
2688 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002689 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002690 break;
2691 }
2692
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002693 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2694 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002695event_post:
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002696 if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
2697 set_bit(ICNSS_FW_DOWN, &priv->state);
2698 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002699
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002700 fw_down_data.crashed = event_data->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002701 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2702 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002703 icnss_call_driver_uevent(priv,
2704 ICNSS_UEVENT_FW_DOWN,
2705 &fw_down_data);
2706 }
2707
2708 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002709 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2710 ICNSS_EVENT_SYNC, event_data);
2711done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002712 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2713 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002714 return NOTIFY_OK;
2715}
2716
2717static int icnss_get_service_location_notify(struct notifier_block *nb,
2718 unsigned long opcode, void *data)
2719{
2720 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2721 get_service_nb);
2722 struct pd_qmi_client_data *pd = data;
2723 int curr_state;
2724 int ret;
2725 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002726 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002727
2728 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2729 priv->state);
2730
2731 if (opcode != LOCATOR_UP)
2732 return NOTIFY_DONE;
2733
2734 if (pd->total_domains == 0) {
2735 icnss_pr_err("Did not find any domains\n");
2736 ret = -ENOENT;
2737 goto out;
2738 }
2739
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002740 notifier = kcalloc(pd->total_domains,
2741 sizeof(struct service_notifier_context),
2742 GFP_KERNEL);
2743 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002744 ret = -ENOMEM;
2745 goto out;
2746 }
2747
2748 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2749
2750 for (i = 0; i < pd->total_domains; i++) {
2751 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2752 pd->domain_list[i].name,
2753 pd->domain_list[i].instance_id);
2754
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002755 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002756 service_notif_register_notifier(pd->domain_list[i].name,
2757 pd->domain_list[i].instance_id,
2758 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002759 notifier[i].instance_id = pd->domain_list[i].instance_id;
2760 strlcpy(notifier[i].name, pd->domain_list[i].name,
2761 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002762
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002763 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002764 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2765 i, pd->domain_list->name,
2766 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002767 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002768 goto free_handle;
2769 }
2770 }
2771
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002772 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002773 priv->total_domains = pd->total_domains;
2774
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002775 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002776
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002777 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2778 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002779
2780 return NOTIFY_OK;
2781
2782free_handle:
2783 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002784 if (notifier[i].handle)
2785 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002786 &priv->service_notifier_nb);
2787 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002788 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002789
2790out:
2791 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2792 priv->state);
2793
2794 return NOTIFY_OK;
2795}
2796
2797
2798static int icnss_pd_restart_enable(struct icnss_priv *priv)
2799{
2800 int ret;
2801
2802 if (test_bit(SSR_ONLY, &quirks)) {
2803 icnss_pr_dbg("PDR disabled through module parameter\n");
2804 return 0;
2805 }
2806
2807 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2808
2809 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2810 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2811 ICNSS_WLAN_SERVICE_NAME,
2812 &priv->get_service_nb);
2813 if (ret) {
2814 icnss_pr_err("Get service location failed: %d\n", ret);
2815 goto out;
2816 }
2817
2818 return 0;
2819out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002820 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002821 return ret;
2822
2823}
2824
2825
2826static int icnss_enable_recovery(struct icnss_priv *priv)
2827{
2828 int ret;
2829
2830 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2831 icnss_pr_dbg("Recovery disabled through module parameter\n");
2832 return 0;
2833 }
2834
2835 if (test_bit(PDR_ONLY, &quirks)) {
2836 icnss_pr_dbg("SSR disabled through module parameter\n");
2837 goto enable_pdr;
2838 }
2839
2840 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2841 &priv->pdev->dev);
2842 if (!priv->msa0_dump_dev)
2843 return -ENOMEM;
2844
2845 icnss_modem_ssr_register_notifier(priv);
2846 if (test_bit(SSR_ONLY, &quirks)) {
2847 icnss_pr_dbg("PDR disabled through module parameter\n");
2848 return 0;
2849 }
2850
2851enable_pdr:
2852 ret = icnss_pd_restart_enable(priv);
2853
2854 if (ret)
2855 return ret;
2856
2857 return 0;
2858}
2859
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302860int __icnss_register_driver(struct icnss_driver_ops *ops,
2861 struct module *owner, const char *mod_name)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002862{
2863 int ret = 0;
2864
2865 if (!penv || !penv->pdev) {
2866 ret = -ENODEV;
2867 goto out;
2868 }
2869
2870 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2871
2872 if (penv->ops) {
2873 icnss_pr_err("Driver already registered\n");
2874 ret = -EEXIST;
2875 goto out;
2876 }
2877
2878 if (!ops->probe || !ops->remove) {
2879 ret = -EINVAL;
2880 goto out;
2881 }
2882
2883 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302884 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002885
2886 if (ret == -EINTR)
2887 ret = 0;
2888
2889out:
2890 return ret;
2891}
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302892EXPORT_SYMBOL(__icnss_register_driver);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002893
2894int icnss_unregister_driver(struct icnss_driver_ops *ops)
2895{
2896 int ret;
2897
2898 if (!penv || !penv->pdev) {
2899 ret = -ENODEV;
2900 goto out;
2901 }
2902
2903 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2904
2905 if (!penv->ops) {
2906 icnss_pr_err("Driver not registered\n");
2907 ret = -ENOENT;
2908 goto out;
2909 }
2910
2911 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2912 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2913out:
2914 return ret;
2915}
2916EXPORT_SYMBOL(icnss_unregister_driver);
2917
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302918int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002919 irqreturn_t (*handler)(int, void *),
2920 unsigned long flags, const char *name, void *ctx)
2921{
2922 int ret = 0;
2923 unsigned int irq;
2924 struct ce_irq_list *irq_entry;
2925
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302926 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002927 ret = -ENODEV;
2928 goto out;
2929 }
2930
Yuanyuan Liu68939762017-04-04 16:43:03 -07002931 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002932
2933 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2934 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
2935 ret = -EINVAL;
2936 goto out;
2937 }
2938 irq = penv->ce_irqs[ce_id];
2939 irq_entry = &penv->ce_irq_list[ce_id];
2940
2941 if (irq_entry->handler || irq_entry->irq) {
2942 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
2943 irq, ce_id);
2944 ret = -EEXIST;
2945 goto out;
2946 }
2947
2948 ret = request_irq(irq, handler, flags, name, ctx);
2949 if (ret) {
2950 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
2951 irq, ce_id, ret);
2952 goto out;
2953 }
2954 irq_entry->irq = irq;
2955 irq_entry->handler = handler;
2956
Yuanyuan Liu68939762017-04-04 16:43:03 -07002957 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002958
2959 penv->stats.ce_irqs[ce_id].request++;
2960out:
2961 return ret;
2962}
2963EXPORT_SYMBOL(icnss_ce_request_irq);
2964
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302965int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002966{
2967 int ret = 0;
2968 unsigned int irq;
2969 struct ce_irq_list *irq_entry;
2970
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302971 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002972 ret = -ENODEV;
2973 goto out;
2974 }
2975
Yuanyuan Liu68939762017-04-04 16:43:03 -07002976 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002977
2978 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2979 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
2980 ret = -EINVAL;
2981 goto out;
2982 }
2983
2984 irq = penv->ce_irqs[ce_id];
2985 irq_entry = &penv->ce_irq_list[ce_id];
2986 if (!irq_entry->handler || !irq_entry->irq) {
2987 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
2988 ret = -EEXIST;
2989 goto out;
2990 }
2991 free_irq(irq, ctx);
2992 irq_entry->irq = 0;
2993 irq_entry->handler = NULL;
2994
2995 penv->stats.ce_irqs[ce_id].free++;
2996out:
2997 return ret;
2998}
2999EXPORT_SYMBOL(icnss_ce_free_irq);
3000
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303001void icnss_enable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003002{
3003 unsigned int irq;
3004
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303005 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003006 icnss_pr_err("Platform driver not initialized\n");
3007 return;
3008 }
3009
Yuanyuan Liu68939762017-04-04 16:43:03 -07003010 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003011 penv->state);
3012
3013 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3014 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
3015 return;
3016 }
3017
3018 penv->stats.ce_irqs[ce_id].enable++;
3019
3020 irq = penv->ce_irqs[ce_id];
3021 enable_irq(irq);
3022}
3023EXPORT_SYMBOL(icnss_enable_irq);
3024
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303025void icnss_disable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003026{
3027 unsigned int irq;
3028
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303029 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003030 icnss_pr_err("Platform driver not initialized\n");
3031 return;
3032 }
3033
Yuanyuan Liu68939762017-04-04 16:43:03 -07003034 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003035 penv->state);
3036
3037 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3038 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
3039 ce_id);
3040 return;
3041 }
3042
3043 irq = penv->ce_irqs[ce_id];
3044 disable_irq(irq);
3045
3046 penv->stats.ce_irqs[ce_id].disable++;
3047}
3048EXPORT_SYMBOL(icnss_disable_irq);
3049
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303050int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003051{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303052 if (!penv || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003053 icnss_pr_err("Platform driver not initialized\n");
3054 return -EINVAL;
3055 }
3056
3057 info->v_addr = penv->mem_base_va;
3058 info->p_addr = penv->mem_base_pa;
3059 info->chip_id = penv->chip_info.chip_id;
3060 info->chip_family = penv->chip_info.chip_family;
3061 info->board_id = penv->board_info.board_id;
3062 info->soc_id = penv->soc_info.soc_id;
3063 info->fw_version = penv->fw_version_info.fw_version;
3064 strlcpy(info->fw_build_timestamp,
3065 penv->fw_version_info.fw_build_timestamp,
3066 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
3067
3068 return 0;
3069}
3070EXPORT_SYMBOL(icnss_get_soc_info);
3071
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303072int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003073{
3074 int ret;
3075
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303076 if (!dev)
3077 return -ENODEV;
3078
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303079 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
3080 icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
3081 penv->state);
3082 return -EINVAL;
3083 }
3084
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003085 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003086
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003087 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003088 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003089 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3090 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003091 return ret;
3092}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003093EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003094
3095int icnss_athdiag_read(struct device *dev, uint32_t offset,
3096 uint32_t mem_type, uint32_t data_len,
3097 uint8_t *output)
3098{
3099 int ret = 0;
3100 struct icnss_priv *priv = dev_get_drvdata(dev);
3101
3102 if (priv->magic != ICNSS_MAGIC) {
3103 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3104 dev, priv, priv->magic);
3105 return -EINVAL;
3106 }
3107
3108 if (!output || data_len == 0
3109 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3110 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3111 output, data_len);
3112 ret = -EINVAL;
3113 goto out;
3114 }
3115
3116 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3117 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3118 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3119 priv->state);
3120 ret = -EINVAL;
3121 goto out;
3122 }
3123
3124 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3125 data_len, output);
3126out:
3127 return ret;
3128}
3129EXPORT_SYMBOL(icnss_athdiag_read);
3130
3131int icnss_athdiag_write(struct device *dev, uint32_t offset,
3132 uint32_t mem_type, uint32_t data_len,
3133 uint8_t *input)
3134{
3135 int ret = 0;
3136 struct icnss_priv *priv = dev_get_drvdata(dev);
3137
3138 if (priv->magic != ICNSS_MAGIC) {
3139 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3140 dev, priv, priv->magic);
3141 return -EINVAL;
3142 }
3143
3144 if (!input || data_len == 0
3145 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3146 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3147 input, data_len);
3148 ret = -EINVAL;
3149 goto out;
3150 }
3151
3152 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3153 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3154 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3155 priv->state);
3156 ret = -EINVAL;
3157 goto out;
3158 }
3159
3160 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3161 data_len, input);
3162out:
3163 return ret;
3164}
3165EXPORT_SYMBOL(icnss_athdiag_write);
3166
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303167int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003168 enum icnss_driver_mode mode,
3169 const char *host_version)
3170{
3171 struct wlfw_wlan_cfg_req_msg_v01 req;
3172 u32 i;
3173 int ret;
3174
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303175 if (!dev)
3176 return -ENODEV;
3177
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303178 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
3179 icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
3180 penv->state);
3181 return -EINVAL;
3182 }
3183
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003184 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3185 mode, config, host_version);
3186
3187 memset(&req, 0, sizeof(req));
3188
3189 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3190 goto skip;
3191
3192 if (!config || !host_version) {
3193 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3194 config, host_version);
3195 ret = -EINVAL;
3196 goto out;
3197 }
3198
3199 req.host_version_valid = 1;
3200 strlcpy(req.host_version, host_version,
3201 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3202
3203 req.tgt_cfg_valid = 1;
3204 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3205 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3206 else
3207 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3208 for (i = 0; i < req.tgt_cfg_len; i++) {
3209 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3210 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3211 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3212 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3213 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3214 }
3215
3216 req.svc_cfg_valid = 1;
3217 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3218 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3219 else
3220 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3221 for (i = 0; i < req.svc_cfg_len; i++) {
3222 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3223 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3224 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3225 }
3226
3227 req.shadow_reg_valid = 1;
3228 if (config->num_shadow_reg_cfg >
3229 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3230 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3231 else
3232 req.shadow_reg_len = config->num_shadow_reg_cfg;
3233
3234 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3235 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3236
3237 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3238 if (ret)
3239 goto out;
3240skip:
3241 ret = wlfw_wlan_mode_send_sync_msg(mode);
3242out:
3243 if (test_bit(SKIP_QMI, &quirks))
3244 ret = 0;
3245
3246 return ret;
3247}
3248EXPORT_SYMBOL(icnss_wlan_enable);
3249
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303250int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003251{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303252 if (!dev)
3253 return -ENODEV;
3254
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003255 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3256}
3257EXPORT_SYMBOL(icnss_wlan_disable);
3258
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303259bool icnss_is_qmi_disable(struct device *dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003260{
3261 return test_bit(SKIP_QMI, &quirks) ? true : false;
3262}
3263EXPORT_SYMBOL(icnss_is_qmi_disable);
3264
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303265int icnss_get_ce_id(struct device *dev, int irq)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003266{
3267 int i;
3268
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303269 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003270 return -ENODEV;
3271
3272 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3273 if (penv->ce_irqs[i] == irq)
3274 return i;
3275 }
3276
3277 icnss_pr_err("No matching CE id for irq %d\n", irq);
3278
3279 return -EINVAL;
3280}
3281EXPORT_SYMBOL(icnss_get_ce_id);
3282
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303283int icnss_get_irq(struct device *dev, int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003284{
3285 int irq;
3286
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303287 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003288 return -ENODEV;
3289
3290 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3291 return -EINVAL;
3292
3293 irq = penv->ce_irqs[ce_id];
3294
3295 return irq;
3296}
3297EXPORT_SYMBOL(icnss_get_irq);
3298
3299struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3300{
3301 struct icnss_priv *priv = dev_get_drvdata(dev);
3302
3303 if (!priv) {
3304 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3305 dev, priv);
3306 return NULL;
3307 }
3308
3309 return priv->smmu_mapping;
3310}
3311EXPORT_SYMBOL(icnss_smmu_get_mapping);
3312
3313int icnss_smmu_map(struct device *dev,
3314 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3315{
3316 struct icnss_priv *priv = dev_get_drvdata(dev);
3317 unsigned long iova;
3318 size_t len;
3319 int ret = 0;
3320
3321 if (!priv) {
3322 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3323 dev, priv);
3324 return -EINVAL;
3325 }
3326
3327 if (!iova_addr) {
3328 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3329 &paddr, size);
3330 return -EINVAL;
3331 }
3332
3333 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3334 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3335
3336 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3337 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3338 iova,
3339 &priv->smmu_iova_ipa_start,
3340 priv->smmu_iova_ipa_len);
3341 return -ENOMEM;
3342 }
3343
3344 ret = iommu_map(priv->smmu_mapping->domain, iova,
3345 rounddown(paddr, PAGE_SIZE), len,
3346 IOMMU_READ | IOMMU_WRITE);
3347 if (ret) {
3348 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3349 return ret;
3350 }
3351
3352 priv->smmu_iova_ipa_start = iova + len;
3353 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3354
3355 return 0;
3356}
3357EXPORT_SYMBOL(icnss_smmu_map);
3358
3359unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3360{
3361 return socinfo_get_serial_number();
3362}
3363EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3364
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003365int icnss_trigger_recovery(struct device *dev)
3366{
3367 int ret = 0;
3368 struct icnss_priv *priv = dev_get_drvdata(dev);
3369
3370 if (priv->magic != ICNSS_MAGIC) {
3371 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3372 ret = -EINVAL;
3373 goto out;
3374 }
3375
3376 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3377 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3378 priv->state);
3379 ret = -EPERM;
3380 goto out;
3381 }
3382
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003383 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003384 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3385 priv->state);
3386 ret = -EOPNOTSUPP;
3387 goto out;
3388 }
3389
3390 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3391 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3392 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003393 ret = -EINVAL;
3394 goto out;
3395 }
3396
Yuanyuan Liu68939762017-04-04 16:43:03 -07003397 WARN_ON(1);
3398 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3399 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003400
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003401 /*
3402 * Initiate PDR, required only for the first instance
3403 */
3404 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3405 priv->service_notifier[0].instance_id);
3406
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003407 if (!ret)
3408 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3409
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003410out:
3411 return ret;
3412}
3413EXPORT_SYMBOL(icnss_trigger_recovery);
3414
3415
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003416static int icnss_smmu_init(struct icnss_priv *priv)
3417{
3418 struct dma_iommu_mapping *mapping;
3419 int atomic_ctx = 1;
3420 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303421 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003422 int ret = 0;
3423
3424 icnss_pr_dbg("Initializing SMMU\n");
3425
3426 mapping = arm_iommu_create_mapping(&platform_bus_type,
3427 priv->smmu_iova_start,
3428 priv->smmu_iova_len);
3429 if (IS_ERR(mapping)) {
3430 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3431 ret = PTR_ERR(mapping);
3432 goto map_fail;
3433 }
3434
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303435 if (priv->bypass_s1_smmu) {
3436 ret = iommu_domain_set_attr(mapping->domain,
3437 DOMAIN_ATTR_S1_BYPASS,
3438 &s1_bypass);
3439 if (ret < 0) {
3440 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3441 ret);
3442 goto set_attr_fail;
3443 }
3444 icnss_pr_dbg("SMMU S1 BYPASS\n");
3445 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003446 ret = iommu_domain_set_attr(mapping->domain,
3447 DOMAIN_ATTR_ATOMIC,
3448 &atomic_ctx);
3449 if (ret < 0) {
3450 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3451 ret);
3452 goto set_attr_fail;
3453 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303454 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003455
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303456 ret = iommu_domain_set_attr(mapping->domain,
3457 DOMAIN_ATTR_FAST,
3458 &fast);
3459 if (ret < 0) {
3460 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3461 ret);
3462 goto set_attr_fail;
3463 }
3464 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003465 }
3466
3467 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3468 if (ret < 0) {
3469 icnss_pr_err("Attach device failed, err = %d\n", ret);
3470 goto attach_fail;
3471 }
3472
3473 priv->smmu_mapping = mapping;
3474
3475 return ret;
3476
3477attach_fail:
3478set_attr_fail:
3479 arm_iommu_release_mapping(mapping);
3480map_fail:
3481 return ret;
3482}
3483
3484static void icnss_smmu_deinit(struct icnss_priv *priv)
3485{
3486 if (!priv->smmu_mapping)
3487 return;
3488
3489 arm_iommu_detach_device(&priv->pdev->dev);
3490 arm_iommu_release_mapping(priv->smmu_mapping);
3491
3492 priv->smmu_mapping = NULL;
3493}
3494
Yuanyuan Liu68939762017-04-04 16:43:03 -07003495static int icnss_get_vreg_info(struct device *dev,
3496 struct icnss_vreg_info *vreg_info)
3497{
3498 int ret = 0;
3499 char prop_name[MAX_PROP_SIZE];
3500 struct regulator *reg;
3501 const __be32 *prop;
3502 int len = 0;
3503 int i;
3504
3505 reg = devm_regulator_get_optional(dev, vreg_info->name);
3506 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3507 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3508 vreg_info->name);
3509 ret = PTR_ERR(reg);
3510 goto out;
3511 }
3512
3513 if (IS_ERR(reg)) {
3514 ret = PTR_ERR(reg);
3515
3516 if (vreg_info->required) {
3517 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3518 vreg_info->name, ret);
3519 goto out;
3520 } else {
3521 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3522 vreg_info->name, ret);
3523 goto done;
3524 }
3525 }
3526
3527 vreg_info->reg = reg;
3528
3529 snprintf(prop_name, MAX_PROP_SIZE,
3530 "qcom,%s-config", vreg_info->name);
3531
3532 prop = of_get_property(dev->of_node, prop_name, &len);
3533
3534 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3535 prop_name, len);
3536
3537 if (!prop || len < (2 * sizeof(__be32))) {
3538 icnss_pr_dbg("Property %s %s\n", prop_name,
3539 prop ? "invalid format" : "doesn't exist");
3540 goto done;
3541 }
3542
3543 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3544 switch (i) {
3545 case 0:
3546 vreg_info->min_v = be32_to_cpup(&prop[0]);
3547 break;
3548 case 1:
3549 vreg_info->max_v = be32_to_cpup(&prop[1]);
3550 break;
3551 case 2:
3552 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3553 break;
3554 case 3:
3555 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3556 break;
3557 default:
3558 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3559 prop_name, i);
3560 break;
3561 }
3562 }
3563
3564done:
3565 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3566 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3567 vreg_info->load_ua, vreg_info->settle_delay);
3568
3569 return 0;
3570
3571out:
3572 return ret;
3573}
3574
3575static int icnss_get_clk_info(struct device *dev,
3576 struct icnss_clk_info *clk_info)
3577{
3578 struct clk *handle;
3579 int ret = 0;
3580
3581 handle = devm_clk_get(dev, clk_info->name);
3582 if (IS_ERR(handle)) {
3583 ret = PTR_ERR(handle);
3584 if (clk_info->required) {
3585 icnss_pr_err("Clock %s isn't available: %d\n",
3586 clk_info->name, ret);
3587 goto out;
3588 } else {
3589 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3590 ret);
3591 ret = 0;
3592 goto out;
3593 }
3594 }
3595
3596 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3597
3598 clk_info->handle = handle;
3599out:
3600 return ret;
3601}
3602
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003603static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003604{
3605 struct icnss_priv *priv = s->private;
3606
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003607 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003608
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003609 seq_puts(s, "\nCMD: test_mode\n");
3610 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3611 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3612 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003613 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003614
3615 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3616 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003617
3618 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003619 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003620 goto out;
3621 }
3622
3623 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003624 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003625 goto out;
3626 }
3627
3628 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003629 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003630 goto out;
3631 }
3632
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003633out:
3634 seq_puts(s, "\n");
3635 return 0;
3636}
3637
3638static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3639{
3640 int ret;
3641
3642 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3643 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3644 priv->state);
3645 ret = -ENODEV;
3646 goto out;
3647 }
3648
3649 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3650 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3651 priv->state);
3652 ret = -EINVAL;
3653 goto out;
3654 }
3655
3656 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3657 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3658 priv->state);
3659 ret = -EINVAL;
3660 goto out;
3661 }
3662
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303663 icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003664
3665 ret = icnss_hw_power_off(priv);
3666
3667 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3668
3669out:
3670 return ret;
3671}
3672static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3673 enum icnss_driver_mode mode)
3674{
3675 int ret;
3676
3677 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3678 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3679 priv->state);
3680 ret = -ENODEV;
3681 goto out;
3682 }
3683
3684 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3685 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3686 priv->state);
3687 ret = -EINVAL;
3688 goto out;
3689 }
3690
3691 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3692 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3693 priv->state);
3694 ret = -EBUSY;
3695 goto out;
3696 }
3697
3698 ret = icnss_hw_power_on(priv);
3699 if (ret)
3700 goto out;
3701
3702 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3703
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303704 ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003705 if (ret)
3706 goto power_off;
3707
3708 return 0;
3709
3710power_off:
3711 icnss_hw_power_off(priv);
3712 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3713
3714out:
3715 return ret;
3716}
3717
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003718static void icnss_allow_recursive_recovery(struct device *dev)
3719{
3720 struct icnss_priv *priv = dev_get_drvdata(dev);
3721
3722 priv->allow_recursive_recovery = true;
3723
3724 icnss_pr_info("Recursive recovery allowed for WLAN\n");
3725}
3726
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003727static ssize_t icnss_fw_debug_write(struct file *fp,
3728 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003729 size_t count, loff_t *off)
3730{
3731 struct icnss_priv *priv =
3732 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003733 char buf[64];
3734 char *sptr, *token;
3735 unsigned int len = 0;
3736 char *cmd;
3737 uint64_t val;
3738 const char *delim = " ";
3739 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003740
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003741 len = min(count, sizeof(buf) - 1);
3742 if (copy_from_user(buf, user_buf, len))
3743 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003744
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003745 buf[len] = '\0';
3746 sptr = buf;
3747
3748 token = strsep(&sptr, delim);
3749 if (!token)
3750 return -EINVAL;
3751 if (!sptr)
3752 return -EINVAL;
3753 cmd = token;
3754
3755 token = strsep(&sptr, delim);
3756 if (!token)
3757 return -EINVAL;
3758 if (kstrtou64(token, 0, &val))
3759 return -EINVAL;
3760
3761 if (strcmp(cmd, "test_mode") == 0) {
3762 switch (val) {
3763 case 0:
3764 ret = icnss_test_mode_fw_test_off(priv);
3765 break;
3766 case 1:
3767 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3768 break;
3769 case 2:
3770 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3771 break;
3772 case 3:
3773 ret = icnss_trigger_recovery(&priv->pdev->dev);
3774 break;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003775 case 4:
3776 icnss_allow_recursive_recovery(&priv->pdev->dev);
3777 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003778 default:
3779 return -EINVAL;
3780 }
3781 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3782 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3783 } else {
3784 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003785 }
3786
3787 if (ret)
3788 return ret;
3789
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003790 return count;
3791}
3792
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003793static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003794{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003795 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003796}
3797
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003798static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003799 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003800 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003801 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003802 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003803 .owner = THIS_MODULE,
3804 .llseek = seq_lseek,
3805};
3806
3807static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3808 size_t count, loff_t *off)
3809{
3810 struct icnss_priv *priv =
3811 ((struct seq_file *)fp->private_data)->private;
3812 int ret;
3813 u32 val;
3814
3815 ret = kstrtou32_from_user(buf, count, 0, &val);
3816 if (ret)
3817 return ret;
3818
3819 if (ret == 0)
3820 memset(&priv->stats, 0, sizeof(priv->stats));
3821
3822 return count;
3823}
3824
3825static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3826{
3827 enum icnss_driver_state i;
3828 int skip = 0;
3829 unsigned long state;
3830
3831 seq_printf(s, "\nState: 0x%lx(", priv->state);
3832 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3833
3834 if (!(state & 0x1))
3835 continue;
3836
3837 if (skip++)
3838 seq_puts(s, " | ");
3839
3840 switch (i) {
3841 case ICNSS_WLFW_QMI_CONNECTED:
3842 seq_puts(s, "QMI CONN");
3843 continue;
3844 case ICNSS_POWER_ON:
3845 seq_puts(s, "POWER ON");
3846 continue;
3847 case ICNSS_FW_READY:
3848 seq_puts(s, "FW READY");
3849 continue;
3850 case ICNSS_DRIVER_PROBED:
3851 seq_puts(s, "DRIVER PROBED");
3852 continue;
3853 case ICNSS_FW_TEST_MODE:
3854 seq_puts(s, "FW TEST MODE");
3855 continue;
3856 case ICNSS_PM_SUSPEND:
3857 seq_puts(s, "PM SUSPEND");
3858 continue;
3859 case ICNSS_PM_SUSPEND_NOIRQ:
3860 seq_puts(s, "PM SUSPEND NOIRQ");
3861 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003862 case ICNSS_SSR_REGISTERED:
3863 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003864 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003865 case ICNSS_PDR_REGISTERED:
3866 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003867 continue;
3868 case ICNSS_PD_RESTART:
3869 seq_puts(s, "PD RESTART");
3870 continue;
3871 case ICNSS_MSA0_ASSIGNED:
3872 seq_puts(s, "MSA0 ASSIGNED");
3873 continue;
3874 case ICNSS_WLFW_EXISTS:
3875 seq_puts(s, "WLAN FW EXISTS");
3876 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303877 case ICNSS_SHUTDOWN_DONE:
3878 seq_puts(s, "SHUTDOWN DONE");
3879 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003880 case ICNSS_HOST_TRIGGERED_PDR:
3881 seq_puts(s, "HOST TRIGGERED PDR");
3882 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07003883 case ICNSS_FW_DOWN:
3884 seq_puts(s, "FW DOWN");
3885 continue;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08003886 case ICNSS_DRIVER_UNLOADING:
3887 seq_puts(s, "DRIVER UNLOADING");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003888 }
3889
3890 seq_printf(s, "UNKNOWN-%d", i);
3891 }
3892 seq_puts(s, ")\n");
3893
3894 return 0;
3895}
3896
3897static int icnss_stats_show_capability(struct seq_file *s,
3898 struct icnss_priv *priv)
3899{
3900 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3901 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3902 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3903 seq_printf(s, "Chip family: 0x%x\n",
3904 priv->chip_info.chip_family);
3905 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3906 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3907 seq_printf(s, "Firmware Version: 0x%x\n",
3908 priv->fw_version_info.fw_version);
3909 seq_printf(s, "Firmware Build Timestamp: %s\n",
3910 priv->fw_version_info.fw_build_timestamp);
3911 seq_printf(s, "Firmware Build ID: %s\n",
3912 priv->fw_build_id);
3913 }
3914
3915 return 0;
3916}
3917
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003918static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
3919 struct icnss_priv *priv)
3920{
3921 if (priv->stats.rejuvenate_ind) {
3922 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
3923 seq_printf(s, "Number of Rejuvenations: %u\n",
3924 priv->stats.rejuvenate_ind);
3925 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
3926 priv->cause_for_rejuvenation);
3927 seq_printf(s, "Requesting Sub-System: 0x%x\n",
3928 priv->requesting_sub_system);
3929 seq_printf(s, "Line Number: %u\n",
3930 priv->line_number);
3931 seq_printf(s, "Function Name: %s\n",
3932 priv->function_name);
3933 }
3934
3935 return 0;
3936}
3937
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003938static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
3939{
3940 int i;
3941
3942 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
3943 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
3944 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
3945 seq_printf(s, "%24s %16u %16u\n",
3946 icnss_driver_event_to_str(i),
3947 priv->stats.events[i].posted,
3948 priv->stats.events[i].processed);
3949
3950 return 0;
3951}
3952
3953static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
3954{
3955 int i;
3956
3957 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
3958 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
3959 "Free", "Enable", "Disable");
3960 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
3961 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
3962 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
3963 priv->stats.ce_irqs[i].free,
3964 priv->stats.ce_irqs[i].enable,
3965 priv->stats.ce_irqs[i].disable);
3966
3967 return 0;
3968}
3969
3970static int icnss_stats_show(struct seq_file *s, void *data)
3971{
3972#define ICNSS_STATS_DUMP(_s, _priv, _x) \
3973 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
3974
3975 struct icnss_priv *priv = s->private;
3976
3977 ICNSS_STATS_DUMP(s, priv, ind_register_req);
3978 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
3979 ICNSS_STATS_DUMP(s, priv, ind_register_err);
3980 ICNSS_STATS_DUMP(s, priv, msa_info_req);
3981 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
3982 ICNSS_STATS_DUMP(s, priv, msa_info_err);
3983 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
3984 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
3985 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
3986 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
3987 ICNSS_STATS_DUMP(s, priv, cap_req);
3988 ICNSS_STATS_DUMP(s, priv, cap_resp);
3989 ICNSS_STATS_DUMP(s, priv, cap_err);
3990 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
3991 ICNSS_STATS_DUMP(s, priv, cfg_req);
3992 ICNSS_STATS_DUMP(s, priv, cfg_resp);
3993 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
3994 ICNSS_STATS_DUMP(s, priv, mode_req);
3995 ICNSS_STATS_DUMP(s, priv, mode_resp);
3996 ICNSS_STATS_DUMP(s, priv, mode_req_err);
3997 ICNSS_STATS_DUMP(s, priv, ini_req);
3998 ICNSS_STATS_DUMP(s, priv, ini_resp);
3999 ICNSS_STATS_DUMP(s, priv, ini_req_err);
4000 ICNSS_STATS_DUMP(s, priv, vbatt_req);
4001 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
4002 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004003 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004004 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
4005 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
4006 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07004007 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
4008 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
4009 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
4010 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004011
4012 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
4013 ICNSS_STATS_DUMP(s, priv, pm_suspend);
4014 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
4015 ICNSS_STATS_DUMP(s, priv, pm_resume);
4016 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
4017 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
4018 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
4019 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
4020 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
4021 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
4022 ICNSS_STATS_DUMP(s, priv, pm_relax);
4023
4024 icnss_stats_show_irqs(s, priv);
4025
4026 icnss_stats_show_capability(s, priv);
4027
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004028 icnss_stats_show_rejuvenate_info(s, priv);
4029
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004030 icnss_stats_show_events(s, priv);
4031
4032 icnss_stats_show_state(s, priv);
4033
4034 return 0;
4035#undef ICNSS_STATS_DUMP
4036}
4037
4038static int icnss_stats_open(struct inode *inode, struct file *file)
4039{
4040 return single_open(file, icnss_stats_show, inode->i_private);
4041}
4042
4043static const struct file_operations icnss_stats_fops = {
4044 .read = seq_read,
4045 .write = icnss_stats_write,
4046 .release = single_release,
4047 .open = icnss_stats_open,
4048 .owner = THIS_MODULE,
4049 .llseek = seq_lseek,
4050};
4051
4052static int icnss_regwrite_show(struct seq_file *s, void *data)
4053{
4054 struct icnss_priv *priv = s->private;
4055
4056 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4057
4058 if (!test_bit(ICNSS_FW_READY, &priv->state))
4059 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4060
4061 return 0;
4062}
4063
4064static ssize_t icnss_regwrite_write(struct file *fp,
4065 const char __user *user_buf,
4066 size_t count, loff_t *off)
4067{
4068 struct icnss_priv *priv =
4069 ((struct seq_file *)fp->private_data)->private;
4070 char buf[64];
4071 char *sptr, *token;
4072 unsigned int len = 0;
4073 uint32_t reg_offset, mem_type, reg_val;
4074 const char *delim = " ";
4075 int ret = 0;
4076
4077 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4078 !test_bit(ICNSS_POWER_ON, &priv->state))
4079 return -EINVAL;
4080
4081 len = min(count, sizeof(buf) - 1);
4082 if (copy_from_user(buf, user_buf, len))
4083 return -EFAULT;
4084
4085 buf[len] = '\0';
4086 sptr = buf;
4087
4088 token = strsep(&sptr, delim);
4089 if (!token)
4090 return -EINVAL;
4091
4092 if (!sptr)
4093 return -EINVAL;
4094
4095 if (kstrtou32(token, 0, &mem_type))
4096 return -EINVAL;
4097
4098 token = strsep(&sptr, delim);
4099 if (!token)
4100 return -EINVAL;
4101
4102 if (!sptr)
4103 return -EINVAL;
4104
4105 if (kstrtou32(token, 0, &reg_offset))
4106 return -EINVAL;
4107
4108 token = strsep(&sptr, delim);
4109 if (!token)
4110 return -EINVAL;
4111
4112 if (kstrtou32(token, 0, &reg_val))
4113 return -EINVAL;
4114
4115 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4116 sizeof(uint32_t),
4117 (uint8_t *)&reg_val);
4118 if (ret)
4119 return ret;
4120
4121 return count;
4122}
4123
4124static int icnss_regwrite_open(struct inode *inode, struct file *file)
4125{
4126 return single_open(file, icnss_regwrite_show, inode->i_private);
4127}
4128
4129static const struct file_operations icnss_regwrite_fops = {
4130 .read = seq_read,
4131 .write = icnss_regwrite_write,
4132 .open = icnss_regwrite_open,
4133 .owner = THIS_MODULE,
4134 .llseek = seq_lseek,
4135};
4136
4137static int icnss_regread_show(struct seq_file *s, void *data)
4138{
4139 struct icnss_priv *priv = s->private;
4140
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304141 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004142 if (!priv->diag_reg_read_buf) {
4143 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4144
4145 if (!test_bit(ICNSS_FW_READY, &priv->state))
4146 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4147
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304148 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004149 return 0;
4150 }
4151
4152 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4153 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4154 priv->diag_reg_read_len);
4155
4156 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4157 priv->diag_reg_read_len, false);
4158
4159 priv->diag_reg_read_len = 0;
4160 kfree(priv->diag_reg_read_buf);
4161 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304162 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004163
4164 return 0;
4165}
4166
4167static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4168 size_t count, loff_t *off)
4169{
4170 struct icnss_priv *priv =
4171 ((struct seq_file *)fp->private_data)->private;
4172 char buf[64];
4173 char *sptr, *token;
4174 unsigned int len = 0;
4175 uint32_t reg_offset, mem_type;
4176 uint32_t data_len = 0;
4177 uint8_t *reg_buf = NULL;
4178 const char *delim = " ";
4179 int ret = 0;
4180
4181 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4182 !test_bit(ICNSS_POWER_ON, &priv->state))
4183 return -EINVAL;
4184
4185 len = min(count, sizeof(buf) - 1);
4186 if (copy_from_user(buf, user_buf, len))
4187 return -EFAULT;
4188
4189 buf[len] = '\0';
4190 sptr = buf;
4191
4192 token = strsep(&sptr, delim);
4193 if (!token)
4194 return -EINVAL;
4195
4196 if (!sptr)
4197 return -EINVAL;
4198
4199 if (kstrtou32(token, 0, &mem_type))
4200 return -EINVAL;
4201
4202 token = strsep(&sptr, delim);
4203 if (!token)
4204 return -EINVAL;
4205
4206 if (!sptr)
4207 return -EINVAL;
4208
4209 if (kstrtou32(token, 0, &reg_offset))
4210 return -EINVAL;
4211
4212 token = strsep(&sptr, delim);
4213 if (!token)
4214 return -EINVAL;
4215
4216 if (kstrtou32(token, 0, &data_len))
4217 return -EINVAL;
4218
4219 if (data_len == 0 ||
4220 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4221 return -EINVAL;
4222
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304223 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004224 kfree(priv->diag_reg_read_buf);
4225 priv->diag_reg_read_buf = NULL;
4226
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004227 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304228 if (!reg_buf) {
4229 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004230 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304231 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004232
4233 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4234 mem_type, data_len,
4235 reg_buf);
4236 if (ret) {
4237 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304238 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004239 return ret;
4240 }
4241
4242 priv->diag_reg_read_addr = reg_offset;
4243 priv->diag_reg_read_mem_type = mem_type;
4244 priv->diag_reg_read_len = data_len;
4245 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304246 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004247
4248 return count;
4249}
4250
4251static int icnss_regread_open(struct inode *inode, struct file *file)
4252{
4253 return single_open(file, icnss_regread_show, inode->i_private);
4254}
4255
4256static const struct file_operations icnss_regread_fops = {
4257 .read = seq_read,
4258 .write = icnss_regread_write,
4259 .open = icnss_regread_open,
4260 .owner = THIS_MODULE,
4261 .llseek = seq_lseek,
4262};
4263
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004264#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004265static int icnss_debugfs_create(struct icnss_priv *priv)
4266{
4267 int ret = 0;
4268 struct dentry *root_dentry;
4269
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004270 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004271
4272 if (IS_ERR(root_dentry)) {
4273 ret = PTR_ERR(root_dentry);
4274 icnss_pr_err("Unable to create debugfs %d\n", ret);
4275 goto out;
4276 }
4277
4278 priv->root_dentry = root_dentry;
4279
Yuanyuan Liu84132752017-05-24 12:07:12 -07004280 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004281 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004282
Yuanyuan Liu84132752017-05-24 12:07:12 -07004283 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004284 &icnss_stats_fops);
4285 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4286 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004287 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004288 &icnss_regwrite_fops);
4289
4290out:
4291 return ret;
4292}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004293#else
4294static int icnss_debugfs_create(struct icnss_priv *priv)
4295{
4296 int ret = 0;
4297 struct dentry *root_dentry;
4298
4299 root_dentry = debugfs_create_dir("icnss", NULL);
4300
4301 if (IS_ERR(root_dentry)) {
4302 ret = PTR_ERR(root_dentry);
4303 icnss_pr_err("Unable to create debugfs %d\n", ret);
4304 return ret;
4305 }
4306
4307 priv->root_dentry = root_dentry;
4308
4309 debugfs_create_file("stats", 0600, root_dentry, priv,
4310 &icnss_stats_fops);
4311 return 0;
4312}
4313#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004314
4315static void icnss_debugfs_destroy(struct icnss_priv *priv)
4316{
4317 debugfs_remove_recursive(priv->root_dentry);
4318}
4319
4320static int icnss_get_vbatt_info(struct icnss_priv *priv)
4321{
4322 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4323 struct qpnp_vadc_chip *vadc_dev = NULL;
4324 int ret = 0;
4325
4326 if (test_bit(VBATT_DISABLE, &quirks)) {
4327 icnss_pr_dbg("VBATT feature is disabled\n");
4328 return ret;
4329 }
4330
4331 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4332 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4333 icnss_pr_err("adc_tm_dev probe defer\n");
4334 return -EPROBE_DEFER;
4335 }
4336
4337 if (IS_ERR(adc_tm_dev)) {
4338 ret = PTR_ERR(adc_tm_dev);
4339 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4340 ret);
4341 return ret;
4342 }
4343
4344 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4345 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4346 icnss_pr_err("vadc_dev probe defer\n");
4347 return -EPROBE_DEFER;
4348 }
4349
4350 if (IS_ERR(vadc_dev)) {
4351 ret = PTR_ERR(vadc_dev);
4352 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4353 ret);
4354 return ret;
4355 }
4356
4357 priv->adc_tm_dev = adc_tm_dev;
4358 priv->vadc_dev = vadc_dev;
4359
4360 return 0;
4361}
4362
4363static int icnss_probe(struct platform_device *pdev)
4364{
4365 int ret = 0;
4366 struct resource *res;
4367 int i;
4368 struct device *dev = &pdev->dev;
4369 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304370 const __be32 *addrp;
4371 u64 prop_size = 0;
4372 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004373
4374 if (penv) {
4375 icnss_pr_err("Driver is already initialized\n");
4376 return -EEXIST;
4377 }
4378
4379 icnss_pr_dbg("Platform driver probe\n");
4380
4381 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4382 if (!priv)
4383 return -ENOMEM;
4384
4385 priv->magic = ICNSS_MAGIC;
4386 dev_set_drvdata(dev, priv);
4387
4388 priv->pdev = pdev;
4389
4390 ret = icnss_get_vbatt_info(priv);
4391 if (ret == -EPROBE_DEFER)
4392 goto out;
4393
Yuanyuan Liu68939762017-04-04 16:43:03 -07004394 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4395 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4396 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4397
4398 if (ret)
4399 goto out;
4400 }
4401
4402 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4403 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4404 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4405 if (ret)
4406 goto out;
4407 }
4408
4409 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4410 priv->bypass_s1_smmu = true;
4411
4412 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4413
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004414 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4415 if (!res) {
4416 icnss_pr_err("Memory base not found in DT\n");
4417 ret = -EINVAL;
4418 goto out;
4419 }
4420
4421 priv->mem_base_pa = res->start;
4422 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4423 resource_size(res));
4424 if (!priv->mem_base_va) {
4425 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4426 &priv->mem_base_pa);
4427 ret = -EINVAL;
4428 goto out;
4429 }
4430 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4431 priv->mem_base_va);
4432
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004433 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4434 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4435 if (!res) {
4436 icnss_pr_err("Fail to get IRQ-%d\n", i);
4437 ret = -ENODEV;
4438 goto out;
4439 } else {
4440 priv->ce_irqs[i] = res->start;
4441 }
4442 }
4443
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304444 np = of_parse_phandle(dev->of_node,
4445 "qcom,wlan-msa-fixed-region", 0);
4446 if (np) {
4447 addrp = of_get_address(np, 0, &prop_size, NULL);
4448 if (!addrp) {
4449 icnss_pr_err("Failed to get assigned-addresses or property\n");
4450 ret = -EINVAL;
4451 goto out;
4452 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004453
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304454 priv->msa_pa = of_translate_address(np, addrp);
4455 if (priv->msa_pa == OF_BAD_ADDR) {
4456 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4457 ret = -EINVAL;
4458 goto out;
4459 }
4460
4461 priv->msa_va = memremap(priv->msa_pa,
4462 (unsigned long)prop_size, MEMREMAP_WT);
4463 if (!priv->msa_va) {
4464 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4465 &priv->msa_pa);
4466 ret = -EINVAL;
4467 goto out;
4468 }
4469 priv->msa_mem_size = prop_size;
4470 } else {
4471 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4472 &priv->msa_mem_size);
4473 if (ret || priv->msa_mem_size == 0) {
4474 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4475 priv->msa_mem_size, ret);
4476 goto out;
4477 }
4478
4479 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4480 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4481
4482 if (!priv->msa_va) {
4483 icnss_pr_err("DMA alloc failed for MSA\n");
4484 ret = -ENOMEM;
4485 goto out;
4486 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004487 }
4488
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304489 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4490 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004491
4492 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4493 "smmu_iova_base");
4494 if (!res) {
4495 icnss_pr_err("SMMU IOVA base not found\n");
4496 } else {
4497 priv->smmu_iova_start = res->start;
4498 priv->smmu_iova_len = resource_size(res);
4499 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4500 &priv->smmu_iova_start, priv->smmu_iova_len);
4501
4502 res = platform_get_resource_byname(pdev,
4503 IORESOURCE_MEM,
4504 "smmu_iova_ipa");
4505 if (!res) {
4506 icnss_pr_err("SMMU IOVA IPA not found\n");
4507 } else {
4508 priv->smmu_iova_ipa_start = res->start;
4509 priv->smmu_iova_ipa_len = resource_size(res);
4510 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4511 &priv->smmu_iova_ipa_start,
4512 priv->smmu_iova_ipa_len);
4513 }
4514
4515 ret = icnss_smmu_init(priv);
4516 if (ret < 0) {
4517 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4518 ret, &priv->smmu_iova_start,
4519 priv->smmu_iova_len);
4520 goto out;
4521 }
4522 }
4523
4524 spin_lock_init(&priv->event_lock);
4525 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304526 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004527
4528 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4529 if (!priv->event_wq) {
4530 icnss_pr_err("Workqueue creation failed\n");
4531 ret = -EFAULT;
4532 goto out_smmu_deinit;
4533 }
4534
4535 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4536 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4537 INIT_LIST_HEAD(&priv->event_list);
4538
4539 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4540 WLFW_SERVICE_VERS_V01,
4541 WLFW_SERVICE_INS_ID_V01,
4542 &wlfw_clnt_nb);
4543 if (ret < 0) {
4544 icnss_pr_err("Notifier register failed: %d\n", ret);
4545 goto out_destroy_wq;
4546 }
4547
4548 icnss_enable_recovery(priv);
4549
4550 icnss_debugfs_create(priv);
4551
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004552 ret = device_init_wakeup(&priv->pdev->dev, true);
4553 if (ret)
4554 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4555 ret);
4556
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004557 penv = priv;
4558
4559 icnss_pr_info("Platform driver probed successfully\n");
4560
4561 return 0;
4562
4563out_destroy_wq:
4564 destroy_workqueue(priv->event_wq);
4565out_smmu_deinit:
4566 icnss_smmu_deinit(priv);
4567out:
4568 dev_set_drvdata(dev, NULL);
4569
4570 return ret;
4571}
4572
4573static int icnss_remove(struct platform_device *pdev)
4574{
4575 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4576
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004577 device_init_wakeup(&penv->pdev->dev, false);
4578
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004579 icnss_debugfs_destroy(penv);
4580
4581 icnss_modem_ssr_unregister_notifier(penv);
4582
4583 destroy_ramdump_device(penv->msa0_dump_dev);
4584
4585 icnss_pdr_unregister_notifier(penv);
4586
4587 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4588 WLFW_SERVICE_VERS_V01,
4589 WLFW_SERVICE_INS_ID_V01,
4590 &wlfw_clnt_nb);
4591 if (penv->event_wq)
4592 destroy_workqueue(penv->event_wq);
4593
4594 icnss_hw_power_off(penv);
4595
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304596 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4597 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004598
4599 dev_set_drvdata(&pdev->dev, NULL);
4600
4601 return 0;
4602}
4603
4604#ifdef CONFIG_PM_SLEEP
4605static int icnss_pm_suspend(struct device *dev)
4606{
4607 struct icnss_priv *priv = dev_get_drvdata(dev);
4608 int ret = 0;
4609
4610 if (priv->magic != ICNSS_MAGIC) {
4611 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4612 dev, priv, priv->magic);
4613 return -EINVAL;
4614 }
4615
Yuanyuan Liu68939762017-04-04 16:43:03 -07004616 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004617
4618 if (!priv->ops || !priv->ops->pm_suspend ||
4619 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4620 goto out;
4621
4622 ret = priv->ops->pm_suspend(dev);
4623
4624out:
4625 if (ret == 0) {
4626 priv->stats.pm_suspend++;
4627 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4628 } else {
4629 priv->stats.pm_suspend_err++;
4630 }
4631 return ret;
4632}
4633
4634static int icnss_pm_resume(struct device *dev)
4635{
4636 struct icnss_priv *priv = dev_get_drvdata(dev);
4637 int ret = 0;
4638
4639 if (priv->magic != ICNSS_MAGIC) {
4640 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4641 dev, priv, priv->magic);
4642 return -EINVAL;
4643 }
4644
Yuanyuan Liu68939762017-04-04 16:43:03 -07004645 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004646
4647 if (!priv->ops || !priv->ops->pm_resume ||
4648 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4649 goto out;
4650
4651 ret = priv->ops->pm_resume(dev);
4652
4653out:
4654 if (ret == 0) {
4655 priv->stats.pm_resume++;
4656 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4657 } else {
4658 priv->stats.pm_resume_err++;
4659 }
4660 return ret;
4661}
4662
4663static int icnss_pm_suspend_noirq(struct device *dev)
4664{
4665 struct icnss_priv *priv = dev_get_drvdata(dev);
4666 int ret = 0;
4667
4668 if (priv->magic != ICNSS_MAGIC) {
4669 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4670 dev, priv, priv->magic);
4671 return -EINVAL;
4672 }
4673
Yuanyuan Liu68939762017-04-04 16:43:03 -07004674 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004675
4676 if (!priv->ops || !priv->ops->suspend_noirq ||
4677 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4678 goto out;
4679
4680 ret = priv->ops->suspend_noirq(dev);
4681
4682out:
4683 if (ret == 0) {
4684 priv->stats.pm_suspend_noirq++;
4685 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4686 } else {
4687 priv->stats.pm_suspend_noirq_err++;
4688 }
4689 return ret;
4690}
4691
4692static int icnss_pm_resume_noirq(struct device *dev)
4693{
4694 struct icnss_priv *priv = dev_get_drvdata(dev);
4695 int ret = 0;
4696
4697 if (priv->magic != ICNSS_MAGIC) {
4698 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4699 dev, priv, priv->magic);
4700 return -EINVAL;
4701 }
4702
Yuanyuan Liu68939762017-04-04 16:43:03 -07004703 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004704
4705 if (!priv->ops || !priv->ops->resume_noirq ||
4706 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4707 goto out;
4708
4709 ret = priv->ops->resume_noirq(dev);
4710
4711out:
4712 if (ret == 0) {
4713 priv->stats.pm_resume_noirq++;
4714 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4715 } else {
4716 priv->stats.pm_resume_noirq_err++;
4717 }
4718 return ret;
4719}
4720#endif
4721
4722static const struct dev_pm_ops icnss_pm_ops = {
4723 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4724 icnss_pm_resume)
4725 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4726 icnss_pm_resume_noirq)
4727};
4728
4729static const struct of_device_id icnss_dt_match[] = {
4730 {.compatible = "qcom,icnss"},
4731 {}
4732};
4733
4734MODULE_DEVICE_TABLE(of, icnss_dt_match);
4735
4736static struct platform_driver icnss_driver = {
4737 .probe = icnss_probe,
4738 .remove = icnss_remove,
4739 .driver = {
4740 .name = "icnss",
4741 .pm = &icnss_pm_ops,
4742 .owner = THIS_MODULE,
4743 .of_match_table = icnss_dt_match,
4744 },
4745};
4746
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004747static int __init icnss_initialize(void)
4748{
4749 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4750 "icnss", 0);
4751 if (!icnss_ipc_log_context)
4752 icnss_pr_err("Unable to create log context\n");
4753
Yuanyuan Liu68939762017-04-04 16:43:03 -07004754 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4755 "icnss_long", 0);
4756 if (!icnss_ipc_log_long_context)
4757 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004758
4759 return platform_driver_register(&icnss_driver);
4760}
4761
4762static void __exit icnss_exit(void)
4763{
4764 platform_driver_unregister(&icnss_driver);
4765 ipc_log_context_destroy(icnss_ipc_log_context);
4766 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004767 ipc_log_context_destroy(icnss_ipc_log_long_context);
4768 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004769}
4770
4771
4772module_init(icnss_initialize);
4773module_exit(icnss_exit);
4774
4775MODULE_LICENSE("GPL v2");
4776MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");