blob: 95004a193229145cce925f4b8e7d12aca05edc6c [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
1114 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1115
Yuanyuan Liu68939762017-04-04 16:43:03 -07001116 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001117 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001118 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001119 return ret;
1120 }
1121 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001122 spin_unlock(&priv->on_off_lock);
1123
1124 icnss_clk_deinit(priv);
1125
1126 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001127
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001128 return ret;
1129}
1130
1131int icnss_power_on(struct device *dev)
1132{
1133 struct icnss_priv *priv = dev_get_drvdata(dev);
1134
1135 if (!priv) {
1136 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1137 dev, priv);
1138 return -EINVAL;
1139 }
1140
1141 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1142
1143 return icnss_hw_power_on(priv);
1144}
1145EXPORT_SYMBOL(icnss_power_on);
1146
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001147bool icnss_is_fw_ready(void)
1148{
1149 if (!penv)
1150 return false;
1151 else
1152 return test_bit(ICNSS_FW_READY, &penv->state);
1153}
1154EXPORT_SYMBOL(icnss_is_fw_ready);
1155
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001156bool icnss_is_fw_down(void)
1157{
1158 if (!penv)
1159 return false;
1160 else
1161 return test_bit(ICNSS_FW_DOWN, &penv->state);
1162}
1163EXPORT_SYMBOL(icnss_is_fw_down);
1164
1165
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001166int icnss_power_off(struct device *dev)
1167{
1168 struct icnss_priv *priv = dev_get_drvdata(dev);
1169
1170 if (!priv) {
1171 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1172 dev, priv);
1173 return -EINVAL;
1174 }
1175
1176 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1177
1178 return icnss_hw_power_off(priv);
1179}
1180EXPORT_SYMBOL(icnss_power_off);
1181
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001182static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
1183{
1184 struct icnss_priv *priv = ctx;
1185
1186 if (priv)
1187 priv->force_err_fatal = true;
1188
1189 icnss_pr_err("Received force error fatal request from FW\n");
1190
1191 return IRQ_HANDLED;
1192}
1193
1194static void icnss_register_force_error_fatal(struct icnss_priv *priv)
1195{
1196 int gpio, irq, ret;
1197
1198 if (!of_find_property(priv->pdev->dev.of_node,
1199 "qcom,gpio-force-fatal-error", NULL)) {
1200 icnss_pr_dbg("Error fatal smp2p handler not registered\n");
1201 return;
1202 }
1203 gpio = of_get_named_gpio(priv->pdev->dev.of_node,
1204 "qcom,gpio-force-fatal-error", 0);
1205 if (!gpio_is_valid(gpio)) {
1206 icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio);
1207 return;
1208 }
1209 irq = gpio_to_irq(gpio);
1210 if (irq < 0) {
1211 icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq);
1212 return;
1213 }
1214 ret = request_irq(irq, fw_error_fatal_handler,
1215 IRQF_TRIGGER_RISING, "wlanfw-err", priv);
1216 if (ret < 0) {
1217 icnss_pr_err("Unable to regiser for error fatal IRQ handler %d",
1218 irq);
1219 return;
1220 }
1221 icnss_pr_dbg("FW force error fatal handler registered\n");
1222}
1223
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001224static int wlfw_msa_mem_info_send_sync_msg(void)
1225{
1226 int ret;
1227 int i;
1228 struct wlfw_msa_info_req_msg_v01 req;
1229 struct wlfw_msa_info_resp_msg_v01 resp;
1230 struct msg_desc req_desc, resp_desc;
1231
1232 if (!penv || !penv->wlfw_clnt)
1233 return -ENODEV;
1234
1235 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1236
1237 memset(&req, 0, sizeof(req));
1238 memset(&resp, 0, sizeof(resp));
1239
1240 req.msa_addr = penv->msa_pa;
1241 req.size = penv->msa_mem_size;
1242
1243 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1244 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1245 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1246
1247 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1248 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1249 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1250
1251 penv->stats.msa_info_req++;
1252
1253 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1254 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1255 if (ret < 0) {
1256 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1257 goto out;
1258 }
1259
1260 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1261 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1262 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301263 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001264 goto out;
1265 }
1266
1267 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1268 resp.mem_region_info_len);
1269
Yuanyuan Liu68939762017-04-04 16:43:03 -07001270 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001271 icnss_pr_err("Invalid memory region length received: %d\n",
1272 resp.mem_region_info_len);
1273 ret = -EINVAL;
1274 goto out;
1275 }
1276
1277 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001278 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001279 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001280 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001281 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001282 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001283 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001284 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001285 resp.mem_region_info[i].secure_flag;
1286 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001287 i, penv->mem_region[i].reg_addr,
1288 penv->mem_region[i].size,
1289 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001290 }
1291
1292 return 0;
1293
1294out:
1295 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001296 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001297 return ret;
1298}
1299
1300static int wlfw_msa_ready_send_sync_msg(void)
1301{
1302 int ret;
1303 struct wlfw_msa_ready_req_msg_v01 req;
1304 struct wlfw_msa_ready_resp_msg_v01 resp;
1305 struct msg_desc req_desc, resp_desc;
1306
1307 if (!penv || !penv->wlfw_clnt)
1308 return -ENODEV;
1309
1310 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1311 penv->state);
1312
1313 memset(&req, 0, sizeof(req));
1314 memset(&resp, 0, sizeof(resp));
1315
1316 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1317 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1318 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1319
1320 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1321 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1322 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1323
1324 penv->stats.msa_ready_req++;
1325 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1326 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1327 if (ret < 0) {
1328 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1329 goto out;
1330 }
1331
1332 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1333 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1334 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301335 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001336 goto out;
1337 }
1338 penv->stats.msa_ready_resp++;
1339
1340 return 0;
1341
1342out:
1343 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001344 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001345 return ret;
1346}
1347
1348static int wlfw_ind_register_send_sync_msg(void)
1349{
1350 int ret;
1351 struct wlfw_ind_register_req_msg_v01 req;
1352 struct wlfw_ind_register_resp_msg_v01 resp;
1353 struct msg_desc req_desc, resp_desc;
1354
1355 if (!penv || !penv->wlfw_clnt)
1356 return -ENODEV;
1357
1358 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1359 penv->state);
1360
1361 memset(&req, 0, sizeof(req));
1362 memset(&resp, 0, sizeof(resp));
1363
1364 req.client_id_valid = 1;
1365 req.client_id = WLFW_CLIENT_ID;
1366 req.fw_ready_enable_valid = 1;
1367 req.fw_ready_enable = 1;
1368 req.msa_ready_enable_valid = 1;
1369 req.msa_ready_enable = 1;
1370 req.pin_connect_result_enable_valid = 1;
1371 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001372 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1373 req.rejuvenate_enable_valid = 1;
1374 req.rejuvenate_enable = 1;
1375 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001376
1377 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1378 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1379 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1380
1381 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1382 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1383 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1384
1385 penv->stats.ind_register_req++;
1386
1387 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1388 &resp_desc, &resp, sizeof(resp),
1389 WLFW_TIMEOUT_MS);
1390 if (ret < 0) {
1391 icnss_pr_err("Send indication register req failed %d\n", ret);
1392 goto out;
1393 }
1394
1395 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1396 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1397 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301398 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001399 goto out;
1400 }
1401 penv->stats.ind_register_resp++;
1402
1403 return 0;
1404
1405out:
1406 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001407 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001408 return ret;
1409}
1410
1411static int wlfw_cap_send_sync_msg(void)
1412{
1413 int ret;
1414 struct wlfw_cap_req_msg_v01 req;
1415 struct wlfw_cap_resp_msg_v01 resp;
1416 struct msg_desc req_desc, resp_desc;
1417
1418 if (!penv || !penv->wlfw_clnt)
1419 return -ENODEV;
1420
1421 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1422
1423 memset(&resp, 0, sizeof(resp));
1424
1425 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1426 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1427 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1428
1429 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1430 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1431 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1432
1433 penv->stats.cap_req++;
1434 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1435 &resp_desc, &resp, sizeof(resp),
1436 WLFW_TIMEOUT_MS);
1437 if (ret < 0) {
1438 icnss_pr_err("Send capability req failed %d\n", ret);
1439 goto out;
1440 }
1441
1442 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1443 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1444 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301445 ret = -resp.resp.result;
1446 if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
1447 icnss_pr_err("RF card Not present");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001448 goto out;
1449 }
1450
1451 penv->stats.cap_resp++;
1452 /* store cap locally */
1453 if (resp.chip_info_valid)
1454 penv->chip_info = resp.chip_info;
1455 if (resp.board_info_valid)
1456 penv->board_info = resp.board_info;
1457 else
1458 penv->board_info.board_id = 0xFF;
1459 if (resp.soc_info_valid)
1460 penv->soc_info = resp.soc_info;
1461 if (resp.fw_version_info_valid)
1462 penv->fw_version_info = resp.fw_version_info;
1463 if (resp.fw_build_id_valid)
1464 strlcpy(penv->fw_build_id, resp.fw_build_id,
1465 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1466
1467 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",
1468 penv->chip_info.chip_id, penv->chip_info.chip_family,
1469 penv->board_info.board_id, penv->soc_info.soc_id,
1470 penv->fw_version_info.fw_version,
1471 penv->fw_version_info.fw_build_timestamp,
1472 penv->fw_build_id);
1473
1474 return 0;
1475
1476out:
1477 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001478 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001479 return ret;
1480}
1481
1482static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1483{
1484 int ret;
1485 struct wlfw_wlan_mode_req_msg_v01 req;
1486 struct wlfw_wlan_mode_resp_msg_v01 resp;
1487 struct msg_desc req_desc, resp_desc;
1488
1489 if (!penv || !penv->wlfw_clnt)
1490 return -ENODEV;
1491
1492 /* During recovery do not send mode request for WLAN OFF as
1493 * FW not able to process it.
1494 */
1495 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1496 mode == QMI_WLFW_OFF_V01)
1497 return 0;
1498
1499 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1500 penv->state, mode);
1501
1502 memset(&req, 0, sizeof(req));
1503 memset(&resp, 0, sizeof(resp));
1504
1505 req.mode = mode;
1506 req.hw_debug_valid = 1;
1507 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1508
1509 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1510 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1511 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1512
1513 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1514 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1515 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1516
1517 penv->stats.mode_req++;
1518 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1519 &resp_desc, &resp, sizeof(resp),
1520 WLFW_TIMEOUT_MS);
1521 if (ret < 0) {
1522 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1523 mode, ret);
1524 goto out;
1525 }
1526
1527 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1528 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1529 mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301530 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001531 goto out;
1532 }
1533 penv->stats.mode_resp++;
1534
1535 return 0;
1536
1537out:
1538 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001539 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001540 return ret;
1541}
1542
1543static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1544{
1545 int ret;
1546 struct wlfw_wlan_cfg_req_msg_v01 req;
1547 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1548 struct msg_desc req_desc, resp_desc;
1549
1550 if (!penv || !penv->wlfw_clnt)
1551 return -ENODEV;
1552
1553 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1554
1555 memset(&req, 0, sizeof(req));
1556 memset(&resp, 0, sizeof(resp));
1557
1558 memcpy(&req, data, sizeof(req));
1559
1560 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1561 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1562 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1563
1564 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1565 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1566 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1567
1568 penv->stats.cfg_req++;
1569 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1570 &resp_desc, &resp, sizeof(resp),
1571 WLFW_TIMEOUT_MS);
1572 if (ret < 0) {
1573 icnss_pr_err("Send config req failed %d\n", ret);
1574 goto out;
1575 }
1576
1577 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1578 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1579 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301580 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001581 goto out;
1582 }
1583 penv->stats.cfg_resp++;
1584
1585 return 0;
1586
1587out:
1588 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001589 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001590 return ret;
1591}
1592
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001593static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001594{
1595 int ret;
1596 struct wlfw_ini_req_msg_v01 req;
1597 struct wlfw_ini_resp_msg_v01 resp;
1598 struct msg_desc req_desc, resp_desc;
1599
1600 if (!penv || !penv->wlfw_clnt)
1601 return -ENODEV;
1602
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001603 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1604 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001605
1606 memset(&req, 0, sizeof(req));
1607 memset(&resp, 0, sizeof(resp));
1608
1609 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001610 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001611
1612 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1613 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1614 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1615
1616 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1617 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1618 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1619
1620 penv->stats.ini_req++;
1621
1622 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1623 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1624 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001625 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1626 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001627 goto out;
1628 }
1629
1630 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001631 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1632 fw_log_mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301633 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001634 goto out;
1635 }
1636 penv->stats.ini_resp++;
1637
1638 return 0;
1639
1640out:
1641 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001642 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001643 return ret;
1644}
1645
1646static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1647 uint32_t offset, uint32_t mem_type,
1648 uint32_t data_len, uint8_t *data)
1649{
1650 int ret;
1651 struct wlfw_athdiag_read_req_msg_v01 req;
1652 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1653 struct msg_desc req_desc, resp_desc;
1654
1655 if (!priv->wlfw_clnt) {
1656 ret = -ENODEV;
1657 goto out;
1658 }
1659
1660 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1661 priv->state, offset, mem_type, data_len);
1662
1663 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1664 if (!resp) {
1665 ret = -ENOMEM;
1666 goto out;
1667 }
1668 memset(&req, 0, sizeof(req));
1669
1670 req.offset = offset;
1671 req.mem_type = mem_type;
1672 req.data_len = data_len;
1673
1674 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1675 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1676 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1677
1678 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1679 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1680 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1681
1682 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1683 &resp_desc, resp, sizeof(*resp),
1684 WLFW_TIMEOUT_MS);
1685 if (ret < 0) {
1686 icnss_pr_err("send athdiag read req failed %d\n", ret);
1687 goto out;
1688 }
1689
1690 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1691 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1692 resp->resp.result, resp->resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301693 ret = -resp->resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001694 goto out;
1695 }
1696
Yuanyuan Liu68939762017-04-04 16:43:03 -07001697 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001698 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1699 resp->data_valid, resp->data_len);
1700 ret = -EINVAL;
1701 goto out;
1702 }
1703
1704 memcpy(data, resp->data, resp->data_len);
1705
1706out:
1707 kfree(resp);
1708 return ret;
1709}
1710
1711static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1712 uint32_t offset, uint32_t mem_type,
1713 uint32_t data_len, uint8_t *data)
1714{
1715 int ret;
1716 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1717 struct wlfw_athdiag_write_resp_msg_v01 resp;
1718 struct msg_desc req_desc, resp_desc;
1719
1720 if (!priv->wlfw_clnt) {
1721 ret = -ENODEV;
1722 goto out;
1723 }
1724
1725 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1726 priv->state, offset, mem_type, data_len, data);
1727
1728 req = kzalloc(sizeof(*req), GFP_KERNEL);
1729 if (!req) {
1730 ret = -ENOMEM;
1731 goto out;
1732 }
1733 memset(&resp, 0, sizeof(resp));
1734
1735 req->offset = offset;
1736 req->mem_type = mem_type;
1737 req->data_len = data_len;
1738 memcpy(req->data, data, data_len);
1739
1740 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1741 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1742 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1743
1744 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1745 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1746 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1747
1748 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1749 &resp_desc, &resp, sizeof(resp),
1750 WLFW_TIMEOUT_MS);
1751 if (ret < 0) {
1752 icnss_pr_err("send athdiag write req failed %d\n", ret);
1753 goto out;
1754 }
1755
1756 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1757 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1758 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301759 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001760 goto out;
1761 }
1762out:
1763 kfree(req);
1764 return ret;
1765}
1766
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001767static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1768{
1769 struct msg_desc ind_desc;
1770 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1771 int ret = 0;
1772
1773 if (!penv || !penv->wlfw_clnt) {
1774 ret = -ENODEV;
1775 goto out;
1776 }
1777
1778 memset(&ind_msg, 0, sizeof(ind_msg));
1779
1780 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1781 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1782 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1783
1784 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1785 if (ret < 0) {
1786 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1787 ret, msg_len);
1788 goto out;
1789 }
1790
1791 if (ind_msg.cause_for_rejuvenation_valid)
1792 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1793 else
1794 penv->cause_for_rejuvenation = 0;
1795 if (ind_msg.requesting_sub_system_valid)
1796 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1797 else
1798 penv->requesting_sub_system = 0;
1799 if (ind_msg.line_number_valid)
1800 penv->line_number = ind_msg.line_number;
1801 else
1802 penv->line_number = 0;
1803 if (ind_msg.function_name_valid)
1804 memcpy(penv->function_name, ind_msg.function_name,
1805 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1806 else
1807 memset(penv->function_name, 0,
1808 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1809
1810 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1811 penv->cause_for_rejuvenation,
1812 penv->requesting_sub_system,
1813 penv->line_number,
1814 penv->function_name);
1815
1816 penv->stats.rejuvenate_ind++;
1817out:
1818 return ret;
1819}
1820
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001821static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1822{
1823 int ret;
1824 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1825 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1826 struct msg_desc req_desc, resp_desc;
1827
1828 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1829 priv->state);
1830
1831 memset(&req, 0, sizeof(req));
1832 memset(&resp, 0, sizeof(resp));
1833
1834 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1835 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1836 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1837
1838 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1839 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1840 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1841
1842 priv->stats.rejuvenate_ack_req++;
1843 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1844 &resp_desc, &resp, sizeof(resp),
1845 WLFW_TIMEOUT_MS);
1846 if (ret < 0) {
1847 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1848 goto out;
1849 }
1850
1851 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1852 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1853 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301854 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001855 goto out;
1856 }
1857 priv->stats.rejuvenate_ack_resp++;
1858 return 0;
1859
1860out:
1861 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001862 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001863 return ret;
1864}
1865
1866static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1867 uint64_t dynamic_feature_mask)
1868{
1869 int ret;
1870 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1871 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1872 struct msg_desc req_desc, resp_desc;
1873
1874 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1875 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1876 priv->state);
1877 return -EINVAL;
1878 }
1879
1880 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1881 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1882 return 0;
1883 }
1884
1885 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1886 dynamic_feature_mask, priv->state);
1887
1888 memset(&req, 0, sizeof(req));
1889 memset(&resp, 0, sizeof(resp));
1890
1891 req.mask_valid = 1;
1892 req.mask = dynamic_feature_mask;
1893
1894 req_desc.max_msg_len =
1895 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1896 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1897 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1898
1899 resp_desc.max_msg_len =
1900 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1901 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1902 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1903
1904 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1905 &resp_desc, &resp, sizeof(resp),
1906 WLFW_TIMEOUT_MS);
1907 if (ret < 0) {
1908 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1909 goto out;
1910 }
1911
1912 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1913 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1914 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301915 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001916 goto out;
1917 }
1918
1919 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1920 resp.prev_mask_valid, resp.prev_mask,
1921 resp.curr_mask_valid, resp.curr_mask);
1922
1923 return 0;
1924
1925out:
1926 return ret;
1927}
1928
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001929static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1930{
1931 int ret;
1932
1933 if (!penv || !penv->wlfw_clnt)
1934 return;
1935
Yuanyuan Liu68939762017-04-04 16:43:03 -07001936 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001937
1938 do {
1939 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1940
1941 if (ret != -ENOMSG)
1942 icnss_pr_err("Error receiving message: %d\n", ret);
1943
Yuanyuan Liu68939762017-04-04 16:43:03 -07001944 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001945}
1946
1947static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1948 enum qmi_event_type event, void *notify_priv)
1949{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001950 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001951
1952 if (!penv || !penv->wlfw_clnt)
1953 return;
1954
1955 switch (event) {
1956 case QMI_RECV_MSG:
1957 schedule_work(&penv->qmi_recv_msg_work);
1958 break;
1959 default:
1960 icnss_pr_dbg("Unknown Event: %d\n", event);
1961 break;
1962 }
1963}
1964
Yuanyuan Liu68939762017-04-04 16:43:03 -07001965static int icnss_call_driver_uevent(struct icnss_priv *priv,
1966 enum icnss_uevent uevent, void *data)
1967{
1968 struct icnss_uevent_data uevent_data;
1969
1970 if (!priv->ops || !priv->ops->uevent)
1971 return 0;
1972
1973 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
1974 priv->state, uevent);
1975
1976 uevent_data.uevent = uevent;
1977 uevent_data.data = data;
1978
1979 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
1980}
1981
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001982static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
1983 unsigned int msg_id, void *msg,
1984 unsigned int msg_len, void *ind_cb_priv)
1985{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001986 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001987 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001988
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001989 if (!penv)
1990 return;
1991
1992 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
1993
Sameer Thalappil93c00732017-08-18 13:02:32 -07001994 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
1995 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
1996 msg_id, penv->state);
1997 return;
1998 }
1999
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002000 switch (msg_id) {
2001 case QMI_WLFW_FW_READY_IND_V01:
2002 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
2003 0, NULL);
2004 break;
2005 case QMI_WLFW_MSA_READY_IND_V01:
2006 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
2007 msg_id);
2008 penv->stats.msa_ready_ind++;
2009 break;
2010 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
2011 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
2012 msg_id);
2013 icnss_qmi_pin_connect_result_ind(msg, msg_len);
2014 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002015 case QMI_WLFW_REJUVENATE_IND_V01:
2016 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
2017 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002018
2019 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08002020 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002021 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
2022 if (event_data == NULL)
2023 return;
2024 event_data->crashed = true;
2025 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002026 fw_down_data.crashed = true;
2027 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
2028 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002029 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2030 0, event_data);
2031 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002032 default:
2033 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
2034 break;
2035 }
2036}
2037
2038static int icnss_driver_event_server_arrive(void *data)
2039{
2040 int ret = 0;
2041
2042 if (!penv)
2043 return -ENODEV;
2044
2045 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07002046 clear_bit(ICNSS_FW_DOWN, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002047
2048 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
2049 if (!penv->wlfw_clnt) {
2050 icnss_pr_err("QMI client handle create failed\n");
2051 ret = -ENOMEM;
2052 goto out;
2053 }
2054
2055 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2056 WLFW_SERVICE_VERS_V01,
2057 WLFW_SERVICE_INS_ID_V01);
2058 if (ret < 0) {
2059 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2060 goto fail;
2061 }
2062
2063 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2064 icnss_qmi_wlfw_clnt_ind, penv);
2065 if (ret < 0) {
2066 icnss_pr_err("Failed to register indication callback: %d\n",
2067 ret);
2068 goto fail;
2069 }
2070
2071 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2072
2073 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2074
2075 ret = icnss_hw_power_on(penv);
2076 if (ret)
2077 goto fail;
2078
2079 ret = wlfw_ind_register_send_sync_msg();
2080 if (ret < 0)
2081 goto err_power_on;
2082
2083 if (!penv->msa_va) {
2084 icnss_pr_err("Invalid MSA address\n");
2085 ret = -EINVAL;
2086 goto err_power_on;
2087 }
2088
2089 ret = wlfw_msa_mem_info_send_sync_msg();
2090 if (ret < 0)
2091 goto err_power_on;
2092
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302093 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2094 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2095 if (ret < 0)
2096 goto err_power_on;
2097 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2098 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002099
2100 ret = wlfw_msa_ready_send_sync_msg();
2101 if (ret < 0)
2102 goto err_setup_msa;
2103
2104 ret = wlfw_cap_send_sync_msg();
2105 if (ret < 0)
2106 goto err_setup_msa;
2107
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002108 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2109 dynamic_feature_mask);
2110
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002111 icnss_init_vph_monitor(penv);
2112
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002113 icnss_register_force_error_fatal(penv);
2114
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002115 return ret;
2116
2117err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302118 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002119err_power_on:
2120 icnss_hw_power_off(penv);
2121fail:
2122 qmi_handle_destroy(penv->wlfw_clnt);
2123 penv->wlfw_clnt = NULL;
2124out:
2125 ICNSS_ASSERT(0);
2126 return ret;
2127}
2128
2129static int icnss_driver_event_server_exit(void *data)
2130{
2131 if (!penv || !penv->wlfw_clnt)
2132 return -ENODEV;
2133
2134 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2135
2136 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2137 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2138 &penv->vph_monitor_params);
2139
2140 qmi_handle_destroy(penv->wlfw_clnt);
2141
2142 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2143 penv->wlfw_clnt = NULL;
2144
2145 return 0;
2146}
2147
2148static int icnss_call_driver_probe(struct icnss_priv *priv)
2149{
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002150 int ret = 0;
2151 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002152
2153 if (!priv->ops || !priv->ops->probe)
2154 return 0;
2155
Yuanyuan Liu68939762017-04-04 16:43:03 -07002156 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2157 return -EINVAL;
2158
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002159 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2160
2161 icnss_hw_power_on(priv);
2162
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002163 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2164 ret = priv->ops->probe(&priv->pdev->dev);
2165 probe_cnt++;
2166 if (ret != -EPROBE_DEFER)
2167 break;
2168 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002169 if (ret < 0) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002170 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2171 ret, priv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002172 goto out;
2173 }
2174
2175 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2176
2177 return 0;
2178
2179out:
2180 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002181 return ret;
2182}
2183
Yuanyuan Liu68939762017-04-04 16:43:03 -07002184static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2185{
2186 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2187 goto out;
2188
2189 if (!priv->ops || !priv->ops->shutdown)
2190 goto out;
2191
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302192 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2193 goto out;
2194
Yuanyuan Liu68939762017-04-04 16:43:03 -07002195 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2196
2197 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302198 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002199
2200out:
2201 return 0;
2202}
2203
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002204static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002205{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002206 int ret;
2207
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002208 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002209
Anurag Chouhan9de21192017-08-08 15:30:02 +05302210 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002211
2212 clear_bit(ICNSS_PD_RESTART, &priv->state);
2213
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002214 if (!priv->ops || !priv->ops->reinit)
2215 goto out;
2216
Yuanyuan Liu48a51e42017-12-04 16:01:31 -08002217 if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
2218 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2219 priv->state);
2220 goto out;
2221 }
2222
Yuanyuan Liu68939762017-04-04 16:43:03 -07002223 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002224 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002225
2226 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2227
2228 icnss_hw_power_on(priv);
2229
2230 ret = priv->ops->reinit(&priv->pdev->dev);
2231 if (ret < 0) {
2232 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2233 ret, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002234 if (!priv->allow_recursive_recovery)
2235 ICNSS_ASSERT(false);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002236 goto out_power_off;
2237 }
2238
2239out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302240 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002241 return 0;
2242
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002243call_probe:
2244 return icnss_call_driver_probe(priv);
2245
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002246out_power_off:
2247 icnss_hw_power_off(priv);
2248
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002249 return ret;
2250}
2251
2252
2253static int icnss_driver_event_fw_ready_ind(void *data)
2254{
2255 int ret = 0;
2256
2257 if (!penv)
2258 return -ENODEV;
2259
2260 set_bit(ICNSS_FW_READY, &penv->state);
2261
Yuanyuan Liu68939762017-04-04 16:43:03 -07002262 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
2263
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002264 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2265
2266 icnss_hw_power_off(penv);
2267
2268 if (!penv->pdev) {
2269 icnss_pr_err("Device is not ready\n");
2270 ret = -ENODEV;
2271 goto out;
2272 }
2273
2274 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002275 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002276 else
2277 ret = icnss_call_driver_probe(penv);
2278
2279out:
2280 return ret;
2281}
2282
2283static int icnss_driver_event_register_driver(void *data)
2284{
2285 int ret = 0;
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002286 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002287
2288 if (penv->ops)
2289 return -EEXIST;
2290
2291 penv->ops = data;
2292
2293 if (test_bit(SKIP_QMI, &quirks))
2294 set_bit(ICNSS_FW_READY, &penv->state);
2295
Yuanyuan Liu5fa7ec52017-11-30 11:11:42 -08002296 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2297 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2298 penv->state);
2299 return -ENODEV;
2300 }
2301
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002302 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2303 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2304 penv->state);
2305 goto out;
2306 }
2307
2308 ret = icnss_hw_power_on(penv);
2309 if (ret)
2310 goto out;
2311
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002312 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2313 ret = penv->ops->probe(&penv->pdev->dev);
2314 probe_cnt++;
2315 if (ret != -EPROBE_DEFER)
2316 break;
2317 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002318 if (ret) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002319 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2320 ret, penv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002321 goto power_off;
2322 }
2323
2324 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2325
2326 return 0;
2327
2328power_off:
2329 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002330out:
2331 return ret;
2332}
2333
2334static int icnss_driver_event_unregister_driver(void *data)
2335{
2336 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2337 penv->ops = NULL;
2338 goto out;
2339 }
2340
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002341 set_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002342 if (penv->ops)
2343 penv->ops->remove(&penv->pdev->dev);
2344
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002345 clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002346 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2347
2348 penv->ops = NULL;
2349
2350 icnss_hw_power_off(penv);
2351
2352out:
2353 return 0;
2354}
2355
2356static int icnss_call_driver_remove(struct icnss_priv *priv)
2357{
2358 icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
2359
2360 clear_bit(ICNSS_FW_READY, &priv->state);
2361
2362 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2363 return 0;
2364
2365 if (!priv->ops || !priv->ops->remove)
2366 return 0;
2367
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002368 set_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002369 penv->ops->remove(&priv->pdev->dev);
2370
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002371 clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002372 clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002373
2374 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002375
2376 return 0;
2377}
2378
Yuanyuan Liu68939762017-04-04 16:43:03 -07002379static int icnss_fw_crashed(struct icnss_priv *priv,
2380 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002381{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302382 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002383
2384 set_bit(ICNSS_PD_RESTART, &priv->state);
2385 clear_bit(ICNSS_FW_READY, &priv->state);
2386
2387 icnss_pm_stay_awake(priv);
2388
Yuanyuan Liu68939762017-04-04 16:43:03 -07002389 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2390 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002391
Yuanyuan Liu68939762017-04-04 16:43:03 -07002392 if (event_data->fw_rejuvenate)
2393 wlfw_rejuvenate_ack_send_sync_msg(priv);
2394
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002395 return 0;
2396}
2397
2398static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2399 void *data)
2400{
2401 int ret = 0;
2402 struct icnss_event_pd_service_down_data *event_data = data;
2403
2404 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002405 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002406
Sameer Thalappil42ed2cc2017-10-30 11:17:01 -07002407 if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002408 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2409 event_data->crashed, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002410 if (!priv->allow_recursive_recovery)
2411 ICNSS_ASSERT(0);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002412 goto out;
2413 }
2414
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002415 if (priv->force_err_fatal)
2416 ICNSS_ASSERT(0);
2417
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002418 if (event_data->crashed)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002419 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002420 else
2421 icnss_call_driver_remove(priv);
2422
2423out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002424 kfree(data);
2425
Yuanyuan Liu68939762017-04-04 16:43:03 -07002426 icnss_ignore_qmi_timeout(false);
2427
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002428 return ret;
2429}
2430
2431static void icnss_driver_event_work(struct work_struct *work)
2432{
2433 struct icnss_driver_event *event;
2434 unsigned long flags;
2435 int ret;
2436
2437 icnss_pm_stay_awake(penv);
2438
2439 spin_lock_irqsave(&penv->event_lock, flags);
2440
2441 while (!list_empty(&penv->event_list)) {
2442 event = list_first_entry(&penv->event_list,
2443 struct icnss_driver_event, list);
2444 list_del(&event->list);
2445 spin_unlock_irqrestore(&penv->event_lock, flags);
2446
2447 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2448 icnss_driver_event_to_str(event->type),
2449 event->sync ? "-sync" : "", event->type,
2450 penv->state);
2451
2452 switch (event->type) {
2453 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2454 ret = icnss_driver_event_server_arrive(event->data);
2455 break;
2456 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2457 ret = icnss_driver_event_server_exit(event->data);
2458 break;
2459 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2460 ret = icnss_driver_event_fw_ready_ind(event->data);
2461 break;
2462 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2463 ret = icnss_driver_event_register_driver(event->data);
2464 break;
2465 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2466 ret = icnss_driver_event_unregister_driver(event->data);
2467 break;
2468 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2469 ret = icnss_driver_event_pd_service_down(penv,
2470 event->data);
2471 break;
2472 default:
2473 icnss_pr_err("Invalid Event type: %d", event->type);
2474 kfree(event);
2475 continue;
2476 }
2477
2478 penv->stats.events[event->type].processed++;
2479
2480 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2481 icnss_driver_event_to_str(event->type),
2482 event->sync ? "-sync" : "", event->type, ret,
2483 penv->state);
2484
2485 spin_lock_irqsave(&penv->event_lock, flags);
2486 if (event->sync) {
2487 event->ret = ret;
2488 complete(&event->complete);
2489 continue;
2490 }
2491 spin_unlock_irqrestore(&penv->event_lock, flags);
2492
2493 kfree(event);
2494
2495 spin_lock_irqsave(&penv->event_lock, flags);
2496 }
2497 spin_unlock_irqrestore(&penv->event_lock, flags);
2498
2499 icnss_pm_relax(penv);
2500}
2501
2502static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2503 unsigned long code,
2504 void *_cmd)
2505{
2506 int ret = 0;
2507
2508 if (!penv)
2509 return -ENODEV;
2510
2511 icnss_pr_dbg("Event Notify: code: %ld", code);
2512
2513 switch (code) {
2514 case QMI_SERVER_ARRIVE:
2515 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2516 0, NULL);
2517 break;
2518
2519 case QMI_SERVER_EXIT:
2520 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2521 0, NULL);
2522 break;
2523 default:
2524 icnss_pr_dbg("Invalid code: %ld", code);
2525 break;
2526 }
2527 return ret;
2528}
2529
2530static int icnss_msa0_ramdump(struct icnss_priv *priv)
2531{
2532 struct ramdump_segment segment;
2533
2534 memset(&segment, 0, sizeof(segment));
2535 segment.v_address = priv->msa_va;
2536 segment.size = priv->msa_mem_size;
2537 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2538}
2539
2540static struct notifier_block wlfw_clnt_nb = {
2541 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2542};
2543
2544static int icnss_modem_notifier_nb(struct notifier_block *nb,
2545 unsigned long code,
2546 void *data)
2547{
2548 struct icnss_event_pd_service_down_data *event_data;
2549 struct notif_data *notif = data;
2550 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2551 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002552 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302553 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002554
Yuanyuan Liu68939762017-04-04 16:43:03 -07002555 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002556
Sameer Thalappil765cb492017-10-04 17:57:07 -07002557 if (code == SUBSYS_AFTER_SHUTDOWN &&
Yuanyuan Liu52a484b2017-11-15 11:23:45 -08002558 notif->crashed == CRASH_STATUS_ERR_FATAL) {
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302559 ret = icnss_assign_msa_perm_all(priv,
Sameer Thalappil765cb492017-10-04 17:57:07 -07002560 ICNSS_MSA_PERM_HLOS_ALL);
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302561 if (!ret) {
2562 icnss_pr_info("Collecting msa0 segment dump\n");
2563 icnss_msa0_ramdump(priv);
2564 icnss_assign_msa_perm_all(priv,
2565 ICNSS_MSA_PERM_WLAN_HW_RW);
2566 } else {
2567 icnss_pr_err("Not able to Collect msa0 segment dump"
2568 "Apps permissions not assigned %d\n", ret);
2569 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002570 return NOTIFY_OK;
2571 }
2572
2573 if (code != SUBSYS_BEFORE_SHUTDOWN)
2574 return NOTIFY_OK;
2575
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002576 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
2577 set_bit(ICNSS_FW_DOWN, &priv->state);
2578 icnss_ignore_qmi_timeout(true);
2579
2580 fw_down_data.crashed = !!notif->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002581 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2582 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002583 icnss_call_driver_uevent(priv,
2584 ICNSS_UEVENT_FW_DOWN,
2585 &fw_down_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002586 return NOTIFY_OK;
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002587 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002588
Yuanyuan Liu68939762017-04-04 16:43:03 -07002589 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2590 priv->state, notif->crashed);
2591
Sameer Thalappil93c00732017-08-18 13:02:32 -07002592 set_bit(ICNSS_FW_DOWN, &priv->state);
2593
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002594 if (notif->crashed)
2595 priv->stats.recovery.root_pd_crash++;
2596 else
2597 priv->stats.recovery.root_pd_shutdown++;
2598
Yuanyuan Liu68939762017-04-04 16:43:03 -07002599 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002600
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002601 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002602
2603 if (event_data == NULL)
2604 return notifier_from_errno(-ENOMEM);
2605
2606 event_data->crashed = notif->crashed;
2607
Yuanyuan Liu68939762017-04-04 16:43:03 -07002608 fw_down_data.crashed = !!notif->crashed;
2609 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2610
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002611 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2612 ICNSS_EVENT_SYNC, event_data);
2613
2614 return NOTIFY_OK;
2615}
2616
2617static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2618{
2619 int ret = 0;
2620
2621 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2622
2623 priv->modem_notify_handler =
2624 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2625
2626 if (IS_ERR(priv->modem_notify_handler)) {
2627 ret = PTR_ERR(priv->modem_notify_handler);
2628 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2629 }
2630
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002631 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002632
2633 return ret;
2634}
2635
2636static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2637{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002638 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002639 return 0;
2640
2641 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2642 &priv->modem_ssr_nb);
2643 priv->modem_notify_handler = NULL;
2644
2645 return 0;
2646}
2647
2648static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2649{
2650 int i;
2651
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002652 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002653 return 0;
2654
2655 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002656 service_notif_unregister_notifier(
2657 priv->service_notifier[i].handle,
2658 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002659
2660 kfree(priv->service_notifier);
2661
2662 priv->service_notifier = NULL;
2663
2664 return 0;
2665}
2666
2667static int icnss_service_notifier_notify(struct notifier_block *nb,
2668 unsigned long notification, void *data)
2669{
2670 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2671 service_notifier_nb);
2672 enum pd_subsys_state *state = data;
2673 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002674 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002675 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002676
Yuanyuan Liu68939762017-04-04 16:43:03 -07002677 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2678 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002679
Yuanyuan Liu68939762017-04-04 16:43:03 -07002680 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2681 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002682
Yuanyuan Liu68939762017-04-04 16:43:03 -07002683 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002684
Yuanyuan Liu68939762017-04-04 16:43:03 -07002685 if (event_data == NULL)
2686 return notifier_from_errno(-ENOMEM);
2687
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002688 event_data->crashed = true;
2689
Yuanyuan Liu68939762017-04-04 16:43:03 -07002690 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002691 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002692 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002693 }
2694
Yuanyuan Liu68939762017-04-04 16:43:03 -07002695 switch (*state) {
2696 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002697 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002698 break;
2699 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002700 cause = ICNSS_ROOT_PD_SHUTDOWN;
2701 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002702 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002703 break;
2704 case USER_PD_STATE_CHANGE:
2705 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2706 cause = ICNSS_HOST_ERROR;
2707 priv->stats.recovery.pdr_host_error++;
2708 } else {
2709 cause = ICNSS_FW_CRASH;
2710 priv->stats.recovery.pdr_fw_crash++;
2711 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002712 break;
2713 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002714 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002715 break;
2716 }
2717
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002718 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2719 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002720event_post:
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002721 if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
2722 set_bit(ICNSS_FW_DOWN, &priv->state);
2723 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002724
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002725 fw_down_data.crashed = event_data->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002726 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2727 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002728 icnss_call_driver_uevent(priv,
2729 ICNSS_UEVENT_FW_DOWN,
2730 &fw_down_data);
2731 }
2732
2733 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002734 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2735 ICNSS_EVENT_SYNC, event_data);
2736done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002737 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2738 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002739 return NOTIFY_OK;
2740}
2741
2742static int icnss_get_service_location_notify(struct notifier_block *nb,
2743 unsigned long opcode, void *data)
2744{
2745 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2746 get_service_nb);
2747 struct pd_qmi_client_data *pd = data;
2748 int curr_state;
2749 int ret;
2750 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002751 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002752
2753 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2754 priv->state);
2755
2756 if (opcode != LOCATOR_UP)
2757 return NOTIFY_DONE;
2758
2759 if (pd->total_domains == 0) {
2760 icnss_pr_err("Did not find any domains\n");
2761 ret = -ENOENT;
2762 goto out;
2763 }
2764
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002765 notifier = kcalloc(pd->total_domains,
2766 sizeof(struct service_notifier_context),
2767 GFP_KERNEL);
2768 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002769 ret = -ENOMEM;
2770 goto out;
2771 }
2772
2773 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2774
2775 for (i = 0; i < pd->total_domains; i++) {
2776 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2777 pd->domain_list[i].name,
2778 pd->domain_list[i].instance_id);
2779
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002780 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002781 service_notif_register_notifier(pd->domain_list[i].name,
2782 pd->domain_list[i].instance_id,
2783 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002784 notifier[i].instance_id = pd->domain_list[i].instance_id;
2785 strlcpy(notifier[i].name, pd->domain_list[i].name,
2786 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002787
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002788 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002789 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2790 i, pd->domain_list->name,
2791 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002792 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002793 goto free_handle;
2794 }
2795 }
2796
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002797 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002798 priv->total_domains = pd->total_domains;
2799
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002800 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002801
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002802 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2803 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002804
2805 return NOTIFY_OK;
2806
2807free_handle:
2808 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002809 if (notifier[i].handle)
2810 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002811 &priv->service_notifier_nb);
2812 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002813 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002814
2815out:
2816 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2817 priv->state);
2818
2819 return NOTIFY_OK;
2820}
2821
2822
2823static int icnss_pd_restart_enable(struct icnss_priv *priv)
2824{
2825 int ret;
2826
2827 if (test_bit(SSR_ONLY, &quirks)) {
2828 icnss_pr_dbg("PDR disabled through module parameter\n");
2829 return 0;
2830 }
2831
2832 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2833
2834 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2835 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2836 ICNSS_WLAN_SERVICE_NAME,
2837 &priv->get_service_nb);
2838 if (ret) {
2839 icnss_pr_err("Get service location failed: %d\n", ret);
2840 goto out;
2841 }
2842
2843 return 0;
2844out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002845 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002846 return ret;
2847
2848}
2849
2850
2851static int icnss_enable_recovery(struct icnss_priv *priv)
2852{
2853 int ret;
2854
2855 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2856 icnss_pr_dbg("Recovery disabled through module parameter\n");
2857 return 0;
2858 }
2859
2860 if (test_bit(PDR_ONLY, &quirks)) {
2861 icnss_pr_dbg("SSR disabled through module parameter\n");
2862 goto enable_pdr;
2863 }
2864
2865 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2866 &priv->pdev->dev);
2867 if (!priv->msa0_dump_dev)
2868 return -ENOMEM;
2869
2870 icnss_modem_ssr_register_notifier(priv);
2871 if (test_bit(SSR_ONLY, &quirks)) {
2872 icnss_pr_dbg("PDR disabled through module parameter\n");
2873 return 0;
2874 }
2875
2876enable_pdr:
2877 ret = icnss_pd_restart_enable(priv);
2878
2879 if (ret)
2880 return ret;
2881
2882 return 0;
2883}
2884
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302885int __icnss_register_driver(struct icnss_driver_ops *ops,
2886 struct module *owner, const char *mod_name)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002887{
2888 int ret = 0;
2889
2890 if (!penv || !penv->pdev) {
2891 ret = -ENODEV;
2892 goto out;
2893 }
2894
2895 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2896
2897 if (penv->ops) {
2898 icnss_pr_err("Driver already registered\n");
2899 ret = -EEXIST;
2900 goto out;
2901 }
2902
2903 if (!ops->probe || !ops->remove) {
2904 ret = -EINVAL;
2905 goto out;
2906 }
2907
2908 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302909 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002910
2911 if (ret == -EINTR)
2912 ret = 0;
2913
2914out:
2915 return ret;
2916}
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302917EXPORT_SYMBOL(__icnss_register_driver);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002918
2919int icnss_unregister_driver(struct icnss_driver_ops *ops)
2920{
2921 int ret;
2922
2923 if (!penv || !penv->pdev) {
2924 ret = -ENODEV;
2925 goto out;
2926 }
2927
2928 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2929
2930 if (!penv->ops) {
2931 icnss_pr_err("Driver not registered\n");
2932 ret = -ENOENT;
2933 goto out;
2934 }
2935
2936 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2937 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2938out:
2939 return ret;
2940}
2941EXPORT_SYMBOL(icnss_unregister_driver);
2942
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302943int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002944 irqreturn_t (*handler)(int, void *),
2945 unsigned long flags, const char *name, void *ctx)
2946{
2947 int ret = 0;
2948 unsigned int irq;
2949 struct ce_irq_list *irq_entry;
2950
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302951 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002952 ret = -ENODEV;
2953 goto out;
2954 }
2955
Yuanyuan Liu68939762017-04-04 16:43:03 -07002956 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002957
2958 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2959 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
2960 ret = -EINVAL;
2961 goto out;
2962 }
2963 irq = penv->ce_irqs[ce_id];
2964 irq_entry = &penv->ce_irq_list[ce_id];
2965
2966 if (irq_entry->handler || irq_entry->irq) {
2967 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
2968 irq, ce_id);
2969 ret = -EEXIST;
2970 goto out;
2971 }
2972
2973 ret = request_irq(irq, handler, flags, name, ctx);
2974 if (ret) {
2975 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
2976 irq, ce_id, ret);
2977 goto out;
2978 }
2979 irq_entry->irq = irq;
2980 irq_entry->handler = handler;
2981
Yuanyuan Liu68939762017-04-04 16:43:03 -07002982 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002983
2984 penv->stats.ce_irqs[ce_id].request++;
2985out:
2986 return ret;
2987}
2988EXPORT_SYMBOL(icnss_ce_request_irq);
2989
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302990int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002991{
2992 int ret = 0;
2993 unsigned int irq;
2994 struct ce_irq_list *irq_entry;
2995
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302996 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002997 ret = -ENODEV;
2998 goto out;
2999 }
3000
Yuanyuan Liu68939762017-04-04 16:43:03 -07003001 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003002
3003 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3004 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
3005 ret = -EINVAL;
3006 goto out;
3007 }
3008
3009 irq = penv->ce_irqs[ce_id];
3010 irq_entry = &penv->ce_irq_list[ce_id];
3011 if (!irq_entry->handler || !irq_entry->irq) {
3012 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
3013 ret = -EEXIST;
3014 goto out;
3015 }
3016 free_irq(irq, ctx);
3017 irq_entry->irq = 0;
3018 irq_entry->handler = NULL;
3019
3020 penv->stats.ce_irqs[ce_id].free++;
3021out:
3022 return ret;
3023}
3024EXPORT_SYMBOL(icnss_ce_free_irq);
3025
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303026void icnss_enable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003027{
3028 unsigned int irq;
3029
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303030 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003031 icnss_pr_err("Platform driver not initialized\n");
3032 return;
3033 }
3034
Yuanyuan Liu68939762017-04-04 16:43:03 -07003035 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003036 penv->state);
3037
3038 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3039 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
3040 return;
3041 }
3042
3043 penv->stats.ce_irqs[ce_id].enable++;
3044
3045 irq = penv->ce_irqs[ce_id];
3046 enable_irq(irq);
3047}
3048EXPORT_SYMBOL(icnss_enable_irq);
3049
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303050void icnss_disable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003051{
3052 unsigned int irq;
3053
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303054 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003055 icnss_pr_err("Platform driver not initialized\n");
3056 return;
3057 }
3058
Yuanyuan Liu68939762017-04-04 16:43:03 -07003059 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003060 penv->state);
3061
3062 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3063 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
3064 ce_id);
3065 return;
3066 }
3067
3068 irq = penv->ce_irqs[ce_id];
3069 disable_irq(irq);
3070
3071 penv->stats.ce_irqs[ce_id].disable++;
3072}
3073EXPORT_SYMBOL(icnss_disable_irq);
3074
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303075int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003076{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303077 if (!penv || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003078 icnss_pr_err("Platform driver not initialized\n");
3079 return -EINVAL;
3080 }
3081
3082 info->v_addr = penv->mem_base_va;
3083 info->p_addr = penv->mem_base_pa;
3084 info->chip_id = penv->chip_info.chip_id;
3085 info->chip_family = penv->chip_info.chip_family;
3086 info->board_id = penv->board_info.board_id;
3087 info->soc_id = penv->soc_info.soc_id;
3088 info->fw_version = penv->fw_version_info.fw_version;
3089 strlcpy(info->fw_build_timestamp,
3090 penv->fw_version_info.fw_build_timestamp,
3091 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
3092
3093 return 0;
3094}
3095EXPORT_SYMBOL(icnss_get_soc_info);
3096
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303097int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003098{
3099 int ret;
3100
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303101 if (!dev)
3102 return -ENODEV;
3103
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003104 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003105
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003106 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003107 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003108 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3109 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003110 return ret;
3111}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003112EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003113
3114int icnss_athdiag_read(struct device *dev, uint32_t offset,
3115 uint32_t mem_type, uint32_t data_len,
3116 uint8_t *output)
3117{
3118 int ret = 0;
3119 struct icnss_priv *priv = dev_get_drvdata(dev);
3120
3121 if (priv->magic != ICNSS_MAGIC) {
3122 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3123 dev, priv, priv->magic);
3124 return -EINVAL;
3125 }
3126
3127 if (!output || data_len == 0
3128 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3129 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3130 output, data_len);
3131 ret = -EINVAL;
3132 goto out;
3133 }
3134
3135 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3136 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3137 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3138 priv->state);
3139 ret = -EINVAL;
3140 goto out;
3141 }
3142
3143 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3144 data_len, output);
3145out:
3146 return ret;
3147}
3148EXPORT_SYMBOL(icnss_athdiag_read);
3149
3150int icnss_athdiag_write(struct device *dev, uint32_t offset,
3151 uint32_t mem_type, uint32_t data_len,
3152 uint8_t *input)
3153{
3154 int ret = 0;
3155 struct icnss_priv *priv = dev_get_drvdata(dev);
3156
3157 if (priv->magic != ICNSS_MAGIC) {
3158 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3159 dev, priv, priv->magic);
3160 return -EINVAL;
3161 }
3162
3163 if (!input || data_len == 0
3164 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3165 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3166 input, data_len);
3167 ret = -EINVAL;
3168 goto out;
3169 }
3170
3171 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3172 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3173 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3174 priv->state);
3175 ret = -EINVAL;
3176 goto out;
3177 }
3178
3179 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3180 data_len, input);
3181out:
3182 return ret;
3183}
3184EXPORT_SYMBOL(icnss_athdiag_write);
3185
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303186int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003187 enum icnss_driver_mode mode,
3188 const char *host_version)
3189{
3190 struct wlfw_wlan_cfg_req_msg_v01 req;
3191 u32 i;
3192 int ret;
3193
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303194 if (!dev)
3195 return -ENODEV;
3196
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003197 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3198 mode, config, host_version);
3199
3200 memset(&req, 0, sizeof(req));
3201
3202 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3203 goto skip;
3204
3205 if (!config || !host_version) {
3206 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3207 config, host_version);
3208 ret = -EINVAL;
3209 goto out;
3210 }
3211
3212 req.host_version_valid = 1;
3213 strlcpy(req.host_version, host_version,
3214 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3215
3216 req.tgt_cfg_valid = 1;
3217 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3218 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3219 else
3220 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3221 for (i = 0; i < req.tgt_cfg_len; i++) {
3222 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3223 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3224 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3225 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3226 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3227 }
3228
3229 req.svc_cfg_valid = 1;
3230 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3231 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3232 else
3233 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3234 for (i = 0; i < req.svc_cfg_len; i++) {
3235 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3236 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3237 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3238 }
3239
3240 req.shadow_reg_valid = 1;
3241 if (config->num_shadow_reg_cfg >
3242 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3243 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3244 else
3245 req.shadow_reg_len = config->num_shadow_reg_cfg;
3246
3247 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3248 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3249
3250 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3251 if (ret)
3252 goto out;
3253skip:
3254 ret = wlfw_wlan_mode_send_sync_msg(mode);
3255out:
3256 if (test_bit(SKIP_QMI, &quirks))
3257 ret = 0;
3258
3259 return ret;
3260}
3261EXPORT_SYMBOL(icnss_wlan_enable);
3262
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303263int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003264{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303265 if (!dev)
3266 return -ENODEV;
3267
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003268 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3269}
3270EXPORT_SYMBOL(icnss_wlan_disable);
3271
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303272bool icnss_is_qmi_disable(struct device *dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003273{
3274 return test_bit(SKIP_QMI, &quirks) ? true : false;
3275}
3276EXPORT_SYMBOL(icnss_is_qmi_disable);
3277
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303278int icnss_get_ce_id(struct device *dev, int irq)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003279{
3280 int i;
3281
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303282 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003283 return -ENODEV;
3284
3285 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3286 if (penv->ce_irqs[i] == irq)
3287 return i;
3288 }
3289
3290 icnss_pr_err("No matching CE id for irq %d\n", irq);
3291
3292 return -EINVAL;
3293}
3294EXPORT_SYMBOL(icnss_get_ce_id);
3295
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303296int icnss_get_irq(struct device *dev, int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003297{
3298 int irq;
3299
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303300 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003301 return -ENODEV;
3302
3303 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3304 return -EINVAL;
3305
3306 irq = penv->ce_irqs[ce_id];
3307
3308 return irq;
3309}
3310EXPORT_SYMBOL(icnss_get_irq);
3311
3312struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3313{
3314 struct icnss_priv *priv = dev_get_drvdata(dev);
3315
3316 if (!priv) {
3317 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3318 dev, priv);
3319 return NULL;
3320 }
3321
3322 return priv->smmu_mapping;
3323}
3324EXPORT_SYMBOL(icnss_smmu_get_mapping);
3325
3326int icnss_smmu_map(struct device *dev,
3327 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3328{
3329 struct icnss_priv *priv = dev_get_drvdata(dev);
3330 unsigned long iova;
3331 size_t len;
3332 int ret = 0;
3333
3334 if (!priv) {
3335 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3336 dev, priv);
3337 return -EINVAL;
3338 }
3339
3340 if (!iova_addr) {
3341 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3342 &paddr, size);
3343 return -EINVAL;
3344 }
3345
3346 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3347 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3348
3349 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3350 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3351 iova,
3352 &priv->smmu_iova_ipa_start,
3353 priv->smmu_iova_ipa_len);
3354 return -ENOMEM;
3355 }
3356
3357 ret = iommu_map(priv->smmu_mapping->domain, iova,
3358 rounddown(paddr, PAGE_SIZE), len,
3359 IOMMU_READ | IOMMU_WRITE);
3360 if (ret) {
3361 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3362 return ret;
3363 }
3364
3365 priv->smmu_iova_ipa_start = iova + len;
3366 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3367
3368 return 0;
3369}
3370EXPORT_SYMBOL(icnss_smmu_map);
3371
3372unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3373{
3374 return socinfo_get_serial_number();
3375}
3376EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3377
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003378int icnss_trigger_recovery(struct device *dev)
3379{
3380 int ret = 0;
3381 struct icnss_priv *priv = dev_get_drvdata(dev);
3382
3383 if (priv->magic != ICNSS_MAGIC) {
3384 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3385 ret = -EINVAL;
3386 goto out;
3387 }
3388
3389 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3390 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3391 priv->state);
3392 ret = -EPERM;
3393 goto out;
3394 }
3395
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003396 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003397 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3398 priv->state);
3399 ret = -EOPNOTSUPP;
3400 goto out;
3401 }
3402
3403 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3404 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3405 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003406 ret = -EINVAL;
3407 goto out;
3408 }
3409
Yuanyuan Liu68939762017-04-04 16:43:03 -07003410 WARN_ON(1);
3411 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3412 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003413
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003414 /*
3415 * Initiate PDR, required only for the first instance
3416 */
3417 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3418 priv->service_notifier[0].instance_id);
3419
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003420 if (!ret)
3421 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3422
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003423out:
3424 return ret;
3425}
3426EXPORT_SYMBOL(icnss_trigger_recovery);
3427
3428
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003429static int icnss_smmu_init(struct icnss_priv *priv)
3430{
3431 struct dma_iommu_mapping *mapping;
3432 int atomic_ctx = 1;
3433 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303434 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003435 int ret = 0;
3436
3437 icnss_pr_dbg("Initializing SMMU\n");
3438
3439 mapping = arm_iommu_create_mapping(&platform_bus_type,
3440 priv->smmu_iova_start,
3441 priv->smmu_iova_len);
3442 if (IS_ERR(mapping)) {
3443 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3444 ret = PTR_ERR(mapping);
3445 goto map_fail;
3446 }
3447
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303448 if (priv->bypass_s1_smmu) {
3449 ret = iommu_domain_set_attr(mapping->domain,
3450 DOMAIN_ATTR_S1_BYPASS,
3451 &s1_bypass);
3452 if (ret < 0) {
3453 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3454 ret);
3455 goto set_attr_fail;
3456 }
3457 icnss_pr_dbg("SMMU S1 BYPASS\n");
3458 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003459 ret = iommu_domain_set_attr(mapping->domain,
3460 DOMAIN_ATTR_ATOMIC,
3461 &atomic_ctx);
3462 if (ret < 0) {
3463 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3464 ret);
3465 goto set_attr_fail;
3466 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303467 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003468
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303469 ret = iommu_domain_set_attr(mapping->domain,
3470 DOMAIN_ATTR_FAST,
3471 &fast);
3472 if (ret < 0) {
3473 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3474 ret);
3475 goto set_attr_fail;
3476 }
3477 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003478 }
3479
3480 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3481 if (ret < 0) {
3482 icnss_pr_err("Attach device failed, err = %d\n", ret);
3483 goto attach_fail;
3484 }
3485
3486 priv->smmu_mapping = mapping;
3487
3488 return ret;
3489
3490attach_fail:
3491set_attr_fail:
3492 arm_iommu_release_mapping(mapping);
3493map_fail:
3494 return ret;
3495}
3496
3497static void icnss_smmu_deinit(struct icnss_priv *priv)
3498{
3499 if (!priv->smmu_mapping)
3500 return;
3501
3502 arm_iommu_detach_device(&priv->pdev->dev);
3503 arm_iommu_release_mapping(priv->smmu_mapping);
3504
3505 priv->smmu_mapping = NULL;
3506}
3507
Yuanyuan Liu68939762017-04-04 16:43:03 -07003508static int icnss_get_vreg_info(struct device *dev,
3509 struct icnss_vreg_info *vreg_info)
3510{
3511 int ret = 0;
3512 char prop_name[MAX_PROP_SIZE];
3513 struct regulator *reg;
3514 const __be32 *prop;
3515 int len = 0;
3516 int i;
3517
3518 reg = devm_regulator_get_optional(dev, vreg_info->name);
3519 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3520 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3521 vreg_info->name);
3522 ret = PTR_ERR(reg);
3523 goto out;
3524 }
3525
3526 if (IS_ERR(reg)) {
3527 ret = PTR_ERR(reg);
3528
3529 if (vreg_info->required) {
3530 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3531 vreg_info->name, ret);
3532 goto out;
3533 } else {
3534 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3535 vreg_info->name, ret);
3536 goto done;
3537 }
3538 }
3539
3540 vreg_info->reg = reg;
3541
3542 snprintf(prop_name, MAX_PROP_SIZE,
3543 "qcom,%s-config", vreg_info->name);
3544
3545 prop = of_get_property(dev->of_node, prop_name, &len);
3546
3547 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3548 prop_name, len);
3549
3550 if (!prop || len < (2 * sizeof(__be32))) {
3551 icnss_pr_dbg("Property %s %s\n", prop_name,
3552 prop ? "invalid format" : "doesn't exist");
3553 goto done;
3554 }
3555
3556 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3557 switch (i) {
3558 case 0:
3559 vreg_info->min_v = be32_to_cpup(&prop[0]);
3560 break;
3561 case 1:
3562 vreg_info->max_v = be32_to_cpup(&prop[1]);
3563 break;
3564 case 2:
3565 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3566 break;
3567 case 3:
3568 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3569 break;
3570 default:
3571 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3572 prop_name, i);
3573 break;
3574 }
3575 }
3576
3577done:
3578 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3579 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3580 vreg_info->load_ua, vreg_info->settle_delay);
3581
3582 return 0;
3583
3584out:
3585 return ret;
3586}
3587
3588static int icnss_get_clk_info(struct device *dev,
3589 struct icnss_clk_info *clk_info)
3590{
3591 struct clk *handle;
3592 int ret = 0;
3593
3594 handle = devm_clk_get(dev, clk_info->name);
3595 if (IS_ERR(handle)) {
3596 ret = PTR_ERR(handle);
3597 if (clk_info->required) {
3598 icnss_pr_err("Clock %s isn't available: %d\n",
3599 clk_info->name, ret);
3600 goto out;
3601 } else {
3602 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3603 ret);
3604 ret = 0;
3605 goto out;
3606 }
3607 }
3608
3609 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3610
3611 clk_info->handle = handle;
3612out:
3613 return ret;
3614}
3615
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003616static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003617{
3618 struct icnss_priv *priv = s->private;
3619
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003620 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003621
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003622 seq_puts(s, "\nCMD: test_mode\n");
3623 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3624 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3625 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003626 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003627
3628 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3629 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003630
3631 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003632 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003633 goto out;
3634 }
3635
3636 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003637 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003638 goto out;
3639 }
3640
3641 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003642 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003643 goto out;
3644 }
3645
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003646out:
3647 seq_puts(s, "\n");
3648 return 0;
3649}
3650
3651static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3652{
3653 int ret;
3654
3655 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3656 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3657 priv->state);
3658 ret = -ENODEV;
3659 goto out;
3660 }
3661
3662 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3663 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3664 priv->state);
3665 ret = -EINVAL;
3666 goto out;
3667 }
3668
3669 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3670 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3671 priv->state);
3672 ret = -EINVAL;
3673 goto out;
3674 }
3675
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303676 icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003677
3678 ret = icnss_hw_power_off(priv);
3679
3680 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3681
3682out:
3683 return ret;
3684}
3685static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3686 enum icnss_driver_mode mode)
3687{
3688 int ret;
3689
3690 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3691 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3692 priv->state);
3693 ret = -ENODEV;
3694 goto out;
3695 }
3696
3697 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3698 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3699 priv->state);
3700 ret = -EINVAL;
3701 goto out;
3702 }
3703
3704 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3705 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3706 priv->state);
3707 ret = -EBUSY;
3708 goto out;
3709 }
3710
3711 ret = icnss_hw_power_on(priv);
3712 if (ret)
3713 goto out;
3714
3715 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3716
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303717 ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003718 if (ret)
3719 goto power_off;
3720
3721 return 0;
3722
3723power_off:
3724 icnss_hw_power_off(priv);
3725 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3726
3727out:
3728 return ret;
3729}
3730
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003731static void icnss_allow_recursive_recovery(struct device *dev)
3732{
3733 struct icnss_priv *priv = dev_get_drvdata(dev);
3734
3735 priv->allow_recursive_recovery = true;
3736
3737 icnss_pr_info("Recursive recovery allowed for WLAN\n");
3738}
3739
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003740static ssize_t icnss_fw_debug_write(struct file *fp,
3741 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003742 size_t count, loff_t *off)
3743{
3744 struct icnss_priv *priv =
3745 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003746 char buf[64];
3747 char *sptr, *token;
3748 unsigned int len = 0;
3749 char *cmd;
3750 uint64_t val;
3751 const char *delim = " ";
3752 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003753
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003754 len = min(count, sizeof(buf) - 1);
3755 if (copy_from_user(buf, user_buf, len))
3756 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003757
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003758 buf[len] = '\0';
3759 sptr = buf;
3760
3761 token = strsep(&sptr, delim);
3762 if (!token)
3763 return -EINVAL;
3764 if (!sptr)
3765 return -EINVAL;
3766 cmd = token;
3767
3768 token = strsep(&sptr, delim);
3769 if (!token)
3770 return -EINVAL;
3771 if (kstrtou64(token, 0, &val))
3772 return -EINVAL;
3773
3774 if (strcmp(cmd, "test_mode") == 0) {
3775 switch (val) {
3776 case 0:
3777 ret = icnss_test_mode_fw_test_off(priv);
3778 break;
3779 case 1:
3780 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3781 break;
3782 case 2:
3783 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3784 break;
3785 case 3:
3786 ret = icnss_trigger_recovery(&priv->pdev->dev);
3787 break;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003788 case 4:
3789 icnss_allow_recursive_recovery(&priv->pdev->dev);
3790 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003791 default:
3792 return -EINVAL;
3793 }
3794 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3795 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3796 } else {
3797 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003798 }
3799
3800 if (ret)
3801 return ret;
3802
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003803 return count;
3804}
3805
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003806static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003807{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003808 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003809}
3810
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003811static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003812 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003813 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003814 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003815 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003816 .owner = THIS_MODULE,
3817 .llseek = seq_lseek,
3818};
3819
3820static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3821 size_t count, loff_t *off)
3822{
3823 struct icnss_priv *priv =
3824 ((struct seq_file *)fp->private_data)->private;
3825 int ret;
3826 u32 val;
3827
3828 ret = kstrtou32_from_user(buf, count, 0, &val);
3829 if (ret)
3830 return ret;
3831
3832 if (ret == 0)
3833 memset(&priv->stats, 0, sizeof(priv->stats));
3834
3835 return count;
3836}
3837
3838static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3839{
3840 enum icnss_driver_state i;
3841 int skip = 0;
3842 unsigned long state;
3843
3844 seq_printf(s, "\nState: 0x%lx(", priv->state);
3845 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3846
3847 if (!(state & 0x1))
3848 continue;
3849
3850 if (skip++)
3851 seq_puts(s, " | ");
3852
3853 switch (i) {
3854 case ICNSS_WLFW_QMI_CONNECTED:
3855 seq_puts(s, "QMI CONN");
3856 continue;
3857 case ICNSS_POWER_ON:
3858 seq_puts(s, "POWER ON");
3859 continue;
3860 case ICNSS_FW_READY:
3861 seq_puts(s, "FW READY");
3862 continue;
3863 case ICNSS_DRIVER_PROBED:
3864 seq_puts(s, "DRIVER PROBED");
3865 continue;
3866 case ICNSS_FW_TEST_MODE:
3867 seq_puts(s, "FW TEST MODE");
3868 continue;
3869 case ICNSS_PM_SUSPEND:
3870 seq_puts(s, "PM SUSPEND");
3871 continue;
3872 case ICNSS_PM_SUSPEND_NOIRQ:
3873 seq_puts(s, "PM SUSPEND NOIRQ");
3874 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003875 case ICNSS_SSR_REGISTERED:
3876 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003877 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003878 case ICNSS_PDR_REGISTERED:
3879 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003880 continue;
3881 case ICNSS_PD_RESTART:
3882 seq_puts(s, "PD RESTART");
3883 continue;
3884 case ICNSS_MSA0_ASSIGNED:
3885 seq_puts(s, "MSA0 ASSIGNED");
3886 continue;
3887 case ICNSS_WLFW_EXISTS:
3888 seq_puts(s, "WLAN FW EXISTS");
3889 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303890 case ICNSS_SHUTDOWN_DONE:
3891 seq_puts(s, "SHUTDOWN DONE");
3892 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003893 case ICNSS_HOST_TRIGGERED_PDR:
3894 seq_puts(s, "HOST TRIGGERED PDR");
3895 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07003896 case ICNSS_FW_DOWN:
3897 seq_puts(s, "FW DOWN");
3898 continue;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08003899 case ICNSS_DRIVER_UNLOADING:
3900 seq_puts(s, "DRIVER UNLOADING");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003901 }
3902
3903 seq_printf(s, "UNKNOWN-%d", i);
3904 }
3905 seq_puts(s, ")\n");
3906
3907 return 0;
3908}
3909
3910static int icnss_stats_show_capability(struct seq_file *s,
3911 struct icnss_priv *priv)
3912{
3913 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3914 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3915 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3916 seq_printf(s, "Chip family: 0x%x\n",
3917 priv->chip_info.chip_family);
3918 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3919 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3920 seq_printf(s, "Firmware Version: 0x%x\n",
3921 priv->fw_version_info.fw_version);
3922 seq_printf(s, "Firmware Build Timestamp: %s\n",
3923 priv->fw_version_info.fw_build_timestamp);
3924 seq_printf(s, "Firmware Build ID: %s\n",
3925 priv->fw_build_id);
3926 }
3927
3928 return 0;
3929}
3930
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003931static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
3932 struct icnss_priv *priv)
3933{
3934 if (priv->stats.rejuvenate_ind) {
3935 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
3936 seq_printf(s, "Number of Rejuvenations: %u\n",
3937 priv->stats.rejuvenate_ind);
3938 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
3939 priv->cause_for_rejuvenation);
3940 seq_printf(s, "Requesting Sub-System: 0x%x\n",
3941 priv->requesting_sub_system);
3942 seq_printf(s, "Line Number: %u\n",
3943 priv->line_number);
3944 seq_printf(s, "Function Name: %s\n",
3945 priv->function_name);
3946 }
3947
3948 return 0;
3949}
3950
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003951static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
3952{
3953 int i;
3954
3955 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
3956 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
3957 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
3958 seq_printf(s, "%24s %16u %16u\n",
3959 icnss_driver_event_to_str(i),
3960 priv->stats.events[i].posted,
3961 priv->stats.events[i].processed);
3962
3963 return 0;
3964}
3965
3966static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
3967{
3968 int i;
3969
3970 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
3971 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
3972 "Free", "Enable", "Disable");
3973 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
3974 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
3975 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
3976 priv->stats.ce_irqs[i].free,
3977 priv->stats.ce_irqs[i].enable,
3978 priv->stats.ce_irqs[i].disable);
3979
3980 return 0;
3981}
3982
3983static int icnss_stats_show(struct seq_file *s, void *data)
3984{
3985#define ICNSS_STATS_DUMP(_s, _priv, _x) \
3986 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
3987
3988 struct icnss_priv *priv = s->private;
3989
3990 ICNSS_STATS_DUMP(s, priv, ind_register_req);
3991 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
3992 ICNSS_STATS_DUMP(s, priv, ind_register_err);
3993 ICNSS_STATS_DUMP(s, priv, msa_info_req);
3994 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
3995 ICNSS_STATS_DUMP(s, priv, msa_info_err);
3996 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
3997 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
3998 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
3999 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
4000 ICNSS_STATS_DUMP(s, priv, cap_req);
4001 ICNSS_STATS_DUMP(s, priv, cap_resp);
4002 ICNSS_STATS_DUMP(s, priv, cap_err);
4003 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
4004 ICNSS_STATS_DUMP(s, priv, cfg_req);
4005 ICNSS_STATS_DUMP(s, priv, cfg_resp);
4006 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
4007 ICNSS_STATS_DUMP(s, priv, mode_req);
4008 ICNSS_STATS_DUMP(s, priv, mode_resp);
4009 ICNSS_STATS_DUMP(s, priv, mode_req_err);
4010 ICNSS_STATS_DUMP(s, priv, ini_req);
4011 ICNSS_STATS_DUMP(s, priv, ini_resp);
4012 ICNSS_STATS_DUMP(s, priv, ini_req_err);
4013 ICNSS_STATS_DUMP(s, priv, vbatt_req);
4014 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
4015 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004016 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004017 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
4018 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
4019 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07004020 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
4021 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
4022 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
4023 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004024
4025 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
4026 ICNSS_STATS_DUMP(s, priv, pm_suspend);
4027 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
4028 ICNSS_STATS_DUMP(s, priv, pm_resume);
4029 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
4030 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
4031 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
4032 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
4033 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
4034 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
4035 ICNSS_STATS_DUMP(s, priv, pm_relax);
4036
4037 icnss_stats_show_irqs(s, priv);
4038
4039 icnss_stats_show_capability(s, priv);
4040
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004041 icnss_stats_show_rejuvenate_info(s, priv);
4042
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004043 icnss_stats_show_events(s, priv);
4044
4045 icnss_stats_show_state(s, priv);
4046
4047 return 0;
4048#undef ICNSS_STATS_DUMP
4049}
4050
4051static int icnss_stats_open(struct inode *inode, struct file *file)
4052{
4053 return single_open(file, icnss_stats_show, inode->i_private);
4054}
4055
4056static const struct file_operations icnss_stats_fops = {
4057 .read = seq_read,
4058 .write = icnss_stats_write,
4059 .release = single_release,
4060 .open = icnss_stats_open,
4061 .owner = THIS_MODULE,
4062 .llseek = seq_lseek,
4063};
4064
4065static int icnss_regwrite_show(struct seq_file *s, void *data)
4066{
4067 struct icnss_priv *priv = s->private;
4068
4069 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4070
4071 if (!test_bit(ICNSS_FW_READY, &priv->state))
4072 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4073
4074 return 0;
4075}
4076
4077static ssize_t icnss_regwrite_write(struct file *fp,
4078 const char __user *user_buf,
4079 size_t count, loff_t *off)
4080{
4081 struct icnss_priv *priv =
4082 ((struct seq_file *)fp->private_data)->private;
4083 char buf[64];
4084 char *sptr, *token;
4085 unsigned int len = 0;
4086 uint32_t reg_offset, mem_type, reg_val;
4087 const char *delim = " ";
4088 int ret = 0;
4089
4090 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4091 !test_bit(ICNSS_POWER_ON, &priv->state))
4092 return -EINVAL;
4093
4094 len = min(count, sizeof(buf) - 1);
4095 if (copy_from_user(buf, user_buf, len))
4096 return -EFAULT;
4097
4098 buf[len] = '\0';
4099 sptr = buf;
4100
4101 token = strsep(&sptr, delim);
4102 if (!token)
4103 return -EINVAL;
4104
4105 if (!sptr)
4106 return -EINVAL;
4107
4108 if (kstrtou32(token, 0, &mem_type))
4109 return -EINVAL;
4110
4111 token = strsep(&sptr, delim);
4112 if (!token)
4113 return -EINVAL;
4114
4115 if (!sptr)
4116 return -EINVAL;
4117
4118 if (kstrtou32(token, 0, &reg_offset))
4119 return -EINVAL;
4120
4121 token = strsep(&sptr, delim);
4122 if (!token)
4123 return -EINVAL;
4124
4125 if (kstrtou32(token, 0, &reg_val))
4126 return -EINVAL;
4127
4128 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4129 sizeof(uint32_t),
4130 (uint8_t *)&reg_val);
4131 if (ret)
4132 return ret;
4133
4134 return count;
4135}
4136
4137static int icnss_regwrite_open(struct inode *inode, struct file *file)
4138{
4139 return single_open(file, icnss_regwrite_show, inode->i_private);
4140}
4141
4142static const struct file_operations icnss_regwrite_fops = {
4143 .read = seq_read,
4144 .write = icnss_regwrite_write,
4145 .open = icnss_regwrite_open,
4146 .owner = THIS_MODULE,
4147 .llseek = seq_lseek,
4148};
4149
4150static int icnss_regread_show(struct seq_file *s, void *data)
4151{
4152 struct icnss_priv *priv = s->private;
4153
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304154 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004155 if (!priv->diag_reg_read_buf) {
4156 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4157
4158 if (!test_bit(ICNSS_FW_READY, &priv->state))
4159 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4160
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304161 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004162 return 0;
4163 }
4164
4165 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4166 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4167 priv->diag_reg_read_len);
4168
4169 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4170 priv->diag_reg_read_len, false);
4171
4172 priv->diag_reg_read_len = 0;
4173 kfree(priv->diag_reg_read_buf);
4174 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304175 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004176
4177 return 0;
4178}
4179
4180static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4181 size_t count, loff_t *off)
4182{
4183 struct icnss_priv *priv =
4184 ((struct seq_file *)fp->private_data)->private;
4185 char buf[64];
4186 char *sptr, *token;
4187 unsigned int len = 0;
4188 uint32_t reg_offset, mem_type;
4189 uint32_t data_len = 0;
4190 uint8_t *reg_buf = NULL;
4191 const char *delim = " ";
4192 int ret = 0;
4193
4194 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4195 !test_bit(ICNSS_POWER_ON, &priv->state))
4196 return -EINVAL;
4197
4198 len = min(count, sizeof(buf) - 1);
4199 if (copy_from_user(buf, user_buf, len))
4200 return -EFAULT;
4201
4202 buf[len] = '\0';
4203 sptr = buf;
4204
4205 token = strsep(&sptr, delim);
4206 if (!token)
4207 return -EINVAL;
4208
4209 if (!sptr)
4210 return -EINVAL;
4211
4212 if (kstrtou32(token, 0, &mem_type))
4213 return -EINVAL;
4214
4215 token = strsep(&sptr, delim);
4216 if (!token)
4217 return -EINVAL;
4218
4219 if (!sptr)
4220 return -EINVAL;
4221
4222 if (kstrtou32(token, 0, &reg_offset))
4223 return -EINVAL;
4224
4225 token = strsep(&sptr, delim);
4226 if (!token)
4227 return -EINVAL;
4228
4229 if (kstrtou32(token, 0, &data_len))
4230 return -EINVAL;
4231
4232 if (data_len == 0 ||
4233 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4234 return -EINVAL;
4235
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304236 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004237 kfree(priv->diag_reg_read_buf);
4238 priv->diag_reg_read_buf = NULL;
4239
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004240 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304241 if (!reg_buf) {
4242 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004243 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304244 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004245
4246 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4247 mem_type, data_len,
4248 reg_buf);
4249 if (ret) {
4250 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304251 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004252 return ret;
4253 }
4254
4255 priv->diag_reg_read_addr = reg_offset;
4256 priv->diag_reg_read_mem_type = mem_type;
4257 priv->diag_reg_read_len = data_len;
4258 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304259 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004260
4261 return count;
4262}
4263
4264static int icnss_regread_open(struct inode *inode, struct file *file)
4265{
4266 return single_open(file, icnss_regread_show, inode->i_private);
4267}
4268
4269static const struct file_operations icnss_regread_fops = {
4270 .read = seq_read,
4271 .write = icnss_regread_write,
4272 .open = icnss_regread_open,
4273 .owner = THIS_MODULE,
4274 .llseek = seq_lseek,
4275};
4276
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004277#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004278static int icnss_debugfs_create(struct icnss_priv *priv)
4279{
4280 int ret = 0;
4281 struct dentry *root_dentry;
4282
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004283 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004284
4285 if (IS_ERR(root_dentry)) {
4286 ret = PTR_ERR(root_dentry);
4287 icnss_pr_err("Unable to create debugfs %d\n", ret);
4288 goto out;
4289 }
4290
4291 priv->root_dentry = root_dentry;
4292
Yuanyuan Liu84132752017-05-24 12:07:12 -07004293 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004294 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004295
Yuanyuan Liu84132752017-05-24 12:07:12 -07004296 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004297 &icnss_stats_fops);
4298 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4299 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004300 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004301 &icnss_regwrite_fops);
4302
4303out:
4304 return ret;
4305}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004306#else
4307static int icnss_debugfs_create(struct icnss_priv *priv)
4308{
4309 int ret = 0;
4310 struct dentry *root_dentry;
4311
4312 root_dentry = debugfs_create_dir("icnss", NULL);
4313
4314 if (IS_ERR(root_dentry)) {
4315 ret = PTR_ERR(root_dentry);
4316 icnss_pr_err("Unable to create debugfs %d\n", ret);
4317 return ret;
4318 }
4319
4320 priv->root_dentry = root_dentry;
4321
4322 debugfs_create_file("stats", 0600, root_dentry, priv,
4323 &icnss_stats_fops);
4324 return 0;
4325}
4326#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004327
4328static void icnss_debugfs_destroy(struct icnss_priv *priv)
4329{
4330 debugfs_remove_recursive(priv->root_dentry);
4331}
4332
4333static int icnss_get_vbatt_info(struct icnss_priv *priv)
4334{
4335 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4336 struct qpnp_vadc_chip *vadc_dev = NULL;
4337 int ret = 0;
4338
4339 if (test_bit(VBATT_DISABLE, &quirks)) {
4340 icnss_pr_dbg("VBATT feature is disabled\n");
4341 return ret;
4342 }
4343
4344 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4345 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4346 icnss_pr_err("adc_tm_dev probe defer\n");
4347 return -EPROBE_DEFER;
4348 }
4349
4350 if (IS_ERR(adc_tm_dev)) {
4351 ret = PTR_ERR(adc_tm_dev);
4352 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4353 ret);
4354 return ret;
4355 }
4356
4357 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4358 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4359 icnss_pr_err("vadc_dev probe defer\n");
4360 return -EPROBE_DEFER;
4361 }
4362
4363 if (IS_ERR(vadc_dev)) {
4364 ret = PTR_ERR(vadc_dev);
4365 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4366 ret);
4367 return ret;
4368 }
4369
4370 priv->adc_tm_dev = adc_tm_dev;
4371 priv->vadc_dev = vadc_dev;
4372
4373 return 0;
4374}
4375
4376static int icnss_probe(struct platform_device *pdev)
4377{
4378 int ret = 0;
4379 struct resource *res;
4380 int i;
4381 struct device *dev = &pdev->dev;
4382 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304383 const __be32 *addrp;
4384 u64 prop_size = 0;
4385 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004386
4387 if (penv) {
4388 icnss_pr_err("Driver is already initialized\n");
4389 return -EEXIST;
4390 }
4391
4392 icnss_pr_dbg("Platform driver probe\n");
4393
4394 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4395 if (!priv)
4396 return -ENOMEM;
4397
4398 priv->magic = ICNSS_MAGIC;
4399 dev_set_drvdata(dev, priv);
4400
4401 priv->pdev = pdev;
4402
4403 ret = icnss_get_vbatt_info(priv);
4404 if (ret == -EPROBE_DEFER)
4405 goto out;
4406
Yuanyuan Liu68939762017-04-04 16:43:03 -07004407 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4408 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4409 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4410
4411 if (ret)
4412 goto out;
4413 }
4414
4415 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4416 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4417 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4418 if (ret)
4419 goto out;
4420 }
4421
4422 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4423 priv->bypass_s1_smmu = true;
4424
4425 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4426
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004427 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4428 if (!res) {
4429 icnss_pr_err("Memory base not found in DT\n");
4430 ret = -EINVAL;
4431 goto out;
4432 }
4433
4434 priv->mem_base_pa = res->start;
4435 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4436 resource_size(res));
4437 if (!priv->mem_base_va) {
4438 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4439 &priv->mem_base_pa);
4440 ret = -EINVAL;
4441 goto out;
4442 }
4443 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4444 priv->mem_base_va);
4445
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004446 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4447 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4448 if (!res) {
4449 icnss_pr_err("Fail to get IRQ-%d\n", i);
4450 ret = -ENODEV;
4451 goto out;
4452 } else {
4453 priv->ce_irqs[i] = res->start;
4454 }
4455 }
4456
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304457 np = of_parse_phandle(dev->of_node,
4458 "qcom,wlan-msa-fixed-region", 0);
4459 if (np) {
4460 addrp = of_get_address(np, 0, &prop_size, NULL);
4461 if (!addrp) {
4462 icnss_pr_err("Failed to get assigned-addresses or property\n");
4463 ret = -EINVAL;
4464 goto out;
4465 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004466
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304467 priv->msa_pa = of_translate_address(np, addrp);
4468 if (priv->msa_pa == OF_BAD_ADDR) {
4469 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4470 ret = -EINVAL;
4471 goto out;
4472 }
4473
4474 priv->msa_va = memremap(priv->msa_pa,
4475 (unsigned long)prop_size, MEMREMAP_WT);
4476 if (!priv->msa_va) {
4477 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4478 &priv->msa_pa);
4479 ret = -EINVAL;
4480 goto out;
4481 }
4482 priv->msa_mem_size = prop_size;
4483 } else {
4484 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4485 &priv->msa_mem_size);
4486 if (ret || priv->msa_mem_size == 0) {
4487 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4488 priv->msa_mem_size, ret);
4489 goto out;
4490 }
4491
4492 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4493 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4494
4495 if (!priv->msa_va) {
4496 icnss_pr_err("DMA alloc failed for MSA\n");
4497 ret = -ENOMEM;
4498 goto out;
4499 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004500 }
4501
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304502 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4503 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004504
4505 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4506 "smmu_iova_base");
4507 if (!res) {
4508 icnss_pr_err("SMMU IOVA base not found\n");
4509 } else {
4510 priv->smmu_iova_start = res->start;
4511 priv->smmu_iova_len = resource_size(res);
4512 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4513 &priv->smmu_iova_start, priv->smmu_iova_len);
4514
4515 res = platform_get_resource_byname(pdev,
4516 IORESOURCE_MEM,
4517 "smmu_iova_ipa");
4518 if (!res) {
4519 icnss_pr_err("SMMU IOVA IPA not found\n");
4520 } else {
4521 priv->smmu_iova_ipa_start = res->start;
4522 priv->smmu_iova_ipa_len = resource_size(res);
4523 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4524 &priv->smmu_iova_ipa_start,
4525 priv->smmu_iova_ipa_len);
4526 }
4527
4528 ret = icnss_smmu_init(priv);
4529 if (ret < 0) {
4530 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4531 ret, &priv->smmu_iova_start,
4532 priv->smmu_iova_len);
4533 goto out;
4534 }
4535 }
4536
4537 spin_lock_init(&priv->event_lock);
4538 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304539 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004540
4541 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4542 if (!priv->event_wq) {
4543 icnss_pr_err("Workqueue creation failed\n");
4544 ret = -EFAULT;
4545 goto out_smmu_deinit;
4546 }
4547
4548 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4549 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4550 INIT_LIST_HEAD(&priv->event_list);
4551
4552 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4553 WLFW_SERVICE_VERS_V01,
4554 WLFW_SERVICE_INS_ID_V01,
4555 &wlfw_clnt_nb);
4556 if (ret < 0) {
4557 icnss_pr_err("Notifier register failed: %d\n", ret);
4558 goto out_destroy_wq;
4559 }
4560
4561 icnss_enable_recovery(priv);
4562
4563 icnss_debugfs_create(priv);
4564
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004565 ret = device_init_wakeup(&priv->pdev->dev, true);
4566 if (ret)
4567 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4568 ret);
4569
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004570 penv = priv;
4571
4572 icnss_pr_info("Platform driver probed successfully\n");
4573
4574 return 0;
4575
4576out_destroy_wq:
4577 destroy_workqueue(priv->event_wq);
4578out_smmu_deinit:
4579 icnss_smmu_deinit(priv);
4580out:
4581 dev_set_drvdata(dev, NULL);
4582
4583 return ret;
4584}
4585
4586static int icnss_remove(struct platform_device *pdev)
4587{
4588 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4589
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004590 device_init_wakeup(&penv->pdev->dev, false);
4591
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004592 icnss_debugfs_destroy(penv);
4593
4594 icnss_modem_ssr_unregister_notifier(penv);
4595
4596 destroy_ramdump_device(penv->msa0_dump_dev);
4597
4598 icnss_pdr_unregister_notifier(penv);
4599
4600 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4601 WLFW_SERVICE_VERS_V01,
4602 WLFW_SERVICE_INS_ID_V01,
4603 &wlfw_clnt_nb);
4604 if (penv->event_wq)
4605 destroy_workqueue(penv->event_wq);
4606
4607 icnss_hw_power_off(penv);
4608
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304609 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4610 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004611
4612 dev_set_drvdata(&pdev->dev, NULL);
4613
4614 return 0;
4615}
4616
4617#ifdef CONFIG_PM_SLEEP
4618static int icnss_pm_suspend(struct device *dev)
4619{
4620 struct icnss_priv *priv = dev_get_drvdata(dev);
4621 int ret = 0;
4622
4623 if (priv->magic != ICNSS_MAGIC) {
4624 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4625 dev, priv, priv->magic);
4626 return -EINVAL;
4627 }
4628
Yuanyuan Liu68939762017-04-04 16:43:03 -07004629 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004630
4631 if (!priv->ops || !priv->ops->pm_suspend ||
4632 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4633 goto out;
4634
4635 ret = priv->ops->pm_suspend(dev);
4636
4637out:
4638 if (ret == 0) {
4639 priv->stats.pm_suspend++;
4640 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4641 } else {
4642 priv->stats.pm_suspend_err++;
4643 }
4644 return ret;
4645}
4646
4647static int icnss_pm_resume(struct device *dev)
4648{
4649 struct icnss_priv *priv = dev_get_drvdata(dev);
4650 int ret = 0;
4651
4652 if (priv->magic != ICNSS_MAGIC) {
4653 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4654 dev, priv, priv->magic);
4655 return -EINVAL;
4656 }
4657
Yuanyuan Liu68939762017-04-04 16:43:03 -07004658 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004659
4660 if (!priv->ops || !priv->ops->pm_resume ||
4661 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4662 goto out;
4663
4664 ret = priv->ops->pm_resume(dev);
4665
4666out:
4667 if (ret == 0) {
4668 priv->stats.pm_resume++;
4669 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4670 } else {
4671 priv->stats.pm_resume_err++;
4672 }
4673 return ret;
4674}
4675
4676static int icnss_pm_suspend_noirq(struct device *dev)
4677{
4678 struct icnss_priv *priv = dev_get_drvdata(dev);
4679 int ret = 0;
4680
4681 if (priv->magic != ICNSS_MAGIC) {
4682 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4683 dev, priv, priv->magic);
4684 return -EINVAL;
4685 }
4686
Yuanyuan Liu68939762017-04-04 16:43:03 -07004687 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004688
4689 if (!priv->ops || !priv->ops->suspend_noirq ||
4690 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4691 goto out;
4692
4693 ret = priv->ops->suspend_noirq(dev);
4694
4695out:
4696 if (ret == 0) {
4697 priv->stats.pm_suspend_noirq++;
4698 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4699 } else {
4700 priv->stats.pm_suspend_noirq_err++;
4701 }
4702 return ret;
4703}
4704
4705static int icnss_pm_resume_noirq(struct device *dev)
4706{
4707 struct icnss_priv *priv = dev_get_drvdata(dev);
4708 int ret = 0;
4709
4710 if (priv->magic != ICNSS_MAGIC) {
4711 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4712 dev, priv, priv->magic);
4713 return -EINVAL;
4714 }
4715
Yuanyuan Liu68939762017-04-04 16:43:03 -07004716 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004717
4718 if (!priv->ops || !priv->ops->resume_noirq ||
4719 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4720 goto out;
4721
4722 ret = priv->ops->resume_noirq(dev);
4723
4724out:
4725 if (ret == 0) {
4726 priv->stats.pm_resume_noirq++;
4727 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4728 } else {
4729 priv->stats.pm_resume_noirq_err++;
4730 }
4731 return ret;
4732}
4733#endif
4734
4735static const struct dev_pm_ops icnss_pm_ops = {
4736 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4737 icnss_pm_resume)
4738 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4739 icnss_pm_resume_noirq)
4740};
4741
4742static const struct of_device_id icnss_dt_match[] = {
4743 {.compatible = "qcom,icnss"},
4744 {}
4745};
4746
4747MODULE_DEVICE_TABLE(of, icnss_dt_match);
4748
4749static struct platform_driver icnss_driver = {
4750 .probe = icnss_probe,
4751 .remove = icnss_remove,
4752 .driver = {
4753 .name = "icnss",
4754 .pm = &icnss_pm_ops,
4755 .owner = THIS_MODULE,
4756 .of_match_table = icnss_dt_match,
4757 },
4758};
4759
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004760static int __init icnss_initialize(void)
4761{
4762 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4763 "icnss", 0);
4764 if (!icnss_ipc_log_context)
4765 icnss_pr_err("Unable to create log context\n");
4766
Yuanyuan Liu68939762017-04-04 16:43:03 -07004767 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4768 "icnss_long", 0);
4769 if (!icnss_ipc_log_long_context)
4770 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004771
4772 return platform_driver_register(&icnss_driver);
4773}
4774
4775static void __exit icnss_exit(void)
4776{
4777 platform_driver_unregister(&icnss_driver);
4778 ipc_log_context_destroy(icnss_ipc_log_context);
4779 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004780 ipc_log_context_destroy(icnss_ipc_log_long_context);
4781 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004782}
4783
4784
4785module_init(icnss_initialize);
4786module_exit(icnss_exit);
4787
4788MODULE_LICENSE("GPL v2");
4789MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");